From b26ca3cbddcf20c6e2e2c7f3b3b4faa343b09d82 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 21 Apr 2020 12:00:45 -0700 Subject: [PATCH] Add `test interpret` support to filetests --- Cargo.lock | 16 +++++ cranelift/filetests/Cargo.toml | 1 + .../filetests/filetests/interpreter/add.clif | 13 ++++ .../filetests/interpreter/fibonacci.clif | 64 +++++++++++++++++++ cranelift/filetests/src/lib.rs | 12 ++-- cranelift/filetests/src/test_interpret.rs | 61 ++++++++++++++++++ 6 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 cranelift/filetests/filetests/interpreter/add.clif create mode 100644 cranelift/filetests/filetests/interpreter/fibonacci.clif create mode 100644 cranelift/filetests/src/test_interpret.rs diff --git a/Cargo.lock b/Cargo.lock index 0a4594c3c5..9b920dfae2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -404,6 +404,7 @@ dependencies = [ "byteorder", "cranelift-codegen", "cranelift-frontend", + "cranelift-interpreter", "cranelift-native", "cranelift-preopt", "cranelift-reader", @@ -429,6 +430,21 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cranelift-interpreter" +version = "0.1.0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-reader", + "hashbrown 0.7.2", + "log", + "pretty_env_logger", + "thiserror", + "walkdir", +] + [[package]] name = "cranelift-module" version = "0.63.0" diff --git a/cranelift/filetests/Cargo.toml b/cranelift/filetests/Cargo.toml index 7defcea924..481401cf8a 100644 --- a/cranelift/filetests/Cargo.toml +++ b/cranelift/filetests/Cargo.toml @@ -12,6 +12,7 @@ edition = "2018" [dependencies] cranelift-codegen = { path = "../codegen", version = "0.63.0", features = ["testing_hooks"] } cranelift-frontend = { path = "../frontend", version = "0.63.0" } +cranelift-interpreter = { path = "../interpreter", version = "*" } cranelift-native = { path = "../native", version = "0.63.0" } cranelift-reader = { path = "../reader", version = "0.63.0" } cranelift-preopt = { path = "../preopt", version = "0.63.0" } diff --git a/cranelift/filetests/filetests/interpreter/add.clif b/cranelift/filetests/filetests/interpreter/add.clif new file mode 100644 index 0000000000..d95298ad84 --- /dev/null +++ b/cranelift/filetests/filetests/interpreter/add.clif @@ -0,0 +1,13 @@ +test interpret + +function %add() -> i32 { +block0: + v0 = iconst.i32 40 + v1 = iconst.i32 2 + v2 = iadd v0, v1 + jump block1(v2) + +block1(v3: i32): + return v3 +} +; run: %add() == 42 diff --git a/cranelift/filetests/filetests/interpreter/fibonacci.clif b/cranelift/filetests/filetests/interpreter/fibonacci.clif new file mode 100644 index 0000000000..70515c4b20 --- /dev/null +++ b/cranelift/filetests/filetests/interpreter/fibonacci.clif @@ -0,0 +1,64 @@ +test interpret + +; A non-recursive fibonacci implementation. +function %fibonacci(i32) -> i32 { +block0(v0: i32): + v1 = icmp_imm ule v0, 2 + v2 = iconst.i32 1 + brnz v1, block3(v2) ; handle base case, n <= 2 + fallthrough block1(v0, v2) + +block1(v4: i32, v5:i32): + v6 = iconst.i32 1 + v7 = irsub_imm v4, 2 + fallthrough block2(v7, v5, v6) + +block2(v10: i32, v11: i32, v12: i32): ; params: n, fib(n-1), fib(n-2) + v13 = iadd v11, v12 + v14 = irsub_imm v10, 1 + v15 = icmp_imm eq v14, 0 + brnz v15, block3(v13) + jump block2(v14, v13, v11) + +block3(v20: i32): ; early return and end of loop + return v20 +} +; run: %fibonacci(0) == 1 +; run: %fibonacci(1) == 1 +; run: %fibonacci(2) == 1 +; run: %fibonacci(3) == 2 +; run: %fibonacci(4) == 3 +; run: %fibonacci(5) == 5 +; run: %fibonacci(6) == 8 +; run: %fibonacci(10) == 55 + + +; A recursive fibonacci implementation. +function %fibonacci_recursive(i32) -> i32 { + fn0 = %fibonacci_recursive(i32) -> i32 + +block0(v0: i32): + v1 = icmp_imm ule v0, 2 + brnz v1, block2 + fallthrough block1(v0) + +block1(v10: i32): + v11 = irsub_imm v10, 1 + v12 = call fn0(v11) + v13 = irsub_imm v10, 2 + v14 = call fn0(v13) + v15 = iadd v12, v14 + return v15 + +block2: + v20 = iconst.i32 1 + return v20 +} +; run: %fibonacci_recurs(0) == 1 +; run: %fibonacci_recurs(1) == 1 +; run: %fibonacci_recurs(2) == 1 +; run: %fibonacci_recurs(3) == 2 +; run: %fibonacci_recurs(4) == 3 +; run: %fibonacci_recurs(5) == 5 +; run: %fibonacci_recurs(6) == 8 +; run: %fibonacci_recurs(10) == 55 diff --git a/cranelift/filetests/src/lib.rs b/cranelift/filetests/src/lib.rs index 7b831ebb54..bc1a6df1e2 100644 --- a/cranelift/filetests/src/lib.rs +++ b/cranelift/filetests/src/lib.rs @@ -42,6 +42,7 @@ mod test_cat; mod test_compile; mod test_dce; mod test_domtree; +mod test_interpret; mod test_legalizer; mod test_licm; mod test_postopt; @@ -122,23 +123,24 @@ fn new_subtest(parsed: &TestCommand) -> subtest::SubtestResult test_binemit::subtest(parsed), "cat" => test_cat::subtest(parsed), "compile" => test_compile::subtest(parsed), - "rodata" => test_rodata::subtest(parsed), "dce" => test_dce::subtest(parsed), "domtree" => test_domtree::subtest(parsed), + "interpret" => test_interpret::subtest(parsed), "legalizer" => test_legalizer::subtest(parsed), "licm" => test_licm::subtest(parsed), "postopt" => test_postopt::subtest(parsed), - "simple_preopt" => test_simple_preopt::subtest(parsed), + "preopt" => test_preopt::subtest(parsed), "print-cfg" => test_print_cfg::subtest(parsed), "regalloc" => test_regalloc::subtest(parsed), + "rodata" => test_rodata::subtest(parsed), "run" => test_run::subtest(parsed), + "safepoint" => test_safepoint::subtest(parsed), "shrink" => test_shrink::subtest(parsed), "simple-gvn" => test_simple_gvn::subtest(parsed), + "simple_preopt" => test_simple_preopt::subtest(parsed), + "unwind" => test_unwind::subtest(parsed), "vcode" => test_vcode::subtest(parsed), "verifier" => test_verifier::subtest(parsed), - "preopt" => test_preopt::subtest(parsed), - "safepoint" => test_safepoint::subtest(parsed), - "unwind" => test_unwind::subtest(parsed), _ => Err(format!("unknown test command '{}'", parsed.command)), } } diff --git a/cranelift/filetests/src/test_interpret.rs b/cranelift/filetests/src/test_interpret.rs new file mode 100644 index 0000000000..ccde4a0084 --- /dev/null +++ b/cranelift/filetests/src/test_interpret.rs @@ -0,0 +1,61 @@ +//! Test command for interpreting CLIF files and verifying their results +//! +//! The `interpret` test command interprets each function on the host machine using [RunCommand]s. + +use crate::subtest::{Context, SubTest, SubtestResult}; +use cranelift_codegen::{self, ir}; +use cranelift_interpreter::environment::Environment; +use cranelift_interpreter::interpreter::{ControlFlow, Interpreter}; +use cranelift_reader::{parse_run_command, TestCommand}; +use log::trace; +use std::borrow::Cow; + +struct TestInterpret; + +pub fn subtest(parsed: &TestCommand) -> SubtestResult> { + assert_eq!(parsed.command, "interpret"); + if !parsed.options.is_empty() { + Err(format!("No options allowed on {}", parsed)) + } else { + Ok(Box::new(TestInterpret)) + } +} + +impl SubTest for TestInterpret { + fn name(&self) -> &'static str { + "interpret" + } + + fn is_mutating(&self) -> bool { + false + } + + fn needs_isa(&self) -> bool { + false + } + + fn run(&self, func: Cow, context: &Context) -> SubtestResult<()> { + for comment in context.details.comments.iter() { + if let Some(command) = + parse_run_command(comment.text, &func.signature).map_err(|e| e.to_string())? + { + trace!("Parsed run command: {}", command); + + let mut env = Environment::default(); + env.add(func.name.to_string(), func.clone().into_owned()); + let interpreter = Interpreter::new(env); + + command.run(|func_name, args| { + // Because we have stored function names with a leading %, we need to re-add it. + let func_name = &format!("%{}", func_name); + match interpreter.call_by_name(func_name, args) { + Ok(ControlFlow::Return(results)) => Ok(results), + Ok(_) => panic!("Unexpected returned control flow--this is likely a bug."), + Err(t) => Err(t.to_string()), + } + })?; + } + } + Ok(()) + } +}