Skip to content

Commit f56782a

Browse files
committed
Auto merge of #25762 - dotdash:codegen_test, r=alexcrichton
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.
2 parents eb16ad6 + 6773675 commit f56782a

25 files changed

+209
-524
lines changed

mk/tests.mk

-5
Original file line numberDiff line numberDiff line change
@@ -581,10 +581,6 @@ ifeq ($(CFG_LLDB),)
581581
CTEST_DISABLE_debuginfo-lldb = "no lldb found"
582582
endif
583583

584-
ifeq ($(CFG_CLANG),)
585-
CTEST_DISABLE_codegen = "no clang found"
586-
endif
587-
588584
ifneq ($(CFG_OSTYPE),apple-darwin)
589585
CTEST_DISABLE_debuginfo-lldb = "lldb tests are only run on darwin"
590586
endif
@@ -645,7 +641,6 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \
645641
--run-lib-path $$(TLIB$(1)_T_$(2)_H_$(3)) \
646642
--rustc-path $$(HBIN$(1)_H_$(3))/rustc$$(X_$(3)) \
647643
--rustdoc-path $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \
648-
--clang-path $(if $(CFG_CLANG),$(CFG_CLANG),clang) \
649644
--llvm-bin-path $(CFG_LLVM_INST_DIR_$(CFG_BUILD))/bin \
650645
--aux-base $$(S)src/test/auxiliary/ \
651646
--stage-id stage$(1)-$(2) \

src/compiletest/common.rs

-3
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

+4-21
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;
@@ -63,7 +63,6 @@ pub fn parse_config(args: Vec<String> ) -> Config {
6363
reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
6464
reqopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH"),
6565
reqopt("", "python", "path to python to use for doc tests", "PATH"),
66-
optopt("", "clang-path", "path to executable for codegen tests", "PATH"),
6766
optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM"),
6867
optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind"),
6968
optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
@@ -133,7 +132,6 @@ pub fn parse_config(args: Vec<String> ) -> Config {
133132
rustc_path: opt_path(matches, "rustc-path"),
134133
rustdoc_path: opt_path(matches, "rustdoc-path"),
135134
python: matches.opt_str("python").unwrap(),
136-
clang_path: matches.opt_str("clang-path").map(|s| PathBuf::from(&s)),
137135
valgrind_path: matches.opt_str("valgrind-path"),
138136
force_valgrind: matches.opt_present("force-valgrind"),
139137
llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
@@ -284,13 +282,7 @@ pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
284282
let file = file.unwrap().path();
285283
debug!("inspecting file {:?}", file.display());
286284
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)
285+
tests.push(make_test(config, &file))
294286
}
295287
}
296288
tests
@@ -323,16 +315,15 @@ pub fn is_test(config: &Config, testfile: &Path) -> bool {
323315
return valid;
324316
}
325317

326-
pub fn make_test<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
327-
F: FnOnce() -> test::TestFn,
318+
pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn
328319
{
329320
test::TestDescAndFn {
330321
desc: test::TestDesc {
331322
name: make_test_name(config, testfile),
332323
ignore: header::is_test_ignored(config, testfile),
333324
should_panic: test::ShouldPanic::No,
334325
},
335-
testfn: f(),
326+
testfn: make_test_closure(config, &testfile),
336327
}
337328
}
338329

@@ -357,14 +348,6 @@ pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
357348
}))
358349
}
359350

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-
368351
fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
369352
match full_version_line {
370353
Some(ref full_version_line)

src/compiletest/runtest.rs

+13-118
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,15 @@ 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(),
17081690
"--crate-type=lib".to_string());
17091691
link_args.extend(llvm_args.into_iter());
17101692
let args = make_compile_args(config,
@@ -1717,121 +1699,34 @@ fn compile_test_and_save_bitcode(config: &Config, props: &TestProps,
17171699
compose_and_run_compiler(config, props, testfile, args, None)
17181700
}
17191701

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");
1702+
fn check_ir_with_filecheck(config: &Config, testfile: &Path) -> ProcRes {
1703+
let irfile = output_base_name(config, testfile).with_extension("ll");
1704+
let prog = config.llvm_bin_path.as_ref().unwrap().join("FileCheck");
17441705
let proc_args = ProcArgs {
17451706
// FIXME (#9639): This needs to handle non-utf8 paths
17461707
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())
1708+
args: vec!(format!("-input-file={}", irfile.to_str().unwrap()),
1709+
testfile.to_str().unwrap().to_string())
17501710
};
17511711
compose_and_run(config, testfile, proc_args, Vec::new(), "", None, None)
17521712
}
17531713

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) {
1714+
fn run_codegen_test(config: &Config, props: &TestProps, testfile: &Path) {
17811715

17821716
if config.llvm_bin_path.is_none() {
17831717
fatal("missing --llvm-bin-path");
17841718
}
17851719

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);
1720+
let mut proc_res = compile_test_and_save_ir(config, props, testfile);
18081721
if !proc_res.status.success() {
18091722
fatal_proc_rec("compilation failed!", &proc_res);
18101723
}
18111724

1812-
proc_res = extract_function_from_bitcode(config, props, "test", testfile, "clang");
1725+
proc_res = check_ir_with_filecheck(config, testfile);
18131726
if !proc_res.status.success() {
1814-
fatal_proc_rec("extracting 'test' function failed",
1727+
fatal_proc_rec("verification with 'FileCheck' failed",
18151728
&proc_res);
18161729
}
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);
18351730
}
18361731

18371732
fn charset() -> &'static str {
+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
// compile-flags: -C no-prepopulate-passes
12+
13+
#![feature(allocator)]
14+
15+
pub struct S {
16+
_field: [i64; 4],
17+
}
18+
19+
pub struct UnsafeInner {
20+
_field: std::cell::UnsafeCell<i16>,
21+
}
22+
23+
// CHECK: zeroext i1 @boolean(i1 zeroext)
24+
#[no_mangle]
25+
pub fn boolean(x: bool) -> bool {
26+
x
27+
}
28+
29+
// CHECK: @readonly_borrow(i32* noalias readonly dereferenceable(4))
30+
// FIXME #25759 This should also have `nocapture`
31+
#[no_mangle]
32+
pub fn readonly_borrow(_: &i32) {
33+
}
34+
35+
// CHECK: @static_borrow(i32* noalias readonly dereferenceable(4))
36+
// static borrow may be captured
37+
#[no_mangle]
38+
pub fn static_borrow(_: &'static i32) {
39+
}
40+
41+
// CHECK: @named_borrow(i32* noalias readonly dereferenceable(4))
42+
// borrow with named lifetime may be captured
43+
#[no_mangle]
44+
pub fn named_borrow<'r>(_: &'r i32) {
45+
}
46+
47+
// CHECK: @unsafe_borrow(%UnsafeInner* dereferenceable(2))
48+
// unsafe interior means this isn't actually readonly and there may be aliases ...
49+
#[no_mangle]
50+
pub fn unsafe_borrow(_: &UnsafeInner) {
51+
}
52+
53+
// CHECK: @mutable_unsafe_borrow(%UnsafeInner* noalias dereferenceable(2))
54+
// ... unless this is a mutable borrow, those never alias
55+
#[no_mangle]
56+
pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) {
57+
}
58+
59+
// CHECK: @mutable_borrow(i32* noalias dereferenceable(4))
60+
// FIXME #25759 This should also have `nocapture`
61+
#[no_mangle]
62+
pub fn mutable_borrow(_: &mut i32) {
63+
}
64+
65+
// CHECK: @indirect_struct(%S* noalias nocapture dereferenceable(32))
66+
#[no_mangle]
67+
pub fn indirect_struct(_: S) {
68+
}
69+
70+
// CHECK: @borrowed_struct(%S* noalias readonly dereferenceable(32))
71+
// FIXME #25759 This should also have `nocapture`
72+
#[no_mangle]
73+
pub fn borrowed_struct(_: &S) {
74+
}
75+
76+
// CHECK: noalias dereferenceable(4) i32* @_box(i32* noalias dereferenceable(4))
77+
#[no_mangle]
78+
pub fn _box(x: Box<i32>) -> Box<i32> {
79+
x
80+
}
81+
82+
// CHECK: @struct_return(%S* noalias nocapture sret dereferenceable(32))
83+
#[no_mangle]
84+
pub fn struct_return() -> S {
85+
S {
86+
_field: [0, 0, 0, 0]
87+
}
88+
}
89+
90+
// CHECK: noalias i8* @allocator()
91+
#[no_mangle]
92+
#[allocator]
93+
pub fn allocator() -> *const i8 {
94+
std::ptr::null()
95+
}

0 commit comments

Comments
 (0)