C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Relocation in C++

From: Sébastien Bini <sebastien.bini_at_[hidden]>
Date: Wed, 21 Dec 2022 17:02:10 +0100
@Edward Catmur <ecatmur_at_[hidden]>
> I agree with the need for the attribute, but wouldn't it be better to
attach it
> to the relocating constructor?
>
> class T { public: T(T) [[no_parameter_reloc]]; };
>
> That way code that wants to be backwards compatible has fewer places
where a
> feature-test macro check is required.

Could be placed on the constructor itself as well, but then you need to
declare the constructor which you may not want to (rule of zero). See
std::array.

@Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
> Have you tried "manually implementing" this idea, by showing some simple
> examples of `T(T)` and then showing the machine code generated from them?
> Assume that nothing is inlined or optimized — so you're not allowed to
"cheat"
> by eliding any operations — just show exactly how the `T(T)` constructor
would
> be codegenned, and then what a call to it would look like, and so on.

Not for the moment. I'm still struggling to have time to work on this. But
writing an implementation is one of the next things to do on the topic.

@Edward Catmur <ecatmur_at_[hidden]>
> Yes, that's another option. Really, it's up to the implementation what
they want to do about ABI:
>
> * full break, use callee-destroy for all relocatable types (for those who
don't
> care about ABI)
> * break with opt-out - an attribute to mark opt-out, and propagation
mechanisms
> (these could be standardized at a later date)
> * no break, but opt-in - an improved [[trivial_abi]] that actually checks
that
> the type is trivially relocatable
> * no break, use callee-destroy only for relocate-only types.
> * and those that are callee-destroy already don't need to do anything!
>
> And more or less orthogonal to that is whether to use trivial ABI for
> trivially-relocatable nontrivial types passed as callee-destroy, or to
always
> pass such types on the stack. An implementation could also choose
something more
> complex such as passing trivially-relocatable types with trivial ABI, but
> non-relocate-only nontrivially-relocatable types as caller-destroy.
>
> Given that there are so many options (and maybe more that I've missed),
maybe it
> would be best just to list them as a sign that we've considered ABI as an
issue.
> If one of them gets settled on across the current caller-destroy
platforms (are
> there any other than Itanium?), that can be standardized (along with
propagation
> mechanisms, if appropriate) at a later date.

Yes, I agree. I'm going to mention the different ABI approaches, and only
mandate that functions that take a relocate-only parameter by value are
callee-destroy. If implementers need a standardized approach to make the
ABI evolve to callee-destroy, that can be in a separate paper.

Note that, even with callee-destroy being enforced only for relocate-only
types, it might still cause ABI breaks:

do_something(std::lock_guard<std::mutex> guard);
// [...]
void foo()
{
  std::mutex m;
  std::jthread t1([&] { do_something(std::lock_guard{m}); });
  std::jthread t2([&] { do_something(std::lock_guard{m}); });
}

This code compiles fine on my gcc, and works fine even. Should we be
tempted to make std::lock_guard relocate-only, it would imply a potential
ABI break on such functions... How often is this seen in real code? Not
often I hope...
We could simply resolve this issue by stating that no relocation
constructor will be added to lock_guard, but that does not fix more
pernicious cases, like functions with signature: void
foo(non_null<unique_ptr<int>>); non_null and unique_ptr will get a
relocation ctor, and then non_null<unique_ptr> will be automatically
relocate-only, potentially changing the ABI of foo.

I don't know to what extent we can simply ditch those cases by saying they
are bad code (really, who would pass a non_null<unique_ptr> by value
today?), and the ABI break is the price to pay for writing such bad code in
the first place. Maybe an ABI break on such functions is acceptable. I
doubt any major libraries provide such APIs?

Regards,
Sébastien

On Wed, Dec 21, 2022 at 10:29 AM Sébastien Bini <sebastien.bini_at_[hidden]>
wrote:

> On Wed, Dec 21, 2022 at 9:52 AM Ville Voutilainen <
> ville.voutilainen_at_[hidden]> wrote:
>
>> On Wed, 21 Dec 2022 at 10:31, Edward Catmur <ecatmur_at_[hidden]>
>> wrote:
>> >> So, rather than have a destructive move where such a semantic
>> >> restriction would be in place, do you prefer not having destructive
>> >> move at all?
>> >
>> > That seems to me a false dichotomy; there is no reason whatsoever why
>> we couldn't have both. As noted elsewhere, the only types for which
>> callee-destroy is an absolute necessity are those which are relocate-only,
>> which currently cannot be passed by value at all, so if implementations
>> choose they can retain ABI compatibility for all class types which can
>> currently be passed by value, even when adding a relocating constructor,
>> trivial or otherwise.
>>
>> I don't know whether it's a dichotomy, I'm merely trying to figure out
>> which pieces of this cake are up for bargaining.
>> I grok the practical need to relocate a container just fine, it's far
>> less clear to me why I need
>> to relocate a reloc-only type, or even introduce the concept of a
>> reloc-only type. What happens if I reloc-move an object and
>> then try to access it? Is that yet another source of UB? I spy with my
>> little eye that if the semantic restriction I ruminate on there
>> is present, that source of UB isn't necessarily introduced.
>>
>
> relocate-only types are already a thing, but are unusable. Consider for
> instance non_null<unique_ptr>. There are also user-defined types that today
> implement move-semantics but have an unnatural empty state. If they don't
> implement move-semantics, then they are often wrapped around an optional or
> unique_ptr, so the wrapper handles the moved-from state for them. Those
> types will be candidates to be relocate-only.
>
> Relocation will happen with the new reloc keyword (or new series of
> symbols if we don't want a keyword). That keyword will mark the end of
> scope of the relocated object, so that it cannot be reused after being
> relocated. The program is ill-formed if a relocated object is reused. This
> is not perfect as we only track the relocated object, and not all the
> potential references to it that still exist. But those issues are inherent
> to the language, as you can already have references on destroyed objects in
> C++.
>
>

Received on 2022-12-21 16:02:24