diff --git a/libc/config/linux/riscv64/entrypoints.txt b/libc/config/linux/riscv64/entrypoints.txt --- a/libc/config/linux/riscv64/entrypoints.txt +++ b/libc/config/linux/riscv64/entrypoints.txt @@ -371,6 +371,10 @@ # sched.h entrypoints libc.src.sched.__sched_getcpucount + # setjmp.h entrypoints + libc.src.setjmp.longjmp + libc.src.setjmp.setjmp + # stdio.h entrypoints libc.src.stdio.clearerr libc.src.stdio.clearerr_unlocked diff --git a/libc/config/linux/riscv64/headers.txt b/libc/config/linux/riscv64/headers.txt --- a/libc/config/linux/riscv64/headers.txt +++ b/libc/config/linux/riscv64/headers.txt @@ -10,6 +10,7 @@ libc.include.sched libc.include.signal libc.include.spawn + libc.include.setjmp libc.include.stdio libc.include.stdlib libc.include.string diff --git a/libc/include/llvm-libc-types/jmp_buf.h b/libc/include/llvm-libc-types/jmp_buf.h --- a/libc/include/llvm-libc-types/jmp_buf.h +++ b/libc/include/llvm-libc-types/jmp_buf.h @@ -19,6 +19,19 @@ __UINT64_TYPE__ r15; __UINTPTR_TYPE__ rsp; __UINTPTR_TYPE__ rip; +#elif defined(__riscv) + /* Program counter. */ + long int __pc; + /* Callee-saved registers. */ + long int __regs[12]; + /* Stack pointer. */ + long int __sp; + /* Callee-saved floating point registers. */ +#if __riscv_float_abi_double + double __fpregs[12]; +#elif defined(__riscv_float_abi_single) +#error "__jmp_buf not available for your target architecture." +#endif #else #error "__jmp_buf not available for your target architecture." #endif diff --git a/libc/src/setjmp/CMakeLists.txt b/libc/src/setjmp/CMakeLists.txt --- a/libc/src/setjmp/CMakeLists.txt +++ b/libc/src/setjmp/CMakeLists.txt @@ -1,3 +1,10 @@ +if(LIBC_TARGET_ARCHITECTURE_IS_RISCV64) + set(FRAME_POINTER -fomit-frame-pointer) +else() + # The implementation assumes frame pointer on to the stack + set(FRAME_POINTER -fno-omit-frame-pointer) +endif() + add_entrypoint_object( setjmp SRCS @@ -6,7 +13,7 @@ setjmp_impl.h COMPILE_OPTIONS -O3 # We do not want any local variables in setjmp - -fno-omit-frame-pointer # The implementation assumes frame pointer on to the stack + "${FRAME_POINTER}" DEPENDS libc.include.setjmp ) diff --git a/libc/src/setjmp/longjmp.cpp b/libc/src/setjmp/longjmp.cpp --- a/libc/src/setjmp/longjmp.cpp +++ b/libc/src/setjmp/longjmp.cpp @@ -6,41 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "src/setjmp/longjmp.h" -#include "src/__support/common.h" #include "src/__support/macros/properties/architectures.h" -#include - -namespace __llvm_libc { - -LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) { -#ifdef LIBC_TARGET_ARCH_IS_X86_64 - register __UINT64_TYPE__ rbx __asm__("rbx"); - register __UINT64_TYPE__ rbp __asm__("rbp"); - register __UINT64_TYPE__ r12 __asm__("r12"); - register __UINT64_TYPE__ r13 __asm__("r13"); - register __UINT64_TYPE__ r14 __asm__("r14"); - register __UINT64_TYPE__ r15 __asm__("r15"); - register __UINT64_TYPE__ rsp __asm__("rsp"); - register __UINT64_TYPE__ rax __asm__("rax"); - - // ABI requires that the return value should be stored in rax. So, we store - // |val| in rax. Note that this has to happen before we restore the registers - // from values in |buf|. Otherwise, once rsp and rbp are updated, we cannot - // read |val|. - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rax) : "m"(val) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rbx) : "m"(buf->rbx) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rbp) : "m"(buf->rbp) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r12) : "m"(buf->r12) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r13) : "m"(buf->r13) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r14) : "m"(buf->r14) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r15) : "m"(buf->r15) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rsp) : "m"(buf->rsp) :); - LIBC_INLINE_ASM("jmp *%0\n\t" : : "m"(buf->rip)); -#else // LIBC_TARGET_ARCH_IS_X86_64 +#if defined(LIBC_TARGET_ARCH_IS_X86_64) +#include "x86_64/longjmp.h" +#elif defined(LIBC_TARGET_ARCH_IS_RISCV64) +#include "riscv64/longjmp.h" +#else #error "longjmp implementation not available for the target architecture." #endif -} - -} // namespace __llvm_libc diff --git a/libc/src/setjmp/riscv64/longjmp.h b/libc/src/setjmp/riscv64/longjmp.h new file mode 100644 --- /dev/null +++ b/libc/src/setjmp/riscv64/longjmp.h @@ -0,0 +1,61 @@ +//===-- Implementation of longjmp -----------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/common.h" +#include "src/__support/macros/properties/architectures.h" +#include "src/setjmp/longjmp.h" + +#include + +#if !defined(LIBC_TARGET_ARCH_IS_RISCV64) +#error "Invalid file include" +#endif + +namespace __llvm_libc { + +LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" + LIBC_INLINE_ASM("ld ra, %0\n\t" : "=m"(buf->__pc)); + LIBC_INLINE_ASM("ld s0, %0\n\t" : "=m"(buf->__regs[0])); + LIBC_INLINE_ASM("ld s1, %0\n\t" : "=m"(buf->__regs[1])); + LIBC_INLINE_ASM("ld s2, %0\n\t" : "=m"(buf->__regs[2])); + LIBC_INLINE_ASM("ld s3, %0\n\t" : "=m"(buf->__regs[3])); + LIBC_INLINE_ASM("ld s4, %0\n\t" : "=m"(buf->__regs[4])); + LIBC_INLINE_ASM("ld s5, %0\n\t" : "=m"(buf->__regs[5])); + LIBC_INLINE_ASM("ld s6, %0\n\t" : "=m"(buf->__regs[6])); + LIBC_INLINE_ASM("ld s7, %0\n\t" : "=m"(buf->__regs[7])); + LIBC_INLINE_ASM("ld s8, %0\n\t" : "=m"(buf->__regs[8])); + LIBC_INLINE_ASM("ld s9, %0\n\t" : "=m"(buf->__regs[9])); + LIBC_INLINE_ASM("ld s10, %0\n\t" : "=m"(buf->__regs[10])); + LIBC_INLINE_ASM("ld s11, %0\n\t" : "=m"(buf->__regs[11])); + LIBC_INLINE_ASM("ld sp, %0\n\t" : "=m"(buf->__sp)); + +#if __riscv_float_abi_double + LIBC_INLINE_ASM("fld fs0, %0\n\t" : "=m"(buf->__fpregs[0]) :); + LIBC_INLINE_ASM("fld fs1, %0\n\t" : "=m"(buf->__fpregs[1]) :); + LIBC_INLINE_ASM("fld fs2, %0\n\t" : "=m"(buf->__fpregs[2]) :); + LIBC_INLINE_ASM("fld fs3, %0\n\t" : "=m"(buf->__fpregs[3]) :); + LIBC_INLINE_ASM("fld fs4, %0\n\t" : "=m"(buf->__fpregs[4]) :); + LIBC_INLINE_ASM("fld fs5, %0\n\t" : "=m"(buf->__fpregs[5]) :); + LIBC_INLINE_ASM("fld fs6, %0\n\t" : "=m"(buf->__fpregs[6]) :); + LIBC_INLINE_ASM("fld fs7, %0\n\t" : "=m"(buf->__fpregs[7]) :); + LIBC_INLINE_ASM("fld fs8, %0\n\t" : "=m"(buf->__fpregs[8]) :); + LIBC_INLINE_ASM("fld fs9, %0\n\t" : "=m"(buf->__fpregs[9]) :); + LIBC_INLINE_ASM("fld fs10, %0\n\t" : "=m"(buf->__fpregs[10]) :); + LIBC_INLINE_ASM("fld fs11, %0\n\t" : "=m"(buf->__fpregs[11])); +#elif defined(__riscv_float_abi_single) +#error "longjmp implementation not available for the target architecture." +#endif + + LIBC_INLINE_ASM("seqz %0, %1" : "+r"(buf) : "r"(val) :); + LIBC_INLINE_ASM("add %0, %0, %1" : "+r"(buf) : "r"(val), "r"(buf) :); +#pragma GCC diagnostic pop +} + +} // namespace __llvm_libc diff --git a/libc/src/setjmp/riscv64/setjmp.h b/libc/src/setjmp/riscv64/setjmp.h new file mode 100644 --- /dev/null +++ b/libc/src/setjmp/riscv64/setjmp.h @@ -0,0 +1,62 @@ +//===-- Implementation of setjmp ------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/common.h" +#include "src/setjmp/setjmp_impl.h" + +#include + +#if !defined(LIBC_TARGET_ARCH_IS_RISCV64) +#error "Invalid file include" +#endif + +namespace __llvm_libc { + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" +#pragma GCC diagnostic ignored "-Wreturn-type" + +LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) { + LIBC_INLINE_ASM("sd ra, %0\n\t" : "=m"(buf->__pc)); + LIBC_INLINE_ASM("sd s0, %0\n\t" : "=m"(buf->__regs[0])); + LIBC_INLINE_ASM("sd s1, %0\n\t" : "=m"(buf->__regs[1])); + LIBC_INLINE_ASM("sd s2, %0\n\t" : "=m"(buf->__regs[2])); + LIBC_INLINE_ASM("sd s3, %0\n\t" : "=m"(buf->__regs[3])); + LIBC_INLINE_ASM("sd s4, %0\n\t" : "=m"(buf->__regs[4])); + LIBC_INLINE_ASM("sd s5, %0\n\t" : "=m"(buf->__regs[5])); + LIBC_INLINE_ASM("sd s6, %0\n\t" : "=m"(buf->__regs[6])); + LIBC_INLINE_ASM("sd s7, %0\n\t" : "=m"(buf->__regs[7])); + LIBC_INLINE_ASM("sd s8, %0\n\t" : "=m"(buf->__regs[8])); + LIBC_INLINE_ASM("sd s9, %0\n\t" : "=m"(buf->__regs[9])); + LIBC_INLINE_ASM("sd s10, %0\n\t" : "=m"(buf->__regs[10])); + LIBC_INLINE_ASM("sd s11, %0\n\t" : "=m"(buf->__regs[11])); + LIBC_INLINE_ASM("sd sp, %0\n\t" : "=m"(buf->__sp)); + +#if __riscv_float_abi_double + LIBC_INLINE_ASM("fsd fs0, %0\n\t" : "=m"(buf->__fpregs[0]) :); + LIBC_INLINE_ASM("fsd fs1, %0\n\t" : "=m"(buf->__fpregs[1]) :); + LIBC_INLINE_ASM("fsd fs2, %0\n\t" : "=m"(buf->__fpregs[2]) :); + LIBC_INLINE_ASM("fsd fs3, %0\n\t" : "=m"(buf->__fpregs[3]) :); + LIBC_INLINE_ASM("fsd fs4, %0\n\t" : "=m"(buf->__fpregs[4]) :); + LIBC_INLINE_ASM("fsd fs5, %0\n\t" : "=m"(buf->__fpregs[5]) :); + LIBC_INLINE_ASM("fsd fs6, %0\n\t" : "=m"(buf->__fpregs[6]) :); + LIBC_INLINE_ASM("fsd fs7, %0\n\t" : "=m"(buf->__fpregs[7]) :); + LIBC_INLINE_ASM("fsd fs8, %0\n\t" : "=m"(buf->__fpregs[8]) :); + LIBC_INLINE_ASM("fsd fs9, %0\n\t" : "=m"(buf->__fpregs[9]) :); + LIBC_INLINE_ASM("fsd fs10, %0\n\t" : "=m"(buf->__fpregs[10]) :); + LIBC_INLINE_ASM("fsd fs11, %0\n\t" : "=m"(buf->__fpregs[11])); +#elif defined(__riscv_float_abi_single) +#error "setjmp implementation not available for the target architecture." +#endif + + LIBC_INLINE_ASM("li %0, 0" : "+r"(buf) : :); + LIBC_INLINE_ASM("ret" : : :); +} +#pragma GCC diagnostic pop + +} // namespace __llvm_libc diff --git a/libc/src/setjmp/setjmp.cpp b/libc/src/setjmp/setjmp.cpp --- a/libc/src/setjmp/setjmp.cpp +++ b/libc/src/setjmp/setjmp.cpp @@ -6,55 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/common.h" #include "src/__support/macros/properties/architectures.h" -#include "src/setjmp/setjmp_impl.h" -#include - -namespace __llvm_libc { - -LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) { -#ifdef LIBC_TARGET_ARCH_IS_X86_64 - register __UINT64_TYPE__ rbx __asm__("rbx"); - register __UINT64_TYPE__ r12 __asm__("r12"); - register __UINT64_TYPE__ r13 __asm__("r13"); - register __UINT64_TYPE__ r14 __asm__("r14"); - register __UINT64_TYPE__ r15 __asm__("r15"); - - // We want to store the register values as is. So, we will suppress the - // compiler warnings about the uninitialized variables declared above. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wuninitialized" - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->rbx) : "r"(rbx) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r12) : "r"(r12) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r13) : "r"(r13) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r14) : "r"(r14) :); - LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r15) : "r"(r15) :); -#pragma GCC diagnostic pop - - // We want the rbp of the caller, which is what __builtin_frame_address(1) - // should return. But, compilers generate a warning that calling - // __builtin_frame_address with non-zero argument is unsafe. So, we use - // the knowledge of the x86_64 ABI to fetch the callers rbp. As per the ABI, - // the rbp of the caller is pushed on to the stack and then new top is saved - // in this function's rbp. So, we fetch it from location at which this - // functions's rbp is pointing. - buf->rbp = *reinterpret_cast<__UINTPTR_TYPE__ *>(__builtin_frame_address(0)); - - // The callers stack address is exactly 2 pointer widths ahead of the current - // frame pointer - between the current frame pointer and the rsp of the caller - // are the return address (pushed by the x86_64 call instruction) and the - // previous stack pointer as required by the x86_64 ABI. - // The stack pointer is ahead because the stack grows down on x86_64. - buf->rsp = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_frame_address(0)) + - sizeof(__UINTPTR_TYPE__) * 2; - buf->rip = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_return_address(0)); -#else // LIBC_TARGET_ARCH_IS_X86_64 +#if defined(LIBC_TARGET_ARCH_IS_X86_64) +#include "x86_64/setjmp.h" +#elif defined(LIBC_TARGET_ARCH_IS_RISCV64) +#include "riscv64/setjmp.h" +#else #error "setjmp implementation not available for the target architecture." #endif - - return 0; -} - -} // namespace __llvm_libc diff --git a/libc/src/setjmp/longjmp.cpp b/libc/src/setjmp/x86_64/longjmp.h copy from libc/src/setjmp/longjmp.cpp copy to libc/src/setjmp/x86_64/longjmp.h --- a/libc/src/setjmp/longjmp.cpp +++ b/libc/src/setjmp/x86_64/longjmp.h @@ -6,16 +6,18 @@ // //===----------------------------------------------------------------------===// -#include "src/setjmp/longjmp.h" #include "src/__support/common.h" -#include "src/__support/macros/properties/architectures.h" +#include "src/setjmp/longjmp.h" #include +#if !defined(LIBC_TARGET_ARCH_IS_X86_64) +#error "Invalid file include" +#endif + namespace __llvm_libc { LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) { -#ifdef LIBC_TARGET_ARCH_IS_X86_64 register __UINT64_TYPE__ rbx __asm__("rbx"); register __UINT64_TYPE__ rbp __asm__("rbp"); register __UINT64_TYPE__ r12 __asm__("r12"); @@ -38,9 +40,7 @@ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r15) : "m"(buf->r15) :); LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rsp) : "m"(buf->rsp) :); LIBC_INLINE_ASM("jmp *%0\n\t" : : "m"(buf->rip)); -#else // LIBC_TARGET_ARCH_IS_X86_64 -#error "longjmp implementation not available for the target architecture." -#endif +#pragma GCC diagnostic pop } } // namespace __llvm_libc diff --git a/libc/src/setjmp/setjmp.cpp b/libc/src/setjmp/x86_64/setjmp.h copy from libc/src/setjmp/setjmp.cpp copy to libc/src/setjmp/x86_64/setjmp.h --- a/libc/src/setjmp/setjmp.cpp +++ b/libc/src/setjmp/x86_64/setjmp.h @@ -7,15 +7,17 @@ //===----------------------------------------------------------------------===// #include "src/__support/common.h" -#include "src/__support/macros/properties/architectures.h" #include "src/setjmp/setjmp_impl.h" #include +#if !defined(LIBC_TARGET_ARCH_IS_X86_64) +#error "Invalid file include" +#endif + namespace __llvm_libc { LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) { -#ifdef LIBC_TARGET_ARCH_IS_X86_64 register __UINT64_TYPE__ rbx __asm__("rbx"); register __UINT64_TYPE__ r12 __asm__("r12"); register __UINT64_TYPE__ r13 __asm__("r13"); @@ -50,10 +52,6 @@ buf->rsp = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_frame_address(0)) + sizeof(__UINTPTR_TYPE__) * 2; buf->rip = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_return_address(0)); -#else // LIBC_TARGET_ARCH_IS_X86_64 -#error "setjmp implementation not available for the target architecture." -#endif - return 0; }