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

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