C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Copy-construct, move-construct, and PR-construct

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Wed, 23 Aug 2023 14:43:58 +0100
On Wed, Aug 23, 2023 at 2:11 PM Sarah Kernighan wrote:
>
> Could it be used for any function? Or is it exclusively for constructor and assignment operators?


For the time being I'm just talking about constructors and the
assignment operator. Maybe in the future we can discuss other uses but
let's keep it simple for now. I'm going to focus on the assignment
operator in this post, and my implementation will be for System V
x86_64 (which is used by every x86_64 operating system except for
MS-Windows).

Here is some sample code that makes use of a PR-assignment operator:

    extern string SomeFunc(void);
    extern mutex FuncThatReturnsMutex(int, int, int, int, string const &);

    optional<mutex> om;

    void Work(void)
    {
        om = FuncThatReturnsMutex( 1, 2, 3, 4, SomeFunc() );
    }

    int main(void)
    {
        Work();
    }

So inside the function "Work", the very first thing that will happen
is that the 5 arguments will be evaluated, meaning that 'SomeFunc'
will be called. After the arguments have been evaluated, they'll be
pushed onto the stack right-to-left, with the address of the allocated
space for the return value put in RDI, and the address of the thunk
put in RSI, so the assembler for 'Work' will look something like as
follows:

Work:
    sub rsp, 32 // Allocate space on stack for string
    mov rdi, rsp // Set return value address for SomeFunc
    call SomeFunc // Returns a string by value
    push rsp // Push 5th argument onto the stack
    push 4 // Push 4th argument onto the stack
    push 3 // Push 3rd argument onto the stack
    push 2 // Push 2nd argument onto the stack
    push 1 // Push 1st argument onto the stack
    mov rdi, myglobal // Set the 'this' pointer to address
of 'myglobal' in RDI
    mov rsi, thunk // Put the address of thunk in RSI
    call optional::operator=(T^^)
    add rsp, 40 // Pop 5 arguments off the stack (5*8 = 40)
    mov rdi, rsp // Set the 'this' pointer to the
address of the string
    call string::~string // Destroy the string
    add rsp, 32 // Deallocate stack space for the string
    ret

The C++ code for the PR-assignment operator is as follows:

    optional &operator=(T ^^arg) : buf(arg)
    {
        this->reset();

        __emplace;

        this->bool_has_value = true;

        return *this;
    }

Note the use of the '__emplace' keyword which is where you tell the
compiler that it's now time to generate the PRvalue. The PRvalue is
generated by a thunk.

The purpose of the thunk is to copy all the arguments from the stack
to the parameter-passing registers (i.e. rdi,rsi,rdx,rcx,r8,r9) and
then to invoke 'FuncThatReturnsMutex' as follows:

thunk:
    // rdi should contain the address of allocated space for mutex
    mov rsi, [rsp+24] // Put 1st argument in register
    mov rdx, [rsp+32] // Put 2nd argument in register
    mov rcx, [rsp+40] // Put 3rd argument in register
    mov r8, [rsp+48] // Put 4th argument in register
    mov r9, [rsp+56] // Put 5th argument in register
    push r12
    call FuncThatReturnsMutex // Invoke the function that returns mutex
    pop r12
    ret

The thunk will be invoked by optional::operator=(T^^), the assembler
for which will be something like:

optional::operator=(T^^):
    // rdi should contain the 'this' pointer
    push rdi // Save to restore later
    call optional::reset
    mov rdi, [rsp] // Restore RDI after call to reset
    call thunk // Call the 'thunk' function
    pop rdi // Restore RDI after call to thunk
    mov byte ptr [rdi+40], 1 // bool_has_value = true
    mov rax, rdi // return *this
    ret

I've put this all together and gotten it working up on GodBolt:

        https://godbolt.org/z/fMrGjoWGs

I really think we could do with having PR-construction and
PR-assignment in C++26.

Received on 2023-08-23 13:44:10