Fixes #13: Enable conditional compilation of ISAs through features;

This commit is contained in:
Benjamin Bouvier
2019-02-11 15:01:10 +01:00
committed by Dan Gohman
parent 049f067168
commit a45b814de8
12 changed files with 129 additions and 90 deletions

View File

@@ -29,16 +29,24 @@ log = { version = "0.4.6", default-features = false }
cranelift-codegen-meta = { path = "meta", version = "0.28.0" } cranelift-codegen-meta = { path = "meta", version = "0.28.0" }
[features] [features]
default = ["std", "x86", "arm32", "arm64", "riscv"]
# The "std" feature enables use of libstd. The "core" feature enables use # The "std" feature enables use of libstd. The "core" feature enables use
# of some minimal std-like replacement libraries. At least one of these two # of some minimal std-like replacement libraries. At least one of these two
# features need to be enabled. # features need to be enabled.
default = ["std"]
std = ["cranelift-entity/std", "cranelift-bforest/std", "target-lexicon/std"] std = ["cranelift-entity/std", "cranelift-bforest/std", "target-lexicon/std"]
core = ["hashmap_core"] core = ["hashmap_core"]
# This enables some additional functions useful for writing tests, but which # This enables some additional functions useful for writing tests, but which
# can significantly increase the size of the library. # can significantly increase the size of the library.
testing_hooks = [] testing_hooks = []
# ISA targets for which we should build.
x86 = []
arm32 = []
arm64 = []
riscv = []
[badges] [badges]
maintenance = { status = "experimental" } maintenance = { status = "experimental" }
travis-ci = { repository = "CraneStation/cranelift" } travis-ci = { repository = "CraneStation/cranelift" }

View File

@@ -11,10 +11,6 @@
// TARGET // TARGET
// Target triple provided by Cargo. // Target triple provided by Cargo.
// //
// CRANELIFT_TARGETS (Optional)
// A setting for conditional compilation of isa targets. Possible values can be "native" or
// known isa targets separated by ','.
//
// The build script expects to be run from the directory where this build.rs file lives. The // The build script expects to be run from the directory where this build.rs file lives. The
// current directory is used to find the sources. // current directory is used to find the sources.
@@ -31,22 +27,24 @@ fn main() {
let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set"); let target_triple = env::var("TARGET").expect("The TARGET environment variable must be set");
// Configure isa targets cfg. // Configure isa targets cfg.
let cranelift_targets = env::var("CRANELIFT_TARGETS").ok(); let isa_targets = meta::isa::Isa::all()
let cranelift_targets = cranelift_targets .into_iter()
.as_ref() .cloned()
.map(|s| s.as_ref()) .filter(|isa| {
.filter(|s: &&str| s.len() > 0); let env_key = format!("CARGO_FEATURE_{}", isa.to_string().to_uppercase());
env::var(env_key).is_ok()
})
.collect::<Vec<_>>();
let isas = match cranelift_targets { let isas = if isa_targets.is_empty() {
Some("native") => meta::isa_from_arch(&target_triple.split('-').next().unwrap()), // Try to match native target.
Some(targets) => meta::isas_from_targets(targets.split(',').collect::<Vec<_>>()), let target_name = target_triple.split('-').next().unwrap();
None => meta::all_isas(), let isa = meta::isa_from_arch(&target_name).expect("error when identifying target");
} println!("cargo:rustc-cfg=feature=\"{}\"", isa);
.expect("Error when identifying CRANELIFT_TARGETS and TARGET"); vec![isa]
} else {
for isa in &isas { isa_targets
println!("cargo:rustc-cfg=build_{}", isa.to_string()); };
}
let cur_dir = env::current_dir().expect("Can't access current working directory"); let cur_dir = env::current_dir().expect("Can't access current working directory");
let crate_dir = cur_dir.as_path(); let crate_dir = cur_dir.as_path();

View File

@@ -44,6 +44,7 @@ impl Isa {
} }
impl fmt::Display for Isa { impl fmt::Display for Isa {
// These names should be kept in sync with the crate features.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Isa::Riscv => write!(f, "riscv"), Isa::Riscv => write!(f, "riscv"),

View File

@@ -13,43 +13,8 @@ mod shared;
mod srcgen; mod srcgen;
mod unique_table; mod unique_table;
pub fn isa_from_arch(arch: &str) -> Result<Vec<isa::Isa>, String> { pub fn isa_from_arch(arch: &str) -> Result<isa::Isa, String> {
isa::Isa::from_arch(arch) isa::Isa::from_arch(arch).ok_or_else(|| format!("no supported isa found for arch `{}`", arch))
.ok_or_else(|| format!("no supported isa found for arch `{}`", arch))
.and_then(|isa| Ok(vec![isa]))
}
pub fn isas_from_targets(targets: Vec<&str>) -> Result<Vec<isa::Isa>, String> {
type R<'a> = Vec<(&'a str, Option<isa::Isa>)>;
let (known, unknown): (R, R) = targets
.into_iter()
.map(|target| (target, isa::Isa::from_name(target)))
.partition(|(_, opt_isa)| opt_isa.is_some());
if !unknown.is_empty() {
let unknown_targets = unknown
.into_iter()
.map(|(target, _)| target)
.collect::<Vec<_>>()
.join(", ");
return Err(format!("unknown isa targets: {}", unknown_targets));
}
let isas = if known.is_empty() {
isa::Isa::all().to_vec()
} else {
known
.into_iter()
.map(|(_, opt_isa)| opt_isa.unwrap())
.collect()
};
Ok(isas)
}
pub fn all_isas() -> Result<Vec<isa::Isa>, String> {
isas_from_targets(vec![])
} }
/// Generates all the Rust source files used in Cranelift from the meta-language. /// Generates all the Rust source files used in Cranelift from the meta-language.

View File

@@ -68,16 +68,16 @@ use failure_derive::Fail;
use std::boxed::Box; use std::boxed::Box;
use target_lexicon::{Architecture, PointerWidth, Triple}; use target_lexicon::{Architecture, PointerWidth, Triple};
#[cfg(build_riscv)] #[cfg(feature = "riscv")]
mod riscv; mod riscv;
#[cfg(build_x86)] #[cfg(feature = "x86")]
mod x86; mod x86;
#[cfg(build_arm32)] #[cfg(feature = "arm32")]
mod arm32; mod arm32;
#[cfg(build_arm64)] #[cfg(feature = "arm64")]
mod arm64; mod arm64;
mod call_conv; mod call_conv;
@@ -90,12 +90,12 @@ mod stack;
/// Returns a builder that can create a corresponding `TargetIsa` /// Returns a builder that can create a corresponding `TargetIsa`
/// or `Err(LookupError::Unsupported)` if not enabled. /// or `Err(LookupError::Unsupported)` if not enabled.
macro_rules! isa_builder { macro_rules! isa_builder {
($module:ident, $name:ident) => {{ ($name:ident, $feature:tt) => {{
#[cfg($name)] #[cfg(feature = $feature)]
fn $name(triple: Triple) -> Result<Builder, LookupError> { fn $name(triple: Triple) -> Result<Builder, LookupError> {
Ok($module::isa_builder(triple)) Ok($name::isa_builder(triple))
}; };
#[cfg(not($name))] #[cfg(not(feature = $feature))]
fn $name(_triple: Triple) -> Result<Builder, LookupError> { fn $name(_triple: Triple) -> Result<Builder, LookupError> {
Err(LookupError::Unsupported) Err(LookupError::Unsupported)
} }
@@ -107,9 +107,9 @@ macro_rules! isa_builder {
/// Return a builder that can create a corresponding `TargetIsa`. /// Return a builder that can create a corresponding `TargetIsa`.
pub fn lookup(triple: Triple) -> Result<Builder, LookupError> { pub fn lookup(triple: Triple) -> Result<Builder, LookupError> {
match triple.architecture { match triple.architecture {
Architecture::Riscv32 | Architecture::Riscv64 => isa_builder!(riscv, build_riscv)(triple), Architecture::Riscv32 | Architecture::Riscv64 => isa_builder!(riscv, "riscv")(triple),
Architecture::I386 | Architecture::I586 | Architecture::I686 | Architecture::X86_64 => { Architecture::I386 | Architecture::I586 | Architecture::I686 | Architecture::X86_64 => {
isa_builder!(x86, build_x86)(triple) isa_builder!(x86, "x86")(triple)
} }
Architecture::Thumbv6m Architecture::Thumbv6m
| Architecture::Thumbv7em | Architecture::Thumbv7em
@@ -118,8 +118,8 @@ pub fn lookup(triple: Triple) -> Result<Builder, LookupError> {
| Architecture::Armv4t | Architecture::Armv4t
| Architecture::Armv5te | Architecture::Armv5te
| Architecture::Armv7 | Architecture::Armv7
| Architecture::Armv7s => isa_builder!(arm32, build_arm32)(triple), | Architecture::Armv7s => isa_builder!(arm32, "arm32")(triple),
Architecture::Aarch64 => isa_builder!(arm64, build_arm64)(triple), Architecture::Aarch64 => isa_builder!(arm64, "arm64")(triple),
_ => Err(LookupError::Unsupported), _ => Err(LookupError::Unsupported),
} }
} }

View File

@@ -270,7 +270,7 @@ impl fmt::Display for Pressure {
} }
#[cfg(test)] #[cfg(test)]
#[cfg(build_arm32)] #[cfg(feature = "arm32")]
mod tests { mod tests {
use super::Pressure; use super::Pressure;
use crate::isa::{RegClass, TargetIsa}; use crate::isa::{RegClass, TargetIsa};

View File

@@ -1127,7 +1127,7 @@ impl fmt::Display for Solver {
} }
#[cfg(test)] #[cfg(test)]
#[cfg(build_arm32)] #[cfg(feature = "arm32")]
mod tests { mod tests {
use super::{Move, Solver}; use super::{Move, Solver};
use crate::entity::EntityRef; use crate::entity::EntityRef;

View File

@@ -83,7 +83,7 @@ pub struct TestRunner {
} }
impl TestRunner { impl TestRunner {
/// Create a new blank TrstRunner. /// Create a new blank TestRunner.
pub fn new(verbose: bool, report_times: bool) -> Self { pub fn new(verbose: bool, report_times: bool) -> Self {
Self { Self {
verbose, verbose,

View File

@@ -33,7 +33,21 @@ pub fn run(path: &Path, passes: Option<&[String]>, target: Option<&str>) -> Test
info!("---\nFile: {}", path.to_string_lossy()); info!("---\nFile: {}", path.to_string_lossy());
let started = time::Instant::now(); let started = time::Instant::now();
let buffer = read_to_string(path).map_err(|e| e.to_string())?; let buffer = read_to_string(path).map_err(|e| e.to_string())?;
let testfile = parse_test(&buffer, passes, target).map_err(|e| e.to_string())?;
let testfile = match parse_test(&buffer, passes, target) {
Ok(testfile) => testfile,
Err(e) => {
if e.is_warning {
println!(
"skipping test {:?} (line {}): {}",
path, e.location.line_number, e.message
);
return Ok(started.elapsed());
}
return Err(e.to_string());
}
};
if testfile.functions.is_empty() { if testfile.functions.is_empty() {
return Err("no functions found".to_string()); return Err("no functions found".to_string());
} }

View File

@@ -19,6 +19,8 @@ pub struct ParseError {
pub location: Location, pub location: Location,
/// Error message. /// Error message.
pub message: String, pub message: String,
/// Whether it's a warning or a plain error.
pub is_warning: bool,
} }
impl fmt::Display for ParseError { impl fmt::Display for ParseError {
@@ -40,6 +42,7 @@ macro_rules! err {
Err($crate::ParseError { Err($crate::ParseError {
location: $loc.clone(), location: $loc.clone(),
message: $msg.to_string(), message: $msg.to_string(),
is_warning: false,
}) })
}; };
@@ -47,6 +50,17 @@ macro_rules! err {
Err($crate::ParseError { Err($crate::ParseError {
location: $loc.clone(), location: $loc.clone(),
message: format!( $fmt, $( $arg ),+ ), message: format!( $fmt, $( $arg ),+ ),
is_warning: false,
})
};
}
macro_rules! warn {
( $loc:expr, $fmt:expr, $( $arg:expr ),+ ) => {
Err($crate::ParseError {
location: $loc.clone(),
message: format!($fmt, $( $arg ),+ ),
is_warning: true,
}) })
}; };
} }

View File

@@ -17,7 +17,7 @@ pub enum IsaSpec {
/// which are reflected in the finished `Flags` object. /// which are reflected in the finished `Flags` object.
None(Flags), None(Flags),
/// The parsed file does contains `isa` commands. /// The parsed file does contain `isa` commands.
/// Each `isa` command is used to configure a `TargetIsa` trait object. /// Each `isa` command is used to configure a `TargetIsa` trait object.
Some(Vec<Box<TargetIsa>>), Some(Vec<Box<TargetIsa>>),
} }

View File

@@ -530,6 +530,7 @@ impl<'a> Parser<'a> {
ParseError { ParseError {
location: self.loc, location: self.loc,
message: message.to_string(), message: message.to_string(),
is_warning: false,
} }
} }
@@ -778,10 +779,7 @@ impl<'a> Parser<'a> {
/// ///
/// Accept the target from the command line for pass command. /// Accept the target from the command line for pass command.
/// ///
pub fn parse_cmdline_target( fn parse_cmdline_target(&mut self, target_pass: Option<&str>) -> ParseResult<isaspec::IsaSpec> {
&mut self,
target_pass: Option<&str>,
) -> ParseResult<isaspec::IsaSpec> {
// Were there any `target` commands specified? // Were there any `target` commands specified?
let mut specified_target = false; let mut specified_target = false;
@@ -799,7 +797,7 @@ impl<'a> Parser<'a> {
return err!(loc, "support disabled target '{}'", targ); return err!(loc, "support disabled target '{}'", targ);
} }
Err(isa::LookupError::Unsupported) => { Err(isa::LookupError::Unsupported) => {
return err!(loc, "unsupported target '{}'", targ); return warn!(loc, "unsupported target '{}'", targ);
} }
Ok(b) => b, Ok(b) => b,
}; };
@@ -821,7 +819,7 @@ impl<'a> Parser<'a> {
/// ///
/// Accept a mix of `target` 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_target_specs(&mut self) -> ParseResult<isaspec::IsaSpec> { fn parse_target_specs(&mut self) -> ParseResult<isaspec::IsaSpec> {
// Were there any `target` commands? // Were there any `target` commands?
let mut seen_target = false; let mut seen_target = false;
// Location of last `set` command since the last `target`. // Location of last `set` command since the last `target`.
@@ -859,7 +857,7 @@ impl<'a> Parser<'a> {
continue; continue;
} }
Err(isa::LookupError::Unsupported) => { Err(isa::LookupError::Unsupported) => {
return err!(loc, "unsupported target '{}'", target_name); return warn!(loc, "unsupported target '{}'", target_name);
} }
Ok(b) => b, Ok(b) => b,
}; };
@@ -874,6 +872,7 @@ impl<'a> Parser<'a> {
_ => break, _ => break,
} }
} }
if !seen_target { if !seen_target {
// No `target` commands, but we allow for `set` commands. // No `target` commands, but we allow for `set` commands.
Ok(isaspec::IsaSpec::None(settings::Flags::new(flag_builder))) Ok(isaspec::IsaSpec::None(settings::Flags::new(flag_builder)))
@@ -2588,9 +2587,14 @@ mod tests {
assert_eq!(arg.value_type, types::I32); assert_eq!(arg.value_type, types::I32);
assert_eq!(arg.extension, ArgumentExtension::Sext); assert_eq!(arg.extension, ArgumentExtension::Sext);
assert_eq!(arg.purpose, ArgumentPurpose::Normal); assert_eq!(arg.purpose, ArgumentPurpose::Normal);
let ParseError { location, message } = p.parse_abi_param(None).unwrap_err(); let ParseError {
location,
message,
is_warning,
} = p.parse_abi_param(None).unwrap_err();
assert_eq!(location.line_number, 1); assert_eq!(location.line_number, 1);
assert_eq!(message, "expected parameter type"); assert_eq!(message, "expected parameter type");
assert!(!is_warning);
} }
#[test] #[test]
@@ -2736,7 +2740,11 @@ mod tests {
#[test] #[test]
fn duplicate_ebb() { fn duplicate_ebb() {
let ParseError { location, message } = Parser::new( let ParseError {
location,
message,
is_warning,
} = Parser::new(
"function %ebbs() system_v { "function %ebbs() system_v {
ebb0: ebb0:
ebb0: ebb0:
@@ -2747,11 +2755,16 @@ mod tests {
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
assert_eq!(message, "duplicate entity: ebb0"); assert_eq!(message, "duplicate entity: ebb0");
assert!(!is_warning);
} }
#[test] #[test]
fn duplicate_jt() { fn duplicate_jt() {
let ParseError { location, message } = Parser::new( let ParseError {
location,
message,
is_warning,
} = Parser::new(
"function %ebbs() system_v { "function %ebbs() system_v {
jt0 = jump_table [] jt0 = jump_table []
jt0 = jump_table []", jt0 = jump_table []",
@@ -2761,11 +2774,16 @@ mod tests {
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
assert_eq!(message, "duplicate entity: jt0"); assert_eq!(message, "duplicate entity: jt0");
assert!(!is_warning);
} }
#[test] #[test]
fn duplicate_ss() { fn duplicate_ss() {
let ParseError { location, message } = Parser::new( let ParseError {
location,
message,
is_warning,
} = Parser::new(
"function %ebbs() system_v { "function %ebbs() system_v {
ss0 = explicit_slot 8 ss0 = explicit_slot 8
ss0 = explicit_slot 8", ss0 = explicit_slot 8",
@@ -2775,11 +2793,16 @@ mod tests {
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
assert_eq!(message, "duplicate entity: ss0"); assert_eq!(message, "duplicate entity: ss0");
assert!(!is_warning);
} }
#[test] #[test]
fn duplicate_gv() { fn duplicate_gv() {
let ParseError { location, message } = Parser::new( let ParseError {
location,
message,
is_warning,
} = Parser::new(
"function %ebbs() system_v { "function %ebbs() system_v {
gv0 = vmctx gv0 = vmctx
gv0 = vmctx", gv0 = vmctx",
@@ -2789,11 +2812,16 @@ mod tests {
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
assert_eq!(message, "duplicate entity: gv0"); assert_eq!(message, "duplicate entity: gv0");
assert!(!is_warning);
} }
#[test] #[test]
fn duplicate_heap() { fn duplicate_heap() {
let ParseError { location, message } = Parser::new( let ParseError {
location,
message,
is_warning,
} = Parser::new(
"function %ebbs() system_v { "function %ebbs() system_v {
heap0 = static gv0, min 0x1000, bound 0x10_0000, offset_guard 0x1000 heap0 = static gv0, min 0x1000, bound 0x10_0000, offset_guard 0x1000
heap0 = static gv0, min 0x1000, bound 0x10_0000, offset_guard 0x1000", heap0 = static gv0, min 0x1000, bound 0x10_0000, offset_guard 0x1000",
@@ -2803,11 +2831,16 @@ mod tests {
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
assert_eq!(message, "duplicate entity: heap0"); assert_eq!(message, "duplicate entity: heap0");
assert!(!is_warning);
} }
#[test] #[test]
fn duplicate_sig() { fn duplicate_sig() {
let ParseError { location, message } = Parser::new( let ParseError {
location,
message,
is_warning,
} = Parser::new(
"function %ebbs() system_v { "function %ebbs() system_v {
sig0 = () sig0 = ()
sig0 = ()", sig0 = ()",
@@ -2817,11 +2850,16 @@ mod tests {
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
assert_eq!(message, "duplicate entity: sig0"); assert_eq!(message, "duplicate entity: sig0");
assert!(!is_warning);
} }
#[test] #[test]
fn duplicate_fn() { fn duplicate_fn() {
let ParseError { location, message } = Parser::new( let ParseError {
location,
message,
is_warning,
} = Parser::new(
"function %ebbs() system_v { "function %ebbs() system_v {
sig0 = () sig0 = ()
fn0 = %foo sig0 fn0 = %foo sig0
@@ -2832,6 +2870,7 @@ mod tests {
assert_eq!(location.line_number, 4); assert_eq!(location.line_number, 4);
assert_eq!(message, "duplicate entity: fn0"); assert_eq!(message, "duplicate entity: fn0");
assert!(!is_warning);
} }
#[test] #[test]
@@ -2905,7 +2944,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(build_riscv)] #[cfg(feature = "riscv")]
fn isa_spec() { fn isa_spec() {
assert!(parse_test( assert!(parse_test(
"target "target