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"];
+}
# 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
libc.include.sched
libc.include.signal
libc.include.spawn
+ libc.include.setjmp
libc.include.stdio
libc.include.stdlib
libc.include.string
)
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
GEN_HDR string.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)
--- /dev/null
+//===-- 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__
--- /dev/null
+//===-- 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
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">;
]
>;
+ 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,
StdIO,
StdLib,
IntTypes,
+ SetJmp,
Signal,
Threads,
Time,
# 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)
--- /dev/null
+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
+)
--- /dev/null
+//===-- 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
--- /dev/null
+//===-- 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
--- /dev/null
+//===-- 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
--- /dev/null
+//===-- 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
# 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)
--- /dev/null
+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
+)
--- /dev/null
+//===-- 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);
+}