C++ Logo

std-proposals

Advanced search

[std-proposals] Monitor recursion by checking the frame pointer

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Mon, 17 Apr 2023 12:09:25 +0100
Lately I've been messing around trying to write assembler thunks for
invoking lambdas without the hidden 'this' paramater, and I had to
consider the complications of multi-threading, and also of recursive
function calls.

Let me stick to single-threaded programs for now. I will use the term
'caller' to describe a recursive function which contains a lambda,
so something like:

    extern void SomeLibraryFunc( void(*)(void) );

    void Func(int const arg)
    {
        if ( -1 == arg ) return;

        auto mylambda = [arg](void)->void { /* do something */ };

        SomeLibraryFunc( thunk(mylambda) );

        Func(arg - 1);
    }

In order to write an efficient implementation of the thunk generator to
accommodate recursive calls, I was thinking it would be cool if I could
somehow get the thunk to correctly identify whether the caller has
been re-entered since the thunk was generated. Then I realised that this
could be ascertained from the stack pointer (i.e. the RSP register on
x86_64, or the x31sp register on aarch64). As a function is executed
though, it might push and pop and push and pop, and so the stack pointer
would be going up and down by 8 bytes here and there, it would be
fluctuating a little. But if the frame pointer is used instead (i.e. the
RBP register on x86_64, or the x29FP register on aarch64), then it could
be used to reliably check if re-entry has occurred.

What if the Standard library were to have:

    namespace std {
        void *frame_pointer(void) noexcept;
        void *stack_pointer(void) noexcept;
    }

The contents of "frame_and_stack_pointer.S" on x86_64 could be:

        frame_pointer:
            mov rax,rbp
            ret
        stack_pointer:
            mov rax,rsp
            add rax,8 ; disregard the return address on top of stack
            ret

Also maybe it would be helpful if the return address were available too,
something like:

    namespace std {
        void (*return_address(void))(void) noexcept;
    }

The contents of "return_address.S" on x86_64:

        return_address:
            mov rax,[rsp] ; return address is at top of stack
            ret

Nine times out of ten, the return address will just be the 8 bytes at
the top of the stack, but it won't be if the function has supernumerary
parameters or a very big return type, so the compiler would have to
be a bit smarter.

If the function "std::frame_pointer" is invoked inside a function that
was compiled with a compiler flag to prevent the use of a frame pointer
(e.g. '-fomit-frame-pointer' on the GNU g++ compiler), or if it is a very
simply function that doesn't use the stack and so doesn't bother with
a frame pointer, then it should return a nullptr.

Received on 2023-04-17 11:09:37