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>
file_test
BUILD
A typical BUILD target will look like:
load("rules.bzl", "file_test")
file_test(
name = "my_file_test",
srcs = ["my_file_test.cpp"],
tests = glob(["testdata/**"]),
deps = [
":my_lib",
"//testing/file_test:file_test_base",
"@googletest//:gtest",
"@llvm-project//llvm:Support",
],
)
Implementation
A typical implementation will look like:
#include "my_library.h"
#include "testing/file_test/file_test_base.h"
namespace Carbon::Testing {
namespace {
class MyFileTest : public FileTestBase {
public:
using FileTestBase::FileTestBase;
// Called as part of individual test executions.
auto Run(const llvm::SmallVector<llvm::StringRef>& test_args,
const llvm::SmallVector<TestFile>& test_files,
llvm::raw_pwrite_stream& stdout, llvm::raw_pwrite_stream& stderr)
-> ErrorOr<RunResult> override {
return MyFunctionality(test_args, stdout, stderr);
}
// Provides arguments which are used in tests that don't provide ARGS.
auto GetDefaultArgs() -> llvm::SmallVector<std::string> override {
return {"default_args", "%s"};
}
};
} // namespace
// Registers for the framework to construct the tests.
CARBON_FILE_TEST_FACTORY(MyFileTest);
} // namespace Carbon::Testing
Filename fail_ prefixes
When a run fails, information about what pieces failed are returned on
RunResult. This affects whether a fail_ prefix on the file is required,
including in combination with split-file tests (using the // --- <filename>
comment marker).
The main test file and any split-files must have a fail_ prefix if and only if
they have an associated error. An exception is that the main test file may omit
fail_ when it contains split-files that have a fail_ prefix.
Content replacement
Some keywords can be inserted for content:
-
[[@TEST_NAME]]Replaces with the test name, which is the filename with the extension and any
fail_ortodo_prefixes removed. For split files, this is based on the split filename.
The [[@ string is reserved for future replacements, but [[ is allowed in
content (comment markers don't allow [[).
Comment markers
Settings in files are provided in comments, similar to FileCheck syntax.
bazel run :file_test -- --autoupdate automatically constructs compatible
CHECK:STDOUT: and CHECK:STDERR: lines.
Supported comment markers are:
-
// AUTOUDPATE // NOAUTOUPDATEControls whether the checks in the file will be autoupdated if --autoupdate is passed. Exactly one of these two markers must be present. If the file uses splits, AUTOUPDATE must currently be before any splits.
When autoupdating, CHECKs will be inserted starting below AUTOUPDATE. When a CHECK has line information, autoupdate will try to insert the CHECK immediately next to the line it's associated with, with stderr CHECKs preceding the line and stdout CHECKs following the line. When that happens, any subsequent CHECK lines without line information, or that refer to lines appearing earlier, will immediately follow. As an exception, if no STDOUT check line refers to any line in the test, all STDOUT check lines are placed at the end of the file instead of immediately after AUTOUPDATE.
-
// ARGS: <arguments>Provides a space-separated list of arguments, which will be passed to RunWithFiles as test_args. These are intended for use by the command as arguments.
Supported replacements within arguments are:
-
%sReplaced with the list of files. Currently only allowed as a standalone argument, not a substring.
-
%tReplaced with
${TEST_TMPDIR}/temp_file. -
%{identifier}Replaces some implementation-specific identifier with a value. (Mappings provided by way of an optional
MyFileTest::GetArgReplacements)
ARGS can be specified at most once. If not provided, the FileTestBase child is responsible for providing default arguments.
-
-
// SET-CHECK-SUBSETBy default, all lines of output must have a CHECK match. Adding this as a option sets it so that non-matching lines are ignored. All provided CHECK:STDOUT: and CHECK:STDERR: lines must still have a match in output.
SET-CHECK-SUBSET can be specified at most once.
-
// --- <filename>By default, all file content is provided to the test as a single file in test_files. Using this marker allows the file to be split into multiple files which will all be passed to test_files.
Files are not created on disk; it's expected the child will create an InMemoryFilesystem if needed.
-
// CHECK:STDOUT: <output line> // CHECK:STDERR: <output line>These provide a match for output from the command. See
SET-CHECK-SUBSETfor how to change from full to subset matching of output.Output line matchers may contain
[[@LINE+offset]and{{regex}}syntaxes, similar toFileCheck. -
// TIP: <tip>Tips like this are added by autoupdate, for example providing commands to run the test directly. Tips have no impact on validation; the marker informs autoupdate that it can update or remove them as needed.