diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt index 4e3d1cb9f5337a..ff192edfffe3df 100644 --- a/libc/config/baremetal/arm/entrypoints.txt +++ b/libc/config/baremetal/arm/entrypoints.txt @@ -26,6 +26,10 @@ set(TARGET_LIBC_ENTRYPOINTS # errno.h entrypoints libc.src.errno.errno + # setjmp.h entrypoints + libc.src.setjmp.longjmp + libc.src.setjmp.setjmp + # string.h entrypoints libc.src.string.bcmp libc.src.string.bcopy diff --git a/libc/config/baremetal/arm/headers.txt b/libc/config/baremetal/arm/headers.txt index 3608364e45bdeb..28fd1ce9128d25 100644 --- a/libc/config/baremetal/arm/headers.txt +++ b/libc/config/baremetal/arm/headers.txt @@ -1,13 +1,14 @@ set(TARGET_PUBLIC_HEADERS libc.include.assert libc.include.ctype - libc.include.fenv libc.include.errno + libc.include.fenv libc.include.float - libc.include.stdint libc.include.inttypes libc.include.math + libc.include.setjmp libc.include.stdfix + libc.include.stdint libc.include.stdio libc.include.stdlib libc.include.string diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt index 335981ff7dc7cf..af824d294d7d7a 100644 --- a/libc/config/linux/arm/entrypoints.txt +++ b/libc/config/linux/arm/entrypoints.txt @@ -20,6 +20,10 @@ set(TARGET_LIBC_ENTRYPOINTS # errno.h entrypoints libc.src.errno.errno + # setjmp.h entrypoints + libc.src.setjmp.longjmp + libc.src.setjmp.setjmp + # string.h entrypoints libc.src.string.bcmp libc.src.string.bcopy diff --git a/libc/config/linux/arm/headers.txt b/libc/config/linux/arm/headers.txt index 1180564fe458c8..84078a947031ac 100644 --- a/libc/config/linux/arm/headers.txt +++ b/libc/config/linux/arm/headers.txt @@ -1,19 +1,20 @@ set(TARGET_PUBLIC_HEADERS libc.include.ctype - libc.include.fenv libc.include.errno + libc.include.fenv libc.include.float - libc.include.stdint libc.include.inttypes libc.include.math - libc.include.stdckdint + libc.include.search + libc.include.setjmp libc.include.stdbit + libc.include.stdckdint + libc.include.stdint libc.include.stdlib libc.include.string libc.include.strings - libc.include.search - libc.include.wchar libc.include.uchar + libc.include.wchar # Disabled due to epoll_wait syscalls not being available on this platform. # libc.include.sys_epoll diff --git a/libc/include/llvm-libc-types/jmp_buf.h b/libc/include/llvm-libc-types/jmp_buf.h index 29a1df9ad6823a..8949be9fa0ab78 100644 --- a/libc/include/llvm-libc-types/jmp_buf.h +++ b/libc/include/llvm-libc-types/jmp_buf.h @@ -32,6 +32,9 @@ typedef struct { #elif defined(__riscv_float_abi_single) #error "__jmp_buf not available for your target architecture." #endif +#elif defined(__arm__) + // r4, r5, r6, r7, r8, r9, r10, r11, r12, lr + long opaque[10]; #else #error "__jmp_buf not available for your target architecture." #endif diff --git a/libc/include/setjmp.h.def b/libc/include/setjmp.h.def index 670bc1ac0fe245..cb083b8cd023e0 100644 --- a/libc/include/setjmp.h.def +++ b/libc/include/setjmp.h.def @@ -10,6 +10,7 @@ #define LLVM_LIBC_SETJMP_H #include "__llvm-libc-common.h" +#include "llvm-libc-types/jmp_buf.h" %%public_api() diff --git a/libc/src/setjmp/arm/CMakeLists.txt b/libc/src/setjmp/arm/CMakeLists.txt new file mode 100644 index 00000000000000..da97b79c9fea01 --- /dev/null +++ b/libc/src/setjmp/arm/CMakeLists.txt @@ -0,0 +1,19 @@ +add_entrypoint_object( + setjmp + SRCS + setjmp.cpp + HDRS + ../setjmp_impl.h + DEPENDS + libc.include.setjmp +) + +add_entrypoint_object( + longjmp + SRCS + longjmp.cpp + HDRS + ../longjmp.h + DEPENDS + libc.include.setjmp +) diff --git a/libc/src/setjmp/arm/longjmp.cpp b/libc/src/setjmp/arm/longjmp.cpp new file mode 100644 index 00000000000000..a088b5800fd14e --- /dev/null +++ b/libc/src/setjmp/arm/longjmp.cpp @@ -0,0 +1,74 @@ + +//===-- 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/setjmp/longjmp.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE { + +#if defined(__thumb__) && __ARM_ARCH_ISA_THUMB == 1 + +[[gnu::naked, gnu::target("thumb")]] +LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) { + asm(R"( + # Reload r4, r5, r6, r7. + ldmia r0!, {r4-r7} + + # Reload r8, r9. They cannot appear in register lists so load them + # into the lower registers, then move them into place. + ldmia r0!, {r2-r3} + mov r8, r2 + mov r9, r3 + + # Reload r10, r11. They cannot appear in register lists so load them + # into the lower registers, then move them into place. + ldmia r0!, {r2-r3} + mov r10, r2 + mov r11, r3 + + # Reload sp, lr. They cannot appear in register lists so load them + # into the lower registers, then move them into place. + ldmia r0!, {r2-r3} + mov sp, r2 + mov lr, r3 + + # return val ?: 1; + movs r0, r1 + bne .Lret_val + movs r0, #1 + + .Lret_val: + bx lr)"); +} + +#else // Thumb2 or ARM + +// TODO(https://github.com/llvm/llvm-project/issues/94061): fp registers +// (d0-d16) +// TODO(https://github.com/llvm/llvm-project/issues/94062): pac+bti +[[gnu::naked]] +LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) { + asm(R"( + # While sp may appear in a register list for ARM mode, it may not for + # Thumb2 mode. Just load the previous value of sp into r12 then move it + # into sp, so that this code is portable between ARM and Thumb2. + + ldm r0, {r4-r12, lr} + mov sp, r12 + + # return val ?: 1; + movs r0, r1 + it eq + moveq r0, #1 + bx lr)"); +} + +#endif + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/setjmp/arm/setjmp.cpp b/libc/src/setjmp/arm/setjmp.cpp new file mode 100644 index 00000000000000..287e09c4a08ba6 --- /dev/null +++ b/libc/src/setjmp/arm/setjmp.cpp @@ -0,0 +1,64 @@ +//===-- 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" + +namespace LIBC_NAMESPACE { + +#if defined(__thumb__) && __ARM_ARCH_ISA_THUMB == 1 + +[[gnu::naked, gnu::target("thumb")]] +LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) { + asm(R"( + # Store r4, r5, r6, and r7 into buf. + stmia r0!, {r4-r7} + + # Store r8, r9, r10, r11, sp, and lr into buf. Thumb(1) doesn't support + # the high registers > r7 in stmia, so move them into lower GPRs first. + # Thumb(1) also doesn't support using str with sp or lr, move them + # together with the rest. + mov r1, r8 + mov r2, r9 + mov r3, r10 + stmia r0!, {r1-r3} + + mov r1, r11 + mov r2, sp + mov r3, lr + stmia r0!, {r1-r3} + + # Return 0. + movs r0, #0 + bx lr)"); +} + +#else // Thumb2 or ARM + +// TODO(https://github.com/llvm/llvm-project/issues/94061): fp registers +// (d0-d16) +// TODO(https://github.com/llvm/llvm-project/issues/94062): pac+bti +[[gnu::naked]] +LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) { + asm(R"( + # While sp may appear in a register list for ARM mode, it may not for + # Thumb2 mode. Just move it into r12 then stm that, so that this code + # is portable between ARM and Thumb2. + mov r12, sp + + # Store r4, r5, r6, r7, r8, r9, r10, r11, sp, and lr into buf. + stm r0, {r4-r12, lr} + + # Return zero. + mov r0, #0 + bx lr)"); +} + +#endif + +} // namespace LIBC_NAMESPACE