Skip to content

Commit 6044b30

Browse files
authored
feat: add basic support for function call (#12)
* Fix bug in auxiliary test case compilation in TestCommand * feat: add basic support for function call Signed-off-by: xizheyin <[email protected]>
1 parent ca3ebea commit 6044b30

File tree

11 files changed

+407
-55
lines changed

11 files changed

+407
-55
lines changed

bootstrap/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ trait Run {
7777
output
7878
}
7979
// Command ran but did not complete
80-
Ok(output) => panic!("command failed: {output:?}"),
80+
Ok(output) => panic!("command failed: {}", String::from_utf8_lossy(&output.stderr)),
8181
Err(e) => panic!("command failed: {e:?}"),
8282
}
8383
}

bootstrap/src/test.rs

+222-44
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ impl Run for TestCommand {
5858
self.log_action_context("source", &testcase.source.display());
5959
self.log_action_context("output", &testcase.output_file.display());
6060
testcase.build(manifest);
61-
bless(self.bless, &testcase);
61+
self.bless(self.bless, &testcase);
6262
}
6363
TestType::Compile => {
6464
self.log_action_start("TEST Compile", &testcase.name);
@@ -73,6 +73,7 @@ impl Run for TestCommand {
7373
testcase.build_lib(manifest);
7474
}
7575
}
76+
self.check_and_run_directives(&testcase);
7677
}
7778
}
7879

@@ -84,7 +85,6 @@ impl Run for TestCommand {
8485
impl TestCommand {
8586
pub fn collect_testcases(&self, manifest: &Manifest) -> Vec<TestCase> {
8687
let mut cases = vec![];
87-
8888
let verbose = self.verbose;
8989

9090
// Examples
@@ -107,7 +107,7 @@ impl TestCommand {
107107
cases.push(testcase);
108108
}
109109

110-
// Bless tests - the output should be the same as the last run
110+
// Bless tests
111111
for case in glob("tests/bless/*.rs").unwrap() {
112112
let case = case.unwrap();
113113
let filename = case.file_stem().unwrap();
@@ -117,27 +117,26 @@ impl TestCommand {
117117
cases.push(testcase);
118118
}
119119

120-
// Collect test-auxiliary
121-
let aux_use = regex::Regex::new(r"(?m)//@\s*aux-build:(?P<fname>.*)").unwrap();
120+
// Collect and process auxiliary builds from directives
122121
let mut auxiliaries = vec![];
123122
for case in cases.iter() {
124-
let content = std::fs::read_to_string(&case.source).unwrap();
125-
for cap in aux_use.captures_iter(&content) {
126-
println!("{:?}", case.source);
127-
let fname = cap.name("fname").unwrap().as_str();
128-
let source = Path::new("tests/auxiliary").join(fname);
129-
let filename = source.file_stem().unwrap();
130-
let name = format!("auxiliary/{}", filename.to_string_lossy());
131-
132-
// deduplication
133-
if auxiliaries.iter().any(|aux: &TestCase| aux.name == name) {
134-
continue;
123+
let directives = case.parse_directives();
124+
for directive in directives {
125+
if let TestDirective::AuxBuild(fname) = directive {
126+
let source = Path::new("tests/auxiliary").join(&fname);
127+
let filename = source.file_stem().unwrap();
128+
let name = format!("auxiliary/{}", filename.to_string_lossy());
129+
130+
// deduplication
131+
if auxiliaries.iter().any(|aux: &TestCase| aux.name == name) {
132+
continue;
133+
}
134+
135+
let output_file = manifest.out_dir.join(filename); // aux files are output to the base directory
136+
let testcase =
137+
TestCase::new(name, source, output_file, TestType::CompileLib, verbose);
138+
auxiliaries.push(testcase);
135139
}
136-
137-
let output_file = manifest.out_dir.join(filename); // aux files are output to the base directory
138-
let testcase =
139-
TestCase::new(name, source, output_file, TestType::CompileLib, verbose);
140-
auxiliaries.push(testcase);
141140
}
142141
}
143142

@@ -146,6 +145,141 @@ impl TestCommand {
146145
testcases.extend(cases);
147146
testcases
148147
}
148+
149+
fn bless(&self, update: bool, case: &TestCase) {
150+
let output = case.generated();
151+
let blessed = case.source.with_extension("c");
152+
153+
self.log_action_context("checking", &blessed.display());
154+
if update {
155+
self.log_action_context("updating", &blessed.display());
156+
std::fs::copy(output, &blessed).unwrap();
157+
self.log_action_context("result", "updated");
158+
} else {
159+
let output = std::fs::read_to_string(output).unwrap();
160+
let blessed = std::fs::read_to_string(&blessed).unwrap();
161+
162+
let diff = TextDiff::from_lines(&blessed, &output);
163+
if diff.ratio() < 1.0 {
164+
cprintln!("<r,s>output does not match blessed output</r,s>");
165+
for change in diff.iter_all_changes() {
166+
let lineno = change.old_index().unwrap_or(change.new_index().unwrap_or(0));
167+
match change.tag() {
168+
ChangeTag::Equal => print!(" {:4}| {}", lineno, change),
169+
ChangeTag::Insert => cprint!("<g>+{:4}| {}</g>", lineno, change),
170+
ChangeTag::Delete => cprint!("<r>-{:4}| {}</r>", lineno, change),
171+
}
172+
}
173+
std::process::exit(1);
174+
}
175+
self.log_action_context("result", "passed");
176+
}
177+
}
178+
179+
/// Run a runtime test and check its output against directives
180+
fn check_and_run_directives(&self, testcase: &TestCase) {
181+
// Parse directives from source
182+
let directives = testcase.parse_directives();
183+
self.log_action_context("directives", &format!("found {} directives", directives.len()));
184+
185+
let mut runpass = false;
186+
let mut exitcode = None;
187+
let mut stdout = None;
188+
let mut stderr = None;
189+
190+
// Check each directive
191+
for directive in directives {
192+
match directive {
193+
TestDirective::RunPass => runpass = true,
194+
TestDirective::CheckStdout(expected) => stdout = Some(expected),
195+
TestDirective::CheckStderr(expected) => stderr = Some(expected),
196+
TestDirective::ExitCode(expected) => exitcode = Some(expected),
197+
TestDirective::AuxBuild(_) => {
198+
// AuxBuild directives are handled during test collection
199+
// No need to check them during test execution
200+
}
201+
}
202+
}
203+
204+
if !runpass && (exitcode.is_some() | stdout.is_some() | stderr.is_some()) {
205+
panic!("Directives conflicts, lack of '//@ run-pass'");
206+
}
207+
208+
if runpass {
209+
self.run_and_check_output(testcase, exitcode, stdout, stderr);
210+
}
211+
212+
self.log_action_context("result", "all checks passed");
213+
}
214+
215+
fn run_and_check_output(
216+
&self,
217+
testcase: &TestCase,
218+
expected_exit: Option<i32>,
219+
expected_stdout: Option<String>,
220+
expected_stderr: Option<String>,
221+
) {
222+
// Run the test
223+
self.log_action_context("running", &testcase.output_file.display());
224+
let output = std::process::Command::new(&testcase.output_file)
225+
.output()
226+
.unwrap_or_else(|e| panic!("failed to run {}: {}", testcase.output_file.display(), e));
227+
228+
// Get actual outputs
229+
let actual_return = output.status.code().unwrap_or_else(|| {
230+
panic!("Process terminated by signal: {}", testcase.output_file.display())
231+
});
232+
let actual_stdout = String::from_utf8_lossy(&output.stdout).into_owned();
233+
let actual_stderr = String::from_utf8_lossy(&output.stderr).into_owned();
234+
235+
{
236+
let expected_exit = expected_exit.unwrap_or(0);
237+
self.log_action_context("checking exit code", &expected_exit.to_string());
238+
if actual_return != expected_exit {
239+
cprintln!("<r,s>exit code does not match expected value</r,s>");
240+
cprintln!("expected: {}", expected_exit);
241+
cprintln!("actual: {}", actual_return);
242+
std::process::exit(1);
243+
}
244+
self.log_action_context("exit code", "passed");
245+
}
246+
247+
if let Some(expected_stdout) = expected_stdout {
248+
self.log_action_context("checking stdout", &expected_stdout);
249+
let diff = TextDiff::from_lines(&expected_stdout, &actual_stdout);
250+
if diff.ratio() < 1.0 {
251+
cprintln!("<r,s>stdout does not match expected output</r,s>");
252+
for change in diff.iter_all_changes() {
253+
let lineno = change.old_index().unwrap_or(change.new_index().unwrap_or(0));
254+
match change.tag() {
255+
ChangeTag::Equal => print!(" {:4}| {}", lineno, change),
256+
ChangeTag::Insert => cprint!("<g>+{:4}| {}</g>", lineno, change),
257+
ChangeTag::Delete => cprint!("<r>-{:4}| {}</r>", lineno, change),
258+
}
259+
}
260+
std::process::exit(1);
261+
}
262+
self.log_action_context("stdout", "passed");
263+
}
264+
265+
if let Some(expected_stderr) = expected_stderr {
266+
self.log_action_context("checking stderr", &expected_stderr);
267+
let diff = TextDiff::from_lines(&expected_stderr, &actual_stderr);
268+
if diff.ratio() < 1.0 {
269+
cprintln!("<r,s>stderr does not match expected output</r,s>");
270+
for change in diff.iter_all_changes() {
271+
let lineno = change.old_index().unwrap_or(change.new_index().unwrap_or(0));
272+
match change.tag() {
273+
ChangeTag::Equal => print!(" {:4}| {}", lineno, change),
274+
ChangeTag::Insert => cprint!("<g>+{:4}| {}</g>", lineno, change),
275+
ChangeTag::Delete => cprint!("<r>-{:4}| {}</r>", lineno, change),
276+
}
277+
}
278+
std::process::exit(1);
279+
}
280+
self.log_action_context("stderr", "passed");
281+
}
282+
}
149283
}
150284

151285
#[derive(Debug)]
@@ -159,6 +293,7 @@ pub enum TestType {
159293
/// Bless test - the output should be the same as the last run
160294
Bless,
161295
}
296+
162297
impl TestType {
163298
pub fn as_str(&self) -> &'static str {
164299
match self {
@@ -241,6 +376,58 @@ impl TestCase {
241376
assert!(generated.is_some(), "could not find {case}'s generated file");
242377
generated.unwrap().path()
243378
}
379+
380+
/// Parse test directives from the source file
381+
fn parse_directives(&self) -> Vec<TestDirective> {
382+
let source = std::fs::read_to_string(&self.source)
383+
.unwrap_or_else(|e| panic!("failed to read {}: {}", self.source.display(), e));
384+
385+
let mut directives = Vec::new();
386+
387+
// Regular expressions for matching directives
388+
let run_pass = regex::Regex::new(r"^//@\s*run-pass").unwrap();
389+
let stdout_re = regex::Regex::new(r"^//@\s*check-stdout:\s*(.*)").unwrap();
390+
let stderr_re = regex::Regex::new(r"^//@\s*check-stderr:\s*(.*)").unwrap();
391+
let exit_re = regex::Regex::new(r"^//@\s*exit-code:\s*(\d+)").unwrap();
392+
let aux_re = regex::Regex::new(r"^//@\s*aux-build:\s*(.*)").unwrap();
393+
// Regex to match any directive pattern
394+
let directive_re = regex::Regex::new(r"^//@\s*([^:]+)").unwrap();
395+
396+
for (line_num, line) in source.lines().enumerate() {
397+
if let Some(_cap) = run_pass.captures(line) {
398+
directives.push(TestDirective::RunPass);
399+
} else if let Some(cap) = stdout_re.captures(line) {
400+
let content = cap[1].trim().to_string();
401+
directives.push(TestDirective::CheckStdout(content));
402+
} else if let Some(cap) = stderr_re.captures(line) {
403+
let content = cap[1].trim().to_string();
404+
directives.push(TestDirective::CheckStderr(content));
405+
} else if let Some(cap) = exit_re.captures(line) {
406+
if let Ok(code) = cap[1].parse() {
407+
directives.push(TestDirective::ExitCode(code));
408+
} else {
409+
panic!(
410+
"{}:{}: invalid exit code in directive",
411+
self.source.display(),
412+
line_num + 1
413+
);
414+
}
415+
} else if let Some(cap) = aux_re.captures(line) {
416+
let fname = cap[1].trim().to_string();
417+
directives.push(TestDirective::AuxBuild(fname));
418+
} else if let Some(cap) = directive_re.captures(line) {
419+
let directive_name = cap[1].trim();
420+
panic!(
421+
"{}:{}: unknown directive '{}', supported directives are: check-stdout, check-stderr, exit-code, aux-build",
422+
self.source.display(),
423+
line_num + 1,
424+
directive_name
425+
);
426+
}
427+
}
428+
429+
directives
430+
}
244431
}
245432

246433
struct FileChecker {
@@ -288,27 +475,18 @@ impl FileChecker {
288475
}
289476
}
290477

291-
fn bless(update: bool, case: &TestCase) {
292-
let output = case.generated();
293-
let blessed = case.source.with_extension("c");
294-
if update {
295-
std::fs::copy(output, blessed).unwrap();
296-
} else {
297-
let output = std::fs::read_to_string(output).unwrap();
298-
let blessed = std::fs::read_to_string(blessed).unwrap();
299-
300-
let diff = TextDiff::from_lines(&blessed, &output);
301-
if diff.ratio() < 1.0 {
302-
cprintln!("<r,s>output does not match blessed output</r,s>");
303-
for change in diff.iter_all_changes() {
304-
let lineno = change.old_index().unwrap_or(change.new_index().unwrap_or(0));
305-
match change.tag() {
306-
ChangeTag::Equal => print!(" {:4}| {}", lineno, change),
307-
ChangeTag::Insert => cprint!("<g>+{:4}| {}</g>", lineno, change),
308-
ChangeTag::Delete => cprint!("<r>-{:4}| {}</r>", lineno, change),
309-
}
310-
}
311-
std::process::exit(1);
312-
}
313-
}
478+
/// Test directives that can appear in source files
479+
#[derive(Debug)]
480+
enum TestDirective {
481+
/// Compile and run a testcase,
482+
/// expect a success (exit with 0)
483+
RunPass,
484+
/// Expected stdout content
485+
CheckStdout(String),
486+
/// Expected stderr content
487+
CheckStderr(String),
488+
/// Expected exit code
489+
ExitCode(i32),
490+
/// Auxiliary build requirement
491+
AuxBuild(String),
314492
}

crates/rustc_codegen_c/src/builder.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,19 @@ impl<'a, 'tcx, 'mx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx, 'mx> {
714714
funclet: Option<&Self::Funclet>,
715715
instance: Option<rustc_middle::ty::Instance<'tcx>>,
716716
) -> Self::Value {
717-
todo!()
717+
use crate::rustc_codegen_ssa::traits::LayoutTypeMethods;
718+
719+
let fn_abi = fn_abi.unwrap();
720+
let ret_ty = self.cx.immediate_backend_type(fn_abi.ret.layout);
721+
722+
let args = args.iter().map(|v| self.mcx.value(*v)).collect();
723+
724+
let call = self.mcx.call(self.mcx.value(llfn), args);
725+
let ret = self.bb.0.next_local_var();
726+
727+
self.bb.0.push_stmt(self.mcx.decl_stmt(self.mcx.var(ret, ret_ty, Some(call))));
728+
729+
ret
718730
}
719731

720732
fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value {

crates/rustc_codegen_c/src/context/layout_type.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use rustc_abi::Abi;
2+
use rustc_codegen_c_ast::ty::CTy;
23
use rustc_codegen_ssa::traits::LayoutTypeMethods;
34
use rustc_middle::ty::layout::TyAndLayout;
45
use rustc_middle::ty::Ty;
@@ -17,7 +18,7 @@ impl<'tcx, 'mx> LayoutTypeMethods<'tcx> for CodegenCx<'tcx, 'mx> {
1718
}
1819

1920
fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type {
20-
todo!()
21+
CTy::Void
2122
}
2223

2324
fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Self::Type {

crates/rustc_codegen_c/src/context/misc.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::cell::RefCell;
22

3+
use rustc_codegen_c_ast::expr::CValue;
34
use rustc_codegen_ssa::traits::MiscMethods;
45
use rustc_hash::FxHashMap;
56
use rustc_middle::mir::mono::CodegenUnit;
@@ -19,7 +20,11 @@ impl<'tcx, 'mx> MiscMethods<'tcx> for CodegenCx<'tcx, 'mx> {
1920
}
2021

2122
fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value {
22-
todo!()
23+
let funcs = self.mcx.module().funcs.borrow();
24+
let path = self.tcx.def_path_debug_str(instance.def_id());
25+
let name = path.split("::").last().unwrap();
26+
let func = funcs.iter().find(|f| f.0.name == name).unwrap();
27+
CValue::Func(func.0.name)
2328
}
2429

2530
fn eh_personality(&self) -> Self::Value {

0 commit comments

Comments
 (0)