I'm not sure I follow all of your comments.

For the rest my comments, let's assume that the compiler may assume that __move_post_blt is a no-op if hasElaborateMove returns false.


On 17/05/18 14:33, kinke wrote:
3. When deciding to move a struct instance, the compiler MUST emit a call to the struct's __move_post_blt after blitting the instance and before releasing the memory containing the old instance. __move_post_blt MUST receive references to both the pre- and post-move instances.

This implies that such structs must not be considered PODs, i.e., cannot be passed in registers and must be passed on the stack.

I'm not familiar with passing structs in registers. I am familiar with passing pointer to the structs in registers, which should not be affected by this.

If actual (I'm assuming short) structs can be passed in registers, then, yes, structs with elaborate move are not PoDs.

It also means that the compiler will have to insert a __move_post_blt call right before the call (as the callee has no idea about the old address),

Again, as far as I know, structs are not copied when passed as arguments. They are allocated on the caller's stack and a reference is passed to the callee. If that's the case, no move (of any kind) is done.

I might be missing something. Can you write a code snippet to which you are referring?

after blitting the arg to the callee params stack; this may be tricky to implement for LDC, as that last blit is implicit in LLVM IR (LLVM byval attribute).

And yet, C++ clang managed to do it in classes with && constructors.

As a side note, when passing a postblit-struct lvalue arg by value,

Just to be clear, are we talking about passing by reference an instance of a struct that has postblit?

the compiler first copies the lvalue


to a temporary on the caller's stack, incl. postblit call, and then moves that copy to the callee. So this requires either a postblit+postmove combo on the caller side before the actual call, or a single postblit call for the final address (callee's param).

Again, that does not align with my familiarity of the ABI. If I do:

struct S {
  ...

  this(this) {
    // some code
  }

  void opPostMove(ref S new, const ref S old) {
    // some code
  }
}

void func1(ref S arg) {
}

void func2(S arg) {
}


As far as I know the ABI, in func1, a pointer to S is passed. In func2, a pointer to caller stack allocate instance is passed, and the original is copied in. It sounds to me like you are talking about the case of:

S s;

func2(s);

in which case you need to copy s to a temporary, and then pass a pointer to that temporary to func2. I don't see where a move enters here.

However, if a move does enter here (and one is necessary if, for example, func2's frame needs to be dynamically allocated because an escaping delegate references it), then, yes, an opPostMove will also need to be called.

Again, if hasElaborateMove returns false, then no change from current behavior is needed.

Shachar

Reply via email to