On Tue, 23 Aug 2022 at 12:40, Sébastien Bini <sebastien.bini@gmail.com> wrote:
On Mon, Aug 22, 2022 at 10:08 PM Edward Catmur <ecatmur@googlemail.com> wrote:
On Mon, 22 Aug 2022 at 14:36, Sébastien Bini <sebastien.bini@gmail.com> wrote:
I'm in favor of the default operator to be aliased. However this is not enough as if the assignment operator of each subclass is not aliased, then you may still end-up making that many copies (or relocations).

I believe we do need to provide a way to clearly annotate the assignment operator as aliased (that reloc keyword comes in). Also, it would serve for assignment operators that are defaulted in the implementation file only.

I contend that it's enough to mandate aliasing for relocatable-by-ABI types. Note that this doesn't affect ABI in that the callee still does the same amount of work; the difference is that the caller is required to observe that the source and target cannot alias and that therefore the parameter can alias the source instead of having to be a relocated temporary.

If I understand correctly, you suggest that aliasing be mandated only for types that have a relocation constructor and have not opted-out of the ABI break? I like this idea, it further avoids another keyword in the operator declaration.

But what about trivial types, which follow the rule of zero?

struct S { int _d; S& operator=(S rhs) { _d = rhs._d; return *this; }  };

They will get a relocation constructor for free, even if none of their subobjects explicitly provides a relocation constructor. Had they declared a prvalue-assignment operator, then it would change its ABI silently. It's okay for them to silently go from caller-destroy to callee-destroy as their destructor is a no-op, but aliasing will also force the parameter to share the same address as the caller's site source object, which is a silent ABI break.

It's not always an ABI break.  On all 64-bit ABIs that I'm aware of (see https://www.agner.org/optimize/calling_conventions.pdf), the caller is responsible for allocating and cleaning up stack space used by oversized / non-trivial objects, so the callee doesn't need to know whether aliasing is occurring.

On the other hand, it is an ABI break on most 32-bit ABIs.  But your example struct doesn't even follow the rule of zero (once relocating assignment becomes a special member function), so maybe it would be OK if aliasing doesn't occur in that specific case.  In fact, it's quite possible that the presence of a user-declared relocating assignment operator would suppress the relocating constructor, so it definitely wouldn't be a candidate for aliasing?