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

Nick Desaulniers via libc-commits libc-commits at lists.llvm.org
Fri May 24 15:15:21 PDT 2024


https://github.com/nickdesaulniers updated https://github.com/llvm/llvm-project/pull/93220

>From 42078cb53551478adac1b0b0d2ca4dccedd87401 Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Thu, 23 May 2024 10:29:09 -0700
Subject: [PATCH 1/7] [libc][arm] implement setjmp/longjmp

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). Unclear yet if we need to support THUMB(1); may
depend on whether downstream users are using llvm-libc's cmake.
---
 libc/config/baremetal/arm/entrypoints.txt |  4 +++
 libc/config/baremetal/arm/headers.txt     |  5 ++--
 libc/config/linux/arm/entrypoints.txt     |  4 +++
 libc/config/linux/arm/headers.txt         | 11 ++++----
 libc/include/llvm-libc-types/jmp_buf.h    |  3 +++
 libc/include/setjmp.h.def                 |  1 +
 libc/src/setjmp/arm/CMakeLists.txt        | 19 ++++++++++++++
 libc/src/setjmp/arm/longjmp.cpp           | 31 +++++++++++++++++++++++
 libc/src/setjmp/arm/setjmp.cpp            | 27 ++++++++++++++++++++
 9 files changed, 98 insertions(+), 7 deletions(-)
 create mode 100644 libc/src/setjmp/arm/CMakeLists.txt
 create mode 100644 libc/src/setjmp/arm/longjmp.cpp
 create mode 100644 libc/src/setjmp/arm/setjmp.cpp

diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 4e3d1cb9f5337..ff192edfffe3d 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 335981ff7dc7c..af824d294d7d7 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..a5379336865de 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..752318edfa580
--- /dev/null
+++ b/libc/src/setjmp/arm/longjmp.cpp
@@ -0,0 +1,31 @@
+
+//===-- 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"
+#include "src/__support/macros/properties/architectures.h"
+
+#if !defined(LIBC_TARGET_ARCH_IS_ARM)
+#error "Invalid file include"
+#endif
+
+namespace LIBC_NAMESPACE {
+
+[[gnu::naked]]
+LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) {
+  asm("ldm.w r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n\t"
+      "mov sp, r12\n\t"
+      "movs r0, r1\n\t"
+      "it eq\n\t"
+      "moveq r0, 1\n\t"
+      "bx lr"
+  );
+}
+
+} // 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..9304ff6b47050
--- /dev/null
+++ b/libc/src/setjmp/arm/setjmp.cpp
@@ -0,0 +1,27 @@
+//===-- 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"
+
+#if !defined(LIBC_TARGET_ARCH_IS_ARM)
+#error "Invalid file include"
+#endif
+
+namespace LIBC_NAMESPACE {
+
+[[gnu::naked]]
+LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) {
+  asm("mov r12, sp\n\t"
+      "stm.w r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n\t"
+      "mov.w r0, 0\n\t"
+      "bx lr\n\t"
+  );
+}
+
+} // namespace LIBC_NAMESPACE

>From cbe594f7439d1b85ec85da1b32a3a2da25dc923f Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Thu, 23 May 2024 10:40:44 -0700
Subject: [PATCH 2/7] format

---
 libc/include/llvm-libc-types/jmp_buf.h | 2 +-
 libc/src/setjmp/arm/longjmp.cpp        | 3 +--
 libc/src/setjmp/arm/setjmp.cpp         | 3 +--
 3 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/libc/include/llvm-libc-types/jmp_buf.h b/libc/include/llvm-libc-types/jmp_buf.h
index a5379336865de..8949be9fa0ab7 100644
--- a/libc/include/llvm-libc-types/jmp_buf.h
+++ b/libc/include/llvm-libc-types/jmp_buf.h
@@ -34,7 +34,7 @@ typedef struct {
 #endif
 #elif defined(__arm__)
   // r4, r5, r6, r7, r8, r9, r10, r11, r12, lr
-  long opaque [10];
+  long opaque[10];
 #else
 #error "__jmp_buf not available for your target architecture."
 #endif
diff --git a/libc/src/setjmp/arm/longjmp.cpp b/libc/src/setjmp/arm/longjmp.cpp
index 752318edfa580..edf605c0de544 100644
--- a/libc/src/setjmp/arm/longjmp.cpp
+++ b/libc/src/setjmp/arm/longjmp.cpp
@@ -24,8 +24,7 @@ LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) {
       "movs r0, r1\n\t"
       "it eq\n\t"
       "moveq r0, 1\n\t"
-      "bx lr"
-  );
+      "bx lr");
 }
 
 } // namespace LIBC_NAMESPACE
diff --git a/libc/src/setjmp/arm/setjmp.cpp b/libc/src/setjmp/arm/setjmp.cpp
index 9304ff6b47050..52ceeee3c4237 100644
--- a/libc/src/setjmp/arm/setjmp.cpp
+++ b/libc/src/setjmp/arm/setjmp.cpp
@@ -20,8 +20,7 @@ LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) {
   asm("mov r12, sp\n\t"
       "stm.w r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n\t"
       "mov.w r0, 0\n\t"
-      "bx lr\n\t"
-  );
+      "bx lr");
 }
 
 } // namespace LIBC_NAMESPACE

>From 77384336fb4ef69a615b59fc0152ffdc74a4fb4e Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Thu, 23 May 2024 10:43:25 -0700
Subject: [PATCH 3/7] remove useless preprocessor warnings

---
 libc/src/setjmp/arm/longjmp.cpp | 5 -----
 libc/src/setjmp/arm/setjmp.cpp  | 4 ----
 2 files changed, 9 deletions(-)

diff --git a/libc/src/setjmp/arm/longjmp.cpp b/libc/src/setjmp/arm/longjmp.cpp
index edf605c0de544..e496af8394490 100644
--- a/libc/src/setjmp/arm/longjmp.cpp
+++ b/libc/src/setjmp/arm/longjmp.cpp
@@ -9,11 +9,6 @@
 
 #include "src/setjmp/longjmp.h"
 #include "src/__support/common.h"
-#include "src/__support/macros/properties/architectures.h"
-
-#if !defined(LIBC_TARGET_ARCH_IS_ARM)
-#error "Invalid file include"
-#endif
 
 namespace LIBC_NAMESPACE {
 
diff --git a/libc/src/setjmp/arm/setjmp.cpp b/libc/src/setjmp/arm/setjmp.cpp
index 52ceeee3c4237..a5f0110ad2b17 100644
--- a/libc/src/setjmp/arm/setjmp.cpp
+++ b/libc/src/setjmp/arm/setjmp.cpp
@@ -9,10 +9,6 @@
 #include "src/__support/common.h"
 #include "src/setjmp/setjmp_impl.h"
 
-#if !defined(LIBC_TARGET_ARCH_IS_ARM)
-#error "Invalid file include"
-#endif
-
 namespace LIBC_NAMESPACE {
 
 [[gnu::naked]]

>From 96d8f14ca1e83bc6af942a70b5a30fd410a67266 Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Thu, 23 May 2024 11:04:07 -0700
Subject: [PATCH 4/7] raw string literals

---
 libc/src/setjmp/arm/longjmp.cpp | 13 +++++++------
 libc/src/setjmp/arm/setjmp.cpp  |  9 +++++----
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/libc/src/setjmp/arm/longjmp.cpp b/libc/src/setjmp/arm/longjmp.cpp
index e496af8394490..e5e3b28468a67 100644
--- a/libc/src/setjmp/arm/longjmp.cpp
+++ b/libc/src/setjmp/arm/longjmp.cpp
@@ -14,12 +14,13 @@ namespace LIBC_NAMESPACE {
 
 [[gnu::naked]]
 LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) {
-  asm("ldm.w r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n\t"
-      "mov sp, r12\n\t"
-      "movs r0, r1\n\t"
-      "it eq\n\t"
-      "moveq r0, 1\n\t"
-      "bx lr");
+  asm(R"(
+      ldm r0, {r4-r12, lr}
+      mov sp, r12
+      movs r0, r1
+      it eq
+      moveq r0, #1
+      bx lr)");
 }
 
 } // namespace LIBC_NAMESPACE
diff --git a/libc/src/setjmp/arm/setjmp.cpp b/libc/src/setjmp/arm/setjmp.cpp
index a5f0110ad2b17..4618416146ba3 100644
--- a/libc/src/setjmp/arm/setjmp.cpp
+++ b/libc/src/setjmp/arm/setjmp.cpp
@@ -13,10 +13,11 @@ namespace LIBC_NAMESPACE {
 
 [[gnu::naked]]
 LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) {
-  asm("mov r12, sp\n\t"
-      "stm.w r0!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n\t"
-      "mov.w r0, 0\n\t"
-      "bx lr");
+  asm(R"(
+      mov r12, sp
+      stm r0, {r4-r12, lr}
+      mov r0, #0
+      bx lr)");
 }
 
 } // namespace LIBC_NAMESPACE

>From 51464ea52da8c76234008df8192b3d1b82dc9dbf Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Fri, 24 May 2024 14:33:17 -0700
Subject: [PATCH 5/7] fix up setjmp for thumb1

---
 libc/src/setjmp/arm/setjmp.cpp | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/libc/src/setjmp/arm/setjmp.cpp b/libc/src/setjmp/arm/setjmp.cpp
index 4618416146ba3..4225185bbc243 100644
--- a/libc/src/setjmp/arm/setjmp.cpp
+++ b/libc/src/setjmp/arm/setjmp.cpp
@@ -11,6 +11,34 @@
 
 namespace LIBC_NAMESPACE {
 
+#if defined(__thumb__) && __ARM_ARCH_ISA_THUMB == 1
+
+[[gnu::naked]]
+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, and r11 into buf. Thumb(1) doesn't support the high
+      # registers > r7 in stmia, so move them into lower GPRs first.
+      mov r4, r8
+      mov r5, r9
+      mov r6, r10
+      mov r7, r11
+      stmia r0!, {r4-r7}
+
+      # Store sp into buf. Thumb(1) doesn't support sp in str, move to GPR
+      # first.
+      mov r4, sp
+      str r4, [r0]
+
+      # Return 0.
+      movs r0, #0
+      bx lr)");
+}
+
+#else // Thumb2 or ARM
+
 [[gnu::naked]]
 LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) {
   asm(R"(
@@ -20,4 +48,6 @@ LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) {
       bx lr)");
 }
 
+#endif
+
 } // namespace LIBC_NAMESPACE

>From 2d13bf8e4501673be13b0a633a654fc9aeff5bb3 Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Fri, 24 May 2024 14:47:22 -0700
Subject: [PATCH 6/7] save lr, add more comments

---
 libc/src/setjmp/arm/setjmp.cpp | 32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/libc/src/setjmp/arm/setjmp.cpp b/libc/src/setjmp/arm/setjmp.cpp
index 4225185bbc243..11e65561a7b45 100644
--- a/libc/src/setjmp/arm/setjmp.cpp
+++ b/libc/src/setjmp/arm/setjmp.cpp
@@ -13,24 +13,23 @@ namespace LIBC_NAMESPACE {
 
 #if defined(__thumb__) && __ARM_ARCH_ISA_THUMB == 1
 
-[[gnu::naked]]
+[[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, and r11 into buf. Thumb(1) doesn't support the high
-      # registers > r7 in stmia, so move them into lower GPRs first.
-      mov r4, r8
-      mov r5, r9
-      mov r6, r10
-      mov r7, r11
-      stmia r0!, {r4-r7}
-
-      # Store sp into buf. Thumb(1) doesn't support sp in str, move to GPR
-      # first.
-      mov r4, sp
-      str r4, [r0]
+      # 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 r2, r8
+      mov r3, r9
+      mov r4, r10
+      mov r5, r11
+      mov r6, sp
+      mov r7, lr
+      stmia r0!, {r2-r7}
 
       # Return 0.
       movs r0, #0
@@ -42,8 +41,15 @@ LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) {
 [[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)");
 }

>From 23a39436a262ee20f26924e467d559498155e862 Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Fri, 24 May 2024 15:14:32 -0700
Subject: [PATCH 7/7] implement longjmp for thumb1

---
 libc/src/setjmp/arm/longjmp.cpp | 42 +++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/libc/src/setjmp/arm/longjmp.cpp b/libc/src/setjmp/arm/longjmp.cpp
index e5e3b28468a67..23906c357103a 100644
--- a/libc/src/setjmp/arm/longjmp.cpp
+++ b/libc/src/setjmp/arm/longjmp.cpp
@@ -12,15 +12,57 @@
 
 namespace LIBC_NAMESPACE {
 
+#if defined(__thumb__) && __ARM_ARCH_ISA_THUMB == 1
+
+[[gnu::naked]]
+LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) {
+  asm(R"(
+      # Reload r4, r5, r6, r7.
+      ldmia r0!, {r4, r5, r6, r7}
+
+      # Reload r8, r9, r10. They cannot appear in register lists so load them
+      # into the lower registers, then move them into place.
+      ldmia r0!, {r1, r2, r3}
+      mov r8, r1
+      mov r9, r2
+      mov r10, r3
+
+      # Reload r11, sp, lr. They cannot appear in register lists so load them
+      # into the lower registers, then move them into place.
+      ldmia r0!, {r1, r2, r3}
+      mov r11, r1
+      mov sp, r2
+      mov lr, r3
+
+      # return val ?: 1;
+      movs r0, r1
+      beq .Lret_one
+      bx lr
+
+    .Lret_one:
+      movs r0, #1
+      bx lr)");
+}
+
+#else // Thumb2 or ARM
+
 [[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



More information about the libc-commits mailing list