Skip to content

[libc][arm] implement a basic setjmp/longjmp #93220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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")]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious, does arm/aarch64 also support multi-versioned symbols?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If multi-versioned symbols is referring to function multi-versioning then there is a beta version defined in https://arm-software.github.io/acle/main/acle.html#function-multi-versioning AFAIK it has been concentrating on AArch64 though.

Unlikely to be used in bare-metal cases like cortex-m0 though.

Copy link
Member Author

@nickdesaulniers nickdesaulniers May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, depends on loader support, which we currently lack. Also, not sure yet we want to support a libc with mixed arm/thumb mode support. Frankly, I'd suspect that folks that care about libc support on 32b ARM only really care about Thumb mode (1 or 2) (unless they're running on some variant with only ARM mode support).

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
Loading