From b47c9f535c8a0fffeb7634a82e3901d416915938 Mon Sep 17 00:00:00 2001 From: Alex Brachet Date: Wed, 11 Mar 2020 23:45:58 -0400 Subject: [PATCH] [libc] Add initial assert definition Summary: This patch adds a temporary `__assert_fail` and `assert` definition to make it available to internal llvm libc code. `__assert_fail` writes to fd 2 directly instead of `stderr`, using SYS_write. I have not put it in its own linux directory because this is temporary and it should be using stdio's api in the future. It does not currently print out the line number (although we could do that by stringifying `__LINE__` if reviewers wish). Reviewers: sivachandra, gchatelet, PaulkaToast Reviewed By: sivachandra Subscribers: mgorny, MaskRay, tschuett, libc-commits Differential Revision: https://reviews.llvm.org/D75420 --- libc/config/linux/api.td | 36 ++++++++++++++++++++++++++++++++++ libc/include/CMakeLists.txt | 8 ++++++++ libc/include/assert.h.def | 14 +++++++++++++ libc/lib/CMakeLists.txt | 3 +++ libc/spec/stdc.td | 12 ++++++++++++ libc/src/CMakeLists.txt | 1 + libc/src/assert/CMakeLists.txt | 13 ++++++++++++ libc/src/assert/__assert_fail.cpp | 38 ++++++++++++++++++++++++++++++++++++ libc/src/assert/assert.h | 31 +++++++++++++++++++++++++++++ libc/test/src/CMakeLists.txt | 1 + libc/test/src/assert/CMakeLists.txt | 15 ++++++++++++++ libc/test/src/assert/assert_test.cpp | 32 ++++++++++++++++++++++++++++++ 12 files changed, 204 insertions(+) create mode 100644 libc/include/assert.h.def create mode 100644 libc/src/assert/CMakeLists.txt create mode 100644 libc/src/assert/__assert_fail.cpp create mode 100644 libc/src/assert/assert.h create mode 100644 libc/test/src/assert/CMakeLists.txt create mode 100644 libc/test/src/assert/assert_test.cpp diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td index 1d78a59..aecdce2 100644 --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -18,6 +18,35 @@ def OffT : TypeDecl<"off_t"> { }]; } +def AssertMacro : MacroDef<"assert"> { + let Defn = [{ + #undef assert + + #ifdef NDEBUG + #define assert(e) (void)0 + #else + + #ifdef __cplusplus + extern "C" + #endif + _Noreturn void __assert_fail(const char *, const char *, unsigned, const char *); + + #define assert(e) \ + ((e) ? (void)0 : __assert_fail(#e, __FILE__, __LINE__, __PRETTY_FUNCTION__)) + + #endif + }]; +} + +def StaticAssertMacro : MacroDef<"static_assert"> { + let Defn = [{ + #ifndef __cplusplus + #undef static_assert + #define static_assert _Static_assert + #endif + }]; +} + def NullMacro : MacroDef<"NULL"> { let Defn = [{ #define __need_NULL @@ -35,6 +64,13 @@ def ErrnoMacro : MacroDef<"errno"> { }]; } +def AssertAPI : PublicAPI<"assert.h"> { + let Macros = [ + AssertMacro, + StaticAssertMacro, + ]; +} + def MathAPI : PublicAPI<"math.h"> { let Functions = [ "acos", diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index bbc41b0..2ff0423 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -28,6 +28,14 @@ add_header( ) add_gen_header( + assert_h + DEF_FILE assert.h.def + GEN_HDR assert.h + DEPENDS + llvm_libc_common_h +) + +add_gen_header( string_h DEF_FILE string.h.def GEN_HDR string.h diff --git a/libc/include/assert.h.def b/libc/include/assert.h.def new file mode 100644 index 0000000..3d78277 --- /dev/null +++ b/libc/include/assert.h.def @@ -0,0 +1,14 @@ +//===---------------- C standard library header assert.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 +// +//===----------------------------------------------------------------------===// + +#include <__llvm-libc-common.h> + +// This file may be usefully included multiple times to change assert()'s +// definition based on NDEBUG. + +%%public_api() diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt index ed91367..238bcb4 100644 --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -2,6 +2,9 @@ add_entrypoint_library( llvmlibc DEPENDS + # assert.h entrypoints + __assert_fail + # errno.h entrypoints __errno_location diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index babfce4..4df8550 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -16,6 +16,17 @@ def StdC : StandardSpec<"stdc"> { PtrType IntPtr = PtrType; + HeaderSpec Assert = HeaderSpec< + "assert.h", + [ + Macro<"static_assert">, + Macro<"assert">, + ], + [], // Types + [], // Enumerations + [] + >; + HeaderSpec String = HeaderSpec< "string.h", [ @@ -285,6 +296,7 @@ def StdC : StandardSpec<"stdc"> { >; let Headers = [ + Assert, Errno, Math, String, diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt index 4e661c8..3ee30fd 100644 --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(assert) add_subdirectory(errno) add_subdirectory(math) add_subdirectory(signal) diff --git a/libc/src/assert/CMakeLists.txt b/libc/src/assert/CMakeLists.txt new file mode 100644 index 0000000..4c39b0f --- /dev/null +++ b/libc/src/assert/CMakeLists.txt @@ -0,0 +1,13 @@ +add_entrypoint_object( + __assert_fail + SRCS + __assert_fail.cpp + HDRS + assert.h + DEPENDS + abort + # These two dependencies are temporary and should be replaced by fprintf + # later. + sys_syscall_h + linux_syscall_h +) diff --git a/libc/src/assert/__assert_fail.cpp b/libc/src/assert/__assert_fail.cpp new file mode 100644 index 0000000..4e72ac7 --- /dev/null +++ b/libc/src/assert/__assert_fail.cpp @@ -0,0 +1,38 @@ +//===----------------- Implementation of __assert_fail --------------------===// +// +// 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/assert/assert.h" +#include "src/stdlib/abort.h" + +// These includes are temporary. +#include "config/linux/syscall.h" // For internal syscall function. +#include "include/sys/syscall.h" // For syscall numbers. + +namespace __llvm_libc { + +// This is just a temporary solution to make assert available to internal +// llvm libc code. In the future writeToStderr will not exist and __assert_fail +// will call fprintf(stderr, ...). +static void writeToStderr(const char *s) { + size_t length = 0; + for (const char *curr = s; *curr; ++curr, ++length); + __llvm_libc::syscall(SYS_write, 2, s, length); +} + +void LLVM_LIBC_ENTRYPOINT(__assert_fail)(const char *assertion, const char *file, + unsigned line, const char *function) { + writeToStderr(file); + writeToStderr(": Assertion failed: '"); + writeToStderr(assertion); + writeToStderr("' in function: '"); + writeToStderr(function); + writeToStderr("'\n"); + __llvm_libc::abort(); +} + +} // namespace __llvm_libc diff --git a/libc/src/assert/assert.h b/libc/src/assert/assert.h new file mode 100644 index 0000000..ef596da --- /dev/null +++ b/libc/src/assert/assert.h @@ -0,0 +1,31 @@ +//===-------------------- Internal header for assert ----------*- 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_ASSERT_ASSERT_H +#define LLVM_LIBC_SRC_ASSERT_ASSERT_H + +#include + +namespace __llvm_libc { + +[[noreturn]] void __assert_fail(const char *assertion, const char *file, unsigned line, + const char *function); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_ASSERT_ASSERT_H + +#undef assert + +#ifdef NDEBUG +#define assert(e) (void)0 +#else +#define assert(e) \ + ((e) ? (void)0 : \ + __llvm_libc::__assert_fail(#e, __FILE__, __LINE__, __PRETTY_FUNCTION__)) +#endif diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt index 2ff4ca4..30c5616 100644 --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(assert) add_subdirectory(errno) add_subdirectory(signal) add_subdirectory(stdlib) diff --git a/libc/test/src/assert/CMakeLists.txt b/libc/test/src/assert/CMakeLists.txt new file mode 100644 index 0000000..c79c76c --- /dev/null +++ b/libc/test/src/assert/CMakeLists.txt @@ -0,0 +1,15 @@ +add_libc_testsuite(libc_assert_unittests) + +add_libc_unittest( + assert_test + SUITE + libc_assert_unittests + SRCS + assert_test.cpp + DEPENDS + __assert_fail + # These are necessary for now because dependencies are not properly added. + abort + raise + _Exit +) diff --git a/libc/test/src/assert/assert_test.cpp b/libc/test/src/assert/assert_test.cpp new file mode 100644 index 0000000..7d96f98 --- /dev/null +++ b/libc/test/src/assert/assert_test.cpp @@ -0,0 +1,32 @@ +//===---------------------- Unittests for assert --------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#undef NDEBUG +#include "src/assert/assert.h" +#include "utils/UnitTest/Test.h" + +extern "C" int close(int); + +TEST(Assert, Enabled) { + // -1 matches against any signal, which is necessary for now until + // __llvm_libc::abort() unblocks SIGABRT. Close standard error for the + // child process so we don't print the assertion failure message. + EXPECT_DEATH( + [] { + close(2); + assert(0); + }, + -1); +} + +#define NDEBUG +#include "src/assert/assert.h" + +TEST(Assert, Disabled) { + EXPECT_EXITS([] { assert(0); }, 0); +} -- 2.7.4