Skip to content

Commit

Permalink
setjmp: add custom setjmp for clang/android
Browse files Browse the repository at this point in the history
Based on code from valgrind.
  • Loading branch information
stsp committed Dec 21, 2024
1 parent e6f456a commit 71ebac2
Showing 1 changed file with 111 additions and 1 deletion.
112 changes: 111 additions & 1 deletion include/setjmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,133 @@ namespace std {

#ifndef __dj_ENFORCE_ANSI_FREESTANDING

#if defined(__clang__) && defined(__aarch64__) && defined(__ANDROID__)
#define CUSTOM_SETJMP 1
#else
#define CUSTOM_SETJMP 0
#endif

#if CUSTOM_SETJMP
typedef unsigned long long int minimal_jmp_buf[14];
#else
typedef void *minimal_jmp_buf[5];
#endif

typedef struct __jmp_buf {
void *buf[5];
minimal_jmp_buf buf;
volatile int rc;
} jmp_buf[1];

#if CUSTOM_SETJMP
__attribute__((returns_twice,always_inline))
static inline int do_setjmp(minimal_jmp_buf _env)
{
int ret;
asm volatile(
// x9 is the first of the regular temporary registers
// per the above-mentioned Procedule Call Standard document.
// Use it as temporary to hold the value of SP, since str does
// not accept SP as operand.
" mov x9, sp \n"
" mov x10, _out \n"
// Store the general-purpose registers that we need to save
// per the above discussion.
// Note that x30 is the link register.
" stp x19, x20, [%[_env], 0] \n"
" stp x21, x22, [%[_env], 0x10] \n"
" stp x23, x24, [%[_env], 0x20] \n"
" stp x25, x26, [%[_env], 0x30] \n"
" stp x27, x28, [%[_env], 0x40] \n"
" stp x29, x30, [%[_env], 0x50] \n"
// Store the value of SP,PC.
" stp x9, x10, [%[_env], 0x60] \n"
" mov x0, 0 \n"
"_out: \n"
" mov %[ret], x0 \n"
:
// Outputs
[ret]"=r"(ret)
:
// Inputs
[_env]"r"(_env)
:
// Clobbers.
// We have used x9 as a temporary
"x0","x1","x2","x3","x4","x5","x6","x7","x8","x9","x10",
"x11","x12","x13","x14","x15","x16","x17","x18",
// We have written to memory locations
"memory");

// Direct invokation of setjmp always returns 0.
// The pseudo returning of the value 1 as a return from longjmp
// is implemented in longjmp.
return ret;
}

__attribute__((noreturn))
static inline void do_longjmp(minimal_jmp_buf _env)
{
asm volatile(
// Loads to match the stores in the above setjmp implementation.
" ldp x19, x20, [%[_env], 0] \n"
" ldp x21, x22, [%[_env], 0x10] \n"
" ldp x23, x24, [%[_env], 0x20] \n"
" ldp x25, x26, [%[_env], 0x30] \n"
" ldp x27, x28, [%[_env], 0x40] \n"
" ldp x29, x30, [%[_env], 0x50] \n"
" ldp x9, x10, [%[_env], 0x60] \n"
" mov sp, x9 \n"
// return as setjmp for the second time, returning 1.
// Since we have loaded the link register x30 from the saved buffer
// that was set by the above setjmp implementation, the 'ret' instruction
// here is going to return to where setjmp was called.
// Per the setjmp/longjmp contract, that pseudo second returning
// of setjmp should return the value 1.
// x0 is holds the integer return value.
" mov x0, 1 \n"
" br x10 \n"
:
// No outputs
:
// Inputs
[_env]"r"(_env)
:
// Clobbers.
// We have used x9 as a temporary
"x0","x9","x10");

// This statement is unreachable because the above asm statement
// unconditionally does a 'ret' instruction. The purpose of this
// statement is to silence clang warnings about this function returning
// while having the 'noreturn' attribute.
__builtin_unreachable();
}
#endif

__attribute__((noreturn))
static inline void longjmp(jmp_buf env, int val)
{
env[0].rc = val;
#if CUSTOM_SETJMP
do_longjmp(env[0].buf);
#else
__builtin_longjmp(env[0].buf, 1);
#endif
}

#if CUSTOM_SETJMP
#define setjmp(env) ( \
{ \
int _rc = do_setjmp(env[0].buf); \
(_rc ? env[0].rc : 0); \
})
#else
#define setjmp(env) ( \
{ \
int _rc = __builtin_setjmp(env[0].buf); \
(_rc ? env[0].rc : 0); \
})
#endif

#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) \
|| !defined(__STRICT_ANSI__) || defined(__cplusplus)
Expand Down

0 comments on commit 71ebac2

Please sign in to comment.