Date: Wed, 23 Aug 2023 09:32:45 -0500
On Wed, 23 Aug 2023 at 08:44, Frederick Virchanza Gotham via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> 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.
>
There were proposals for destructive move / non-trivial relocation
presented and discussed at Varna (as well as trivial relocation, which
seems easier).
std-proposals_at_[hidden]> wrote:
> 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.
>
There were proposals for destructive move / non-trivial relocation
presented and discussed at Varna (as well as trivial relocation, which
seems easier).
Received on 2023-08-23 14:32:59