Skip to content

Commit 5f60dea

Browse files
committed
Revamp codegen tests to check IR quality instead of quantity
The current codegen tests only compare IR line counts between similar rust and C programs, the latter getting compiled with clang. That looked like a good idea back then, but actually things like lifetime intrinsics mean that less IR isn't always better, so the metric isn't really helpful. Instead, we can start doing tests that check specific aspects of the generated IR, like attributes or metadata. To do that, we can use LLVM's FileCheck tool which has a number of useful features for such tests. To start off, I created some tests for a few things that were recently added and/or broken.
1 parent ba0e1cd commit 5f60dea

24 files changed

+204
-518
lines changed

src/compiletest/common.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,6 @@ pub struct Config {
8080
// The python executable
8181
pub python: String,
8282

83-
// The clang executable
84-
pub clang_path: Option<PathBuf>,
85-
8683
// The llvm binaries path
8784
pub llvm_bin_path: Option<PathBuf>,
8885

src/compiletest/compiletest.rs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use std::fs;
3333
use std::path::{Path, PathBuf};
3434
use getopts::{optopt, optflag, reqopt};
3535
use common::Config;
36-
use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
36+
use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
3737
use util::logv;
3838

3939
pub mod procsrv;
@@ -133,7 +133,6 @@ pub fn parse_config(args: Vec<String> ) -> Config {
133133
rustc_path: opt_path(matches, "rustc-path"),
134134
rustdoc_path: opt_path(matches, "rustdoc-path"),
135135
python: matches.opt_str("python").unwrap(),
136-
clang_path: matches.opt_str("clang-path").map(|s| PathBuf::from(&s)),
137136
valgrind_path: matches.opt_str("valgrind-path"),
138137
force_valgrind: matches.opt_present("force-valgrind"),
139138
llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
@@ -284,13 +283,7 @@ pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
284283
let file = file.unwrap().path();
285284
debug!("inspecting file {:?}", file.display());
286285
if is_test(config, &file) {
287-
let t = make_test(config, &file, || {
288-
match config.mode {
289-
Codegen => make_metrics_test_closure(config, &file),
290-
_ => make_test_closure(config, &file)
291-
}
292-
});
293-
tests.push(t)
286+
tests.push(make_test(config, &file))
294287
}
295288
}
296289
tests
@@ -323,16 +316,15 @@ pub fn is_test(config: &Config, testfile: &Path) -> bool {
323316
return valid;
324317
}
325318

326-
pub fn make_test<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
327-
F: FnOnce() -> test::TestFn,
319+
pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn
328320
{
329321
test::TestDescAndFn {
330322
desc: test::TestDesc {
331323
name: make_test_name(config, testfile),
332324
ignore: header::is_test_ignored(config, testfile),
333325
should_panic: test::ShouldPanic::No,
334326
},
335-
testfn: f(),
327+
testfn: make_test_closure(config, &testfile),
336328
}
337329
}
338330

@@ -357,14 +349,6 @@ pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
357349
}))
358350
}
359351

360-
pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
361-
let config = (*config).clone();
362-
let testfile = testfile.to_path_buf();
363-
test::DynMetricFn(box move |mm: &mut test::MetricMap| {
364-
runtest::run_metrics(config, &testfile, mm)
365-
})
366-
}
367-
368352
fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
369353
match full_version_line {
370354
Some(ref full_version_line)

src/compiletest/runtest.rs

Lines changed: 14 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ use std::iter::repeat;
2828
use std::net::TcpStream;
2929
use std::path::{Path, PathBuf};
3030
use std::process::{Command, Output, ExitStatus};
31-
use std::str;
32-
use test::MetricMap;
3331

3432
pub fn run(config: Config, testfile: &Path) {
3533
match &*config.target {
@@ -43,11 +41,6 @@ pub fn run(config: Config, testfile: &Path) {
4341
_=> { }
4442
}
4543

46-
let mut _mm = MetricMap::new();
47-
run_metrics(config, testfile, &mut _mm);
48-
}
49-
50-
pub fn run_metrics(config: Config, testfile: &Path, mm: &mut MetricMap) {
5144
if config.verbose {
5245
// We're going to be dumping a lot of info. Start on a new line.
5346
print!("\n\n");
@@ -64,7 +57,7 @@ pub fn run_metrics(config: Config, testfile: &Path, mm: &mut MetricMap) {
6457
Pretty => run_pretty_test(&config, &props, &testfile),
6558
DebugInfoGdb => run_debuginfo_gdb_test(&config, &props, &testfile),
6659
DebugInfoLldb => run_debuginfo_lldb_test(&config, &props, &testfile),
67-
Codegen => run_codegen_test(&config, &props, &testfile, mm),
60+
Codegen => run_codegen_test(&config, &props, &testfile),
6861
Rustdoc => run_rustdoc_test(&config, &props, &testfile),
6962
}
7063
}
@@ -1685,26 +1678,16 @@ fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) {
16851678
}
16861679
}
16871680

1688-
// codegen tests (vs. clang)
1689-
1690-
fn append_suffix_to_stem(p: &Path, suffix: &str) -> PathBuf {
1691-
if suffix.is_empty() {
1692-
p.to_path_buf()
1693-
} else {
1694-
let mut stem = p.file_stem().unwrap().to_os_string();
1695-
stem.push("-");
1696-
stem.push(suffix);
1697-
p.with_file_name(&stem)
1698-
}
1699-
}
1681+
// codegen tests (using FileCheck)
17001682

1701-
fn compile_test_and_save_bitcode(config: &Config, props: &TestProps,
1683+
fn compile_test_and_save_ir(config: &Config, props: &TestProps,
17021684
testfile: &Path) -> ProcRes {
17031685
let aux_dir = aux_output_dir_name(config, testfile);
17041686
// FIXME (#9639): This needs to handle non-utf8 paths
17051687
let mut link_args = vec!("-L".to_string(),
17061688
aux_dir.to_str().unwrap().to_string());
1707-
let llvm_args = vec!("--emit=llvm-bc,obj".to_string(),
1689+
let llvm_args = vec!("--emit=llvm-ir".to_string(),
1690+
"-Cno-prepopulate-passes".to_string(),
17081691
"--crate-type=lib".to_string());
17091692
link_args.extend(llvm_args.into_iter());
17101693
let args = make_compile_args(config,
@@ -1717,121 +1700,34 @@ fn compile_test_and_save_bitcode(config: &Config, props: &TestProps,
17171700
compose_and_run_compiler(config, props, testfile, args, None)
17181701
}
17191702

1720-
fn compile_cc_with_clang_and_save_bitcode(config: &Config, _props: &TestProps,
1721-
testfile: &Path) -> ProcRes {
1722-
let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1723-
let bitcodefile = append_suffix_to_stem(&bitcodefile, "clang");
1724-
let testcc = testfile.with_extension("cc");
1725-
let proc_args = ProcArgs {
1726-
// FIXME (#9639): This needs to handle non-utf8 paths
1727-
prog: config.clang_path.as_ref().unwrap().to_str().unwrap().to_string(),
1728-
args: vec!("-c".to_string(),
1729-
"-emit-llvm".to_string(),
1730-
"-o".to_string(),
1731-
bitcodefile.to_str().unwrap().to_string(),
1732-
testcc.to_str().unwrap().to_string())
1733-
};
1734-
compose_and_run(config, testfile, proc_args, Vec::new(), "", None, None)
1735-
}
1736-
1737-
fn extract_function_from_bitcode(config: &Config, _props: &TestProps,
1738-
fname: &str, testfile: &Path,
1739-
suffix: &str) -> ProcRes {
1740-
let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1741-
let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
1742-
let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
1743-
let prog = config.llvm_bin_path.as_ref().unwrap().join("llvm-extract");
1703+
fn check_ir_with_filecheck(config: &Config, testfile: &Path) -> ProcRes {
1704+
let irfile = output_base_name(config, testfile).with_extension("ll");
1705+
let prog = config.llvm_bin_path.as_ref().unwrap().join("FileCheck");
17441706
let proc_args = ProcArgs {
17451707
// FIXME (#9639): This needs to handle non-utf8 paths
17461708
prog: prog.to_str().unwrap().to_string(),
1747-
args: vec!(format!("-func={}", fname),
1748-
format!("-o={}", extracted_bc.to_str().unwrap()),
1749-
bitcodefile.to_str().unwrap().to_string())
1709+
args: vec!(format!("-input-file={}", irfile.to_str().unwrap()),
1710+
testfile.to_str().unwrap().to_string())
17501711
};
17511712
compose_and_run(config, testfile, proc_args, Vec::new(), "", None, None)
17521713
}
17531714

1754-
fn disassemble_extract(config: &Config, _props: &TestProps,
1755-
testfile: &Path, suffix: &str) -> ProcRes {
1756-
let bitcodefile = output_base_name(config, testfile).with_extension("bc");
1757-
let bitcodefile = append_suffix_to_stem(&bitcodefile, suffix);
1758-
let extracted_bc = append_suffix_to_stem(&bitcodefile, "extract");
1759-
let extracted_ll = extracted_bc.with_extension("ll");
1760-
let prog = config.llvm_bin_path.as_ref().unwrap().join("llvm-dis");
1761-
let proc_args = ProcArgs {
1762-
// FIXME (#9639): This needs to handle non-utf8 paths
1763-
prog: prog.to_str().unwrap().to_string(),
1764-
args: vec!(format!("-o={}", extracted_ll.to_str().unwrap()),
1765-
extracted_bc.to_str().unwrap().to_string())
1766-
};
1767-
compose_and_run(config, testfile, proc_args, Vec::new(), "", None, None)
1768-
}
1769-
1770-
1771-
fn count_extracted_lines(p: &Path) -> usize {
1772-
let mut x = Vec::new();
1773-
File::open(&p.with_extension("ll")).unwrap().read_to_end(&mut x).unwrap();
1774-
let x = str::from_utf8(&x).unwrap();
1775-
x.lines().count()
1776-
}
1777-
1778-
1779-
fn run_codegen_test(config: &Config, props: &TestProps,
1780-
testfile: &Path, mm: &mut MetricMap) {
1715+
fn run_codegen_test(config: &Config, props: &TestProps, testfile: &Path) {
17811716

17821717
if config.llvm_bin_path.is_none() {
17831718
fatal("missing --llvm-bin-path");
17841719
}
17851720

1786-
if config.clang_path.is_none() {
1787-
fatal("missing --clang-path");
1788-
}
1789-
1790-
let mut proc_res = compile_test_and_save_bitcode(config, props, testfile);
1791-
if !proc_res.status.success() {
1792-
fatal_proc_rec("compilation failed!", &proc_res);
1793-
}
1794-
1795-
proc_res = extract_function_from_bitcode(config, props, "test", testfile, "");
1796-
if !proc_res.status.success() {
1797-
fatal_proc_rec("extracting 'test' function failed",
1798-
&proc_res);
1799-
}
1800-
1801-
proc_res = disassemble_extract(config, props, testfile, "");
1802-
if !proc_res.status.success() {
1803-
fatal_proc_rec("disassembling extract failed", &proc_res);
1804-
}
1805-
1806-
1807-
let mut proc_res = compile_cc_with_clang_and_save_bitcode(config, props, testfile);
1721+
let mut proc_res = compile_test_and_save_ir(config, props, testfile);
18081722
if !proc_res.status.success() {
18091723
fatal_proc_rec("compilation failed!", &proc_res);
18101724
}
18111725

1812-
proc_res = extract_function_from_bitcode(config, props, "test", testfile, "clang");
1726+
proc_res = check_ir_with_filecheck(config, testfile);
18131727
if !proc_res.status.success() {
1814-
fatal_proc_rec("extracting 'test' function failed",
1728+
fatal_proc_rec("verification with 'FileCheck' failed",
18151729
&proc_res);
18161730
}
1817-
1818-
proc_res = disassemble_extract(config, props, testfile, "clang");
1819-
if !proc_res.status.success() {
1820-
fatal_proc_rec("disassembling extract failed", &proc_res);
1821-
}
1822-
1823-
let base = output_base_name(config, testfile);
1824-
let base_extract = append_suffix_to_stem(&base, "extract");
1825-
1826-
let base_clang = append_suffix_to_stem(&base, "clang");
1827-
let base_clang_extract = append_suffix_to_stem(&base_clang, "extract");
1828-
1829-
let base_lines = count_extracted_lines(&base_extract);
1830-
let clang_lines = count_extracted_lines(&base_clang_extract);
1831-
1832-
mm.insert_metric("clang-codegen-ratio",
1833-
(base_lines as f64) / (clang_lines as f64),
1834-
0.001);
18351731
}
18361732

18371733
fn charset() -> &'static str {
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(allocator)]
12+
13+
pub struct S {
14+
_field: [i64; 4],
15+
}
16+
17+
pub struct UnsafeInner {
18+
_field: std::cell::UnsafeCell<i16>,
19+
}
20+
21+
// CHECK: zeroext i1 @boolean(i1 zeroext)
22+
#[no_mangle]
23+
pub fn boolean(x: bool) -> bool {
24+
x
25+
}
26+
27+
// CHECK: @readonly_borrow(i32* noalias readonly dereferenceable(4))
28+
// FIXME #25759 This should also have `nocapture`
29+
#[no_mangle]
30+
pub fn readonly_borrow(_: &i32) {
31+
}
32+
33+
// CHECK: @static_borrow(i32* noalias readonly dereferenceable(4))
34+
// static borrow may be captured
35+
#[no_mangle]
36+
pub fn static_borrow(_: &'static i32) {
37+
}
38+
39+
// CHECK: @named_borrow(i32* noalias readonly dereferenceable(4))
40+
// borrow with named lifetime may be captured
41+
#[no_mangle]
42+
pub fn named_borrow<'r>(_: &'r i32) {
43+
}
44+
45+
// CHECK: @unsafe_borrow(%UnsafeInner* dereferenceable(2))
46+
// unsafe interior means this isn't actually readonly and there may be aliases ...
47+
#[no_mangle]
48+
pub fn unsafe_borrow(_: &UnsafeInner) {
49+
}
50+
51+
// CHECK: @mutable_unsafe_borrow(%UnsafeInner* noalias dereferenceable(2))
52+
// ... unless this is a mutable borrow, those never alias
53+
#[no_mangle]
54+
pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) {
55+
}
56+
57+
// CHECK: @mutable_borrow(i32* noalias dereferenceable(4))
58+
// FIXME #25759 This should also have `nocapture`
59+
#[no_mangle]
60+
pub fn mutable_borrow(_: &mut i32) {
61+
}
62+
63+
// CHECK: @indirect_struct(%S* noalias nocapture dereferenceable(32))
64+
#[no_mangle]
65+
pub fn indirect_struct(_: S) {
66+
}
67+
68+
// CHECK: @borrowed_struct(%S* noalias readonly dereferenceable(32))
69+
// FIXME #25759 This should also have `nocapture`
70+
#[no_mangle]
71+
pub fn borrowed_struct(_: &S) {
72+
}
73+
74+
// CHECK: noalias dereferenceable(4) i32* @_box(i32* noalias dereferenceable(4))
75+
#[no_mangle]
76+
pub fn _box(x: Box<i32>) -> Box<i32> {
77+
x
78+
}
79+
80+
// CHECK: @struct_return(%S* noalias nocapture sret dereferenceable(32))
81+
#[no_mangle]
82+
pub fn struct_return() -> S {
83+
S {
84+
_field: [0, 0, 0, 0]
85+
}
86+
}
87+
88+
// CHECK: noalias i8* @allocator()
89+
#[no_mangle]
90+
#[allocator]
91+
pub fn allocator() -> *const i8 {
92+
std::ptr::null()
93+
}

src/test/codegen/iterate-over-array.cc

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)