Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ target-lexicon = "0.4.0"
pretty_env_logger = "0.3.0"
file-per-thread-logger = "0.1.2"
indicatif = "0.11.0"
walkdir = "2.2"

[features]
default = ["disas", "wasm", "cranelift-codegen/all-arch"]
Expand Down
10 changes: 10 additions & 0 deletions cranelift-codegen/src/binemit/memorysink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,16 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
}
}

/// A `RelocSink` implementation that does nothing, which is convenient when
/// compiling code that does not relocate anything.
pub struct NullRelocSink {}

impl RelocSink for NullRelocSink {
fn reloc_ebb(&mut self, _: u32, _: Reloc, _: u32) {}
fn reloc_external(&mut self, _: u32, _: Reloc, _: &ExternalName, _: i64) {}
fn reloc_jt(&mut self, _: u32, _: Reloc, _: JumpTable) {}
}

/// A `TrapSink` implementation that does nothing, which is convenient when
/// compiling code that does not rely on trapping semantics.
pub struct NullTrapSink {}
Expand Down
3 changes: 2 additions & 1 deletion cranelift-codegen/src/binemit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ mod shrink;
mod stackmap;

pub use self::memorysink::{
MemoryCodeSink, NullStackmapSink, NullTrapSink, RelocSink, StackmapSink, TrapSink,
MemoryCodeSink, NullRelocSink, NullStackmapSink, NullTrapSink, RelocSink, StackmapSink,
TrapSink,
};
pub use self::relaxation::relax_branches;
pub use self::shrink::shrink_instructions;
Expand Down
5 changes: 4 additions & 1 deletion cranelift-filetests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ edition = "2018"

[dependencies]
cranelift-codegen = { path = "../cranelift-codegen", version = "0.40.0", features = ["testing_hooks"] }
cranelift-native = { path = "../cranelift-native", version = "0.40.0" }
cranelift-reader = { path = "../cranelift-reader", version = "0.40.0" }
cranelift-preopt = { path = "../cranelift-preopt", version = "0.40.0" }
file-per-thread-logger = "0.1.2"
filecheck = "0.4.0"
num_cpus = "1.8.0"
log = "0.4.6"
mmap = "0.1.1"
num_cpus = "1.8.0"
region = "2.1.2"
Comment thread
abrown marked this conversation as resolved.
118 changes: 118 additions & 0 deletions cranelift-filetests/src/function_runner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use core::mem;
use cranelift_codegen::binemit::{NullRelocSink, NullStackmapSink, NullTrapSink};
use cranelift_codegen::ir::Function;
use cranelift_codegen::isa::{CallConv, TargetIsa};
use cranelift_codegen::{settings, Context};
use cranelift_native::builder as host_isa_builder;
use mmap::{MapOption, MemoryMap};
use region;
use region::Protection;

/// Run a function on a host
pub struct FunctionRunner {
function: Function,
isa: Box<dyn TargetIsa>,
}

impl FunctionRunner {
/// Build a function runner from a function and the ISA to run on (must be the host machine's ISA)
pub fn new(function: Function, isa: Box<dyn TargetIsa>) -> Self {
FunctionRunner { function, isa }
}

/// Build a function runner using the host machine's ISA and the passed flags
pub fn with_host_isa(function: Function, flags: settings::Flags) -> Self {
let builder = host_isa_builder().expect("Unable to build a TargetIsa for the current host");
let isa = builder.finish(flags);
FunctionRunner::new(function, isa)
}

/// Build a function runner using the host machine's ISA and the default flags for this ISA
pub fn with_default_host_isa(function: Function) -> Self {
let flags = settings::Flags::new(settings::builder());
FunctionRunner::with_host_isa(function, flags)
}

/// Compile and execute a single function, expecting a boolean to be returned; a 'true' value is
/// interpreted as a successful test execution and mapped to Ok whereas a 'false' value is
/// interpreted as a failed test and mapped to Err.
pub fn run(&self) -> Result<(), String> {
let func = self.function.clone();
if !(func.signature.params.is_empty()
&& func.signature.returns.len() == 1
&& func.signature.returns.first().unwrap().value_type.is_bool())
{
return Err(String::from(
"Functions must have a signature like: () -> boolean",
));
}

if func.signature.call_conv != self.isa.default_call_conv()
&& func.signature.call_conv != CallConv::Fast
{
// ideally we wouldn't have to also check for Fast here but currently there is no way to inform the filetest parser that we would like to use a default other than Fast
return Err(String::from(
"Functions only run on the host's default calling convention; remove the specified calling convention in the function signature to use the host's default.",
));
}

// set up the context
let mut context = Context::new();
context.func = func;

// compile and encode the result to machine code
let relocs = &mut NullRelocSink {};
let traps = &mut NullTrapSink {};
let stackmaps = &mut NullStackmapSink {};
let code_info = context
.compile(self.isa.as_ref())
.map_err(|e| e.to_string())?;
let code_page = MemoryMap::new(code_info.total_size as usize, &[MapOption::MapWritable])
.map_err(|e| e.to_string())?;
let callable_fn: fn() -> bool = unsafe {
context.emit_to_memory(
self.isa.as_ref(),
code_page.data(),
relocs,
traps,
stackmaps,
);
region::protect(code_page.data(), code_page.len(), Protection::ReadExecute)
.map_err(|e| e.to_string())?;
mem::transmute(code_page.data())
};

// execute
match callable_fn() {
true => Ok(()),
false => Err(format!("Failed: {}", context.func.name.to_string())),
}
}
}

#[cfg(test)]
mod test {
use super::*;
use cranelift_reader::parse_test;

#[test]
fn nop() {
let code = String::from(
"function %test() -> b8 system_v {
ebb0:
nop
v1 = bconst.b8 true
return v1
}",
);

// extract function
let test_file = parse_test(code.as_str(), None, None).unwrap();
assert_eq!(1, test_file.functions.len());
let function = test_file.functions[0].0.clone();

// execute function
let runner = FunctionRunner::with_default_host_isa(function);
runner.run().unwrap() // will panic if execution fails
}
}
4 changes: 4 additions & 0 deletions cranelift-filetests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
)
)]

pub use crate::function_runner::FunctionRunner;
use crate::runner::TestRunner;
use cranelift_codegen::timing;
use cranelift_reader::TestCommand;
use std::path::Path;
use std::time;

mod concurrent;
mod function_runner;
mod match_directive;
mod runner;
mod runone;
Expand All @@ -46,6 +48,7 @@ mod test_postopt;
mod test_preopt;
mod test_print_cfg;
mod test_regalloc;
mod test_run;
mod test_safepoint;
mod test_shrink;
mod test_simple_gvn;
Expand Down Expand Up @@ -124,6 +127,7 @@ fn new_subtest(parsed: &TestCommand) -> subtest::SubtestResult<Box<dyn subtest::
"simple_preopt" => test_simple_preopt::subtest(parsed),
"print-cfg" => test_print_cfg::subtest(parsed),
"regalloc" => test_regalloc::subtest(parsed),
"run" => test_run::subtest(parsed),
"shrink" => test_shrink::subtest(parsed),
"simple-gvn" => test_simple_gvn::subtest(parsed),
"verifier" => test_verifier::subtest(parsed),
Expand Down
46 changes: 46 additions & 0 deletions cranelift-filetests/src/test_run.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Test command for running CLIF files and verifying their results
//!
//! The `run` test command compiles each function on the host machine and executes it

use crate::function_runner::FunctionRunner;
use crate::subtest::{Context, SubTest, SubtestResult};
use cranelift_codegen;
use cranelift_codegen::ir;
use cranelift_reader::TestCommand;
use std::borrow::Cow;

struct TestRun;

pub fn subtest(parsed: &TestCommand) -> SubtestResult<Box<dyn SubTest>> {
assert_eq!(parsed.command, "run");
if !parsed.options.is_empty() {
Err(format!("No options allowed on {}", parsed))
} else {
Ok(Box::new(TestRun))
}
}

impl SubTest for TestRun {
fn name(&self) -> &'static str {
"run"
}

fn is_mutating(&self) -> bool {
false
}

fn needs_isa(&self) -> bool {
false
}

fn run(&self, func: Cow<ir::Function>, context: &Context) -> SubtestResult<()> {
for comment in context.details.comments.iter() {
if comment.text.contains("run") {
let runner =
FunctionRunner::with_host_isa(func.clone().into_owned(), context.flags.clone());
runner.run()?
}
}
Ok(())
}
}
11 changes: 11 additions & 0 deletions filetests/isa/x86/run-const.clif
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
test run

function %test_compare_i32() -> b1 {
ebb0:
v0 = iconst.i32 42
v1 = iconst.i32 42
v2 = icmp eq v0, v1
return v2
}

; run
16 changes: 16 additions & 0 deletions src/clif-util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mod cat;
mod compile;
mod disasm;
mod print_cfg;
mod run;
mod utils;

/// A command either succeeds or fails with an error message.
Expand Down Expand Up @@ -162,6 +163,13 @@ fn main() {
.arg(add_input_file_arg())
.arg(add_debug_flag()),
)
.subcommand(
SubCommand::with_name("run")
.about("Execute CLIF code and verify with test expressions")
.arg(add_verbose_flag())
.arg(add_input_file_arg())
.arg(add_debug_flag()),
)
.subcommand(
SubCommand::with_name("cat")
.about("Outputs .clif file")
Expand Down Expand Up @@ -224,6 +232,14 @@ fn main() {
)
.map(|_time| ())
}
("run", Some(rest_cmd)) => {
handle_debug_flag(rest_cmd.is_present("debug"));
run::run(
get_vec(rest_cmd.values_of("file")),
rest_cmd.is_present("verbose"),
)
.map(|_time| ())
}
("pass", Some(rest_cmd)) => {
handle_debug_flag(rest_cmd.is_present("debug"));

Expand Down
Loading