Use the target-lexicon crate.
This switches from a custom list of architectures to use the target-lexicon crate. - "set is_64bit=1; isa x86" is replaced with "target x86_64", and similar for other architectures, and the `is_64bit` flag is removed entirely. - The `is_compressed` flag is removed too; it's no longer being used to control REX prefixes on x86-64, ARM and Thumb are separate architectures in target-lexicon, and we can figure out how to select RISC-V compressed encodings when we're ready.
This commit is contained in:
@@ -15,6 +15,7 @@ cretonne-entity = { path = "../entity", version = "0.8.0", default-features = fa
|
||||
failure = { version = "0.1.1", default-features = false, features = ["derive"] }
|
||||
failure_derive = { version = "0.1.1", default-features = false }
|
||||
hashmap_core = { version = "0.1.4", optional = true }
|
||||
target-lexicon = { version = "0.0.0", default-features = false }
|
||||
# It is a goal of the cretonne-codegen crate to have minimal external dependencies.
|
||||
# Please don't add any unless they are essential to the task of creating binary
|
||||
# machine code. Integration tests that need external dependencies can be
|
||||
|
||||
@@ -27,8 +27,6 @@ enable_verifier = BoolSetting(
|
||||
""",
|
||||
default=True)
|
||||
|
||||
is_64bit = BoolSetting("Enable 64-bit code generation")
|
||||
|
||||
call_conv = EnumSetting(
|
||||
"""
|
||||
Default calling convention:
|
||||
@@ -89,8 +87,6 @@ avoid_div_traps = BoolSetting(
|
||||
this setting has no effect - explicit checks are always inserted.
|
||||
""")
|
||||
|
||||
is_compressed = BoolSetting("Enable compressed instructions")
|
||||
|
||||
enable_float = BoolSetting(
|
||||
"""
|
||||
Enable the use of floating-point instructions
|
||||
|
||||
@@ -15,32 +15,43 @@ use isa::{EncInfo, RegClass, RegInfo, TargetIsa};
|
||||
use regalloc;
|
||||
use std::boxed::Box;
|
||||
use std::fmt;
|
||||
use target_lexicon::{Architecture, Triple};
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Isa {
|
||||
triple: Triple,
|
||||
shared_flags: shared_settings::Flags,
|
||||
isa_flags: settings::Flags,
|
||||
cpumode: &'static [shared_enc_tables::Level1Entry<u16>],
|
||||
}
|
||||
|
||||
/// Get an ISA builder for creating ARM32 targets.
|
||||
pub fn isa_builder() -> IsaBuilder {
|
||||
pub fn isa_builder(triple: Triple) -> IsaBuilder {
|
||||
IsaBuilder {
|
||||
triple,
|
||||
setup: settings::builder(),
|
||||
constructor: isa_constructor,
|
||||
}
|
||||
}
|
||||
|
||||
fn isa_constructor(
|
||||
triple: Triple,
|
||||
shared_flags: shared_settings::Flags,
|
||||
builder: shared_settings::Builder,
|
||||
) -> Box<TargetIsa> {
|
||||
let level1 = if shared_flags.is_compressed() {
|
||||
&enc_tables::LEVEL1_T32[..]
|
||||
} else {
|
||||
&enc_tables::LEVEL1_A32[..]
|
||||
let level1 = match triple.architecture {
|
||||
Architecture::Thumbv6m | Architecture::Thumbv7em | Architecture::Thumbv7m => {
|
||||
&enc_tables::LEVEL1_T32[..]
|
||||
}
|
||||
Architecture::Arm
|
||||
| Architecture::Armv4t
|
||||
| Architecture::Armv5te
|
||||
| Architecture::Armv7
|
||||
| Architecture::Armv7s => &enc_tables::LEVEL1_A32[..],
|
||||
_ => panic!(),
|
||||
};
|
||||
Box::new(Isa {
|
||||
triple,
|
||||
isa_flags: settings::Flags::new(&shared_flags, builder),
|
||||
shared_flags,
|
||||
cpumode: level1,
|
||||
@@ -52,6 +63,10 @@ impl TargetIsa for Isa {
|
||||
"arm32"
|
||||
}
|
||||
|
||||
fn triple(&self) -> &Triple {
|
||||
&self.triple
|
||||
}
|
||||
|
||||
fn flags(&self) -> &shared_settings::Flags {
|
||||
&self.shared_flags
|
||||
}
|
||||
|
||||
@@ -15,26 +15,31 @@ use isa::{EncInfo, RegClass, RegInfo, TargetIsa};
|
||||
use regalloc;
|
||||
use std::boxed::Box;
|
||||
use std::fmt;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Isa {
|
||||
triple: Triple,
|
||||
shared_flags: shared_settings::Flags,
|
||||
isa_flags: settings::Flags,
|
||||
}
|
||||
|
||||
/// Get an ISA builder for creating ARM64 targets.
|
||||
pub fn isa_builder() -> IsaBuilder {
|
||||
pub fn isa_builder(triple: Triple) -> IsaBuilder {
|
||||
IsaBuilder {
|
||||
triple,
|
||||
setup: settings::builder(),
|
||||
constructor: isa_constructor,
|
||||
}
|
||||
}
|
||||
|
||||
fn isa_constructor(
|
||||
triple: Triple,
|
||||
shared_flags: shared_settings::Flags,
|
||||
builder: shared_settings::Builder,
|
||||
) -> Box<TargetIsa> {
|
||||
Box::new(Isa {
|
||||
triple,
|
||||
isa_flags: settings::Flags::new(&shared_flags, builder),
|
||||
shared_flags,
|
||||
})
|
||||
@@ -45,6 +50,10 @@ impl TargetIsa for Isa {
|
||||
"arm64"
|
||||
}
|
||||
|
||||
fn triple(&self) -> &Triple {
|
||||
&self.triple
|
||||
}
|
||||
|
||||
fn flags(&self) -> &shared_settings::Flags {
|
||||
&self.shared_flags
|
||||
}
|
||||
|
||||
@@ -20,13 +20,18 @@
|
||||
//! appropriate for the requested ISA:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate cretonne_codegen;
|
||||
//! # #[macro_use] extern crate target_lexicon;
|
||||
//! # fn main() {
|
||||
//! use cretonne_codegen::settings::{self, Configurable};
|
||||
//! use cretonne_codegen::isa;
|
||||
//! use std::str::FromStr;
|
||||
//! use target_lexicon::Triple;
|
||||
//!
|
||||
//! let shared_builder = settings::builder();
|
||||
//! let shared_flags = settings::Flags::new(shared_builder);
|
||||
//!
|
||||
//! match isa::lookup("riscv") {
|
||||
//! match isa::lookup(triple!("riscv32")) {
|
||||
//! Err(_) => {
|
||||
//! // The RISC-V target ISA is not available.
|
||||
//! }
|
||||
@@ -35,6 +40,7 @@
|
||||
//! let isa = isa_builder.finish(shared_flags);
|
||||
//! }
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The configured target ISA trait object is a `Box<TargetIsa>` which can be used for multiple
|
||||
@@ -55,6 +61,7 @@ use settings;
|
||||
use settings::CallConv;
|
||||
use std::boxed::Box;
|
||||
use std::fmt;
|
||||
use target_lexicon::{Architecture, Triple};
|
||||
use timing;
|
||||
|
||||
#[cfg(build_riscv)]
|
||||
@@ -80,51 +87,61 @@ mod stack;
|
||||
macro_rules! isa_builder {
|
||||
($module:ident, $name:ident) => {{
|
||||
#[cfg($name)]
|
||||
fn $name() -> Result<Builder, LookupError> {
|
||||
Ok($module::isa_builder())
|
||||
fn $name(triple: Triple) -> Result<Builder, LookupError> {
|
||||
Ok($module::isa_builder(triple))
|
||||
};
|
||||
#[cfg(not($name))]
|
||||
fn $name() -> Result<Builder, LookupError> {
|
||||
fn $name(_triple: Triple) -> Result<Builder, LookupError> {
|
||||
Err(LookupError::Unsupported)
|
||||
}
|
||||
$name()
|
||||
$name
|
||||
}};
|
||||
}
|
||||
|
||||
/// Look for a supported ISA with the given `name`.
|
||||
/// Return a builder that can create a corresponding `TargetIsa`.
|
||||
pub fn lookup(name: &str) -> Result<Builder, LookupError> {
|
||||
match name {
|
||||
"riscv" => isa_builder!(riscv, build_riscv),
|
||||
"x86" => isa_builder!(x86, build_x86),
|
||||
"arm32" => isa_builder!(arm32, build_arm32),
|
||||
"arm64" => isa_builder!(arm64, build_arm64),
|
||||
_ => Err(LookupError::Unknown),
|
||||
pub fn lookup(triple: Triple) -> Result<Builder, LookupError> {
|
||||
match triple.architecture {
|
||||
Architecture::Riscv32 | Architecture::Riscv64 => isa_builder!(riscv, build_riscv)(triple),
|
||||
Architecture::I386 | Architecture::I586 | Architecture::I686 | Architecture::X86_64 => {
|
||||
isa_builder!(x86, build_x86)(triple)
|
||||
}
|
||||
Architecture::Thumbv6m
|
||||
| Architecture::Thumbv7em
|
||||
| Architecture::Thumbv7m
|
||||
| Architecture::Arm
|
||||
| Architecture::Armv4t
|
||||
| Architecture::Armv5te
|
||||
| Architecture::Armv7
|
||||
| Architecture::Armv7s => isa_builder!(arm32, build_arm32)(triple),
|
||||
Architecture::Aarch64 => isa_builder!(arm64, build_arm64)(triple),
|
||||
_ => Err(LookupError::Unsupported),
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes reason for target lookup failure
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum LookupError {
|
||||
/// Unknown Target
|
||||
Unknown,
|
||||
/// Support for this target was disabled in the current build.
|
||||
SupportDisabled,
|
||||
|
||||
/// Target known but not built and thus not supported
|
||||
/// Support for this target has not yet been implemented.
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
/// Builder for a `TargetIsa`.
|
||||
/// Modify the ISA-specific settings before creating the `TargetIsa` trait object with `finish`.
|
||||
pub struct Builder {
|
||||
triple: Triple,
|
||||
setup: settings::Builder,
|
||||
constructor: fn(settings::Flags, settings::Builder) -> Box<TargetIsa>,
|
||||
constructor: fn(Triple, settings::Flags, settings::Builder) -> Box<TargetIsa>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Combine the ISA-specific settings with the provided ISA-independent settings and allocate a
|
||||
/// fully configured `TargetIsa` trait object.
|
||||
pub fn finish(self, shared_flags: settings::Flags) -> Box<TargetIsa> {
|
||||
(self.constructor)(shared_flags, self.setup)
|
||||
(self.constructor)(self.triple, shared_flags, self.setup)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,9 +168,17 @@ pub trait TargetIsa: fmt::Display {
|
||||
/// Get the name of this ISA.
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
/// Get the target triple that was used to make this trait object.
|
||||
fn triple(&self) -> &Triple;
|
||||
|
||||
/// Get the ISA-independent flags that were used to make this trait object.
|
||||
fn flags(&self) -> &settings::Flags;
|
||||
|
||||
/// Get the pointer type of this ISA.
|
||||
fn pointer_type(&self) -> ir::Type {
|
||||
ir::Type::int(u16::from(self.triple().pointer_width().unwrap().bits())).unwrap()
|
||||
}
|
||||
|
||||
/// Does the CPU implement scalar comparisons using a CPU flags register?
|
||||
fn uses_cpu_flags(&self) -> bool {
|
||||
false
|
||||
@@ -252,7 +277,7 @@ pub trait TargetIsa: fmt::Display {
|
||||
use ir::stackslot::{StackOffset, StackSize};
|
||||
use stack_layout::layout_stack;
|
||||
|
||||
let word_size = if self.flags().is_64bit() { 8 } else { 4 };
|
||||
let word_size = StackSize::from(self.triple().pointer_width().unwrap().bytes());
|
||||
|
||||
// Account for the SpiderMonkey standard prologue pushes.
|
||||
if func.signature.call_conv == CallConv::Baldrdash {
|
||||
|
||||
@@ -11,12 +11,12 @@ use abi::{legalize_args, ArgAction, ArgAssigner, ValueConversion};
|
||||
use ir::{self, AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose, Type};
|
||||
use isa::RegClass;
|
||||
use regalloc::RegisterSet;
|
||||
use settings as shared_settings;
|
||||
use std::i32;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
struct Args {
|
||||
pointer_bits: u16,
|
||||
pointer_bytes: u32,
|
||||
pointer_bits: u8,
|
||||
pointer_bytes: u8,
|
||||
pointer_type: Type,
|
||||
regs: u32,
|
||||
reg_limit: u32,
|
||||
@@ -24,11 +24,11 @@ struct Args {
|
||||
}
|
||||
|
||||
impl Args {
|
||||
fn new(bits: u16, enable_e: bool) -> Self {
|
||||
fn new(bits: u8, enable_e: bool) -> Self {
|
||||
Self {
|
||||
pointer_bits: bits,
|
||||
pointer_bytes: u32::from(bits) / 8,
|
||||
pointer_type: Type::int(bits).unwrap(),
|
||||
pointer_bytes: bits / 8,
|
||||
pointer_type: Type::int(u16::from(bits)).unwrap(),
|
||||
regs: 0,
|
||||
reg_limit: if enable_e { 6 } else { 8 },
|
||||
offset: 0,
|
||||
@@ -51,15 +51,15 @@ impl ArgAssigner for Args {
|
||||
}
|
||||
|
||||
// Large integers and booleans are broken down to fit in a register.
|
||||
if !ty.is_float() && ty.bits() > self.pointer_bits {
|
||||
if !ty.is_float() && ty.bits() > u16::from(self.pointer_bits) {
|
||||
// Align registers and stack to a multiple of two pointers.
|
||||
self.regs = align(self.regs, 2);
|
||||
self.offset = align(self.offset, 2 * self.pointer_bytes);
|
||||
self.offset = align(self.offset, 2 * u32::from(self.pointer_bytes));
|
||||
return ValueConversion::IntSplit.into();
|
||||
}
|
||||
|
||||
// Small integers are extended to the size of a pointer register.
|
||||
if ty.is_int() && ty.bits() < self.pointer_bits {
|
||||
if ty.is_int() && ty.bits() < u16::from(self.pointer_bits) {
|
||||
match arg.extension {
|
||||
ArgumentExtension::None => {}
|
||||
ArgumentExtension::Uext => return ValueConversion::Uext(self.pointer_type).into(),
|
||||
@@ -79,7 +79,7 @@ impl ArgAssigner for Args {
|
||||
} else {
|
||||
// Assign a stack location.
|
||||
let loc = ArgumentLoc::Stack(self.offset as i32);
|
||||
self.offset += self.pointer_bytes;
|
||||
self.offset += u32::from(self.pointer_bytes);
|
||||
debug_assert!(self.offset <= i32::MAX as u32);
|
||||
loc.into()
|
||||
}
|
||||
@@ -89,11 +89,11 @@ impl ArgAssigner for Args {
|
||||
/// Legalize `sig` for RISC-V.
|
||||
pub fn legalize_signature(
|
||||
sig: &mut ir::Signature,
|
||||
flags: &shared_settings::Flags,
|
||||
triple: &Triple,
|
||||
isa_flags: &settings::Flags,
|
||||
current: bool,
|
||||
) {
|
||||
let bits = if flags.is_64bit() { 64 } else { 32 };
|
||||
let bits = triple.pointer_width().unwrap().bits();
|
||||
|
||||
let mut args = Args::new(bits, isa_flags.enable_e());
|
||||
legalize_args(&mut sig.params, &mut args);
|
||||
@@ -102,7 +102,7 @@ pub fn legalize_signature(
|
||||
legalize_args(&mut sig.returns, &mut rets);
|
||||
|
||||
if current {
|
||||
let ptr = Type::int(bits).unwrap();
|
||||
let ptr = Type::int(u16::from(bits)).unwrap();
|
||||
|
||||
// Add the link register as an argument and return value.
|
||||
//
|
||||
|
||||
@@ -15,32 +15,37 @@ use isa::{EncInfo, RegClass, RegInfo, TargetIsa};
|
||||
use regalloc;
|
||||
use std::boxed::Box;
|
||||
use std::fmt;
|
||||
use target_lexicon::{PointerWidth, Triple};
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Isa {
|
||||
triple: Triple,
|
||||
shared_flags: shared_settings::Flags,
|
||||
isa_flags: settings::Flags,
|
||||
cpumode: &'static [shared_enc_tables::Level1Entry<u16>],
|
||||
}
|
||||
|
||||
/// Get an ISA builder for creating RISC-V targets.
|
||||
pub fn isa_builder() -> IsaBuilder {
|
||||
pub fn isa_builder(triple: Triple) -> IsaBuilder {
|
||||
IsaBuilder {
|
||||
triple,
|
||||
setup: settings::builder(),
|
||||
constructor: isa_constructor,
|
||||
}
|
||||
}
|
||||
|
||||
fn isa_constructor(
|
||||
triple: Triple,
|
||||
shared_flags: shared_settings::Flags,
|
||||
builder: shared_settings::Builder,
|
||||
) -> Box<TargetIsa> {
|
||||
let level1 = if shared_flags.is_64bit() {
|
||||
&enc_tables::LEVEL1_RV64[..]
|
||||
} else {
|
||||
&enc_tables::LEVEL1_RV32[..]
|
||||
let level1 = match triple.pointer_width().unwrap() {
|
||||
PointerWidth::U16 => panic!("16-bit RISC-V unrecognized"),
|
||||
PointerWidth::U32 => &enc_tables::LEVEL1_RV32[..],
|
||||
PointerWidth::U64 => &enc_tables::LEVEL1_RV64[..],
|
||||
};
|
||||
Box::new(Isa {
|
||||
triple,
|
||||
isa_flags: settings::Flags::new(&shared_flags, builder),
|
||||
shared_flags,
|
||||
cpumode: level1,
|
||||
@@ -52,6 +57,10 @@ impl TargetIsa for Isa {
|
||||
"riscv"
|
||||
}
|
||||
|
||||
fn triple(&self) -> &Triple {
|
||||
&self.triple
|
||||
}
|
||||
|
||||
fn flags(&self) -> &shared_settings::Flags {
|
||||
&self.shared_flags
|
||||
}
|
||||
@@ -85,7 +94,7 @@ impl TargetIsa for Isa {
|
||||
}
|
||||
|
||||
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {
|
||||
abi::legalize_signature(sig, &self.shared_flags, &self.isa_flags, current)
|
||||
abi::legalize_signature(sig, &self.triple, &self.isa_flags, current)
|
||||
}
|
||||
|
||||
fn regclass_for_abi_type(&self, ty: ir::Type) -> RegClass {
|
||||
@@ -117,7 +126,9 @@ mod tests {
|
||||
use ir::{Function, InstructionData, Opcode};
|
||||
use isa;
|
||||
use settings::{self, Configurable};
|
||||
use std::str::FromStr;
|
||||
use std::string::{String, ToString};
|
||||
use target_lexicon;
|
||||
|
||||
fn encstr(isa: &isa::TargetIsa, enc: Result<isa::Encoding, isa::Legalize>) -> String {
|
||||
match enc {
|
||||
@@ -128,10 +139,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_64bitenc() {
|
||||
let mut shared_builder = settings::builder();
|
||||
shared_builder.enable("is_64bit").unwrap();
|
||||
let shared_builder = settings::builder();
|
||||
let shared_flags = settings::Flags::new(shared_builder);
|
||||
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
||||
let isa = isa::lookup(triple!("riscv64"))
|
||||
.unwrap()
|
||||
.finish(shared_flags);
|
||||
|
||||
let mut func = Function::new();
|
||||
let ebb = func.dfg.make_ebb();
|
||||
@@ -178,10 +190,11 @@ mod tests {
|
||||
// Same as above, but for RV32.
|
||||
#[test]
|
||||
fn test_32bitenc() {
|
||||
let mut shared_builder = settings::builder();
|
||||
shared_builder.set("is_64bit", "false").unwrap();
|
||||
let shared_builder = settings::builder();
|
||||
let shared_flags = settings::Flags::new(shared_builder);
|
||||
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
||||
let isa = isa::lookup(triple!("riscv32"))
|
||||
.unwrap()
|
||||
.finish(shared_flags);
|
||||
|
||||
let mut func = Function::new();
|
||||
let ebb = func.dfg.make_ebb();
|
||||
@@ -232,13 +245,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_rv32m() {
|
||||
let mut shared_builder = settings::builder();
|
||||
shared_builder.set("is_64bit", "false").unwrap();
|
||||
let shared_builder = settings::builder();
|
||||
let shared_flags = settings::Flags::new(shared_builder);
|
||||
|
||||
// Set the supports_m stting which in turn enables the use_m predicate that unlocks
|
||||
// encodings for imul.
|
||||
let mut isa_builder = isa::lookup("riscv").unwrap();
|
||||
let mut isa_builder = isa::lookup(triple!("riscv32")).unwrap();
|
||||
isa_builder.enable("supports_m").unwrap();
|
||||
|
||||
let isa = isa_builder.finish(shared_flags);
|
||||
|
||||
@@ -11,10 +11,10 @@ use ir::{get_probestack_funcref, AbiParam, ArgumentExtension, ArgumentLoc, Argum
|
||||
use isa::{RegClass, RegUnit, TargetIsa};
|
||||
use regalloc::RegisterSet;
|
||||
use result;
|
||||
use settings as shared_settings;
|
||||
use settings::CallConv;
|
||||
use stack_layout::layout_stack;
|
||||
use std::i32;
|
||||
use target_lexicon::{PointerWidth, Triple};
|
||||
|
||||
/// Argument registers for x86-64
|
||||
static ARG_GPRS: [RU; 6] = [RU::rdi, RU::rsi, RU::rdx, RU::rcx, RU::r8, RU::r9];
|
||||
@@ -29,8 +29,8 @@ static ARG_GPRS_WIN_FASTCALL_X64: [RU; 4] = [RU::rcx, RU::rdx, RU::r8, RU::r9];
|
||||
static RET_GPRS_WIN_FASTCALL_X64: [RU; 1] = [RU::rax];
|
||||
|
||||
struct Args {
|
||||
pointer_bytes: u32,
|
||||
pointer_bits: u16,
|
||||
pointer_bytes: u8,
|
||||
pointer_bits: u8,
|
||||
pointer_type: ir::Type,
|
||||
gpr: &'static [RU],
|
||||
gpr_used: usize,
|
||||
@@ -41,7 +41,7 @@ struct Args {
|
||||
}
|
||||
|
||||
impl Args {
|
||||
fn new(bits: u16, gpr: &'static [RU], fpr_limit: usize, call_conv: CallConv) -> Self {
|
||||
fn new(bits: u8, gpr: &'static [RU], fpr_limit: usize, call_conv: CallConv) -> Self {
|
||||
let offset = if let CallConv::WindowsFastcall = call_conv {
|
||||
// [1] "The caller is responsible for allocating space for parameters to the callee,
|
||||
// and must always allocate sufficient space to store four register parameters"
|
||||
@@ -51,9 +51,9 @@ impl Args {
|
||||
};
|
||||
|
||||
Self {
|
||||
pointer_bytes: u32::from(bits) / 8,
|
||||
pointer_bytes: bits / 8,
|
||||
pointer_bits: bits,
|
||||
pointer_type: ir::Type::int(bits).unwrap(),
|
||||
pointer_type: ir::Type::int(u16::from(bits)).unwrap(),
|
||||
gpr,
|
||||
gpr_used: 0,
|
||||
fpr_limit,
|
||||
@@ -75,12 +75,12 @@ impl ArgAssigner for Args {
|
||||
}
|
||||
|
||||
// Large integers and booleans are broken down to fit in a register.
|
||||
if !ty.is_float() && ty.bits() > self.pointer_bits {
|
||||
if !ty.is_float() && ty.bits() > u16::from(self.pointer_bits) {
|
||||
return ValueConversion::IntSplit.into();
|
||||
}
|
||||
|
||||
// Small integers are extended to the size of a pointer register.
|
||||
if ty.is_int() && ty.bits() < self.pointer_bits {
|
||||
if ty.is_int() && ty.bits() < u16::from(self.pointer_bits) {
|
||||
match arg.extension {
|
||||
ArgumentExtension::None => {}
|
||||
ArgumentExtension::Uext => return ValueConversion::Uext(self.pointer_type).into(),
|
||||
@@ -122,27 +122,31 @@ impl ArgAssigner for Args {
|
||||
|
||||
// Assign a stack location.
|
||||
let loc = ArgumentLoc::Stack(self.offset as i32);
|
||||
self.offset += self.pointer_bytes;
|
||||
self.offset += u32::from(self.pointer_bytes);
|
||||
debug_assert!(self.offset <= i32::MAX as u32);
|
||||
loc.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Legalize `sig`.
|
||||
pub fn legalize_signature(sig: &mut ir::Signature, flags: &shared_settings::Flags, _current: bool) {
|
||||
pub fn legalize_signature(sig: &mut ir::Signature, triple: &Triple, _current: bool) {
|
||||
let bits;
|
||||
let mut args;
|
||||
|
||||
if flags.is_64bit() {
|
||||
bits = 64;
|
||||
args = if sig.call_conv == CallConv::WindowsFastcall {
|
||||
Args::new(bits, &ARG_GPRS_WIN_FASTCALL_X64[..], 4, sig.call_conv)
|
||||
} else {
|
||||
Args::new(bits, &ARG_GPRS[..], 8, sig.call_conv)
|
||||
};
|
||||
} else {
|
||||
bits = 32;
|
||||
args = Args::new(bits, &[], 0, sig.call_conv);
|
||||
match triple.pointer_width().unwrap() {
|
||||
PointerWidth::U16 => panic!(),
|
||||
PointerWidth::U32 => {
|
||||
bits = 32;
|
||||
args = Args::new(bits, &[], 0, sig.call_conv);
|
||||
}
|
||||
PointerWidth::U64 => {
|
||||
bits = 64;
|
||||
args = if sig.call_conv == CallConv::WindowsFastcall {
|
||||
Args::new(bits, &ARG_GPRS_WIN_FASTCALL_X64[..], 4, sig.call_conv)
|
||||
} else {
|
||||
Args::new(bits, &ARG_GPRS[..], 8, sig.call_conv)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
legalize_args(&mut sig.params, &mut args);
|
||||
@@ -167,13 +171,13 @@ pub fn regclass_for_abi_type(ty: ir::Type) -> RegClass {
|
||||
}
|
||||
|
||||
/// Get the set of allocatable registers for `func`.
|
||||
pub fn allocatable_registers(_func: &ir::Function, flags: &shared_settings::Flags) -> RegisterSet {
|
||||
pub fn allocatable_registers(_func: &ir::Function, triple: &Triple) -> RegisterSet {
|
||||
let mut regs = RegisterSet::new();
|
||||
regs.take(GPR, RU::rsp as RegUnit);
|
||||
regs.take(GPR, RU::rbp as RegUnit);
|
||||
|
||||
// 32-bit arch only has 8 registers.
|
||||
if !flags.is_64bit() {
|
||||
if triple.pointer_width().unwrap() != PointerWidth::U64 {
|
||||
for i in 8..16 {
|
||||
regs.take(GPR, GPR.unit(i));
|
||||
regs.take(FPR, FPR.unit(i));
|
||||
@@ -184,34 +188,36 @@ pub fn allocatable_registers(_func: &ir::Function, flags: &shared_settings::Flag
|
||||
}
|
||||
|
||||
/// Get the set of callee-saved registers.
|
||||
fn callee_saved_gprs(flags: &shared_settings::Flags) -> &'static [RU] {
|
||||
if flags.is_64bit() {
|
||||
if flags.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
|
||||
// RSP & RSB are not listed below, since they are restored automatically during
|
||||
// a function call. If that wasn't the case, function calls (RET) would not work.
|
||||
&[
|
||||
RU::rbx,
|
||||
RU::rdi,
|
||||
RU::rsi,
|
||||
RU::r12,
|
||||
RU::r13,
|
||||
RU::r14,
|
||||
RU::r15,
|
||||
]
|
||||
} else {
|
||||
&[RU::rbx, RU::r12, RU::r13, RU::r14, RU::r15]
|
||||
fn callee_saved_gprs(isa: &TargetIsa) -> &'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 {
|
||||
// "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
|
||||
// RSP & RSB are not listed below, since they are restored automatically during
|
||||
// a function call. If that wasn't the case, function calls (RET) would not work.
|
||||
&[
|
||||
RU::rbx,
|
||||
RU::rdi,
|
||||
RU::rsi,
|
||||
RU::r12,
|
||||
RU::r13,
|
||||
RU::r14,
|
||||
RU::r15,
|
||||
]
|
||||
} else {
|
||||
&[RU::rbx, RU::r12, RU::r13, RU::r14, RU::r15]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
&[RU::rbx, RU::rsi, RU::rdi]
|
||||
}
|
||||
}
|
||||
|
||||
fn callee_saved_gprs_used(flags: &shared_settings::Flags, func: &ir::Function) -> RegisterSet {
|
||||
fn callee_saved_gprs_used(isa: &TargetIsa, func: &ir::Function) -> RegisterSet {
|
||||
let mut all_callee_saved = RegisterSet::empty();
|
||||
for reg in callee_saved_gprs(flags) {
|
||||
for reg in callee_saved_gprs(isa) {
|
||||
all_callee_saved.free(GPR, *reg as RegUnit);
|
||||
}
|
||||
|
||||
@@ -271,7 +277,7 @@ pub fn baldrdash_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) ->
|
||||
|
||||
// Baldrdash on 32-bit x86 always aligns its stack pointer to 16 bytes.
|
||||
let stack_align = 16;
|
||||
let word_size = if isa.flags().is_64bit() { 8 } else { 4 };
|
||||
let word_size = StackSize::from(isa.triple().pointer_width().unwrap().bytes());
|
||||
let bytes = StackSize::from(isa.flags().baldrdash_prologue_words()) * word_size;
|
||||
|
||||
let mut ss = ir::StackSlotData::new(ir::StackSlotKind::IncomingArg, bytes);
|
||||
@@ -285,7 +291,7 @@ pub fn baldrdash_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) ->
|
||||
/// Implementation of the fastcall-based Win64 calling convention described at [1]
|
||||
/// [1] https://msdn.microsoft.com/en-us/library/ms235286.aspx
|
||||
pub fn fastcall_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> result::CtonResult {
|
||||
if !isa.flags().is_64bit() {
|
||||
if isa.triple().pointer_width().unwrap() != PointerWidth::U64 {
|
||||
panic!("TODO: windows-fastcall: x86-32 not implemented yet");
|
||||
}
|
||||
|
||||
@@ -293,14 +299,10 @@ pub fn fastcall_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> r
|
||||
// which are aligned to 16 bytes in order to aid performance"
|
||||
let stack_align = 16;
|
||||
|
||||
let word_size = if isa.flags().is_64bit() { 8 } else { 4 };
|
||||
let reg_type = if isa.flags().is_64bit() {
|
||||
ir::types::I64
|
||||
} else {
|
||||
ir::types::I32
|
||||
};
|
||||
let word_size = isa.triple().pointer_width().unwrap().bytes() as usize;
|
||||
let reg_type = isa.pointer_type();
|
||||
|
||||
let csrs = callee_saved_gprs_used(isa.flags(), func);
|
||||
let csrs = callee_saved_gprs_used(isa, func);
|
||||
|
||||
// [1] "Space is allocated on the call stack as a shadow store for callees to save"
|
||||
// This shadow store contains the parameters which are passed through registers (ARG_GPRS)
|
||||
@@ -364,14 +366,11 @@ pub fn system_v_prologue_epilogue(func: &mut ir::Function, isa: &TargetIsa) -> r
|
||||
// The original 32-bit x86 ELF ABI had a 4-byte aligned stack pointer, but
|
||||
// newer versions use a 16-byte aligned stack pointer.
|
||||
let stack_align = 16;
|
||||
let word_size = if isa.flags().is_64bit() { 8 } else { 4 };
|
||||
let reg_type = if isa.flags().is_64bit() {
|
||||
ir::types::I64
|
||||
} else {
|
||||
ir::types::I32
|
||||
};
|
||||
let pointer_width = isa.triple().pointer_width().unwrap();
|
||||
let word_size = pointer_width.bytes() as usize;
|
||||
let reg_type = ir::Type::int(u16::from(pointer_width.bits())).unwrap();
|
||||
|
||||
let csrs = callee_saved_gprs_used(isa.flags(), func);
|
||||
let csrs = callee_saved_gprs_used(isa, func);
|
||||
|
||||
// The reserved stack area is composed of:
|
||||
// return address + frame pointer + all callee-saved registers
|
||||
@@ -463,7 +462,8 @@ fn insert_common_prologue(
|
||||
let callee = get_probestack_funcref(pos.func, reg_type, rax, isa);
|
||||
|
||||
// Make the call.
|
||||
let call = if !isa.flags().is_pic() && isa.flags().is_64bit()
|
||||
let call = if !isa.flags().is_pic()
|
||||
&& isa.triple().pointer_width().unwrap() == PointerWidth::U64
|
||||
&& !pos.func.dfg.ext_funcs[callee].colocated
|
||||
{
|
||||
// 64-bit non-PIC non-colocated calls need to be legalized to call_indirect.
|
||||
|
||||
@@ -16,33 +16,38 @@ use regalloc;
|
||||
use result;
|
||||
use std::boxed::Box;
|
||||
use std::fmt;
|
||||
use target_lexicon::{PointerWidth, Triple};
|
||||
use timing;
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Isa {
|
||||
triple: Triple,
|
||||
shared_flags: shared_settings::Flags,
|
||||
isa_flags: settings::Flags,
|
||||
cpumode: &'static [shared_enc_tables::Level1Entry<u16>],
|
||||
}
|
||||
|
||||
/// Get an ISA builder for creating x86 targets.
|
||||
pub fn isa_builder() -> IsaBuilder {
|
||||
pub fn isa_builder(triple: Triple) -> IsaBuilder {
|
||||
IsaBuilder {
|
||||
triple,
|
||||
setup: settings::builder(),
|
||||
constructor: isa_constructor,
|
||||
}
|
||||
}
|
||||
|
||||
fn isa_constructor(
|
||||
triple: Triple,
|
||||
shared_flags: shared_settings::Flags,
|
||||
builder: shared_settings::Builder,
|
||||
) -> Box<TargetIsa> {
|
||||
let level1 = if shared_flags.is_64bit() {
|
||||
&enc_tables::LEVEL1_I64[..]
|
||||
} else {
|
||||
&enc_tables::LEVEL1_I32[..]
|
||||
let level1 = match triple.pointer_width().unwrap() {
|
||||
PointerWidth::U16 => unimplemented!("x86-16"),
|
||||
PointerWidth::U32 => &enc_tables::LEVEL1_I32[..],
|
||||
PointerWidth::U64 => &enc_tables::LEVEL1_I64[..],
|
||||
};
|
||||
Box::new(Isa {
|
||||
triple,
|
||||
isa_flags: settings::Flags::new(&shared_flags, builder),
|
||||
shared_flags,
|
||||
cpumode: level1,
|
||||
@@ -54,6 +59,10 @@ impl TargetIsa for Isa {
|
||||
"x86"
|
||||
}
|
||||
|
||||
fn triple(&self) -> &Triple {
|
||||
&self.triple
|
||||
}
|
||||
|
||||
fn flags(&self) -> &shared_settings::Flags {
|
||||
&self.shared_flags
|
||||
}
|
||||
@@ -95,7 +104,7 @@ impl TargetIsa for Isa {
|
||||
}
|
||||
|
||||
fn legalize_signature(&self, sig: &mut ir::Signature, current: bool) {
|
||||
abi::legalize_signature(sig, &self.shared_flags, current)
|
||||
abi::legalize_signature(sig, &self.triple, current)
|
||||
}
|
||||
|
||||
fn regclass_for_abi_type(&self, ty: ir::Type) -> RegClass {
|
||||
@@ -103,7 +112,7 @@ impl TargetIsa for Isa {
|
||||
}
|
||||
|
||||
fn allocatable_registers(&self, func: &ir::Function) -> regalloc::RegisterSet {
|
||||
abi::allocatable_registers(func, &self.shared_flags)
|
||||
abi::allocatable_registers(func, &self.triple)
|
||||
}
|
||||
|
||||
fn emit_inst(
|
||||
|
||||
@@ -28,11 +28,7 @@ pub fn expand_call(
|
||||
_ => panic!("Wanted call: {}", func.dfg.display_inst(inst, None)),
|
||||
};
|
||||
|
||||
let ptr_ty = if isa.flags().is_64bit() {
|
||||
ir::types::I64
|
||||
} else {
|
||||
ir::types::I32
|
||||
};
|
||||
let ptr_ty = isa.pointer_type();
|
||||
|
||||
let sig = func.dfg.ext_funcs[func_ref].signature;
|
||||
|
||||
|
||||
@@ -283,11 +283,7 @@ pub fn expand_stack_check(
|
||||
ir::InstructionData::UnaryGlobalVar { global_var, .. } => global_var,
|
||||
_ => panic!("Want stack_check: {}", func.dfg.display_inst(inst, isa)),
|
||||
};
|
||||
let ptr_ty = if isa.flags().is_64bit() {
|
||||
ir::types::I64
|
||||
} else {
|
||||
ir::types::I32
|
||||
};
|
||||
let ptr_ty = isa.pointer_type();
|
||||
|
||||
let mut pos = FuncCursor::new(func).at_inst(inst);
|
||||
pos.use_srcloc(inst);
|
||||
|
||||
@@ -52,6 +52,11 @@ extern crate alloc;
|
||||
extern crate failure;
|
||||
#[macro_use]
|
||||
extern crate failure_derive;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate target_lexicon;
|
||||
#[cfg(not(test))]
|
||||
extern crate target_lexicon;
|
||||
|
||||
pub use context::Context;
|
||||
pub use legalizer::legalize_function;
|
||||
|
||||
@@ -275,6 +275,8 @@ mod tests {
|
||||
use regalloc::RegisterSet;
|
||||
use std::borrow::Borrow;
|
||||
use std::boxed::Box;
|
||||
use std::str::FromStr;
|
||||
use target_lexicon;
|
||||
|
||||
// Make an arm32 `TargetIsa`, if possible.
|
||||
fn arm32() -> Option<Box<TargetIsa>> {
|
||||
@@ -284,7 +286,9 @@ mod tests {
|
||||
let shared_builder = settings::builder();
|
||||
let shared_flags = settings::Flags::new(shared_builder);
|
||||
|
||||
isa::lookup("arm32").ok().map(|b| b.finish(shared_flags))
|
||||
isa::lookup(triple!("arm"))
|
||||
.ok()
|
||||
.map(|b| b.finish(shared_flags))
|
||||
}
|
||||
|
||||
// Get a register class by name.
|
||||
|
||||
@@ -1134,6 +1134,8 @@ mod tests {
|
||||
use isa::{RegClass, RegInfo, RegUnit, TargetIsa};
|
||||
use regalloc::RegisterSet;
|
||||
use std::boxed::Box;
|
||||
use std::str::FromStr;
|
||||
use target_lexicon;
|
||||
|
||||
// Make an arm32 `TargetIsa`, if possible.
|
||||
fn arm32() -> Option<Box<TargetIsa>> {
|
||||
@@ -1143,7 +1145,9 @@ mod tests {
|
||||
let shared_builder = settings::builder();
|
||||
let shared_flags = settings::Flags::new(shared_builder);
|
||||
|
||||
isa::lookup("arm32").ok().map(|b| b.finish(shared_flags))
|
||||
isa::lookup(triple!("arm"))
|
||||
.ok()
|
||||
.map(|b| b.finish(shared_flags))
|
||||
}
|
||||
|
||||
// Get a register class by name.
|
||||
|
||||
@@ -361,13 +361,11 @@ mod tests {
|
||||
"[shared]\n\
|
||||
opt_level = \"default\"\n\
|
||||
enable_verifier = true\n\
|
||||
is_64bit = false\n\
|
||||
call_conv = \"fast\"\n\
|
||||
is_pic = false\n\
|
||||
colocated_libcalls = false\n\
|
||||
return_at_end = false\n\
|
||||
avoid_div_traps = false\n\
|
||||
is_compressed = false\n\
|
||||
enable_float = true\n\
|
||||
enable_nan_canonicalization = false\n\
|
||||
enable_simd = true\n\
|
||||
|
||||
@@ -11,9 +11,10 @@ readme = "README.md"
|
||||
[dependencies]
|
||||
cretonne-codegen = { path = "../codegen", version = "0.8.0" }
|
||||
cretonne-module = { path = "../module", version = "0.8.0" }
|
||||
faerie = "0.3.0"
|
||||
faerie = "0.4.0"
|
||||
goblin = "0.0.14"
|
||||
failure = "0.1.1"
|
||||
target-lexicon = { version = "0.0.0" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
@@ -9,7 +9,7 @@ use cretonne_module::{Backend, DataContext, DataDescription, Init, Linkage, Modu
|
||||
use faerie;
|
||||
use failure::Error;
|
||||
use std::fs::File;
|
||||
use target;
|
||||
use target_lexicon::BinaryFormat;
|
||||
use traps::{FaerieTrapManifest, FaerieTrapSink};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -27,8 +27,7 @@ pub enum FaerieTrapCollection {
|
||||
pub struct FaerieBuilder {
|
||||
isa: Box<TargetIsa>,
|
||||
name: String,
|
||||
format: container::Format,
|
||||
faerie_target: faerie::Target,
|
||||
format: BinaryFormat,
|
||||
collect_traps: FaerieTrapCollection,
|
||||
libcall_names: Box<Fn(ir::LibCall) -> String>,
|
||||
}
|
||||
@@ -50,7 +49,7 @@ impl FaerieBuilder {
|
||||
pub fn new(
|
||||
isa: Box<TargetIsa>,
|
||||
name: String,
|
||||
format: container::Format,
|
||||
format: BinaryFormat,
|
||||
collect_traps: FaerieTrapCollection,
|
||||
libcall_names: Box<Fn(ir::LibCall) -> String>,
|
||||
) -> Result<Self, ModuleError> {
|
||||
@@ -59,12 +58,10 @@ impl FaerieBuilder {
|
||||
"faerie requires TargetIsa be PIC".to_owned(),
|
||||
));
|
||||
}
|
||||
let faerie_target = target::translate(&*isa)?;
|
||||
Ok(Self {
|
||||
isa,
|
||||
name,
|
||||
format,
|
||||
faerie_target,
|
||||
collect_traps,
|
||||
libcall_names,
|
||||
})
|
||||
@@ -92,7 +89,7 @@ impl FaerieBuilder {
|
||||
pub struct FaerieBackend {
|
||||
isa: Box<TargetIsa>,
|
||||
artifact: faerie::Artifact,
|
||||
format: container::Format,
|
||||
format: BinaryFormat,
|
||||
trap_manifest: Option<FaerieTrapManifest>,
|
||||
libcall_names: Box<Fn(ir::LibCall) -> String>,
|
||||
}
|
||||
@@ -119,8 +116,8 @@ impl Backend for FaerieBackend {
|
||||
/// Create a new `FaerieBackend` using the given Cretonne target.
|
||||
fn new(builder: FaerieBuilder) -> Self {
|
||||
Self {
|
||||
artifact: faerie::Artifact::new(builder.isa.triple().clone(), builder.name),
|
||||
isa: builder.isa,
|
||||
artifact: faerie::Artifact::new(builder.faerie_target, builder.name),
|
||||
format: builder.format,
|
||||
trap_manifest: match builder.collect_traps {
|
||||
FaerieTrapCollection::Enabled => Some(FaerieTrapManifest::new()),
|
||||
@@ -290,7 +287,6 @@ impl Backend for FaerieBackend {
|
||||
fn finish(self) -> FaerieProduct {
|
||||
FaerieProduct {
|
||||
artifact: self.artifact,
|
||||
format: self.format,
|
||||
trap_manifest: self.trap_manifest,
|
||||
}
|
||||
}
|
||||
@@ -305,8 +301,6 @@ pub struct FaerieProduct {
|
||||
/// Optional trap manifest. Contains `FaerieTrapManifest` when `FaerieBuilder.collect_traps` is
|
||||
/// set to `FaerieTrapCollection::Enabled`.
|
||||
pub trap_manifest: Option<FaerieTrapManifest>,
|
||||
/// The format that the builder specified for output.
|
||||
format: container::Format,
|
||||
}
|
||||
|
||||
impl FaerieProduct {
|
||||
@@ -317,18 +311,12 @@ impl FaerieProduct {
|
||||
|
||||
/// Call `emit` on the faerie `Artifact`, producing bytes in memory.
|
||||
pub fn emit(&self) -> Result<Vec<u8>, Error> {
|
||||
match self.format {
|
||||
container::Format::ELF => self.artifact.emit::<faerie::Elf>(),
|
||||
container::Format::MachO => self.artifact.emit::<faerie::Mach>(),
|
||||
}
|
||||
self.artifact.emit()
|
||||
}
|
||||
|
||||
/// Call `write` on the faerie `Artifact`, writing to a file.
|
||||
pub fn write(&self, sink: File) -> Result<(), Error> {
|
||||
match self.format {
|
||||
container::Format::ELF => self.artifact.write::<faerie::Elf>(sink),
|
||||
container::Format::MachO => self.artifact.write::<faerie::Mach>(sink),
|
||||
}
|
||||
self.artifact.write(sink)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +346,7 @@ fn translate_data_linkage(linkage: Linkage, writable: bool) -> faerie::Decl {
|
||||
}
|
||||
|
||||
struct FaerieRelocSink<'a> {
|
||||
format: container::Format,
|
||||
format: BinaryFormat,
|
||||
artifact: &'a mut faerie::Artifact,
|
||||
name: &'a str,
|
||||
namespace: &'a ModuleNamespace<'a, FaerieBackend>,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Utilities for working with Faerie container formats.
|
||||
|
||||
use cretonne_codegen::binemit::Reloc;
|
||||
use target_lexicon::BinaryFormat;
|
||||
|
||||
/// An object file format.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
@@ -13,9 +14,9 @@ pub enum Format {
|
||||
|
||||
/// Translate from a Cretonne `Reloc` to a raw object-file-format-specific
|
||||
/// relocation code.
|
||||
pub fn raw_relocation(reloc: Reloc, format: Format) -> u32 {
|
||||
pub fn raw_relocation(reloc: Reloc, format: BinaryFormat) -> u32 {
|
||||
match format {
|
||||
Format::ELF => {
|
||||
BinaryFormat::Elf => {
|
||||
use goblin::elf;
|
||||
match reloc {
|
||||
Reloc::Abs4 => elf::reloc::R_X86_64_32,
|
||||
@@ -28,6 +29,7 @@ pub fn raw_relocation(reloc: Reloc, format: Format) -> u32 {
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
Format::MachO => unimplemented!(),
|
||||
BinaryFormat::Macho => unimplemented!("macho relocations"),
|
||||
_ => unimplemented!("unsupported format"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ extern crate cretonne_module;
|
||||
extern crate faerie;
|
||||
extern crate failure;
|
||||
extern crate goblin;
|
||||
extern crate target_lexicon;
|
||||
|
||||
mod backend;
|
||||
mod container;
|
||||
mod target;
|
||||
pub mod traps;
|
||||
|
||||
pub use backend::{FaerieBackend, FaerieBuilder, FaerieProduct, FaerieTrapCollection};
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
use cretonne_codegen::isa;
|
||||
use cretonne_module::ModuleError;
|
||||
use faerie::Target;
|
||||
|
||||
/// Translate from a Cretonne `TargetIsa` to a Faerie `Target`.
|
||||
pub fn translate(isa: &isa::TargetIsa) -> Result<Target, ModuleError> {
|
||||
let name = isa.name();
|
||||
match name {
|
||||
"x86" => Ok(if isa.flags().is_64bit() {
|
||||
Target::X86_64
|
||||
} else {
|
||||
Target::X86
|
||||
}),
|
||||
"arm32" => Ok(Target::ARMv7),
|
||||
"arm64" => Ok(Target::ARM64),
|
||||
_ => Err(ModuleError::Backend(format!(
|
||||
"unsupported faerie isa: {}",
|
||||
name
|
||||
))),
|
||||
}
|
||||
}
|
||||
@@ -318,11 +318,7 @@ where
|
||||
|
||||
/// Return then pointer type for the current target.
|
||||
pub fn pointer_type(&self) -> ir::types::Type {
|
||||
if self.backend.isa().flags().is_64bit() {
|
||||
ir::types::I64
|
||||
} else {
|
||||
ir::types::I32
|
||||
}
|
||||
self.backend.isa().pointer_type()
|
||||
}
|
||||
|
||||
/// Create a new `Context` initialized for use with this `Module`.
|
||||
|
||||
@@ -9,6 +9,7 @@ readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
cretonne-codegen = { path = "../codegen", version = "0.8.0", default-features = false }
|
||||
target-lexicon = { version = "0.0.0", default-features = false }
|
||||
|
||||
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dependencies]
|
||||
raw-cpuid = "3.1.0"
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
extern crate cretonne_codegen;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
extern crate raw_cpuid;
|
||||
extern crate target_lexicon;
|
||||
|
||||
use cretonne_codegen::isa;
|
||||
use cretonne_codegen::settings::{self, Configurable};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use raw_cpuid::CpuId;
|
||||
@@ -38,25 +40,8 @@ pub fn builders() -> Result<(settings::Builder, isa::Builder), &'static str> {
|
||||
return Err("unrecognized environment");
|
||||
}
|
||||
|
||||
if cfg!(target_pointer_width = "64") {
|
||||
flag_builder.enable("is_64bit").unwrap();
|
||||
} else if !cfg!(target_pointer_width = "32") {
|
||||
return Err("unrecognized pointer size");
|
||||
}
|
||||
|
||||
// TODO: Add RISC-V support once Rust supports it.
|
||||
let name = if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
|
||||
"x86"
|
||||
} else if cfg!(target_arch = "arm") {
|
||||
"arm32"
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"arm64"
|
||||
} else {
|
||||
return Err("unrecognized architecture");
|
||||
};
|
||||
|
||||
let mut isa_builder = isa::lookup(name).map_err(|err| match err {
|
||||
isa::LookupError::Unknown => panic!(),
|
||||
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",
|
||||
})?;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
cretonne-codegen = { path = "../codegen", version = "0.8.0" }
|
||||
target-lexicon = { version = "0.0.0" }
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "experimental" }
|
||||
|
||||
@@ -40,7 +40,7 @@ macro_rules! err {
|
||||
( $loc:expr, $msg:expr ) => {
|
||||
Err($crate::Error {
|
||||
location: $loc.clone(),
|
||||
message: String::from($msg),
|
||||
message: $msg.to_string(),
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
)]
|
||||
|
||||
extern crate cretonne_codegen;
|
||||
extern crate target_lexicon;
|
||||
|
||||
pub use error::{Error, Location, Result};
|
||||
pub use isaspec::{parse_options, IsaSpec};
|
||||
|
||||
@@ -22,12 +22,13 @@ use sourcemap::SourceMap;
|
||||
use std::mem;
|
||||
use std::str::FromStr;
|
||||
use std::{u16, u32};
|
||||
use target_lexicon::Triple;
|
||||
use testcommand::TestCommand;
|
||||
use testfile::{Comment, Details, TestFile};
|
||||
|
||||
/// Parse the entire `text` into a list of functions.
|
||||
///
|
||||
/// Any test commands or ISA declarations are ignored.
|
||||
/// Any test commands or target declarations are ignored.
|
||||
pub fn parse_functions(text: &str) -> Result<Vec<Function>> {
|
||||
let _tt = timing::parse_text();
|
||||
parse_test(text).map(|file| file.functions.into_iter().map(|(func, _)| func).collect())
|
||||
@@ -43,7 +44,7 @@ pub fn parse_test(text: &str) -> Result<TestFile> {
|
||||
parser.start_gathering_comments();
|
||||
|
||||
let commands = parser.parse_test_commands();
|
||||
let isa_spec = parser.parse_isa_specs()?;
|
||||
let isa_spec = parser.parse_target_specs()?;
|
||||
|
||||
parser.token();
|
||||
parser.claim_gathered_comments(AnyEntity::Function);
|
||||
@@ -88,10 +89,10 @@ struct Context<'a> {
|
||||
/// Aliases to resolve once value definitions are known.
|
||||
aliases: Vec<Value>,
|
||||
|
||||
/// Reference to the unique_isa for things like parsing ISA-specific instruction encoding
|
||||
/// Reference to the unique_isa for things like parsing target-specific instruction encoding
|
||||
/// information. This is only `Some` if exactly one set of `isa` directives were found in the
|
||||
/// prologue (it is valid to have directives for multiple different ISAs, but in that case we
|
||||
/// couldn't know which ISA the provided encodings are intended for)
|
||||
/// prologue (it is valid to have directives for multiple different targets, but in that case we
|
||||
/// couldn't know which target the provided encodings are intended for)
|
||||
unique_isa: Option<&'a TargetIsa>,
|
||||
}
|
||||
|
||||
@@ -667,17 +668,17 @@ impl<'a> Parser<'a> {
|
||||
list
|
||||
}
|
||||
|
||||
/// Parse a list of ISA specs.
|
||||
/// Parse a list of target specs.
|
||||
///
|
||||
/// Accept a mix of `isa` and `set` command lines. The `set` commands are cumulative.
|
||||
/// Accept a mix of `target` and `set` command lines. The `set` commands are cumulative.
|
||||
///
|
||||
pub fn parse_isa_specs(&mut self) -> Result<isaspec::IsaSpec> {
|
||||
// Was there any `isa` commands?
|
||||
let mut seen_isa = false;
|
||||
// Location of last `set` command since the last `isa`.
|
||||
pub fn parse_target_specs(&mut self) -> Result<isaspec::IsaSpec> {
|
||||
// Was there any `target` commands?
|
||||
let mut seen_target = false;
|
||||
// Location of last `set` command since the last `target`.
|
||||
let mut last_set_loc = None;
|
||||
|
||||
let mut isas = Vec::new();
|
||||
let mut targets = Vec::new();
|
||||
let mut flag_builder = settings::builder();
|
||||
|
||||
while let Some(Token::Identifier(command)) = self.token() {
|
||||
@@ -690,38 +691,42 @@ impl<'a> Parser<'a> {
|
||||
&self.loc,
|
||||
)?;
|
||||
}
|
||||
"isa" => {
|
||||
"target" => {
|
||||
let loc = self.loc;
|
||||
// Grab the whole line so the lexer won't go looking for tokens on the
|
||||
// following lines.
|
||||
let mut words = self.consume_line().trim().split_whitespace();
|
||||
// Look for `isa foo`.
|
||||
let isa_name = match words.next() {
|
||||
None => return err!(loc, "expected ISA name"),
|
||||
// Look for `target foo`.
|
||||
let target_name = match words.next() {
|
||||
Some(w) => w,
|
||||
None => return err!(loc, "expected target triple"),
|
||||
};
|
||||
let mut isa_builder = match isa::lookup(isa_name) {
|
||||
Err(isa::LookupError::Unknown) => {
|
||||
return err!(loc, "unknown ISA '{}'", isa_name)
|
||||
let triple = match Triple::from_str(target_name) {
|
||||
Ok(triple) => triple,
|
||||
Err(err) => return err!(loc, err),
|
||||
};
|
||||
let mut isa_builder = match isa::lookup(triple) {
|
||||
Err(isa::LookupError::SupportDisabled) => {
|
||||
continue;
|
||||
}
|
||||
Err(isa::LookupError::Unsupported) => {
|
||||
continue;
|
||||
return err!(loc, "unsupported target '{}'", target_name)
|
||||
}
|
||||
Ok(b) => b,
|
||||
};
|
||||
last_set_loc = None;
|
||||
seen_isa = true;
|
||||
// Apply the ISA-specific settings to `isa_builder`.
|
||||
seen_target = true;
|
||||
// Apply the target-specific settings to `isa_builder`.
|
||||
isaspec::parse_options(words, &mut isa_builder, &self.loc)?;
|
||||
|
||||
// Construct a trait object with the aggregate settings.
|
||||
isas.push(isa_builder.finish(settings::Flags::new(flag_builder.clone())));
|
||||
targets.push(isa_builder.finish(settings::Flags::new(flag_builder.clone())));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
if !seen_isa {
|
||||
// No `isa` commands, but we allow for `set` commands.
|
||||
if !seen_target {
|
||||
// No `target` commands, but we allow for `set` commands.
|
||||
Ok(isaspec::IsaSpec::None(settings::Flags::new(flag_builder)))
|
||||
} else if let Some(loc) = last_set_loc {
|
||||
err!(
|
||||
@@ -729,7 +734,7 @@ impl<'a> Parser<'a> {
|
||||
"dangling 'set' command after ISA specification has no effect."
|
||||
)
|
||||
} else {
|
||||
Ok(isaspec::IsaSpec::Some(isas))
|
||||
Ok(isaspec::IsaSpec::Some(targets))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2466,14 +2471,14 @@ mod tests {
|
||||
fn isa_spec() {
|
||||
assert!(
|
||||
parse_test(
|
||||
"isa
|
||||
"target
|
||||
function %foo() system_v {}",
|
||||
).is_err()
|
||||
);
|
||||
|
||||
assert!(
|
||||
parse_test(
|
||||
"isa riscv
|
||||
"target riscv32
|
||||
set enable_float=false
|
||||
function %foo() system_v {}",
|
||||
).is_err()
|
||||
|
||||
@@ -15,6 +15,7 @@ cretonne-native = { path = "../native", version = "0.8.0", default-features = fa
|
||||
region = "0.2.0"
|
||||
libc = { version = "0.2.40", default-features = false }
|
||||
errno = "0.2.3"
|
||||
target-lexicon = { version = "0.0.0", default-features = false }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3", features = ["winbase", "memoryapi"] }
|
||||
|
||||
@@ -10,6 +10,7 @@ use libc;
|
||||
use memory::Memory;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
use target_lexicon::PointerWidth;
|
||||
#[cfg(windows)]
|
||||
use winapi;
|
||||
|
||||
@@ -173,10 +174,10 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
|
||||
}
|
||||
}
|
||||
|
||||
let reloc = if self.isa.flags().is_64bit() {
|
||||
Reloc::Abs8
|
||||
} else {
|
||||
Reloc::Abs4
|
||||
let reloc = match self.isa.triple().pointer_width().unwrap() {
|
||||
PointerWidth::U16 => panic!(),
|
||||
PointerWidth::U32 => Reloc::Abs4,
|
||||
PointerWidth::U64 => Reloc::Abs8,
|
||||
};
|
||||
let mut relocs = Vec::new();
|
||||
for &(offset, id) in function_relocs {
|
||||
|
||||
@@ -18,6 +18,7 @@ extern crate cretonne_native;
|
||||
extern crate errno;
|
||||
extern crate libc;
|
||||
extern crate region;
|
||||
extern crate target_lexicon;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate winapi;
|
||||
|
||||
@@ -15,6 +15,7 @@ cretonne-frontend = { path = "../frontend", version = "0.8.0", default-features
|
||||
hashmap_core = { version = "0.1.4", 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.0", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
wabt = "0.3"
|
||||
|
||||
@@ -8,6 +8,7 @@ use environ::{FuncEnvironment, GlobalValue, ModuleEnvironment, WasmResult};
|
||||
use func_translator::FuncTranslator;
|
||||
use std::string::String;
|
||||
use std::vec::Vec;
|
||||
use target_lexicon::Triple;
|
||||
use translation_utils::{FunctionIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
|
||||
Table, TableIndex};
|
||||
use wasmparser;
|
||||
@@ -39,6 +40,9 @@ 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,
|
||||
|
||||
@@ -69,8 +73,9 @@ pub struct DummyModuleInfo {
|
||||
|
||||
impl DummyModuleInfo {
|
||||
/// Allocates the data structures with the given flags.
|
||||
pub fn with_flags(flags: settings::Flags) -> Self {
|
||||
pub fn with_triple_flags(triple: Triple, flags: settings::Flags) -> Self {
|
||||
Self {
|
||||
triple,
|
||||
flags,
|
||||
signatures: Vec::new(),
|
||||
imported_funcs: Vec::new(),
|
||||
@@ -100,14 +105,14 @@ pub struct DummyEnvironment {
|
||||
|
||||
impl DummyEnvironment {
|
||||
/// Allocates the data structures with default flags.
|
||||
pub fn default() -> Self {
|
||||
Self::with_flags(settings::Flags::new(settings::builder()))
|
||||
pub fn with_triple(triple: Triple) -> Self {
|
||||
Self::with_triple_flags(triple, settings::Flags::new(settings::builder()))
|
||||
}
|
||||
|
||||
/// Allocates the data structures with the given flags.
|
||||
pub fn with_flags(flags: settings::Flags) -> Self {
|
||||
pub fn with_triple_flags(triple: Triple, flags: settings::Flags) -> Self {
|
||||
Self {
|
||||
info: DummyModuleInfo::with_flags(flags),
|
||||
info: DummyModuleInfo::with_triple_flags(triple, flags),
|
||||
trans: FuncTranslator::new(),
|
||||
func_bytecode_sizes: Vec::new(),
|
||||
}
|
||||
@@ -143,6 +148,10 @@ 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
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use cretonne_codegen::cursor::FuncCursor;
|
||||
use cretonne_codegen::ir::{self, InstBuilder};
|
||||
use cretonne_codegen::settings::Flags;
|
||||
use std::vec::Vec;
|
||||
use target_lexicon::Triple;
|
||||
use translation_utils::{FunctionIndex, Global, GlobalIndex, Memory, MemoryIndex, SignatureIndex,
|
||||
Table, TableIndex};
|
||||
use wasmparser::BinaryReaderError;
|
||||
@@ -74,6 +75,9 @@ pub type WasmResult<T> = Result<T, WasmError>;
|
||||
/// 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;
|
||||
|
||||
@@ -81,11 +85,7 @@ pub trait FuncEnvironment {
|
||||
///
|
||||
/// This returns `I64` for 64-bit architectures and `I32` for 32-bit architectures.
|
||||
fn native_pointer(&self) -> ir::Type {
|
||||
if self.flags().is_64bit() {
|
||||
ir::types::I64
|
||||
} else {
|
||||
ir::types::I32
|
||||
}
|
||||
ir::Type::int(u16::from(self.triple().pointer_width().unwrap().bits())).unwrap()
|
||||
}
|
||||
|
||||
/// Set up the necessary preamble definitions in `func` to access the global variable
|
||||
|
||||
@@ -237,6 +237,7 @@ mod tests {
|
||||
use cretonne_codegen::ir::types::I32;
|
||||
use cretonne_codegen::{ir, Context};
|
||||
use environ::{DummyEnvironment, FuncEnvironment};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
#[test]
|
||||
fn small1() {
|
||||
@@ -256,7 +257,7 @@ mod tests {
|
||||
];
|
||||
|
||||
let mut trans = FuncTranslator::new();
|
||||
let runtime = DummyEnvironment::default();
|
||||
let runtime = DummyEnvironment::with_triple(Triple::default());
|
||||
let mut ctx = Context::new();
|
||||
|
||||
ctx.func.name = ir::ExternalName::testcase("small1");
|
||||
@@ -289,7 +290,7 @@ mod tests {
|
||||
];
|
||||
|
||||
let mut trans = FuncTranslator::new();
|
||||
let runtime = DummyEnvironment::default();
|
||||
let runtime = DummyEnvironment::with_triple(Triple::default());
|
||||
let mut ctx = Context::new();
|
||||
|
||||
ctx.func.name = ir::ExternalName::testcase("small2");
|
||||
@@ -335,7 +336,7 @@ mod tests {
|
||||
];
|
||||
|
||||
let mut trans = FuncTranslator::new();
|
||||
let runtime = DummyEnvironment::default();
|
||||
let runtime = DummyEnvironment::with_triple(Triple::default());
|
||||
let mut ctx = Context::new();
|
||||
|
||||
ctx.func.name = ir::ExternalName::testcase("infloop");
|
||||
|
||||
@@ -34,6 +34,7 @@ extern crate hashmap_core;
|
||||
#[macro_use(dbg)]
|
||||
extern crate cretonne_codegen;
|
||||
extern crate cretonne_frontend;
|
||||
extern crate target_lexicon;
|
||||
extern crate wasmparser;
|
||||
|
||||
extern crate failure;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
extern crate cretonne_codegen;
|
||||
extern crate cretonne_wasm;
|
||||
#[macro_use]
|
||||
extern crate target_lexicon;
|
||||
extern crate wabt;
|
||||
|
||||
use cretonne_codegen::print_errors::pretty_verifier_error;
|
||||
@@ -11,6 +13,7 @@ use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use wabt::wat2wasm;
|
||||
|
||||
#[test]
|
||||
@@ -70,7 +73,7 @@ fn handle_module(path: &Path, flags: &Flags) {
|
||||
None | Some(&_) => panic!("the file extension for {:?} is not wasm or wat", path),
|
||||
},
|
||||
};
|
||||
let mut dummy_environ = DummyEnvironment::with_flags(flags.clone());
|
||||
let mut dummy_environ = DummyEnvironment::with_triple_flags(triple!("riscv64"), flags.clone());
|
||||
translate_module(&data, &mut dummy_environ).unwrap();
|
||||
for func in &dummy_environ.info.function_bodies {
|
||||
verifier::verify_function(func, flags)
|
||||
|
||||
Reference in New Issue
Block a user