Skip to content

Commit

Permalink
[libc][arm] implement a basic setjmp/longjmp (#93220)
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
nickdesaulniers committed Jun 20, 2024
1 parent abad845 commit f1ce6a4
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 7 deletions.
4 changes: 4 additions & 0 deletions libc/config/baremetal/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions libc/config/baremetal/arm/headers.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 4 additions & 0 deletions libc/config/linux/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 6 additions & 5 deletions libc/config/linux/arm/headers.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
3 changes: 3 additions & 0 deletions libc/include/llvm-libc-types/jmp_buf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libc/include/setjmp.h.def
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_LIBC_SETJMP_H

#include "__llvm-libc-common.h"
#include "llvm-libc-types/jmp_buf.h"

%%public_api()

Expand Down
19 changes: 19 additions & 0 deletions libc/src/setjmp/arm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
74 changes: 74 additions & 0 deletions libc/src/setjmp/arm/longjmp.cpp
Original file line number Diff line number Diff line change
@@ -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
64 changes: 64 additions & 0 deletions libc/src/setjmp/arm/setjmp.cpp
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit f1ce6a4

Please sign in to comment.