mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 21:30:12 +01:00
This switches `DCHECK` and `FATAL` as well. The goal is to reduce the code size impact of these assertions so that we can keep more of them enabled. Currently, the largest cost I see from `CHECK` is not the actual check or the cold code itself, but actually the failure to inline trivial functions due to the presence of the cold code. This means that our goal isn't to reduce apparent code size in the final binary but the LLVM IR cost assessed for these routines in the inliner, which closely correlates with code size but is a bit different. As discussed in #4283, experimentation shows that a single function call with a minimal number of arguments is the lowest cost model for these. This is easily achieved with a format-string API that internally uses `llvm::formatv`. This PR is essentially the `CHECK` version of #4283. However, the check macros are substantially harder to make work with both format strings and streaming because they also take a condition. Also, unexpectedly, I was very successful at devising a regular expression based automated rewrite from the streaming to the format string form with only low 10s of manual fixes. This includes compacting strings broken up across lines, etc. Given how well that went, I've prepared this PR which just directly switches to the format string API and migrate everything to use it. One nice side-effect is that the format string approach ends up greatly simplifying the implementation here as well. This is ... *shockingly* effective. Parsing speeds up by more than 3% with just this change. And checking speeds up by **8%** with this change alone: ``` BM_CompileAPIFileDenseDecls<Phase::Parse>/256 86.3µs ± 1% 82.9µs ± 1% -3.94% (p=0.000 n=17+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/1024 431µs ± 1% 415µs ± 1% -3.76% (p=0.000 n=18+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/4096 1.77ms ± 1% 1.71ms ± 1% -3.18% (p=0.000 n=18+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/16384 7.44ms ± 1% 7.17ms ± 2% -3.56% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/65536 30.7ms ± 1% 29.7ms ± 1% -3.15% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/262144 131ms ± 1% 127ms ± 1% -2.81% (p=0.000 n=18+18) BM_CompileAPIFileDenseDecls<Phase::Check>/256 878µs ± 2% 800µs ± 1% -8.91% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/1024 1.88ms ± 2% 1.72ms ± 1% -8.56% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/4096 5.78ms ± 2% 5.28ms ± 1% -8.70% (p=0.000 n=20+18) BM_CompileAPIFileDenseDecls<Phase::Check>/16384 21.9ms ± 1% 20.1ms ± 1% -8.02% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Check>/65536 90.4ms ± 2% 83.1ms ± 1% -8.04% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/262144 381ms ± 2% 352ms ± 1% -7.79% (p=0.000 n=19+19) ``` --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk> Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
111 lines
5.0 KiB
C++
111 lines
5.0 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#ifndef CARBON_COMMON_CHECK_INTERNAL_H_
|
|
#define CARBON_COMMON_CHECK_INTERNAL_H_
|
|
|
|
#include "common/template_string.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
|
|
namespace Carbon::Internal {
|
|
|
|
// Implements the check failure message printing.
|
|
//
|
|
// This is out-of-line and will arrange to stop the program, print any debugging
|
|
// information and this string.
|
|
//
|
|
// This API uses `const char*` C string arguments rather than `llvm::StringRef`
|
|
// because we know that these are available as C strings and passing them that
|
|
// way lets the code size of calling it be smaller: it only needs to materialize
|
|
// a single pointer argument for each. The runtime cost of re-computing the size
|
|
// should be minimal. The extra message however might not be compile-time
|
|
// guaranteed to be a C string so we use a normal `StringRef` there.
|
|
[[noreturn]] auto CheckFailImpl(const char* kind, const char* file, int line,
|
|
const char* condition_str,
|
|
llvm::StringRef extra_message) -> void;
|
|
|
|
// Prints a check failure, including rendering any user-provided message using
|
|
// a format string.
|
|
//
|
|
// Most of the parameters are passed as compile-time template strings to avoid
|
|
// runtime cost of parameter setup in optimized builds. Each of these are passed
|
|
// along to the underlying implementation to include in the final printed
|
|
// message.
|
|
//
|
|
// Any user-provided format string and values are directly passed to
|
|
// `llvm::formatv` which handles all of the formatting of output.
|
|
template <TemplateString Kind, TemplateString File, int Line,
|
|
TemplateString ConditionStr, TemplateString FormatStr, typename... Ts>
|
|
[[noreturn, gnu::cold, clang::noinline, clang::preserve_most]] auto CheckFail(
|
|
Ts&&... values) -> void {
|
|
if constexpr (llvm::StringRef(FormatStr).empty()) {
|
|
// Skip the format string rendering if empty. Note that we don't skip it
|
|
// even if there are no values as we want to have consistent handling of
|
|
// `{}`s in the format string. This case is about when there is no message
|
|
// at all, just the condition.
|
|
CheckFailImpl(Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(), "");
|
|
} else {
|
|
CheckFailImpl(
|
|
Kind.c_str(), File.c_str(), Line, ConditionStr.c_str(),
|
|
llvm::formatv(FormatStr.c_str(), std::forward<Ts>(values)...).str());
|
|
}
|
|
}
|
|
|
|
} // namespace Carbon::Internal
|
|
|
|
// Implements check messages without any formatted values.
|
|
//
|
|
// Passes each of the provided components of the message to the template
|
|
// parameters of the check failure printing function above, including an empty
|
|
// string for the format string. Because there are multiple template arguments,
|
|
// the entire call is wrapped in parentheses.
|
|
#define CARBON_INTERNAL_CHECK_IMPL(kind, file, line, condition_str) \
|
|
(Carbon::Internal::CheckFail<kind, file, line, condition_str, "">())
|
|
|
|
// Implements check messages with a format string and potentially formatted
|
|
// values.
|
|
//
|
|
// Each of the main components is passed as a template arguments, and then any
|
|
// formatted values are passed as arguments. Because there are multiple template
|
|
// arguments, the entire call is wrapped in parentheses.
|
|
#define CARBON_INTERNAL_CHECK_IMPL_FORMAT(kind, file, line, condition_str, \
|
|
format_str, ...) \
|
|
(Carbon::Internal::CheckFail<kind, file, line, condition_str, format_str>( \
|
|
__VA_ARGS__))
|
|
|
|
// Implements the failure of a check.
|
|
//
|
|
// Collects all the metadata about the failure to be printed, such as source
|
|
// location and stringified condition, and passes those, any format string and
|
|
// formatted arguments to the correct implementation macro above.
|
|
#define CARBON_INTERNAL_CHECK(condition, ...) \
|
|
CARBON_INTERNAL_CHECK_IMPL##__VA_OPT__(_FORMAT)( \
|
|
"CHECK", __FILE__, __LINE__, #condition __VA_OPT__(, ) __VA_ARGS__)
|
|
|
|
// Implements the fatal macro.
|
|
//
|
|
// Similar to the check failure macro, but tags the message as a fatal one and
|
|
// leaves the stringified condition empty.
|
|
#define CARBON_INTERNAL_FATAL(...) \
|
|
CARBON_INTERNAL_CHECK_IMPL##__VA_OPT__(_FORMAT)( \
|
|
"FATAL", __FILE__, __LINE__, "" __VA_OPT__(, ) __VA_ARGS__)
|
|
|
|
#ifdef NDEBUG
|
|
// For `DCHECK` in optimized builds we have a dead check that we want to
|
|
// potentially "use" arguments, but otherwise have the minimal overhead. We
|
|
// avoid forming interesting format strings here so that we don't have to
|
|
// repeatedly instantiate the `Check` function above. This format string would
|
|
// be an error if actually used.
|
|
#define CARBON_INTERNAL_DEAD_DCHECK(condition, ...) \
|
|
CARBON_INTERNAL_DEAD_DCHECK_IMPL##__VA_OPT__(_FORMAT)(__VA_ARGS__)
|
|
|
|
#define CARBON_INTERNAL_DEAD_DCHECK_IMPL() \
|
|
Carbon::Internal::CheckFail<"", "", 0, "", "">()
|
|
|
|
#define CARBON_INTERNAL_DEAD_DCHECK_IMPL_FORMAT(format_str, ...) \
|
|
Carbon::Internal::CheckFail<"", "", 0, "", "">(__VA_ARGS__)
|
|
#endif
|
|
|
|
#endif // CARBON_COMMON_CHECK_INTERNAL_H_
|