Previously, every call was lowered on AArch64 to a `call` instruction, which takes a signed 26-bit PC-relative offset. Including the 2-bit left shift, this gives a range of +/- 128 MB. Longer-distance offsets would cause an impossible relocation record to be emitted (or rather, a record that a more sophisticated linker would fix up by inserting a shim/veneer). This commit adds a notion of "relocation distance" in the MachInst backends, and provides this information for every call target and symbol reference. The intent is that backends on architectures like AArch64, where there are different offset sizes / addressing strategies to choose from, can either emit a regular call or a load-64-bit-constant / call-indirect sequence, as necessary. This avoids the need to implement complex linking behavior. The MachInst driver code provides this information based on the "colocated" bit in the CLIF symbol references, which appears to have been designed for this purpose, or at least a similar one. Combined with the `use_colocated_libcalls` setting, this allows client code to ensure that library calls can link to library code at any location in the address space. Separately, the `simplejit` example did not handle `Arm64Call`; rather than doing so, it appears all that is necessary to get its tests to pass is to set the `use_colocated_libcalls` flag to false, to make use of the above change. This fixes the `libcall_function` unit-test in this crate.
205 lines
6.3 KiB
Rust
205 lines
6.3 KiB
Rust
use cranelift_codegen::binemit::NullTrapSink;
|
|
use cranelift_codegen::ir::*;
|
|
use cranelift_codegen::isa::CallConv;
|
|
use cranelift_codegen::{ir::types::I16, Context};
|
|
use cranelift_entity::EntityRef;
|
|
use cranelift_frontend::*;
|
|
use cranelift_module::*;
|
|
use cranelift_simplejit::*;
|
|
|
|
#[test]
|
|
fn error_on_incompatible_sig_in_declare_function() {
|
|
let mut module: Module<SimpleJITBackend> =
|
|
Module::new(SimpleJITBuilder::new(default_libcall_names()));
|
|
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 Module<SimpleJITBackend>) -> 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]
|
|
fn double_finalize() {
|
|
let mut module: Module<SimpleJITBackend> =
|
|
Module::new(SimpleJITBuilder::new(default_libcall_names()));
|
|
|
|
define_simple_function(&mut module);
|
|
module.finalize_definitions();
|
|
|
|
// Calling `finalize_definitions` a second time without any new definitions
|
|
// should have no effect.
|
|
module.finalize_definitions();
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic(expected = "Result::unwrap()` on an `Err` value: DuplicateDefinition(\"abc\")")]
|
|
fn panic_on_define_after_finalize() {
|
|
let mut module: Module<SimpleJITBackend> =
|
|
Module::new(SimpleJITBuilder::new(default_libcall_names()));
|
|
|
|
define_simple_function(&mut module);
|
|
module.finalize_definitions();
|
|
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 mut module: Module<SimpleJITBackend> =
|
|
Module::new(SimpleJITBuilder::new(default_libcall_names()));
|
|
|
|
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.finalize_definitions();
|
|
}
|