[libc] Add initial assert definition
authorAlex Brachet <alexbrachetmialot@gmail.com>
Thu, 12 Mar 2020 03:45:58 +0000 (23:45 -0400)
committerAlex Brachet <alexbrachetmialot@gmail.com>
Thu, 12 Mar 2020 03:45:58 +0000 (23:45 -0400)
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

12 files changed:
libc/config/linux/api.td
libc/include/CMakeLists.txt
libc/include/assert.h.def [new file with mode: 0644]
libc/lib/CMakeLists.txt
libc/spec/stdc.td
libc/src/CMakeLists.txt
libc/src/assert/CMakeLists.txt [new file with mode: 0644]
libc/src/assert/__assert_fail.cpp [new file with mode: 0644]
libc/src/assert/assert.h [new file with mode: 0644]
libc/test/src/CMakeLists.txt
libc/test/src/assert/CMakeLists.txt [new file with mode: 0644]
libc/test/src/assert/assert_test.cpp [new file with mode: 0644]

index 1d78a59..aecdce2 100644 (file)
@@ -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",
index bbc41b0..2ff0423 100644 (file)
@@ -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 (file)
index 0000000..3d78277
--- /dev/null
@@ -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()
index ed91367..238bcb4 100644 (file)
@@ -2,6 +2,9 @@
 add_entrypoint_library(
   llvmlibc
   DEPENDS
+    # assert.h entrypoints
+    __assert_fail
+
     # errno.h entrypoints
     __errno_location
 
index babfce4..4df8550 100644 (file)
@@ -16,6 +16,17 @@ def StdC : StandardSpec<"stdc"> {
 
   PtrType IntPtr = PtrType<IntType>;
 
+  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,
index 4e661c8..3ee30fd 100644 (file)
@@ -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 (file)
index 0000000..4c39b0f
--- /dev/null
@@ -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 (file)
index 0000000..4e72ac7
--- /dev/null
@@ -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 (file)
index 0000000..ef596da
--- /dev/null
@@ -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 <stddef.h>
+
+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
index 2ff4ca4..30c5616 100644 (file)
@@ -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 (file)
index 0000000..c79c76c
--- /dev/null
@@ -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 (file)
index 0000000..7d96f98
--- /dev/null
@@ -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);
+}