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.
This commit is contained in:
Dan Gohman
2018-11-02 13:51:42 -07:00
committed by GitHub
parent 7094d9f470
commit d4f8eb7453
27 changed files with 297 additions and 168 deletions

View File

@@ -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 {

View File

@@ -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"

View File

@@ -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();
});

View File

@@ -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);

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)));
}

View File

@@ -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<Self, Self::Err> {
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(()),
}
}
}

View File

@@ -46,6 +46,7 @@
//! The configured target ISA trait object is a `Box<TargetIsa>` 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?

View File

@@ -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);
}

View File

@@ -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\

View File

@@ -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:

View File

@@ -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};
//!

View File

@@ -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.

View File

@@ -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<isa::Builder, &'static str> {
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);
}
}
}
}

View File

@@ -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};

View File

@@ -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)

View File

@@ -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::*;

View File

@@ -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};
}

View File

@@ -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]

View File

@@ -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<T> Exportable<T> {
/// `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<SignatureIndex, ir::Signature>,
@@ -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 {

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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");

View File

@@ -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)))