[libc-commits] [libc] 3b82b4f - [libc] Add x86_64 implementation of setjmp and longjmp.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Tue Nov 1 15:58:43 PDT 2022
Author: Siva Chandra Reddy
Date: 2022-11-01T22:58:35Z
New Revision: 3b82b4fbd50ac15584ac88075f356f3ebf946515
URL: https://github.com/llvm/llvm-project/commit/3b82b4fbd50ac15584ac88075f356f3ebf946515
DIFF: https://github.com/llvm/llvm-project/commit/3b82b4fbd50ac15584ac88075f356f3ebf946515.diff
LOG: [libc] Add x86_64 implementation of setjmp and longjmp.
Reviewed By: michaelrj
Differential Revision: https://reviews.llvm.org/D137147
Added:
libc/include/llvm-libc-types/jmp_buf.h
libc/include/setjmp.h.def
libc/src/setjmp/CMakeLists.txt
libc/src/setjmp/longjmp.cpp
libc/src/setjmp/longjmp.h
libc/src/setjmp/setjmp.cpp
libc/src/setjmp/setjmp_impl.h
libc/test/src/setjmp/CMakeLists.txt
libc/test/src/setjmp/setjmp_test.cpp
Modified:
libc/config/linux/api.td
libc/config/linux/x86_64/entrypoints.txt
libc/config/linux/x86_64/headers.txt
libc/include/CMakeLists.txt
libc/include/llvm-libc-types/CMakeLists.txt
libc/spec/stdc.td
libc/src/CMakeLists.txt
libc/test/src/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 25e19e83fbcf9..a22b3772b303e 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -314,3 +314,7 @@ def SpawnAPI : PublicAPI<"spawn.h"> {
def TermiosAPI : PublicAPI<"termios.h"> {
let Types = ["cc_t", "pid_t", "speed_t", "struct termios", "tcflag_t"];
}
+
+def SetJmpAPI : PublicAPI<"setjmp.h"> {
+ let Types = ["jmp_buf"];
+}
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index b8d962dc7e2a2..7b0fb53451004 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -362,6 +362,10 @@ if(LLVM_LIBC_FULL_BUILD)
# sched.h entrypoints
libc.src.sched.__sched_getcpucount
+ # setjmp.h entrypoints
+ libc.src.setjmp.longjmp
+ libc.src.setjmp.setjmp
+
# stdio.h entrypoints
libc.src.stdio.clearerr
libc.src.stdio.clearerr_unlocked
diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt
index 123fc7ef43286..a878e6d89e003 100644
--- a/libc/config/linux/x86_64/headers.txt
+++ b/libc/config/linux/x86_64/headers.txt
@@ -11,6 +11,7 @@ set(TARGET_PUBLIC_HEADERS
libc.include.sched
libc.include.signal
libc.include.spawn
+ libc.include.setjmp
libc.include.stdio
libc.include.stdlib
libc.include.string
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 3e63695e12a2a..066c7783d82a8 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -76,6 +76,15 @@ add_gen_header(
.llvm_libc_common_h
)
+add_gen_header(
+ setjmp
+ DEF_FILE setjmp.h.def
+ GEN_HDR setjmp.h
+ DEPENDS
+ .llvm_libc_common_h
+ .llvm-libc-types.jmp_buf
+)
+
add_gen_header(
string
DEF_FILE string.h.def
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index e4e9567d5fdc9..4bd69be95cfe3 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -36,6 +36,7 @@ add_header(gid_t HDR gid_t.h)
add_header(uid_t HDR uid_t.h)
add_header(imaxdiv_t HDR imaxdiv_t.h)
add_header(ino_t HDR ino_t.h)
+add_header(jmp_buf HDR jmp_buf.h)
add_header(mode_t HDR mode_t.h)
add_header(mtx_t HDR mtx_t.h DEPENDS .__futex_word .__mutex_type)
add_header(nlink_t HDR nlink_t.h)
diff --git a/libc/include/llvm-libc-types/jmp_buf.h b/libc/include/llvm-libc-types/jmp_buf.h
new file mode 100644
index 0000000000000..e948a7f42248a
--- /dev/null
+++ b/libc/include/llvm-libc-types/jmp_buf.h
@@ -0,0 +1,29 @@
+//===-- Definition of type jmp_buf ----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_TYPES_JMP_BUF_H__
+#define __LLVM_LIBC_TYPES_JMP_BUF_H__
+
+typedef struct {
+#ifdef __x86_64__
+ __UINT64_TYPE__ rbx;
+ __UINT64_TYPE__ rbp;
+ __UINT64_TYPE__ r12;
+ __UINT64_TYPE__ r13;
+ __UINT64_TYPE__ r14;
+ __UINT64_TYPE__ r15;
+ __UINTPTR_TYPE__ rsp;
+ __UINTPTR_TYPE__ rip;
+#else
+#error "__jmp_buf not available for your target architecture."
+#endif
+} __jmp_buf;
+
+typedef __jmp_buf jmp_buf[1];
+
+#endif // __LLVM_LIBC_TYPES_JMP_BUF_H__
diff --git a/libc/include/setjmp.h.def b/libc/include/setjmp.h.def
new file mode 100644
index 0000000000000..7447be2415bd5
--- /dev/null
+++ b/libc/include/setjmp.h.def
@@ -0,0 +1,16 @@
+//===-- C standard library header setjmp.h --------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SETJMP_H
+#define LLVM_LIBC_SETJMP_H
+
+#include <__llvm-libc-common.h>
+
+%%public_api()
+
+#endif // LLVM_LIBC_SETJMP_H
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 1c0d7c2dd8d99..964b3e7c27338 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -9,6 +9,8 @@ def StdC : StandardSpec<"stdc"> {
NamedType LDivTType = NamedType<"ldiv_t">;
NamedType LLDivTType = NamedType<"lldiv_t">;
+ NamedType JmpBuf = NamedType<"jmp_buf">;
+
NamedType TssTType = NamedType<"tss_t">;
PtrType TssTPtr = PtrType<TssTType>;
NamedType TssDtorTType = NamedType<"tss_dtor_t">;
@@ -988,6 +990,30 @@ def StdC : StandardSpec<"stdc"> {
]
>;
+ HeaderSpec SetJmp = HeaderSpec<
+ "setjmp.h",
+ [], // Macros
+ [JmpBuf],
+ [], // Enumerations
+ [
+ FunctionSpec<
+ "longjmp",
+ RetValSpec<NoReturn>,
+ [ArgSpec<JmpBuf>, ArgSpec<IntType>]
+ >,
+ FunctionSpec<
+ "setjmp",
+ RetValSpec<IntType>,
+ [ArgSpec<JmpBuf>]
+ >,
+ FunctionSpec<
+ "longjmp",
+ RetValSpec<VoidType>,
+ [ArgSpec<JmpBuf>, ArgSpec<IntType>]
+ >,
+ ]
+ >;
+
let Headers = [
Assert,
CType,
@@ -998,6 +1024,7 @@ def StdC : StandardSpec<"stdc"> {
StdIO,
StdLib,
IntTypes,
+ SetJmp,
Signal,
Threads,
Time,
diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 63a7258064dcc..eac68d3187ca0 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -26,6 +26,7 @@ endif()
# The signal API is currently disabled as signal.h is incorrect.
# since assert uses the signal API, we disable assert also.
# add_subdirectory(assert)
+add_subdirectory(setjmp)
add_subdirectory(signal)
add_subdirectory(spawn)
add_subdirectory(threads)
diff --git a/libc/src/setjmp/CMakeLists.txt b/libc/src/setjmp/CMakeLists.txt
new file mode 100644
index 0000000000000..5b79155cb4f05
--- /dev/null
+++ b/libc/src/setjmp/CMakeLists.txt
@@ -0,0 +1,24 @@
+add_entrypoint_object(
+ setjmp
+ SRCS
+ setjmp.cpp
+ HDRS
+ setjmp_impl.h
+ COMPILE_OPTIONS
+ -O3 # We do not want any local variables in setjmp
+ -fno-omit-frame-pointer # The implementation assumes frame pointer on to the stack
+ DEPENDS
+ libc.include.setjmp
+)
+
+add_entrypoint_object(
+ longjmp
+ SRCS
+ longjmp.cpp
+ HDRS
+ longjmp.h
+ COMPILE_OPTIONS
+ -O3 # We do not want any local variables in longjmp
+ DEPENDS
+ libc.include.setjmp
+)
diff --git a/libc/src/setjmp/longjmp.cpp b/libc/src/setjmp/longjmp.cpp
new file mode 100644
index 0000000000000..9d2d783a536fc
--- /dev/null
+++ b/libc/src/setjmp/longjmp.cpp
@@ -0,0 +1,46 @@
+//===-- 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/architectures.h"
+#include "src/__support/common.h"
+
+#include <setjmp.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) {
+#ifdef LLVM_LIBC_ARCH_X86_64
+ register __UINT64_TYPE__ rbx __asm__("rbx");
+ register __UINT64_TYPE__ rbp __asm__("rbp");
+ register __UINT64_TYPE__ r12 __asm__("r12");
+ register __UINT64_TYPE__ r13 __asm__("r13");
+ register __UINT64_TYPE__ r14 __asm__("r14");
+ register __UINT64_TYPE__ r15 __asm__("r15");
+ register __UINT64_TYPE__ rsp __asm__("rsp");
+ register __UINT64_TYPE__ rax __asm__("rax");
+
+ // ABI requires that the return value should be stored in rax. So, we store
+ // |val| in rax. Note that this has to happen before we restore the registers
+ // from values in |buf|. Otherwise, once rsp and rbp are updated, we cannot
+ // read |val|.
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rax) : "m"(val) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rbx) : "m"(buf->rbx) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rbp) : "m"(buf->rbp) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r12) : "m"(buf->r12) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r13) : "m"(buf->r13) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r14) : "m"(buf->r14) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r15) : "m"(buf->r15) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rsp) : "m"(buf->rsp) :);
+ LIBC_INLINE_ASM("jmp *%0\n\t" : : "m"(buf->rip));
+#else // LLVM_LIBC_ARCH_X86_64
+#error "longjmp implementation not available for the target architecture."
+#endif
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/setjmp/longjmp.h b/libc/src/setjmp/longjmp.h
new file mode 100644
index 0000000000000..05d8baaa43913
--- /dev/null
+++ b/libc/src/setjmp/longjmp.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for longjmp -----------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_LONGJMP_LONGJMP_H
+#define LLVM_LIBC_SRC_LONGJMP_LONGJMP_H
+
+#include <setjmp.h>
+
+namespace __llvm_libc {
+
+void longjmp(__jmp_buf *buf, int val);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_LONGJMP_LONGJMP_H
diff --git a/libc/src/setjmp/setjmp.cpp b/libc/src/setjmp/setjmp.cpp
new file mode 100644
index 0000000000000..11d2d7079ad54
--- /dev/null
+++ b/libc/src/setjmp/setjmp.cpp
@@ -0,0 +1,60 @@
+//===-- 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/architectures.h"
+#include "src/__support/common.h"
+#include "src/setjmp/setjmp_impl.h"
+
+#include <setjmp.h>
+
+namespace __llvm_libc {
+
+LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) {
+#ifdef LLVM_LIBC_ARCH_X86_64
+ register __UINT64_TYPE__ rbx __asm__("rbx");
+ register __UINT64_TYPE__ r12 __asm__("r12");
+ register __UINT64_TYPE__ r13 __asm__("r13");
+ register __UINT64_TYPE__ r14 __asm__("r14");
+ register __UINT64_TYPE__ r15 __asm__("r15");
+
+ // We want to store the register values as is. So, we will suppress the
+ // compiler warnings about the uninitialized variables declared above.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->rbx) : "r"(rbx) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r12) : "r"(r12) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r13) : "r"(r13) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r14) : "r"(r14) :);
+ LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r15) : "r"(r15) :);
+#pragma GCC diagnostic pop
+
+ // We want the rbp of the caller, which is what __builtin_frame_address(1)
+ // should return. But, compilers generate a warning that calling
+ // __builtin_frame_address with non-zero argument is unsafe. So, we use
+ // the knowledge of the x86_64 ABI to fetch the callers rbp. As per the ABI,
+ // the rbp of the caller is pushed on to the stack and then new top is saved
+ // in this function's rbp. So, we fetch it from location at which this
+ // functions's rbp is pointing.
+ buf->rbp = *reinterpret_cast<__UINTPTR_TYPE__ *>(__builtin_frame_address(0));
+
+ // The callers stack address is exactly 2 pointer widths ahead of the current
+ // frame pointer - between the current frame pointer and the rsp of the caller
+ // are the return address (pushed by the x86_64 call instruction) and the
+ // previous stack pointer as required by the x86_64 ABI.
+ // The stack pointer is ahead because the stack grows down on x86_64.
+ buf->rsp = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_frame_address(0)) +
+ sizeof(__UINTPTR_TYPE__) * 2;
+ buf->rip = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_return_address(0));
+#else // LLVM_LIBC_ARCH_X86_64
+#error "setjmp implementation not available for the target architecture."
+#endif
+
+ return 0;
+}
+
+} // namespace __llvm_libc
diff --git a/libc/src/setjmp/setjmp_impl.h b/libc/src/setjmp/setjmp_impl.h
new file mode 100644
index 0000000000000..6b5115e112a56
--- /dev/null
+++ b/libc/src/setjmp/setjmp_impl.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for setjmp ------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SETJMP_SETJMP_H
+#define LLVM_LIBC_SRC_SETJMP_SETJMP_H
+
+// This header has the _impl prefix in its name to avoid conflict with the
+// public header setjmp.h which is also included. here.
+#include <setjmp.h>
+
+namespace __llvm_libc {
+
+int setjmp(__jmp_buf *buf);
+
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_SETJMP_SETJMP_H
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index 5e954e0d05352..bb5bbb1e5b34d 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -52,6 +52,7 @@ add_subdirectory(dirent)
# The signal API is currently disabled as signal.h is incorrect.
# since assert uses the signal API, we disable assert also.
# add_subdirectory(assert)
+add_subdirectory(setjmp)
add_subdirectory(signal)
add_subdirectory(spawn)
add_subdirectory(time)
diff --git a/libc/test/src/setjmp/CMakeLists.txt b/libc/test/src/setjmp/CMakeLists.txt
new file mode 100644
index 0000000000000..b8f261d99856f
--- /dev/null
+++ b/libc/test/src/setjmp/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_libc_testsuite(libc_setjmp_unittests)
+
+add_libc_unittest(
+ setjmp_test
+ SUITE
+ libc_setjmp_unittests
+ SRCS
+ setjmp_test.cpp
+ DEPENDS
+ libc.include.setjmp
+ libc.src.setjmp.longjmp
+ libc.src.setjmp.setjmp
+)
diff --git a/libc/test/src/setjmp/setjmp_test.cpp b/libc/test/src/setjmp/setjmp_test.cpp
new file mode 100644
index 0000000000000..6dd1a22b8b2a9
--- /dev/null
+++ b/libc/test/src/setjmp/setjmp_test.cpp
@@ -0,0 +1,30 @@
+//===-- Unittests for setjmp and 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/setjmp/setjmp_impl.h"
+#include "utils/UnitTest/Test.h"
+
+jmp_buf buf;
+constexpr int MAX_LOOP = 123;
+
+void jump_back(int n) {
+ __llvm_libc::longjmp(buf, n); // Will return |n| out of setjmp
+}
+
+TEST(LlvmLibcSetJmpTest, SetAndJumpBack) {
+ // Local variables in setjmp scope should be declared volatile.
+ volatile int n = 0;
+ // The first time setjmp is called, it should return 0.
+ // Subsequent calls will return the value passed to jump_back below.
+ if (__llvm_libc::setjmp(buf) <= MAX_LOOP) {
+ ++n;
+ jump_back(n);
+ }
+ ASSERT_EQ(n, MAX_LOOP + 1);
+}
More information about the libc-commits
mailing list