Skip to content

Commit

Permalink
Auto merge of rust-lang#120656 - Zalathar:filecheck-flags, r=wesleywiser
Browse files Browse the repository at this point in the history
Allow tests to specify a `//@ filecheck-flags:` header

This allows individual codegen/assembly/mir-opt tests to pass extra flags to the LLVM `filecheck` tool as needed.

---

The original motivation was noticing that `tests/run-make/instrument-coverage` was very close to being an ordinary codegen test, except that it needs some extra logic to set up platform-specific variables to be passed into filecheck.

I then saw the comment in `verify_with_filecheck` indicating that a `filecheck-flags` header might be useful for other purposes as well.
  • Loading branch information
bors committed Feb 23, 2024
2 parents 8f359be + e56cc84 commit b9ad718
Show file tree
Hide file tree
Showing 14 changed files with 190 additions and 168 deletions.
8 changes: 8 additions & 0 deletions src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ pub struct TestProps {
/// Extra flags to pass to `llvm-cov` when producing coverage reports.
/// Only used by the "coverage-run" test mode.
pub llvm_cov_flags: Vec<String>,
/// Extra flags to pass to LLVM's `filecheck` tool, in tests that use it.
pub filecheck_flags: Vec<String>,
}

mod directives {
Expand Down Expand Up @@ -236,6 +238,7 @@ mod directives {
pub const REMAP_SRC_BASE: &'static str = "remap-src-base";
pub const COMPARE_OUTPUT_LINES_BY_SUBSET: &'static str = "compare-output-lines-by-subset";
pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags";
pub const FILECHECK_FLAGS: &'static str = "filecheck-flags";
// This isn't a real directive, just one that is probably mistyped often
pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags";
}
Expand Down Expand Up @@ -286,6 +289,7 @@ impl TestProps {
mir_unit_test: None,
remap_src_base: false,
llvm_cov_flags: vec![],
filecheck_flags: vec![],
}
}

Expand Down Expand Up @@ -542,6 +546,10 @@ impl TestProps {
if let Some(flags) = config.parse_name_value_directive(ln, LLVM_COV_FLAGS) {
self.llvm_cov_flags.extend(split_flags(&flags));
}

if let Some(flags) = config.parse_name_value_directive(ln, FILECHECK_FLAGS) {
self.filecheck_flags.extend(split_flags(&flags));
}
},
);

Expand Down
39 changes: 23 additions & 16 deletions src/tools/compiletest/src/runtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2906,25 +2906,32 @@ impl<'test> TestCx<'test> {
fn verify_with_filecheck(&self, output: &Path) -> ProcRes {
let mut filecheck = Command::new(self.config.llvm_filecheck.as_ref().unwrap());
filecheck.arg("--input-file").arg(output).arg(&self.testpaths.file);
// It would be more appropriate to make most of the arguments configurable through
// a comment-attribute similar to `compile-flags`. For example, --check-prefixes is a very
// useful flag.
//
// For now, though…
let prefix_for_target =
if self.config.target.contains("msvc") { "MSVC" } else { "NONMSVC" };
let prefixes = if let Some(rev) = self.revision {
format!("CHECK,{},{}", prefix_for_target, rev)
} else {
format!("CHECK,{}", prefix_for_target)
};
if self.config.llvm_version.unwrap_or(0) >= 130000 {
filecheck.args(&["--allow-unused-prefixes", "--check-prefixes", &prefixes]);
} else {
filecheck.args(&["--check-prefixes", &prefixes]);

// FIXME: Consider making some of these prefix flags opt-in per test,
// via `filecheck-flags` or by adding new header directives.

// Because we use custom prefixes, we also have to register the default prefix.
filecheck.arg("--check-prefix=CHECK");

// Some tests use the current revision name as a check prefix.
if let Some(rev) = self.revision {
filecheck.arg("--check-prefix").arg(rev);
}

// Some tests also expect either the MSVC or NONMSVC prefix to be defined.
let msvc_or_not = if self.config.target.contains("msvc") { "MSVC" } else { "NONMSVC" };
filecheck.arg("--check-prefix").arg(msvc_or_not);

// The filecheck tool normally fails if a prefix is defined but not used.
// However, we define several prefixes globally for all tests.
filecheck.arg("--allow-unused-prefixes");

// Provide more context on failures.
filecheck.args(&["--dump-input-context", "100"]);

// Add custom flags supplied by the `filecheck-flags:` test header.
filecheck.args(&self.props.filecheck_flags);

self.compose_and_run(filecheck, "", None, None)
}

Expand Down
File renamed without changes.
File renamed without changes.
120 changes: 120 additions & 0 deletions tests/codegen/instrument-coverage/testprog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//@ edition: 2021
//@ needs-profiler-support
//@ compile-flags: -Cinstrument-coverage -Copt-level=0
//@ revisions: LINUX DARWIN WINDOWS

//@ [LINUX] only-linux
//@ [LINUX] filecheck-flags: -DINSTR_PROF_DATA=__llvm_prf_data
//@ [LINUX] filecheck-flags: -DINSTR_PROF_NAME=__llvm_prf_names
//@ [LINUX] filecheck-flags: -DINSTR_PROF_CNTS=__llvm_prf_cnts
//@ [LINUX] filecheck-flags: -DINSTR_PROF_COVMAP=__llvm_covmap
//@ [LINUX] filecheck-flags: -DINSTR_PROF_COVFUN=__llvm_covfun
//@ [LINUX] filecheck-flags: '-DCOMDAT_IF_SUPPORTED=, comdat'

//@ [DARWIN] only-macos
//@ [DARWIN] filecheck-flags: -DINSTR_PROF_DATA=__DATA,__llvm_prf_data,regular,live_support
//@ [DARWIN] filecheck-flags: -DINSTR_PROF_NAME=__DATA,__llvm_prf_names
//@ [DARWIN] filecheck-flags: -DINSTR_PROF_CNTS=__DATA,__llvm_prf_cnts
//@ [DARWIN] filecheck-flags: -DINSTR_PROF_COVMAP=__LLVM_COV,__llvm_covmap
//@ [DARWIN] filecheck-flags: -DINSTR_PROF_COVFUN=__LLVM_COV,__llvm_covfun
//@ [DARWIN] filecheck-flags: -DCOMDAT_IF_SUPPORTED=

//@ [WINDOWS] only-windows
//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_DATA=.lprfd$M
//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_NAME=.lprfn$M
//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_CNTS=.lprfc$M
//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_COVMAP=.lcovmap$M
//@ [WINDOWS] filecheck-flags: -DINSTR_PROF_COVFUN=.lcovfun$M
//@ [WINDOWS] filecheck-flags: '-DCOMDAT_IF_SUPPORTED=, comdat'

// ignore-tidy-linelength

pub fn will_be_called() -> &'static str {
let val = "called";
println!("{}", val);
val
}

pub fn will_not_be_called() -> bool {
println!("should not have been called");
false
}

pub fn print<T>(left: &str, value: T, right: &str)
where
T: std::fmt::Display,
{
println!("{}{}{}", left, value, right);
}

pub fn wrap_with<F, T>(inner: T, should_wrap: bool, wrapper: F)
where
F: FnOnce(&T)
{
if should_wrap {
wrapper(&inner)
}
}

fn main() {
let less = 1;
let more = 100;

if less < more {
wrap_with(will_be_called(), less < more, |inner| print(" ***", inner, "*** "));
wrap_with(will_be_called(), more < less, |inner| print(" ***", inner, "*** "));
} else {
wrap_with(will_not_be_called(), true, |inner| print("wrapped result is: ", inner, ""));
}
}

// Check for metadata, variables, declarations, and function definitions injected
// into LLVM IR when compiling with -Cinstrument-coverage.

// WINDOWS: $__llvm_profile_runtime_user = comdat any

// CHECK: @__llvm_coverage_mapping = private constant
// CHECK-SAME: section "[[INSTR_PROF_COVMAP]]", align 8

// CHECK: @__covrec_{{[A-F0-9]+}}u = linkonce_odr hidden constant
// CHECK-SAME: section "[[INSTR_PROF_COVFUN]]"[[COMDAT_IF_SUPPORTED]], align 8

// WINDOWS: @__llvm_profile_runtime = external{{.*}}global i32

// CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global
// CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8

// CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global
// CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called
// CHECK-SAME: section "[[INSTR_PROF_DATA]]"{{.*}}, align 8

// CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main = {{private|internal}} global
// CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8

// CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog4main = {{private|internal}} global
// CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main
// CHECK-SAME: section "[[INSTR_PROF_DATA]]"{{.*}}, align 8

// CHECK: @__llvm_prf_nm = private constant
// CHECK-SAME: section "[[INSTR_PROF_NAME]]", align 1

// CHECK: @llvm.used = appending global
// CHECK-SAME: @__llvm_coverage_mapping
// CHECK-SAME: @__llvm_prf_nm
// CHECK-SAME: section "llvm.metadata"

// CHECK: define internal { {{.*}} } @_R{{[a-zA-Z0-9_]+}}testprog14will_be_called() unnamed_addr #{{[0-9]+}} {
// CHECK-NEXT: start:
// CHECK-NOT: define internal
// CHECK: atomicrmw add ptr
// CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called,

// CHECK: declare void @llvm.instrprof.increment(ptr, i64, i32, i32) #[[LLVM_INSTRPROF_INCREMENT_ATTR:[0-9]+]]

// WINDOWS: define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #[[LLVM_PROFILE_RUNTIME_USER_ATTR:[0-9]+]] comdat {
// WINDOWS-NEXT: %1 = load i32, ptr @__llvm_profile_runtime
// WINDOWS-NEXT: ret i32 %1
// WINDOWS-NEXT: }

// CHECK: attributes #[[LLVM_INSTRPROF_INCREMENT_ATTR]] = { nounwind }
// WINDOWS: attributes #[[LLVM_PROFILE_RUNTIME_USER_ATTR]] = { noinline }
4 changes: 4 additions & 0 deletions tests/codegen/meta-filecheck/check-prefix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Simple test that uses the default CHECK prefix and should always succeed.

// CHECK: main
fn main() {}
8 changes: 8 additions & 0 deletions tests/codegen/meta-filecheck/filecheck-flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Arguments provided via `filecheck-flags` should be passed to `filecheck`.

//@ revisions: good bad
//@ [good] filecheck-flags: --check-prefix=CUSTOM
//@ [bad] should-fail

// CUSTOM: main
fn main() {}
7 changes: 7 additions & 0 deletions tests/codegen/meta-filecheck/msvc-prefix-bad.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// This is exactly like `msvc-prefix-good.rs`, except that it should always fail.

//@ should-fail

// MSVC: text that should not match
// NONMSVC: text that should not match
fn main() {}
7 changes: 7 additions & 0 deletions tests/codegen/meta-filecheck/msvc-prefix-good.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// One of MSVC or NONMSVC should always be defined, so this test should pass.

// (one of these should always be present)

// MSVC: main
// NONMSVC: main
fn main() {}
5 changes: 5 additions & 0 deletions tests/codegen/meta-filecheck/no-directives.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// A test that doesn't include any filecheck directives should fail.

//@ should-fail

fn main() {}
8 changes: 8 additions & 0 deletions tests/codegen/meta-filecheck/revision-prefix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// The current revision name is registered as a filecheck prefix.

//@ revisions: GOOD BAD
//@ [BAD] should-fail

// GOOD: main
// BAD: text that should not match
fn main() {}
64 changes: 0 additions & 64 deletions tests/run-make/coverage-llvmir/Makefile

This file was deleted.

50 changes: 0 additions & 50 deletions tests/run-make/coverage-llvmir/filecheck.testprog.txt

This file was deleted.

Loading

0 comments on commit b9ad718

Please sign in to comment.