diff --git a/Cargo.lock b/Cargo.lock index b9441fb68f..138db21439 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -435,6 +435,8 @@ version = "0.68.0" dependencies = [ "anyhow", "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", "cranelift-module", "log", "object", diff --git a/cranelift/module/src/lib.rs b/cranelift/module/src/lib.rs index cc927fed7f..857aa87382 100644 --- a/cranelift/module/src/lib.rs +++ b/cranelift/module/src/lib.rs @@ -52,7 +52,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION"); /// Default names for `ir::LibCall`s. A function by this name is imported into the object as /// part of the translation of a `ir::ExternalName::LibCall` variant. -pub fn default_libcall_names() -> Box String> { +pub fn default_libcall_names() -> Box String + Send + Sync> { Box::new(move |libcall| match libcall { ir::LibCall::Probestack => "__cranelift_probestack".to_owned(), ir::LibCall::UdivI64 => "__udivdi3".to_owned(), diff --git a/cranelift/object/Cargo.toml b/cranelift/object/Cargo.toml index 2b1bb0598f..914ac4f4f5 100644 --- a/cranelift/object/Cargo.toml +++ b/cranelift/object/Cargo.toml @@ -17,5 +17,9 @@ target-lexicon = "0.11" anyhow = "1.0" log = { version = "0.4.6", default-features = false } +[dev-dependencies] +cranelift-frontend = { path = "../frontend", version = "0.68.0" } +cranelift-entity = { path = "../entity", version = "0.68.0" } + [badges] maintenance = { status = "experimental" } diff --git a/cranelift/object/tests/basic.rs b/cranelift/object/tests/basic.rs new file mode 100644 index 0000000000..18acae5733 --- /dev/null +++ b/cranelift/object/tests/basic.rs @@ -0,0 +1,199 @@ +use cranelift_codegen::ir::*; +use cranelift_codegen::isa::CallConv; +use cranelift_codegen::{binemit::NullTrapSink, settings}; +use cranelift_codegen::{ir::types::I16, Context}; +use cranelift_entity::EntityRef; +use cranelift_frontend::*; +use cranelift_module::*; +use cranelift_object::*; + +#[test] +fn error_on_incompatible_sig_in_declare_function() { + let flag_builder = settings::builder(); + let isa_builder = cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu").unwrap(); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); + let mut module = + ObjectModule::new(ObjectBuilder::new(isa, "foo", default_libcall_names()).unwrap()); + let mut sig = Signature { + params: vec![AbiParam::new(types::I64)], + returns: vec![], + call_conv: CallConv::SystemV, + }; + module + .declare_function("abc", Linkage::Local, &sig) + .unwrap(); + sig.params[0] = AbiParam::new(types::I32); + module + .declare_function("abc", Linkage::Local, &sig) + .err() + .unwrap(); // Make sure this is an error +} + +fn define_simple_function(module: &mut ObjectModule) -> FuncId { + let sig = Signature { + params: vec![], + returns: vec![], + call_conv: CallConv::SystemV, + }; + + let func_id = module + .declare_function("abc", Linkage::Local, &sig) + .unwrap(); + + let mut ctx = Context::new(); + ctx.func = Function::with_name_signature(ExternalName::user(0, func_id.as_u32()), sig); + let mut func_ctx = FunctionBuilderContext::new(); + { + let mut bcx: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); + let block = bcx.create_block(); + bcx.switch_to_block(block); + bcx.ins().return_(&[]); + } + + let mut trap_sink = NullTrapSink {}; + module + .define_function(func_id, &mut ctx, &mut trap_sink) + .unwrap(); + + func_id +} + +#[test] +#[should_panic(expected = "Result::unwrap()` on an `Err` value: DuplicateDefinition(\"abc\")")] +fn panic_on_define_after_finalize() { + let flag_builder = settings::builder(); + let isa_builder = cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu").unwrap(); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); + let mut module = + ObjectModule::new(ObjectBuilder::new(isa, "foo", default_libcall_names()).unwrap()); + + define_simple_function(&mut module); + define_simple_function(&mut module); +} + +#[test] +fn switch_error() { + use cranelift_codegen::settings; + + let sig = Signature { + params: vec![AbiParam::new(types::I32)], + returns: vec![AbiParam::new(types::I32)], + call_conv: CallConv::SystemV, + }; + + let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig); + + let mut func_ctx = FunctionBuilderContext::new(); + { + let mut bcx: FunctionBuilder = FunctionBuilder::new(&mut func, &mut func_ctx); + let start = bcx.create_block(); + let bb0 = bcx.create_block(); + let bb1 = bcx.create_block(); + let bb2 = bcx.create_block(); + let bb3 = bcx.create_block(); + println!("{} {} {} {} {}", start, bb0, bb1, bb2, bb3); + + bcx.declare_var(Variable::new(0), types::I32); + bcx.declare_var(Variable::new(1), types::I32); + let in_val = bcx.append_block_param(start, types::I32); + bcx.switch_to_block(start); + bcx.def_var(Variable::new(0), in_val); + bcx.ins().jump(bb0, &[]); + + bcx.switch_to_block(bb0); + let discr = bcx.use_var(Variable::new(0)); + let mut switch = cranelift_frontend::Switch::new(); + for &(index, bb) in &[ + (9, bb1), + (13, bb1), + (10, bb1), + (92, bb1), + (39, bb1), + (34, bb1), + ] { + switch.set_entry(index, bb); + } + switch.emit(&mut bcx, discr, bb2); + + bcx.switch_to_block(bb1); + let v = bcx.use_var(Variable::new(0)); + bcx.def_var(Variable::new(1), v); + bcx.ins().jump(bb3, &[]); + + bcx.switch_to_block(bb2); + let v = bcx.use_var(Variable::new(0)); + bcx.def_var(Variable::new(1), v); + bcx.ins().jump(bb3, &[]); + + bcx.switch_to_block(bb3); + let r = bcx.use_var(Variable::new(1)); + bcx.ins().return_(&[r]); + + bcx.seal_all_blocks(); + bcx.finalize(); + } + + let flags = settings::Flags::new(settings::builder()); + match cranelift_codegen::verify_function(&func, &flags) { + Ok(_) => {} + Err(err) => { + let pretty_error = + cranelift_codegen::print_errors::pretty_verifier_error(&func, None, None, err); + panic!("pretty_error:\n{}", pretty_error); + } + } +} + +#[test] +fn libcall_function() { + let flag_builder = settings::builder(); + let isa_builder = cranelift_codegen::isa::lookup_by_name("x86_64-unknown-linux-gnu").unwrap(); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); + let mut module = + ObjectModule::new(ObjectBuilder::new(isa, "foo", default_libcall_names()).unwrap()); + + let sig = Signature { + params: vec![], + returns: vec![], + call_conv: CallConv::SystemV, + }; + + let func_id = module + .declare_function("function", Linkage::Local, &sig) + .unwrap(); + + let mut ctx = Context::new(); + ctx.func = Function::with_name_signature(ExternalName::user(0, func_id.as_u32()), sig); + let mut func_ctx = FunctionBuilderContext::new(); + { + let mut bcx: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); + let block = bcx.create_block(); + bcx.switch_to_block(block); + + let int = module.target_config().pointer_type(); + let zero = bcx.ins().iconst(I16, 0); + let size = bcx.ins().iconst(int, 10); + + let mut signature = module.make_signature(); + signature.params.push(AbiParam::new(int)); + signature.returns.push(AbiParam::new(int)); + let callee = module + .declare_function("malloc", Linkage::Import, &signature) + .expect("declare malloc function"); + let local_callee = module.declare_func_in_func(callee, &mut bcx.func); + let argument_exprs = vec![size]; + let call = bcx.ins().call(local_callee, &argument_exprs); + let buffer = bcx.inst_results(call)[0]; + + bcx.call_memset(module.target_config(), buffer, zero, size); + + bcx.ins().return_(&[]); + } + + let mut trap_sink = NullTrapSink {}; + module + .define_function(func_id, &mut ctx, &mut trap_sink) + .unwrap(); + + module.finish(); +} diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index 4306f3502b..527aadf0cb 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -33,7 +33,7 @@ const READONLY_DATA_ALIGNMENT: u64 = 0x1; pub struct SimpleJITBuilder { isa: Box, symbols: HashMap, - libcall_names: Box String>, + libcall_names: Box String + Send + Sync>, } impl SimpleJITBuilder { @@ -43,7 +43,7 @@ impl SimpleJITBuilder { /// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain /// floating point instructions, and for stack probes. If you don't know what to use for this /// argument, use `cranelift_module::default_libcall_names()`. - pub fn new(libcall_names: Box String>) -> Self { + pub fn new(libcall_names: Box String + Send + Sync>) -> Self { let mut flag_builder = settings::builder(); // On at least AArch64, "colocated" calls use shorter-range relocations, // which might not reach all definitions; we can't handle that here, so @@ -70,7 +70,7 @@ impl SimpleJITBuilder { /// argument, use `cranelift_module::default_libcall_names()`. pub fn with_isa( isa: Box, - libcall_names: Box String>, + libcall_names: Box String + Send + Sync>, ) -> Self { debug_assert!(!isa.flags().is_pic(), "SimpleJIT requires non-PIC code"); let symbols = HashMap::new();