From d4f8eb7453c8e8a060f26ddc9ac7983eb0bbb8be Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 2 Nov 2018 13:51:42 -0700 Subject: [PATCH] Introduce a `TargetFrontendConfig` type. (#570) * Introduce a `TargetFrontendConfig` type. `TargetFrontendConfig` is information specific to the target which is provided to frontends to allow them to produce Cranelift IR for the target. Currently this includes the pointer size and the default calling convention. The default calling convention is now inferred from the target, rather than being a setting. cranelift-native is now just a provider of target information, rather than also being a provider of settings, which gives it a clearer role. And instead of having cranelift-frontend routines require the whole `TargetIsa`, just require the `TargetFrontendConfig`, and add a way to get the `TargetFrontendConfig` from a `Module`. Fixes #529. Fixes #555. --- .../filetests/isa/x86/legalize-libcall.clif | 1 - cranelift/fuzz/Cargo.toml | 1 + cranelift/fuzz/fuzz_translate_module.rs | 11 ++- cranelift/src/wasm.rs | 6 +- lib/codegen/meta-python/base/settings.py | 24 ------ lib/codegen/src/ir/extfunc.rs | 3 +- lib/codegen/src/ir/function.rs | 3 +- lib/codegen/src/ir/libcall.rs | 6 +- lib/codegen/src/isa/call_conv.rs | 74 +++++++++++++++++++ lib/codegen/src/isa/mod.rs | 56 +++++++++++++- lib/codegen/src/isa/x86/abi.rs | 9 +-- lib/codegen/src/settings.rs | 1 - lib/frontend/src/frontend.rs | 66 +++++++++++------ lib/frontend/src/lib.rs | 3 +- lib/module/src/module.rs | 17 +++-- lib/native/src/lib.rs | 47 ++++++++---- lib/reader/src/parser.rs | 5 +- lib/simplejit/src/backend.rs | 5 +- lib/simplejit/tests/basic.rs | 2 +- lib/umbrella/src/lib.rs | 2 +- lib/wasm/Cargo.toml | 4 +- lib/wasm/src/environ/dummy.rs | 48 ++++-------- lib/wasm/src/environ/spec.rs | 18 ++--- lib/wasm/src/func_translator.rs | 42 ++++++++--- lib/wasm/src/lib.rs | 1 + lib/wasm/src/sections_translator.rs | 2 +- lib/wasm/tests/wasm_testsuite.rs | 8 +- 27 files changed, 297 insertions(+), 168 deletions(-) create mode 100644 lib/codegen/src/isa/call_conv.rs 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)))