diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt --- a/libc/src/__support/CMakeLists.txt +++ b/libc/src/__support/CMakeLists.txt @@ -6,6 +6,7 @@ HDRS blockstore.h DEPENDS + .libc_assert libc.src.__support.CPP.new ) @@ -87,6 +88,7 @@ float_to_string.h ryu_constants.h DEPENDS + .libc_assert libc.src.__support.CPP.type_traits libc.src.__support.FPUtil.fp_bits libc.src.__support.common @@ -190,6 +192,15 @@ .uint ) +add_header_library( + libc_assert + HDRS + libc_assert.h + DEPENDS + .integer_to_string + libc.src.__support.OSUtil.osutil +) + add_subdirectory(FPUtil) add_subdirectory(OSUtil) add_subdirectory(StringUtil) diff --git a/libc/src/__support/blockstore.h b/libc/src/__support/blockstore.h --- a/libc/src/__support/blockstore.h +++ b/libc/src/__support/blockstore.h @@ -10,15 +10,11 @@ #define LLVM_LIBC_SUPPORT_BLOCKSTORE_H #include +#include #include #include -// TODO: fix our assert.h to make it useable -#define assert(x) \ - if (!(x)) \ - __builtin_trap() - namespace __llvm_libc { namespace cpp { @@ -56,7 +52,7 @@ Block *curr = &first; for (; curr->next; prev = curr, curr = curr->next) ; - assert(curr == current); + LIBC_ASSERT(curr == current); return {curr, prev}; } @@ -152,10 +148,10 @@ return; auto [last, prev] = getLastBlocks(); if (REVERSE_ORDER) { - assert(last == current); + LIBC_ASSERT(last == current); current = current->next; } else { - assert(prev->next == last); + LIBC_ASSERT(prev->next == last); current = prev; current->next = nullptr; } diff --git a/libc/src/__support/float_to_string.h b/libc/src/__support/float_to_string.h --- a/libc/src/__support/float_to_string.h +++ b/libc/src/__support/float_to_string.h @@ -15,6 +15,7 @@ #include "src/__support/FPUtil/FPBits.h" #include "src/__support/UInt.h" #include "src/__support/common.h" +#include "src/__support/libc_assert.h" #include "src/__support/ryu_constants.h" // This implementation is based on the Ryu Printf algorithm by Ulf Adams: @@ -61,7 +62,9 @@ // Returns floor(log_10(2^e)); requires 0 <= e <= 1650. LIBC_INLINE constexpr uint32_t log10_pow2(const uint32_t e) { // The first value this approximation fails for is 2^1651 which is just - // greater than 10^297. assert(e >= 0); assert(e <= 1650); + // greater than 10^297. + LIBC_ASSERT(e >= 0 && e <= 1650 && + "Incorrect exponent to perform log10_pow2 approximation."); return (e * 78913) >> 18; } diff --git a/libc/src/__support/libc_assert.h b/libc/src/__support/libc_assert.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/libc_assert.h @@ -0,0 +1,61 @@ +//===-- Definition of a libc internal assert macro --------------*- 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_SUPPORT_LIBC_ASSERT_H +#define LLVM_LIBC_SRC_SUPPORT_LIBC_ASSERT_H + +#include "src/__support/OSUtil/io.h" +#include "src/__support/OSUtil/quick_exit.h" +#include "src/__support/integer_to_string.h" +#include "src/__support/macros/attributes.h" // For LIBC_INLINE + +namespace __llvm_libc { + +LIBC_INLINE void report_assertion_failure(const char *assertion, + const char *filename, unsigned line, + const char *funcname) { + char line_str[IntegerToString::dec_bufsize()]; + IntegerToString::dec(line, line_str); + __llvm_libc::write_to_stderr(filename); + __llvm_libc::write_to_stderr(":"); + __llvm_libc::write_to_stderr(line_str); + __llvm_libc::write_to_stderr(": Assertion failed: '"); + __llvm_libc::write_to_stderr(assertion); + __llvm_libc::write_to_stderr("' in function: '"); + __llvm_libc::write_to_stderr(funcname); + __llvm_libc::write_to_stderr("'\n"); +} + +} // namespace __llvm_libc + +#ifdef LIBC_ASSERT +#error "Unexpected: LIBC_ASSERT macro already defined" +#endif + +// The public "assert" macro calls abort on failure. Should it be same here? +// The libc intenral assert can fire from anywhere inside the libc. So, to +// avoid potential chicken-and-egg problems, it is simple to do a quick_exit +// on assertion failure instead of calling abort. We also don't want to use +// __builtin_trap as it could potentially be implemented using illegal +// instructions which can be very misleading when debugging. +#ifdef NDEBUG +#define LIBC_ASSERT(COND) \ + do { \ + } while (false) +#else +#define LIBC_ASSERT(COND) \ + do { \ + if (!(COND)) { \ + __llvm_libc::report_assertion_failure(#COND, __FILE__, __LINE__, \ + __PRETTY_FUNCTION__); \ + __llvm_libc::quick_exit(0xFF); \ + } \ + } while (false) +#endif // NDEBUG + +#endif // LLVM_LIBC_SRC_SUPPORT_LIBC_ASSERT_H diff --git a/libc/src/assert/__assert_fail.cpp b/libc/src/assert/__assert_fail.cpp --- a/libc/src/assert/__assert_fail.cpp +++ b/libc/src/assert/__assert_fail.cpp @@ -6,8 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "src/__support/OSUtil/io.h" #include "src/assert/__assert_fail.h" +#include "src/__support/OSUtil/io.h" +#include "src/__support/libc_assert.h" #include "src/stdlib/abort.h" namespace __llvm_libc { @@ -15,13 +16,7 @@ LLVM_LIBC_FUNCTION(void, __assert_fail, (const char *assertion, const char *file, unsigned line, const char *function)) { - (void)line; // Suppress warning as long as line is unused. - write_to_stderr(file); - write_to_stderr(": Assertion failed: '"); - write_to_stderr(assertion); - write_to_stderr("' in function: '"); - write_to_stderr(function); - write_to_stderr("'\n"); + __llvm_libc::report_assertion_failure(assertion, file, line, function); __llvm_libc::abort(); } diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt --- a/libc/src/stdio/printf_core/CMakeLists.txt +++ b/libc/src/stdio/printf_core/CMakeLists.txt @@ -90,6 +90,7 @@ libc.src.__support.FPUtil.fp_bits libc.src.__support.FPUtil.fenv_impl libc.src.__support.common + libc.src.__support.libc_assert libc.src.__support.uint libc.src.__support.uint128 libc.src.__support.integer_to_string diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h --- a/libc/src/stdio/printf_core/float_dec_converter.h +++ b/libc/src/stdio/printf_core/float_dec_converter.h @@ -18,6 +18,7 @@ #include "src/__support/common.h" #include "src/__support/float_to_string.h" #include "src/__support/integer_to_string.h" +#include "src/__support/libc_assert.h" #include "src/stdio/printf_core/converter_utils.h" #include "src/stdio/printf_core/core_structs.h" #include "src/stdio/printf_core/float_inf_nan_converter.h" @@ -1058,7 +1059,7 @@ } digits_checked += digits_requested; - // assert(digits_checked == init_precision); + LIBC_ASSERT(digits_checked == init_precision); // At this point we should have checked all the digits requested by the // precision. We may increment this number 1 more if we round up all of the // digits, but at this point in the code digits_checked should always equal