diff --git a/cranelift/filetests/isa/x86/legalize-libcall.clif b/cranelift/filetests/isa/x86/legalize-libcall.clif index 3605f2ff7b..e28bebd668 100644 --- a/cranelift/filetests/isa/x86/legalize-libcall.clif +++ b/cranelift/filetests/isa/x86/legalize-libcall.clif @@ -2,7 +2,6 @@ test legalizer ; Pre-SSE 4.1, we need to use runtime library calls for floating point rounding operations. set is_pic -set call_conv=system_v target x86_64 function %floor(f32) -> f32 { diff --git a/cranelift/fuzz/Cargo.toml b/cranelift/fuzz/Cargo.toml index af5f216836..3c2930ae07 100644 --- a/cranelift/fuzz/Cargo.toml +++ b/cranelift/fuzz/Cargo.toml @@ -11,6 +11,7 @@ cargo-fuzz = true cargo-fuzz = "*" binaryen = { git = "https://github.com/pepyakin/binaryen-rs.git" } libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } +cranelift-codegen = { path = "../lib/codegen" } cranelift-wasm = { path = "../lib/wasm" } cranelift-reader = { path = "../lib/reader" } target-lexicon = "0.0.3" diff --git a/cranelift/fuzz/fuzz_translate_module.rs b/cranelift/fuzz/fuzz_translate_module.rs index df339be545..abc742a62f 100644 --- a/cranelift/fuzz/fuzz_translate_module.rs +++ b/cranelift/fuzz/fuzz_translate_module.rs @@ -1,11 +1,15 @@ #![no_main] + #[macro_use] extern crate libfuzzer_sys; extern crate binaryen; +extern crate cranelift_codegen; extern crate cranelift_wasm; #[macro_use] extern crate target_lexicon; -use cranelift_wasm::{translate_module, DummyEnvironment}; + +use cranelift_codegen::{isa, settings}; +use cranelift_wasm::{translate_module, DummyEnvironment, ReturnMode}; use std::str::FromStr; fuzz_target!(|data: &[u8]| { @@ -13,6 +17,9 @@ fuzz_target!(|data: &[u8]| { let wasm = binaryen_module.write(); - let mut dummy_environ = DummyEnvironment::with_triple(triple!("x86_64")); + let flags = settings::Flags::new(settings::builder()); + let triple = triple!("x86_64"); + let isa = isa::lookup(triple).unwrap().finish(flags); + let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), ReturnMode::NormalReturns); translate_module(&wasm, &mut dummy_environ).unwrap(); }); diff --git a/cranelift/src/wasm.rs b/cranelift/src/wasm.rs index bab6e1394a..3eeddf5241 100644 --- a/cranelift/src/wasm.rs +++ b/cranelift/src/wasm.rs @@ -106,11 +106,7 @@ fn handle_module( } }; - let mut dummy_environ = DummyEnvironment::with_triple_flags( - isa.triple().clone(), - fisa.flags.clone(), - ReturnMode::NormalReturns, - ); + let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), ReturnMode::NormalReturns); translate_module(&module_binary, &mut dummy_environ).map_err(|e| e.to_string())?; let _ = terminal.fg(term::color::GREEN); diff --git a/lib/codegen/meta-python/base/settings.py b/lib/codegen/meta-python/base/settings.py index 4eb7541840..534ef28830 100644 --- a/lib/codegen/meta-python/base/settings.py +++ b/lib/codegen/meta-python/base/settings.py @@ -27,30 +27,6 @@ enable_verifier = BoolSetting( """, default=True) -call_conv = EnumSetting( - """ - Default calling convention: - - - fast: not-ABI-stable convention for best performance - - cold: not-ABI-stable convention for infrequently executed code - - system_v: System V-style convention used on many platforms - - windows_fastcall: Windows "fastcall" convention, also used for - x64 and ARM - - baldrdash: SpiderMonkey WebAssembly convention - - probestack: specialized convention for the probestack function - - The default calling convention may be overridden by individual - functions. - """, - - 'fast', - 'cold', - 'system_v', - 'windows_fastcall', - 'baldrdash', - 'probestack' -) - # Note that Cranelift doesn't currently need an is_pie flag, because PIE is # just PIC where symbols can't be pre-empted, which can be expressed with the # `colocated` flag on external functions and global values. diff --git a/lib/codegen/src/ir/extfunc.rs b/lib/codegen/src/ir/extfunc.rs index 451a90a7f9..7ee4fbe8e2 100644 --- a/lib/codegen/src/ir/extfunc.rs +++ b/lib/codegen/src/ir/extfunc.rs @@ -6,8 +6,7 @@ //! This module declares the data types used to represent external functions and call signatures. use ir::{ArgumentLoc, ExternalName, SigRef, Type}; -use isa::{RegInfo, RegUnit}; -use settings::CallConv; +use isa::{CallConv, RegInfo, RegUnit}; use std::fmt; use std::str::FromStr; use std::vec::Vec; diff --git a/lib/codegen/src/ir/function.rs b/lib/codegen/src/ir/function.rs index f2db834eab..629741b4ae 100644 --- a/lib/codegen/src/ir/function.rs +++ b/lib/codegen/src/ir/function.rs @@ -13,9 +13,8 @@ use ir::{ }; use ir::{EbbOffsets, InstEncodings, SourceLocs, StackSlots, ValueLocations}; use ir::{JumpTableOffsets, JumpTables}; -use isa::{EncInfo, Encoding, Legalize, TargetIsa}; +use isa::{CallConv, EncInfo, Encoding, Legalize, TargetIsa}; use regalloc::RegDiversions; -use settings::CallConv; use std::fmt; use write::write_function; diff --git a/lib/codegen/src/ir/libcall.rs b/lib/codegen/src/ir/libcall.rs index 61766dc8ae..1e5297022e 100644 --- a/lib/codegen/src/ir/libcall.rs +++ b/lib/codegen/src/ir/libcall.rs @@ -4,8 +4,7 @@ use ir::{ types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Inst, Opcode, Signature, Type, }; -use isa::{RegUnit, TargetIsa}; -use settings::CallConv; +use isa::{CallConv, RegUnit, TargetIsa}; use std::fmt; use std::str::FromStr; @@ -166,8 +165,7 @@ fn make_funcref_for_inst( inst: Inst, isa: &TargetIsa, ) -> FuncRef { - // Start with a fast calling convention. We'll give the ISA a chance to change it. - let mut sig = Signature::new(isa.flags().call_conv()); + let mut sig = Signature::new(isa.default_call_conv()); for &v in func.dfg.inst_args(inst) { sig.params.push(AbiParam::new(func.dfg.value_type(v))); } diff --git a/lib/codegen/src/isa/call_conv.rs b/lib/codegen/src/isa/call_conv.rs new file mode 100644 index 0000000000..ce5b10b315 --- /dev/null +++ b/lib/codegen/src/isa/call_conv.rs @@ -0,0 +1,74 @@ +use std::fmt; +use std::str; +use target_lexicon::{OperatingSystem, Triple}; + +/// Calling convention identifiers. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum CallConv { + /// Best performance, not ABI-stable + Fast, + /// Smallest caller code size, not ABI-stable + Cold, + /// System V-style convention used on many platforms + SystemV, + /// Windows "fastcall" convention, also used for x64 and ARM + WindowsFastcall, + /// SpiderMonkey WebAssembly convention + Baldrdash, + /// Specialized convention for the probestack function + Probestack, +} + +impl CallConv { + /// Return the default calling convention for the given target triple. + pub fn default_for_triple(triple: &Triple) -> Self { + match triple.operating_system { + OperatingSystem::Unknown + | OperatingSystem::Bitrig + | OperatingSystem::Cloudabi + | OperatingSystem::Darwin + | OperatingSystem::Dragonfly + | OperatingSystem::Freebsd + | OperatingSystem::Fuchsia + | OperatingSystem::Haiku + | OperatingSystem::Ios + | OperatingSystem::L4re + | OperatingSystem::Linux + | OperatingSystem::Nebulet + | OperatingSystem::Netbsd + | OperatingSystem::Openbsd + | OperatingSystem::Redox + | OperatingSystem::Solaris => CallConv::SystemV, + OperatingSystem::Windows => CallConv::WindowsFastcall, + os => panic!("unsupported operating system: {}", os), + } + } +} + +impl fmt::Display for CallConv { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match *self { + CallConv::Fast => "fast", + CallConv::Cold => "cold", + CallConv::SystemV => "system_v", + CallConv::WindowsFastcall => "windows_fastcall", + CallConv::Baldrdash => "baldrdash", + CallConv::Probestack => "probestack", + }) + } +} + +impl str::FromStr for CallConv { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "fast" => Ok(CallConv::Fast), + "cold" => Ok(CallConv::Cold), + "system_v" => Ok(CallConv::SystemV), + "windows_fastcall" => Ok(CallConv::WindowsFastcall), + "baldrdash" => Ok(CallConv::Baldrdash), + "probestack" => Ok(CallConv::Probestack), + _ => Err(()), + } + } +} diff --git a/lib/codegen/src/isa/mod.rs b/lib/codegen/src/isa/mod.rs index 678395f43c..20469bb6c0 100644 --- a/lib/codegen/src/isa/mod.rs +++ b/lib/codegen/src/isa/mod.rs @@ -46,6 +46,7 @@ //! The configured target ISA trait object is a `Box` which can be used for multiple //! concurrent function compilations. +pub use isa::call_conv::CallConv; pub use isa::constraints::{BranchRange, ConstraintKind, OperandConstraint, RecipeConstraints}; pub use isa::encoding::{base_size, EncInfo, Encoding}; pub use isa::registers::{regs_overlap, RegClass, RegClassIndex, RegInfo, RegUnit}; @@ -58,10 +59,10 @@ use isa::enc_tables::Encodings; use regalloc; use result::CodegenResult; use settings; -use settings::{CallConv, SetResult}; +use settings::SetResult; use std::boxed::Box; use std::fmt; -use target_lexicon::{Architecture, Triple}; +use target_lexicon::{Architecture, PointerWidth, Triple}; use timing; #[cfg(build_riscv)] @@ -76,6 +77,7 @@ mod arm32; #[cfg(build_arm64)] mod arm64; +mod call_conv; mod constraints; mod enc_tables; mod encoding; @@ -164,6 +166,34 @@ impl settings::Configurable for Builder { pub type Legalize = fn(ir::Inst, &mut ir::Function, &mut flowgraph::ControlFlowGraph, &TargetIsa) -> bool; +/// This struct provides information that a frontend may need to know about a target to +/// produce Cranelift IR for the target. +#[derive(Clone, Copy)] +pub struct TargetFrontendConfig { + /// The default calling convention of the target. + pub default_call_conv: CallConv, + + /// The pointer width of the target. + pub pointer_width: PointerWidth, +} + +impl TargetFrontendConfig { + /// Get the pointer type of this target. + pub fn pointer_type(&self) -> ir::Type { + ir::Type::int(u16::from(self.pointer_bits())).unwrap() + } + + /// Get the width of pointers on this target, in units of bits. + pub fn pointer_bits(&self) -> u8 { + self.pointer_width.bits() + } + + /// Get the width of pointers on this target, in units of bytes. + pub fn pointer_bytes(&self) -> u8 { + self.pointer_width.bytes() + } +} + /// Methods that are specialized to a target ISA. Implies a Display trait that shows the /// shared flags, as well as any isa-specific flags. pub trait TargetIsa: fmt::Display { @@ -176,19 +206,37 @@ pub trait TargetIsa: fmt::Display { /// Get the ISA-independent flags that were used to make this trait object. fn flags(&self) -> &settings::Flags; + /// Get the default calling convention of this target. + fn default_call_conv(&self) -> CallConv { + CallConv::default_for_triple(self.triple()) + } + /// Get the pointer type of this ISA. fn pointer_type(&self) -> ir::Type { ir::Type::int(u16::from(self.pointer_bits())).unwrap() } + /// Get the width of pointers on this ISA. + fn pointer_width(&self) -> PointerWidth { + self.triple().pointer_width().unwrap() + } + /// Get the width of pointers on this ISA, in units of bits. fn pointer_bits(&self) -> u8 { - self.triple().pointer_width().unwrap().bits() + self.pointer_width().bits() } /// Get the width of pointers on this ISA, in units of bytes. fn pointer_bytes(&self) -> u8 { - self.triple().pointer_width().unwrap().bytes() + self.pointer_width().bytes() + } + + /// Get the information needed by frontends producing Cranelift IR. + fn frontend_config(&self) -> TargetFrontendConfig { + TargetFrontendConfig { + default_call_conv: self.default_call_conv(), + pointer_width: self.pointer_width(), + } } /// Does the CPU implement scalar comparisons using a CPU flags register? diff --git a/lib/codegen/src/isa/x86/abi.rs b/lib/codegen/src/isa/x86/abi.rs index af7dda69d1..67fe3a486a 100644 --- a/lib/codegen/src/isa/x86/abi.rs +++ b/lib/codegen/src/isa/x86/abi.rs @@ -10,10 +10,9 @@ use ir::{ get_probestack_funcref, AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose, InstBuilder, ValueLoc, }; -use isa::{RegClass, RegUnit, TargetIsa}; +use isa::{CallConv, RegClass, RegUnit, TargetIsa}; use regalloc::RegisterSet; use result::CodegenResult; -use settings::CallConv; use stack_layout::layout_stack; use std::i32; use target_lexicon::{PointerWidth, Triple}; @@ -189,12 +188,12 @@ pub fn allocatable_registers(_func: &ir::Function, triple: &Triple) -> RegisterS } /// Get the set of callee-saved registers. -fn callee_saved_gprs(isa: &TargetIsa) -> &'static [RU] { +fn callee_saved_gprs(isa: &TargetIsa, call_conv: CallConv) -> &'static [RU] { match isa.triple().pointer_width().unwrap() { PointerWidth::U16 => panic!(), PointerWidth::U32 => &[RU::rbx, RU::rsi, RU::rdi], PointerWidth::U64 => { - if isa.flags().call_conv() == CallConv::WindowsFastcall { + if call_conv == CallConv::WindowsFastcall { // "registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15 are considered nonvolatile // and must be saved and restored by a function that uses them." // as per https://msdn.microsoft.com/en-us/library/6t169e9c.aspx @@ -219,7 +218,7 @@ fn callee_saved_gprs(isa: &TargetIsa) -> &'static [RU] { /// Get the set of callee-saved registers that are used. fn callee_saved_gprs_used(isa: &TargetIsa, func: &ir::Function) -> RegisterSet { let mut all_callee_saved = RegisterSet::empty(); - for reg in callee_saved_gprs(isa) { + for reg in callee_saved_gprs(isa, func.signature.call_conv) { all_callee_saved.free(GPR, *reg as RegUnit); } diff --git a/lib/codegen/src/settings.rs b/lib/codegen/src/settings.rs index cd135eba4b..50cd38b7fa 100644 --- a/lib/codegen/src/settings.rs +++ b/lib/codegen/src/settings.rs @@ -372,7 +372,6 @@ mod tests { "[shared]\n\ opt_level = \"default\"\n\ enable_verifier = true\n\ - call_conv = \"fast\"\n\ is_pic = false\n\ colocated_libcalls = false\n\ avoid_div_traps = false\n\ diff --git a/lib/frontend/src/frontend.rs b/lib/frontend/src/frontend.rs index 4014072e4a..ff6efa6835 100644 --- a/lib/frontend/src/frontend.rs +++ b/lib/frontend/src/frontend.rs @@ -9,7 +9,7 @@ use cranelift_codegen::ir::{ JumpTable, JumpTableData, LibCall, MemFlags, SigRef, Signature, StackSlot, StackSlotData, Type, Value, }; -use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa}; use cranelift_codegen::packed_option::PackedOption; use ssa::{Block, SSABuilder, SideEffects}; use std::vec::Vec; @@ -550,10 +550,16 @@ impl<'a> FunctionBuilder<'a> { /// won't overlap onto `dest`. If `dest` and `src` overlap, the behavior is /// undefined. Applications in which `dest` and `src` might overlap should /// use `call_memmove` instead. - pub fn call_memcpy(&mut self, isa: &TargetIsa, dest: Value, src: Value, size: Value) { - let pointer_type = isa.pointer_type(); + pub fn call_memcpy( + &mut self, + config: &TargetFrontendConfig, + dest: Value, + src: Value, + size: Value, + ) { + let pointer_type = config.pointer_type(); let signature = { - let mut s = Signature::new(isa.flags().call_conv()); + let mut s = Signature::new(config.default_call_conv); s.params.push(AbiParam::new(pointer_type)); s.params.push(AbiParam::new(pointer_type)); s.params.push(AbiParam::new(pointer_type)); @@ -572,7 +578,7 @@ impl<'a> FunctionBuilder<'a> { /// Optimised memcpy for small copys. pub fn emit_small_memcpy( &mut self, - isa: &TargetIsa, + config: &TargetFrontendConfig, dest: Value, src: Value, size: u64, @@ -594,8 +600,8 @@ impl<'a> FunctionBuilder<'a> { let load_and_store_amount = size / access_size; if load_and_store_amount > THRESHOLD { - let size_value = self.ins().iconst(isa.pointer_type(), size as i64); - self.call_memcpy(isa, dest, src, size_value); + let size_value = self.ins().iconst(config.pointer_type(), size as i64); + self.call_memcpy(config, dest, src, size_value); return; } @@ -613,10 +619,16 @@ impl<'a> FunctionBuilder<'a> { /// Calls libc.memset /// /// Writes `size` bytes of value `ch` to memory starting at `buffer`. - pub fn call_memset(&mut self, isa: &TargetIsa, buffer: Value, ch: Value, size: Value) { - let pointer_type = isa.pointer_type(); + pub fn call_memset( + &mut self, + config: &TargetFrontendConfig, + buffer: Value, + ch: Value, + size: Value, + ) { + let pointer_type = config.pointer_type(); let signature = { - let mut s = Signature::new(isa.flags().call_conv()); + let mut s = Signature::new(config.default_call_conv); s.params.push(AbiParam::new(pointer_type)); s.params.push(AbiParam::new(types::I32)); s.params.push(AbiParam::new(pointer_type)); @@ -638,7 +650,7 @@ impl<'a> FunctionBuilder<'a> { /// Writes `size` bytes of value `ch` to memory starting at `buffer`. pub fn emit_small_memset( &mut self, - isa: &TargetIsa, + config: &TargetFrontendConfig, buffer: Value, ch: u32, size: u64, @@ -660,8 +672,8 @@ impl<'a> FunctionBuilder<'a> { if load_and_store_amount > THRESHOLD { let ch = self.ins().iconst(types::I32, ch as i64); - let size = self.ins().iconst(isa.pointer_type(), size as i64); - self.call_memset(isa, buffer, ch, size); + let size = self.ins().iconst(config.pointer_type(), size as i64); + self.call_memset(config, buffer, ch, size); } else { let mut flags = MemFlags::new(); flags.set_aligned(); @@ -691,10 +703,16 @@ impl<'a> FunctionBuilder<'a> { /// /// Copies `size` bytes from memory starting at `source` to memory starting /// at `dest`. `source` is always read before writing to `dest`. - pub fn call_memmove(&mut self, isa: &TargetIsa, dest: Value, source: Value, size: Value) { - let pointer_type = isa.pointer_type(); + pub fn call_memmove( + &mut self, + config: &TargetFrontendConfig, + dest: Value, + source: Value, + size: Value, + ) { + let pointer_type = config.pointer_type(); let signature = { - let mut s = Signature::new(isa.flags().call_conv()); + let mut s = Signature::new(config.default_call_conv); s.params.push(AbiParam::new(pointer_type)); s.params.push(AbiParam::new(pointer_type)); s.params.push(AbiParam::new(pointer_type)); @@ -713,7 +731,7 @@ impl<'a> FunctionBuilder<'a> { /// Optimised memmove for small moves. pub fn emit_small_memmove( &mut self, - isa: &TargetIsa, + config: &TargetFrontendConfig, dest: Value, src: Value, size: u64, @@ -735,8 +753,8 @@ impl<'a> FunctionBuilder<'a> { let load_and_store_amount = size / access_size; if load_and_store_amount > THRESHOLD { - let size_value = self.ins().iconst(isa.pointer_type(), size as i64); - self.call_memmove(isa, dest, src, size_value); + let size_value = self.ins().iconst(config.pointer_type(), size as i64); + self.call_memmove(config, dest, src, size_value); return; } @@ -798,8 +816,8 @@ mod tests { use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{AbiParam, ExternalName, Function, InstBuilder, Signature}; + use cranelift_codegen::isa::CallConv; use cranelift_codegen::settings; - use cranelift_codegen::settings::CallConv; use cranelift_codegen::verifier::verify_function; use frontend::{FunctionBuilder, FunctionBuilderContext}; use std::string::ToString; @@ -923,7 +941,7 @@ mod tests { .map(|b| b.finish(shared_flags)) .expect("This test requires arm support."); - let mut sig = Signature::new(target.flags().call_conv()); + let mut sig = Signature::new(target.default_call_conv()); sig.returns.push(AbiParam::new(I32)); let mut fn_ctx = FunctionBuilderContext::new(); @@ -944,7 +962,7 @@ mod tests { let src = builder.use_var(x); let dest = builder.use_var(y); let size = builder.use_var(y); - builder.call_memcpy(&*target, dest, src, size); + builder.call_memcpy(&target.frontend_config(), dest, src, size); builder.ins().return_(&[size]); builder.seal_all_blocks(); @@ -953,8 +971,8 @@ mod tests { assert_eq!( func.display(None).to_string(), - "function %sample() -> i32 fast { - sig0 = (i32, i32, i32) fast + "function %sample() -> i32 system_v { + sig0 = (i32, i32, i32) system_v fn0 = %Memcpy sig0 ebb0: diff --git a/lib/frontend/src/lib.rs b/lib/frontend/src/lib.rs index 045705d723..01dcdc4a2a 100644 --- a/lib/frontend/src/lib.rs +++ b/lib/frontend/src/lib.rs @@ -68,7 +68,8 @@ //! use cranelift_codegen::entity::EntityRef; //! use cranelift_codegen::ir::types::*; //! use cranelift_codegen::ir::{AbiParam, ExternalName, Function, InstBuilder, Signature}; -//! use cranelift_codegen::settings::{self, CallConv}; +//! use cranelift_codegen::isa::CallConv; +//! use cranelift_codegen::settings; //! use cranelift_codegen::verifier::verify_function; //! use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; //! diff --git a/lib/module/src/module.rs b/lib/module/src/module.rs index 626c34e4f5..47daccd768 100644 --- a/lib/module/src/module.rs +++ b/lib/module/src/module.rs @@ -6,7 +6,7 @@ // shared with `DataContext`? use cranelift_codegen::entity::{EntityRef, PrimaryMap}; -use cranelift_codegen::{binemit, ir, CodegenError, Context}; +use cranelift_codegen::{binemit, ir, isa, CodegenError, Context}; use data_context::DataContext; use std::borrow::ToOwned; use std::collections::HashMap; @@ -340,9 +340,10 @@ where self.names.get(name).cloned() } - /// Return then pointer type for the current target. - pub fn pointer_type(&self) -> ir::types::Type { - self.backend.isa().pointer_type() + /// Return the target information needed by frontends to produce Cranelift IR + /// for the current target. + pub fn target_config(&self) -> isa::TargetFrontendConfig { + self.backend.isa().frontend_config() } /// Create a new `Context` initialized for use with this `Module`. @@ -351,7 +352,7 @@ where /// convention for the `TargetIsa`. pub fn make_context(&self) -> Context { let mut ctx = Context::new(); - ctx.func.signature.call_conv = self.backend.isa().flags().call_conv(); + ctx.func.signature.call_conv = self.backend.isa().default_call_conv(); ctx } @@ -361,14 +362,14 @@ where /// convention for the `TargetIsa`. pub fn clear_context(&self, ctx: &mut Context) { ctx.clear(); - ctx.func.signature.call_conv = self.backend.isa().flags().call_conv(); + ctx.func.signature.call_conv = self.backend.isa().default_call_conv(); } /// Create a new empty `Signature` with the default calling convention for /// the `TargetIsa`, to which parameter and return types can be added for /// declaring a function to be called by this `Module`. pub fn make_signature(&self) -> ir::Signature { - ir::Signature::new(self.backend.isa().flags().call_conv()) + ir::Signature::new(self.backend.isa().default_call_conv()) } /// Clear the given `Signature` and reset for use with a new function. @@ -376,7 +377,7 @@ where /// This ensures that the `Signature` is initialized with the default /// calling convention for the `TargetIsa`. pub fn clear_signature(&self, sig: &mut ir::Signature) { - sig.clear(self.backend.isa().flags().call_conv()); + sig.clear(self.backend.isa().default_call_conv()); } /// Declare a function in this module. diff --git a/lib/native/src/lib.rs b/lib/native/src/lib.rs index 7d88984db5..57fa5a4cd9 100644 --- a/lib/native/src/lib.rs +++ b/lib/native/src/lib.rs @@ -37,26 +37,16 @@ extern crate raw_cpuid; extern crate target_lexicon; use cranelift_codegen::isa; -use cranelift_codegen::settings::{self, Configurable}; +use cranelift_codegen::settings::Configurable; use target_lexicon::Triple; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] use raw_cpuid::CpuId; -/// Return `settings` and `isa` builders configured for the current host +/// Return an `isa` builder configured for the current host /// machine, or `Err(())` if the host machine is not supported /// in the current configuration. -pub fn builders() -> Result<(settings::Builder, isa::Builder), &'static str> { - let mut flag_builder = settings::builder(); - - if cfg!(any(unix, target_os = "nebulet")) { - flag_builder.set("call_conv", "system_v").unwrap(); - } else if cfg!(windows) { - flag_builder.set("call_conv", "windows_fastcall").unwrap(); - } else { - return Err("unrecognized environment"); - } - +pub fn builder() -> Result { let mut isa_builder = isa::lookup(Triple::host()).map_err(|err| match err { isa::LookupError::SupportDisabled => "support for architecture disabled at compile time", isa::LookupError::Unsupported => "unsupported architecture", @@ -66,7 +56,7 @@ pub fn builders() -> Result<(settings::Builder, isa::Builder), &'static str> { parse_x86_cpuid(&mut isa_builder)?; } - Ok((flag_builder, isa_builder)) + Ok(isa_builder) } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] @@ -108,3 +98,32 @@ fn parse_x86_cpuid(isa_builder: &mut isa::Builder) -> Result<(), &'static str> { } Ok(()) } + +#[cfg(test)] +mod tests { + use super::builder; + use cranelift_codegen::isa::CallConv; + use cranelift_codegen::settings; + + #[test] + fn test() { + if let Ok(isa_builder) = builder() { + let flag_builder = settings::builder(); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); + if cfg!(any(unix, target_os = "nebulet")) { + assert_eq!(isa.default_call_conv(), CallConv::SystemV); + } else if cfg!(windows) { + assert_eq!(isa.default_call_conv(), CallConv::WindowsFastcall); + } + if cfg!(target_pointer_width = "64") { + assert_eq!(isa.pointer_bits(), 64); + } + if cfg!(target_pointer_width = "32") { + assert_eq!(isa.pointer_bits(), 32); + } + if cfg!(target_pointer_width = "16") { + assert_eq!(isa.pointer_bits(), 16); + } + } + } +} diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 937edbee02..650efe3ea0 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -12,9 +12,8 @@ use cranelift_codegen::ir::{ Opcode, SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, Table, TableData, Type, Value, ValueLoc, }; -use cranelift_codegen::isa::{self, Encoding, RegUnit, TargetIsa}; +use cranelift_codegen::isa::{self, CallConv, Encoding, RegUnit, TargetIsa}; use cranelift_codegen::packed_option::ReservedValue; -use cranelift_codegen::settings::CallConv; use cranelift_codegen::{settings, timing}; use error::{Location, ParseError, ParseResult}; use isaspec; @@ -2562,7 +2561,7 @@ mod tests { use cranelift_codegen::ir::types; use cranelift_codegen::ir::StackSlotKind; use cranelift_codegen::ir::{ArgumentExtension, ArgumentPurpose}; - use cranelift_codegen::settings::CallConv; + use cranelift_codegen::isa::CallConv; use error::ParseError; use isaspec::IsaSpec; use testfile::{Comment, Details}; diff --git a/lib/simplejit/src/backend.rs b/lib/simplejit/src/backend.rs index f774adbbaa..ccf113ce1c 100644 --- a/lib/simplejit/src/backend.rs +++ b/lib/simplejit/src/backend.rs @@ -26,8 +26,9 @@ pub struct SimpleJITBuilder { impl SimpleJITBuilder { /// Create a new `SimpleJITBuilder`. pub fn new() -> Self { - let (flag_builder, isa_builder) = cranelift_native::builders().unwrap_or_else(|_| { - panic!("host machine is not a supported target"); + let flag_builder = settings::builder(); + let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { + panic!("host machine is not supported: {}", msg); }); let isa = isa_builder.finish(settings::Flags::new(flag_builder)); Self::with_isa(isa) diff --git a/lib/simplejit/tests/basic.rs b/lib/simplejit/tests/basic.rs index e76282e74e..1614686911 100644 --- a/lib/simplejit/tests/basic.rs +++ b/lib/simplejit/tests/basic.rs @@ -5,7 +5,7 @@ extern crate cranelift_module; extern crate cranelift_simplejit; use cranelift_codegen::ir::*; -use cranelift_codegen::settings::*; +use cranelift_codegen::isa::CallConv; use cranelift_codegen::Context; use cranelift_entity::EntityRef; use cranelift_frontend::*; diff --git a/lib/umbrella/src/lib.rs b/lib/umbrella/src/lib.rs index 79d262ecfd..7a3a8f00dc 100644 --- a/lib/umbrella/src/lib.rs +++ b/lib/umbrella/src/lib.rs @@ -46,7 +46,7 @@ pub mod prelude { MemFlags, Signature, StackSlotData, StackSlotKind, TrapCode, Type, Value, }; pub use codegen::isa; - pub use codegen::settings::{self, CallConv, Configurable}; + pub use codegen::settings::{self, Configurable}; pub use frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; } diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index d298060f76..28547d784f 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -17,15 +17,15 @@ cranelift-frontend = { path = "../frontend", version = "0.22.0", default-feature hashmap_core = { version = "0.1.9", optional = true } failure = { version = "0.1.1", default-features = false, features = ["derive"] } failure_derive = { version = "0.1.1", default-features = false } -target-lexicon = { version = "0.0.3", default-features = false } log = { version = "0.4.4", default-features = false } [dev-dependencies] wabt = "0.6.0" +target-lexicon = "0.0.3" [features] default = ["std"] -std = ["cranelift-codegen/std", "cranelift-frontend/std", "wasmparser/std", "target-lexicon/std"] +std = ["cranelift-codegen/std", "cranelift-frontend/std", "wasmparser/std"] core = ["hashmap_core", "cranelift-codegen/core", "cranelift-frontend/core", "wasmparser/core"] [badges] diff --git a/lib/wasm/src/environ/dummy.rs b/lib/wasm/src/environ/dummy.rs index 20e1462f50..ae2183efb9 100644 --- a/lib/wasm/src/environ/dummy.rs +++ b/lib/wasm/src/environ/dummy.rs @@ -5,13 +5,12 @@ use cranelift_codegen::cursor::FuncCursor; use cranelift_codegen::ir::immediates::{Imm64, Offset32}; use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::{self, InstBuilder}; -use cranelift_codegen::settings; +use cranelift_codegen::isa::TargetFrontendConfig; use cranelift_entity::{EntityRef, PrimaryMap}; use environ::{FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, WasmResult}; use func_translator::FuncTranslator; use std::string::String; use std::vec::Vec; -use target_lexicon::Triple; use translation_utils::{ DefinedFuncIndex, FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, @@ -44,11 +43,8 @@ impl Exportable { /// `DummyEnvironment` to allow it to be borrowed separately from the /// `FuncTranslator` field. pub struct DummyModuleInfo { - /// Target description. - pub triple: Triple, - - /// Compilation setting flags. - pub flags: settings::Flags, + /// Target description relevant to frontends producing Cranelift IR. + config: TargetFrontendConfig, /// Signatures as provided by `declare_signature`. pub signatures: PrimaryMap, @@ -76,11 +72,10 @@ pub struct DummyModuleInfo { } impl DummyModuleInfo { - /// Allocates the data structures with the given flags. - pub fn with_triple_flags(triple: Triple, flags: settings::Flags) -> Self { + /// Creates a new `DummyModuleInfo` instance. + pub fn new(config: TargetFrontendConfig) -> Self { Self { - triple, - flags, + config, signatures: PrimaryMap::new(), imported_funcs: Vec::new(), functions: PrimaryMap::new(), @@ -111,23 +106,10 @@ pub struct DummyEnvironment { } impl DummyEnvironment { - /// Allocates the data structures with default flags. - pub fn with_triple(triple: Triple) -> Self { - Self::with_triple_flags( - triple, - settings::Flags::new(settings::builder()), - ReturnMode::NormalReturns, - ) - } - - /// Allocates the data structures with the given triple. - pub fn with_triple_flags( - triple: Triple, - flags: settings::Flags, - return_mode: ReturnMode, - ) -> Self { + /// Creates a new `DummyEnvironment` instance. + pub fn new(config: TargetFrontendConfig, return_mode: ReturnMode) -> Self { Self { - info: DummyModuleInfo::with_triple_flags(triple, flags), + info: DummyModuleInfo::new(config), trans: FuncTranslator::new(), func_bytecode_sizes: Vec::new(), return_mode, @@ -169,12 +151,8 @@ impl<'dummy_environment> DummyFuncEnvironment<'dummy_environment> { } impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environment> { - fn triple(&self) -> &Triple { - &self.mod_info.triple - } - - fn flags(&self) -> &settings::Flags { - &self.mod_info.flags + fn target_config(&self) -> TargetFrontendConfig { + self.mod_info.config } fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalVariable { @@ -348,8 +326,8 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ } impl<'data> ModuleEnvironment<'data> for DummyEnvironment { - fn flags(&self) -> &settings::Flags { - &self.info.flags + fn target_config(&self) -> &TargetFrontendConfig { + &self.info.config } fn get_func_name(&self, func_index: FuncIndex) -> ir::ExternalName { diff --git a/lib/wasm/src/environ/spec.rs b/lib/wasm/src/environ/spec.rs index db6d0761d4..7f48ef99d2 100644 --- a/lib/wasm/src/environ/spec.rs +++ b/lib/wasm/src/environ/spec.rs @@ -2,9 +2,8 @@ //! traits `FunctionEnvironment` and `ModuleEnvironment`. use cranelift_codegen::cursor::FuncCursor; use cranelift_codegen::ir::{self, InstBuilder}; -use cranelift_codegen::settings::Flags; +use cranelift_codegen::isa::TargetFrontendConfig; use std::vec::Vec; -use target_lexicon::Triple; use translation_utils::{ FuncIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex, Table, TableIndex, }; @@ -89,22 +88,19 @@ pub enum ReturnMode { /// IR. The function environment provides information about the WebAssembly module as well as the /// runtime environment. pub trait FuncEnvironment { - /// Get the triple for the current compilation. - fn triple(&self) -> &Triple; - - /// Get the flags for the current compilation. - fn flags(&self) -> &Flags; + /// Get the information needed to produce Cranelift IR for the given target. + fn target_config(&self) -> TargetFrontendConfig; /// Get the Cranelift integer type to use for native pointers. /// /// This returns `I64` for 64-bit architectures and `I32` for 32-bit architectures. fn pointer_type(&self) -> ir::Type { - ir::Type::int(u16::from(self.triple().pointer_width().unwrap().bits())).unwrap() + ir::Type::int(u16::from(self.target_config().pointer_bits())).unwrap() } /// Get the size of a native pointer, in bytes. fn pointer_bytes(&self) -> u8 { - self.triple().pointer_width().unwrap().bytes() + self.target_config().pointer_bytes() } /// Set up the necessary preamble definitions in `func` to access the global variable @@ -239,8 +235,8 @@ pub trait FuncEnvironment { /// [`translate_module`](fn.translate_module.html) function. These methods should not be called /// by the user, they are only for `cranelift-wasm` internal use. pub trait ModuleEnvironment<'data> { - /// Get the flags for the current compilation. - fn flags(&self) -> &Flags; + /// Get the information needed to produce Cranelift IR for the current target. + fn target_config(&self) -> &TargetFrontendConfig; /// Return the name for the given function index. fn get_func_name(&self, func_index: FuncIndex) -> ir::ExternalName; diff --git a/lib/wasm/src/func_translator.rs b/lib/wasm/src/func_translator.rs index 0d99754c79..5b7a116454 100644 --- a/lib/wasm/src/func_translator.rs +++ b/lib/wasm/src/func_translator.rs @@ -236,11 +236,11 @@ fn cur_srcloc(reader: &BinaryReader) -> ir::SourceLoc { #[cfg(test)] mod tests { - use super::FuncTranslator; + use super::{FuncTranslator, ReturnMode}; use cranelift_codegen::ir::types::I32; - use cranelift_codegen::{ir, Context}; - use environ::{DummyEnvironment, FuncEnvironment}; - use target_lexicon::Triple; + use cranelift_codegen::{ir, isa, settings, Context}; + use environ::DummyEnvironment; + use target_lexicon::PointerWidth; #[test] fn small1() { @@ -258,7 +258,15 @@ mod tests { ]; let mut trans = FuncTranslator::new(); - let runtime = DummyEnvironment::with_triple(Triple::default()); + let flags = settings::Flags::new(settings::builder()); + let runtime = DummyEnvironment::new( + isa::TargetFrontendConfig { + default_call_conv: isa::CallConv::Fast, + pointer_width: PointerWidth::U64, + }, + ReturnMode::NormalReturns, + ); + let mut ctx = Context::new(); ctx.func.name = ir::ExternalName::testcase("small1"); @@ -269,7 +277,7 @@ mod tests { .translate(&BODY, &mut ctx.func, &mut runtime.func_env()) .unwrap(); debug!("{}", ctx.func.display(None)); - ctx.verify(runtime.func_env().flags()).unwrap(); + ctx.verify(&flags).unwrap(); } #[test] @@ -289,7 +297,14 @@ mod tests { ]; let mut trans = FuncTranslator::new(); - let runtime = DummyEnvironment::with_triple(Triple::default()); + let flags = settings::Flags::new(settings::builder()); + let runtime = DummyEnvironment::new( + isa::TargetFrontendConfig { + default_call_conv: isa::CallConv::Fast, + pointer_width: PointerWidth::U64, + }, + ReturnMode::NormalReturns, + ); let mut ctx = Context::new(); ctx.func.name = ir::ExternalName::testcase("small2"); @@ -300,7 +315,7 @@ mod tests { .translate(&BODY, &mut ctx.func, &mut runtime.func_env()) .unwrap(); debug!("{}", ctx.func.display(None)); - ctx.verify(runtime.func_env().flags()).unwrap(); + ctx.verify(&flags).unwrap(); } #[test] @@ -329,7 +344,14 @@ mod tests { ]; let mut trans = FuncTranslator::new(); - let runtime = DummyEnvironment::with_triple(Triple::default()); + let flags = settings::Flags::new(settings::builder()); + let runtime = DummyEnvironment::new( + isa::TargetFrontendConfig { + default_call_conv: isa::CallConv::Fast, + pointer_width: PointerWidth::U64, + }, + ReturnMode::NormalReturns, + ); let mut ctx = Context::new(); ctx.func.name = ir::ExternalName::testcase("infloop"); @@ -339,6 +361,6 @@ mod tests { .translate(&BODY, &mut ctx.func, &mut runtime.func_env()) .unwrap(); debug!("{}", ctx.func.display(None)); - ctx.verify(runtime.func_env().flags()).unwrap(); + ctx.verify(&flags).unwrap(); } } diff --git a/lib/wasm/src/lib.rs b/lib/wasm/src/lib.rs index 21b757f9c8..76da0113ac 100644 --- a/lib/wasm/src/lib.rs +++ b/lib/wasm/src/lib.rs @@ -40,6 +40,7 @@ extern crate cranelift_codegen; #[macro_use] extern crate cranelift_entity; extern crate cranelift_frontend; +#[cfg(test)] extern crate target_lexicon; extern crate wasmparser; diff --git a/lib/wasm/src/sections_translator.rs b/lib/wasm/src/sections_translator.rs index 1a09033705..3865a9c026 100644 --- a/lib/wasm/src/sections_translator.rs +++ b/lib/wasm/src/sections_translator.rs @@ -35,7 +35,7 @@ pub fn parse_function_signatures( ref params, ref returns, }) => { - let mut sig = Signature::new(environ.flags().call_conv()); + let mut sig = Signature::new(environ.target_config().default_call_conv); sig.params.extend(params.iter().map(|ty| { let cret_arg: ir::Type = type_to_type(*ty) .expect("only numeric types are supported in function signatures"); diff --git a/lib/wasm/tests/wasm_testsuite.rs b/lib/wasm/tests/wasm_testsuite.rs index be9c836463..bf0dee04dc 100644 --- a/lib/wasm/tests/wasm_testsuite.rs +++ b/lib/wasm/tests/wasm_testsuite.rs @@ -75,14 +75,12 @@ fn handle_module(path: &Path, flags: &Flags, return_mode: ReturnMode) { None | Some(&_) => panic!("the file extension for {:?} is not wasm or wat", path), }, }; - let mut dummy_environ = - DummyEnvironment::with_triple_flags(triple!("riscv64"), flags.clone(), return_mode); + let triple = triple!("riscv64"); + let isa = isa::lookup(triple).unwrap().finish(flags.clone()); + let mut dummy_environ = DummyEnvironment::new(isa.frontend_config(), return_mode); translate_module(&data, &mut dummy_environ).unwrap(); - let isa = isa::lookup(dummy_environ.info.triple) - .unwrap() - .finish(dummy_environ.info.flags); for func in dummy_environ.info.function_bodies.values() { verifier::verify_function(func, &*isa) .map_err(|errors| panic!(pretty_verifier_error(func, Some(&*isa), None, errors)))