machinst x64: Only use the feature flag to enable the x64 new backend;

Before this patch, running the x64 new backend would require both
compiling with --features experimental_x64 and running with
`use_new_backend`.

This patches changes this behavior so that the runtime flag is not
needed anymore: using the feature flag will enforce usage of the new
backend everywhere, making using and testing it much simpler:

    cargo run --features experimental_x64 ;; other CLI options/flags

This also gives a hint at what the meta language generation would look
like after switching to the new backend.

Compiling only with the x64 codegen flag gives a nice compile time speedup.
This commit is contained in:
Benjamin Bouvier
2020-07-09 17:18:22 +02:00
parent bc1e960b9e
commit abf157bd69
14 changed files with 134 additions and 44 deletions

View File

@@ -48,3 +48,4 @@ default = ["disas", "wasm", "cranelift-codegen/all-arch"]
disas = ["capstone"]
enable-peepmatic = ["cranelift-codegen/enable-peepmatic", "cranelift-filetests/enable-peepmatic"]
wasm = ["wat", "cranelift-wasm"]
experimental_x64 = ["cranelift-codegen/x64"]

View File

@@ -66,7 +66,6 @@ x64 = [] # New work-in-progress codegen backend for x86_64 based on the new isel
# Option to enable all architectures.
all-arch = [
"x86",
"x64",
"arm32",
"arm64",
"riscv"

View File

@@ -26,7 +26,15 @@ fn main() {
let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set");
let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set");
// Configure isa targets cfg.
let new_backend_isas = if env::var("CARGO_FEATURE_X64").is_ok() {
// The x64 (new backend for x86_64) is a bit particular: it only requires generating
// the shared meta code; the only ISA-specific code is for settings.
vec![meta::isa::Isa::X86]
} else {
Vec::new()
};
// Configure isa targets using the old backend.
let isa_targets = meta::isa::Isa::all()
.iter()
.cloned()
@@ -36,7 +44,7 @@ fn main() {
})
.collect::<Vec<_>>();
let isas = if isa_targets.is_empty() {
let old_backend_isas = if new_backend_isas.is_empty() && isa_targets.is_empty() {
// Try to match native target.
let target_name = target_triple.split('-').next().unwrap();
let isa = meta::isa_from_arch(&target_name).expect("error when identifying target");
@@ -56,14 +64,23 @@ fn main() {
crate_dir.join("build.rs").to_str().unwrap()
);
if let Err(err) = meta::generate(&isas, &out_dir) {
if let Err(err) = meta::generate(&old_backend_isas, &new_backend_isas, &out_dir) {
eprintln!("Error: {}", err);
process::exit(1);
}
if env::var("CRANELIFT_VERBOSE").is_ok() {
for isa in &isas {
println!("cargo:warning=Includes support for {} ISA", isa.to_string());
for isa in &old_backend_isas {
println!(
"cargo:warning=Includes old-backend support for {} ISA",
isa.to_string()
);
}
for isa in &new_backend_isas {
println!(
"cargo:warning=Includes new-backend support for {} ISA",
isa.to_string()
);
}
println!(
"cargo:warning=Build step took {:?}.",

View File

@@ -700,6 +700,7 @@ fn gen_isa(
pub(crate) fn generate(
isas: &[TargetIsa],
transform_groups: &TransformGroups,
extra_legalization_groups: &[&'static str],
filename_prefix: &str,
out_dir: &str,
) -> Result<(), error::Error> {
@@ -711,8 +712,14 @@ pub(crate) fn generate(
fmt.update_file(format!("{}-{}.rs", filename_prefix, isa.name), out_dir)?;
}
// Add extra legalization groups that were explicitly requested.
for group in extra_legalization_groups {
shared_group_names.insert(group);
}
// Generate shared legalize groups.
let mut fmt = Formatter::new();
// Generate shared legalize groups.
let mut type_sets = UniqueTable::new();
let mut sorted_shared_group_names = Vec::from_iter(shared_group_names);
sorted_shared_group_names.sort();

View File

@@ -6,10 +6,10 @@ use std::fmt;
mod arm32;
mod arm64;
mod riscv;
mod x86;
pub(crate) mod x86;
/// Represents known ISA target.
#[derive(Copy, Clone)]
#[derive(PartialEq, Copy, Clone)]
pub enum Isa {
Riscv,
X86,

View File

@@ -14,7 +14,7 @@ mod legalize;
mod opcodes;
mod recipes;
mod registers;
mod settings;
pub(crate) mod settings;
pub(crate) fn define(shared_defs: &mut SharedDefinitions) -> TargetIsa {
let settings = settings::define(&shared_defs.settings);

View File

@@ -3,12 +3,6 @@ use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder};
pub(crate) fn define(shared: &SettingGroup) -> SettingGroup {
let mut settings = SettingGroupBuilder::new("x86");
settings.add_bool(
"use_new_backend",
"Whether to use the new codegen backend using the new isel",
false,
);
// CPUID.01H:ECX
let has_sse3 = settings.add_bool("has_sse3", "SSE3: CPUID.01H:ECX.SSE3[bit 0]", false);
let has_ssse3 = settings.add_bool("has_ssse3", "SSSE3: CPUID.01H:ECX.SSSE3[bit 9]", false);

View File

@@ -25,7 +25,11 @@ pub fn isa_from_arch(arch: &str) -> Result<isa::Isa, String> {
}
/// Generates all the Rust source files used in Cranelift from the meta-language.
pub fn generate(isas: &[isa::Isa], out_dir: &str) -> Result<(), error::Error> {
pub fn generate(
old_backend_isas: &[isa::Isa],
new_backend_isas: &[isa::Isa],
out_dir: &str,
) -> Result<(), error::Error> {
// Create all the definitions:
// - common definitions.
let mut shared_defs = shared::define();
@@ -39,7 +43,7 @@ pub fn generate(isas: &[isa::Isa], out_dir: &str) -> Result<(), error::Error> {
gen_types::generate("types.rs", &out_dir)?;
// - per ISA definitions.
let isas = isa::define(isas, &mut shared_defs);
let target_isas = isa::define(old_backend_isas, &mut shared_defs);
// At this point, all definitions are done.
let all_formats = shared_defs.verify_instruction_formats();
@@ -53,9 +57,22 @@ pub fn generate(isas: &[isa::Isa], out_dir: &str) -> Result<(), error::Error> {
&out_dir,
)?;
gen_legalizer::generate(&isas, &shared_defs.transform_groups, "legalize", &out_dir)?;
let extra_legalization_groups: &[&'static str] = if !new_backend_isas.is_empty() {
// The new backend only requires the "expand" legalization group.
&["expand"]
} else {
&[]
};
for isa in isas {
gen_legalizer::generate(
&target_isas,
&shared_defs.transform_groups,
extra_legalization_groups,
"legalize",
&out_dir,
)?;
for isa in target_isas {
gen_registers::generate(&isa, &format!("registers-{}.rs", isa.name), &out_dir)?;
gen_settings::generate(
@@ -80,5 +97,28 @@ pub fn generate(isas: &[isa::Isa], out_dir: &str) -> Result<(), error::Error> {
)?;
}
for isa in new_backend_isas {
match isa {
isa::Isa::X86 => {
// If the old backend ISAs contained x86, this file has already been generated.
if old_backend_isas.iter().any(|isa| *isa == isa::Isa::X86) {
continue;
}
let settings = crate::isa::x86::settings::define(&shared_defs.settings);
gen_settings::generate(
&settings,
gen_settings::ParentGroup::Shared,
"settings-x86.rs",
&out_dir,
)?;
}
isa::Isa::Arm64 => {
// aarch64 doesn't have platform-specific settings.
}
isa::Isa::Arm32 | isa::Isa::Riscv => todo!(),
}
}
Ok(())
}

View File

@@ -121,7 +121,11 @@ pub fn lookup(triple: Triple) -> Result<Builder, LookupError> {
match triple.architecture {
Architecture::Riscv32 | Architecture::Riscv64 => isa_builder!(riscv, "riscv", triple),
Architecture::I386 | Architecture::I586 | Architecture::I686 | Architecture::X86_64 => {
isa_builder!(x86, "x86", triple)
if cfg!(feature = "x64") {
isa_builder!(x64, "x64", triple)
} else {
isa_builder!(x86, "x86", triple)
}
}
Architecture::Arm { .. } => isa_builder!(arm32, "arm32", triple),
Architecture::Aarch64 { .. } => isa_builder!(aarch64, "arm64", triple),

View File

@@ -11,28 +11,33 @@ use crate::isa::Builder as IsaBuilder;
use crate::machinst::pretty_print::ShowWithRRU;
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
use crate::result::CodegenResult;
use crate::settings::{self, Flags};
use crate::settings::{self as shared_settings, Flags};
use crate::isa::x64::inst::regs::create_reg_universe_systemv;
use crate::isa::x64::{inst::regs::create_reg_universe_systemv, settings as x64_settings};
use super::TargetIsa;
mod abi;
mod inst;
mod lower;
mod settings;
/// An X64 backend.
pub(crate) struct X64Backend {
triple: Triple,
flags: Flags,
_x64_flags: x64_settings::Flags,
reg_universe: RealRegUniverse,
}
impl X64Backend {
/// Create a new X64 backend with the given (shared) flags.
fn new_with_flags(triple: Triple, flags: Flags) -> Self {
fn new_with_flags(triple: Triple, flags: Flags, x64_flags: x64_settings::Flags) -> Self {
let reg_universe = create_reg_universe_systemv(&flags);
Self {
triple,
flags,
_x64_flags: x64_flags,
reg_universe,
}
}
@@ -103,10 +108,17 @@ impl MachBackend for X64Backend {
pub(crate) fn isa_builder(triple: Triple) -> IsaBuilder {
IsaBuilder {
triple,
setup: settings::builder(),
constructor: |triple: Triple, flags: Flags, _arch_flag_builder: settings::Builder| {
let backend = X64Backend::new_with_flags(triple, flags);
Box::new(TargetIsaAdapter::new(backend))
},
setup: x64_settings::builder(),
constructor: isa_constructor,
}
}
fn isa_constructor(
triple: Triple,
shared_flags: Flags,
builder: shared_settings::Builder,
) -> Box<dyn TargetIsa> {
let isa_flags = x64_settings::Flags::new(&shared_flags, builder);
let backend = X64Backend::new_with_flags(triple, shared_flags, isa_flags);
Box::new(TargetIsaAdapter::new(backend))
}

View File

@@ -0,0 +1,9 @@
//! x86 Settings.
use crate::settings::{self, detail, Builder};
use core::fmt;
// Include code generated by `cranelift-codegen/meta/src/gen_settings.rs:`. This file contains a
// public `Flags` struct with an impl for all of the settings defined in
// `cranelift-codegen/meta/src/isa/x86/settings.rs`.
include!(concat!(env!("OUT_DIR"), "/settings-x86.rs"));

View File

@@ -57,20 +57,12 @@ fn isa_constructor(
let isa_flags = settings::Flags::new(&shared_flags, builder);
if isa_flags.use_new_backend() {
#[cfg(not(feature = "x64"))]
panic!("new backend x86 support not included by cargo features!");
#[cfg(feature = "x64")]
super::x64::isa_builder(triple).finish(shared_flags)
} else {
Box::new(Isa {
triple,
isa_flags,
shared_flags,
cpumode: level1,
})
}
Box::new(Isa {
triple,
isa_flags,
shared_flags,
cpumode: level1,
})
}
impl TargetIsa for Isa {

View File

@@ -19,10 +19,24 @@ use crate::flowgraph::ControlFlowGraph;
use crate::ir::types::{I32, I64};
use crate::ir::{self, InstBuilder, MemFlags};
use crate::isa::TargetIsa;
#[cfg(any(
feature = "x86",
feature = "arm32",
feature = "arm64",
feature = "riscv"
))]
use crate::predicates;
#[cfg(any(
feature = "x86",
feature = "arm32",
feature = "arm64",
feature = "riscv"
))]
use alloc::vec::Vec;
use crate::timing;
use alloc::collections::BTreeSet;
use alloc::vec::Vec;
mod boundary;
mod call;

View File

@@ -98,12 +98,13 @@ fn apply_reloc(
write_unaligned(reloc_address as *mut u32, reloc_delta_u32);
},
#[cfg(target_pointer_width = "64")]
(RelocationKind::Relative, RelocationEncoding::X86Branch, 32) => unsafe {
(RelocationKind::Relative, RelocationEncoding::Generic, 32) => unsafe {
let reloc_address = body.add(offset as usize) as usize;
let reloc_addend = r.addend() as isize;
let reloc_delta_u64 = (target_func_address as u64)
.wrapping_sub(reloc_address as u64)
.wrapping_add(reloc_addend as u64);
// TODO implement far calls mode in x64 new backend.
assert!(
reloc_delta_u64 as isize <= i32::max_value() as isize,
"relocation too large to fit in i32"