[libc-commits] [libc] f1ce6a4 - [libc][arm] implement a basic setjmp/longjmp (#93220)

via libc-commits libc-commits at lists.llvm.org
Thu Jun 20 08:16:53 PDT 2024


Author: Nick Desaulniers (paternity leave)
Date: 2024-06-20T08:16:48-07:00
New Revision: f1ce6a465d09f9527151f2f36c19072d2091cbaa

URL: https://github.com/llvm/llvm-project/commit/f1ce6a465d09f9527151f2f36c19072d2091cbaa
DIFF: https://github.com/llvm/llvm-project/commit/f1ce6a465d09f9527151f2f36c19072d2091cbaa.diff

LOG: [libc][arm] implement a basic setjmp/longjmp (#93220)


Note: our baremetal arm configuration compiles this as
`--target=arm-none-eabi`, so this code is built in -marm mode. It could be
smaller with `--target=armv7-none-eabi -mthumb`. The assembler is valid ARMv5,
or THUMB2, but not THUMB(1).

Added: 
    libc/src/setjmp/arm/CMakeLists.txt
    libc/src/setjmp/arm/longjmp.cpp
    libc/src/setjmp/arm/setjmp.cpp

Modified: 
    libc/config/baremetal/arm/entrypoints.txt
    libc/config/baremetal/arm/headers.txt
    libc/config/linux/arm/entrypoints.txt
    libc/config/linux/arm/headers.txt
    libc/include/llvm-libc-types/jmp_buf.h
    libc/include/setjmp.h.def

Removed: 
    


################################################################################
diff  --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 2930d718fdb21..c2aa5258cb7e6 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 3608364e45bde..28fd1ce9128d2 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 d4f932416bd9f..681d3d2583fd3 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 1180564fe458c..84078a947031a 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 29a1df9ad6823..8949be9fa0ab7 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 670bc1ac0fe24..cb083b8cd023e 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 0000000000000..da97b79c9fea0
--- /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 0000000000000..a088b5800fd14
--- /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 0000000000000..287e09c4a08ba
--- /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


        


More information about the libc-commits mailing list