diff --git a/cranelift/codegen/meta/src/cdsl/settings.rs b/cranelift/codegen/meta/src/cdsl/settings.rs index 217bad995529..52c51d54a82d 100644 --- a/cranelift/codegen/meta/src/cdsl/settings.rs +++ b/cranelift/codegen/meta/src/cdsl/settings.rs @@ -20,6 +20,7 @@ pub(crate) enum SpecificSetting { #[derive(Hash, PartialEq, Eq)] pub(crate) struct Setting { pub name: &'static str, + pub description: &'static str, pub comment: &'static str, pub specific: SpecificSetting, pub byte_offset: u8, @@ -88,6 +89,7 @@ impl Into for PresetIndex { #[derive(Hash, PartialEq, Eq)] pub(crate) struct Preset { pub name: &'static str, + pub description: &'static str, values: Vec, } @@ -169,6 +171,7 @@ pub(crate) enum ProtoSpecificSetting { /// This is the information provided during building for a setting. struct ProtoSetting { name: &'static str, + description: &'static str, comment: &'static str, specific: ProtoSpecificSetting, } @@ -251,11 +254,13 @@ impl SettingGroupBuilder { fn add_setting( &mut self, name: &'static str, + description: &'static str, comment: &'static str, specific: ProtoSpecificSetting, ) { self.settings.push(ProtoSetting { name, + description, comment, specific, }) @@ -264,6 +269,7 @@ impl SettingGroupBuilder { pub fn add_bool( &mut self, name: &'static str, + description: &'static str, comment: &'static str, default: bool, ) -> BoolSettingIndex { @@ -271,28 +277,55 @@ impl SettingGroupBuilder { self.predicates.is_empty(), "predicates must be added after the boolean settings" ); - self.add_setting(name, comment, ProtoSpecificSetting::Bool(default)); + self.add_setting( + name, + description, + comment, + ProtoSpecificSetting::Bool(default), + ); BoolSettingIndex(self.settings.len() - 1) } pub fn add_enum( &mut self, name: &'static str, + description: &'static str, comment: &'static str, values: Vec<&'static str>, ) { - self.add_setting(name, comment, ProtoSpecificSetting::Enum(values)); + self.add_setting( + name, + description, + comment, + ProtoSpecificSetting::Enum(values), + ); } - pub fn add_num(&mut self, name: &'static str, comment: &'static str, default: u8) { - self.add_setting(name, comment, ProtoSpecificSetting::Num(default)); + pub fn add_num( + &mut self, + name: &'static str, + description: &'static str, + comment: &'static str, + default: u8, + ) { + self.add_setting( + name, + description, + comment, + ProtoSpecificSetting::Num(default), + ); } pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) { self.predicates.push(ProtoPredicate { name, node }); } - pub fn add_preset(&mut self, name: &'static str, args: Vec) -> PresetIndex { + pub fn add_preset( + &mut self, + name: &'static str, + description: &'static str, + args: Vec, + ) -> PresetIndex { let mut values = Vec::new(); for arg in args { match arg { @@ -302,7 +335,11 @@ impl SettingGroupBuilder { PresetType::BoolSetting(index) => values.push(index), } } - self.presets.push(Preset { name, values }); + self.presets.push(Preset { + name, + description, + values, + }); PresetIndex(self.presets.len() - 1) } @@ -347,6 +384,7 @@ impl SettingGroupBuilder { group.settings.push(Setting { name: s.name, + description: s.description, comment: s.comment, byte_offset, specific, @@ -367,6 +405,7 @@ impl SettingGroupBuilder { }; group.settings.push(Setting { name: s.name, + description: s.description, comment: s.comment, byte_offset: byte_offset + predicate_number / 8, specific: SpecificSetting::Bool(BoolSetting { diff --git a/cranelift/codegen/meta/src/gen_settings.rs b/cranelift/codegen/meta/src/gen_settings.rs index 70b18726387e..2808c2a7772d 100644 --- a/cranelift/codegen/meta/src/gen_settings.rs +++ b/cranelift/codegen/meta/src/gen_settings.rs @@ -202,7 +202,7 @@ fn gen_enum_types(group: &SettingGroup, fmt: &mut Formatter) { /// Emit a getter function for `setting`. fn gen_getter(setting: &Setting, fmt: &mut Formatter) { - fmt.doc_comment(setting.comment); + fmt.doc_comment(format!("{}\n{}", setting.description, setting.comment)); match setting.specific { SpecificSetting::Bool(BoolSetting { predicate_number, .. @@ -320,6 +320,7 @@ fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) { fmtln!(fmt, "detail::Descriptor {"); fmt.indent(|fmt| { fmtln!(fmt, "name: \"{}\",", setting.name); + fmtln!(fmt, "description: \"{}\",", setting.description); fmtln!(fmt, "offset: {},", setting.byte_offset); match setting.specific { SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => { @@ -352,6 +353,7 @@ fn gen_descriptors(group: &SettingGroup, fmt: &mut Formatter) { fmtln!(fmt, "detail::Descriptor {"); fmt.indent(|fmt| { fmtln!(fmt, "name: \"{}\",", preset.name); + fmtln!(fmt, "description: \"{}\",", preset.description); fmtln!(fmt, "offset: {},", (idx as u8) * group.settings_size); fmtln!(fmt, "detail: detail::Detail::Preset,"); }); diff --git a/cranelift/codegen/meta/src/isa/arm64/mod.rs b/cranelift/codegen/meta/src/isa/arm64/mod.rs index 135096324285..4277e147a7d3 100644 --- a/cranelift/codegen/meta/src/isa/arm64/mod.rs +++ b/cranelift/codegen/meta/src/isa/arm64/mod.rs @@ -8,11 +8,8 @@ use crate::cdsl::settings::{SettingGroup, SettingGroupBuilder}; use crate::shared::Definitions as SharedDefinitions; fn define_settings(_shared: &SettingGroup) -> SettingGroup { - // Note: Wasmtime's `compile` command exposes these settings as CLI options - // If the settings change, please update src/commands/compile.rs to match. - let mut setting = SettingGroupBuilder::new("arm64"); - let has_lse = setting.add_bool("has_lse", "Large System Extensions", false); + let has_lse = setting.add_bool("has_lse", "Has Large System Extensions support.", "", false); setting.add_predicate("use_lse", predicate!(has_lse)); setting.build() diff --git a/cranelift/codegen/meta/src/isa/riscv/mod.rs b/cranelift/codegen/meta/src/isa/riscv/mod.rs index 801e61a3d2b9..49f26391cefc 100644 --- a/cranelift/codegen/meta/src/isa/riscv/mod.rs +++ b/cranelift/codegen/meta/src/isa/riscv/mod.rs @@ -17,33 +17,39 @@ fn define_settings(shared: &SettingGroup) -> SettingGroup { let supports_m = setting.add_bool( "supports_m", "CPU supports the 'M' extension (mul/div)", + "", false, ); let supports_a = setting.add_bool( "supports_a", "CPU supports the 'A' extension (atomics)", + "", false, ); let supports_f = setting.add_bool( "supports_f", "CPU supports the 'F' extension (float)", + "", false, ); let supports_d = setting.add_bool( "supports_d", "CPU supports the 'D' extension (double)", + "", false, ); let enable_m = setting.add_bool( "enable_m", "Enable the use of 'M' instructions if available", + "", true, ); setting.add_bool( "enable_e", "Enable the 'RV32E' instruction set with only 16 registers", + "", false, ); diff --git a/cranelift/codegen/meta/src/isa/x86/settings.rs b/cranelift/codegen/meta/src/isa/x86/settings.rs index 2540ac20657a..70b829787df2 100644 --- a/cranelift/codegen/meta/src/isa/x86/settings.rs +++ b/cranelift/codegen/meta/src/isa/x86/settings.rs @@ -3,41 +3,78 @@ use crate::cdsl::settings::{PredicateNode, SettingGroup, SettingGroupBuilder}; pub(crate) fn define(shared: &SettingGroup) -> SettingGroup { let mut settings = SettingGroupBuilder::new("x86"); - // Note: Wasmtime's `compile` command exposes these settings as CLI options - // If the settings change, please update src/commands/compile.rs to match. - // 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); - let has_sse41 = settings.add_bool("has_sse41", "SSE4.1: CPUID.01H:ECX.SSE4_1[bit 19]", false); - let has_sse42 = settings.add_bool("has_sse42", "SSE4.2: CPUID.01H:ECX.SSE4_2[bit 20]", false); - let has_avx = settings.add_bool("has_avx", "AVX: CPUID.01H:ECX.AVX[bit 28]", false); - let has_avx2 = settings.add_bool("has_avx2", "AVX2: CPUID.07H:EBX.AVX2[bit 5]", false); + let has_sse3 = settings.add_bool( + "has_sse3", + "Has support for SSE3.", + "SSE3: CPUID.01H:ECX.SSE3[bit 0]", + false, + ); + let has_ssse3 = settings.add_bool( + "has_ssse3", + "Has support for SSSE3.", + "SSSE3: CPUID.01H:ECX.SSSE3[bit 9]", + false, + ); + let has_sse41 = settings.add_bool( + "has_sse41", + "Has support for SSE4.1.", + "SSE4.1: CPUID.01H:ECX.SSE4_1[bit 19]", + false, + ); + let has_sse42 = settings.add_bool( + "has_sse42", + "Has support for SSE4.2.", + "SSE4.2: CPUID.01H:ECX.SSE4_2[bit 20]", + false, + ); + let has_avx = settings.add_bool( + "has_avx", + "Has support for AVX.", + "AVX: CPUID.01H:ECX.AVX[bit 28]", + false, + ); + let has_avx2 = settings.add_bool( + "has_avx2", + "Has support for AVX2.", + "AVX2: CPUID.07H:EBX.AVX2[bit 5]", + false, + ); let has_avx512dq = settings.add_bool( "has_avx512dq", + "Has support for AVX512DQ.", "AVX512DQ: CPUID.07H:EBX.AVX512DQ[bit 17]", false, ); let has_avx512vl = settings.add_bool( "has_avx512vl", + "Has support for AVX512VL.", "AVX512VL: CPUID.07H:EBX.AVX512VL[bit 31]", false, ); let has_avx512f = settings.add_bool( "has_avx512f", + "Has support for AVX512F.", "AVX512F: CPUID.07H:EBX.AVX512F[bit 16]", false, ); - let has_popcnt = settings.add_bool("has_popcnt", "POPCNT: CPUID.01H:ECX.POPCNT[bit 23]", false); + let has_popcnt = settings.add_bool( + "has_popcnt", + "Has support for POPCNT.", + "POPCNT: CPUID.01H:ECX.POPCNT[bit 23]", + false, + ); // CPUID.(EAX=07H, ECX=0H):EBX let has_bmi1 = settings.add_bool( "has_bmi1", + "Has support for BMI1.", "BMI1: CPUID.(EAX=07H, ECX=0H):EBX.BMI1[bit 3]", false, ); let has_bmi2 = settings.add_bool( "has_bmi2", + "Has support for BMI2.", "BMI2: CPUID.(EAX=07H, ECX=0H):EBX.BMI2[bit 8]", false, ); @@ -45,6 +82,7 @@ pub(crate) fn define(shared: &SettingGroup) -> SettingGroup { // CPUID.EAX=80000001H:ECX let has_lzcnt = settings.add_bool( "has_lzcnt", + "Has support for LZCNT.", "LZCNT: CPUID.EAX=80000001H:ECX.LZCNT[bit 5]", false, ); @@ -107,21 +145,40 @@ pub(crate) fn define(shared: &SettingGroup) -> SettingGroup { // Presets corresponding to x86 CPUs. - settings.add_preset("baseline", preset!()); + settings.add_preset( + "baseline", + "A baseline preset with no extensions enabled.", + preset!(), + ); let nehalem = settings.add_preset( "nehalem", + "Nehalem microarchitecture.", preset!(has_sse3 && has_ssse3 && has_sse41 && has_sse42 && has_popcnt), ); let haswell = settings.add_preset( "haswell", + "Haswell microarchitecture.", preset!(nehalem && has_bmi1 && has_bmi2 && has_lzcnt), ); - let broadwell = settings.add_preset("broadwell", preset!(haswell)); - let skylake = settings.add_preset("skylake", preset!(broadwell)); - let cannonlake = settings.add_preset("cannonlake", preset!(skylake)); - settings.add_preset("icelake", preset!(cannonlake)); + let broadwell = settings.add_preset( + "broadwell", + "Broadwell microarchitecture.", + preset!(haswell), + ); + let skylake = settings.add_preset("skylake", "Skylake microarchitecture.", preset!(broadwell)); + let cannonlake = settings.add_preset( + "cannonlake", + "Canon Lake microarchitecture.", + preset!(skylake), + ); + settings.add_preset( + "icelake", + "Ice Lake microarchitecture.", + preset!(cannonlake), + ); settings.add_preset( "znver1", + "Zen (first generation) microarchitecture.", preset!( has_sse3 && has_ssse3 diff --git a/cranelift/codegen/meta/src/shared/settings.rs b/cranelift/codegen/meta/src/shared/settings.rs index 2acc34118dd7..2233e85dbcb0 100644 --- a/cranelift/codegen/meta/src/shared/settings.rs +++ b/cranelift/codegen/meta/src/shared/settings.rs @@ -5,29 +5,29 @@ pub(crate) fn define() -> SettingGroup { settings.add_enum( "regalloc", - r#"Register allocator to use with the MachInst backend. - - This selects the register allocator as an option among those offered by the `regalloc.rs` - crate. Please report register allocation bugs to the maintainers of this crate whenever - possible. - - Note: this only applies to target that use the MachInst backend. As of 2020-04-17, this - means the x86_64 backend doesn't use this yet. - - Possible values: - - - `backtracking` is a greedy, backtracking register allocator as implemented in - Spidermonkey's optimizing tier IonMonkey. It may take more time to allocate registers, but - it should generate better code in general, resulting in better throughput of generated - code. - - `backtracking_checked` is the backtracking allocator with additional self checks that may - take some time to run, and thus these checks are disabled by default. - - `experimental_linear_scan` is an experimental linear scan allocator. It may take less - time to allocate registers, but generated code's quality may be inferior. As of - 2020-04-17, it is still experimental and it should not be used in production settings. - - `experimental_linear_scan_checked` is the linear scan allocator with additional self - checks that may take some time to run, and thus these checks are disabled by default. - "#, + "Register allocator to use with the MachInst backend.", + r#" + This selects the register allocator as an option among those offered by the `regalloc.rs` + crate. Please report register allocation bugs to the maintainers of this crate whenever + possible. + + Note: this only applies to target that use the MachInst backend. As of 2020-04-17, this + means the x86_64 backend doesn't use this yet. + + Possible values: + + - `backtracking` is a greedy, backtracking register allocator as implemented in + Spidermonkey's optimizing tier IonMonkey. It may take more time to allocate registers, but + it should generate better code in general, resulting in better throughput of generated + code. + - `backtracking_checked` is the backtracking allocator with additional self checks that may + take some time to run, and thus these checks are disabled by default. + - `experimental_linear_scan` is an experimental linear scan allocator. It may take less + time to allocate registers, but generated code's quality may be inferior. As of + 2020-04-17, it is still experimental and it should not be used in production settings. + - `experimental_linear_scan_checked` is the linear scan allocator with additional self + checks that may take some time to run, and thus these checks are disabled by default. + "#, vec![ "backtracking", "backtracking_checked", @@ -38,24 +38,23 @@ pub(crate) fn define() -> SettingGroup { settings.add_enum( "opt_level", + "Optimization level for generated code.", r#" - Optimization level: + Supported levels: - - none: Minimise compile time by disabling most optimizations. - - speed: Generate the fastest possible code - - speed_and_size: like "speed", but also perform transformations - aimed at reducing code size. + - `none`: Minimise compile time by disabling most optimizations. + - `speed`: Generate the fastest possible code + - `speed_and_size`: like "speed", but also perform transformations aimed at reducing code size. "#, vec!["none", "speed", "speed_and_size"], ); settings.add_bool( "enable_verifier", + "Run the Cranelift IR verifier at strategic times during compilation.", r#" - Run the Cranelift IR verifier at strategic times during compilation. - - This makes compilation slower but catches many bugs. The verifier is always enabled by - default, which is useful during development. + This makes compilation slower but catches many bugs. The verifier is always enabled by + default, which is useful during development. "#, true, ); @@ -65,110 +64,110 @@ pub(crate) fn define() -> SettingGroup { // `colocated` flag on external functions and global values. settings.add_bool( "is_pic", - "Enable Position-Independent Code generation", + "Enable Position-Independent Code generation.", + "", false, ); settings.add_bool( "use_colocated_libcalls", + "Use colocated libcalls.", r#" - Use colocated libcalls. - Generate code that assumes that libcalls can be declared "colocated", meaning they will be defined along with the current function, such that they can use more efficient addressing. - "#, + "#, false, ); settings.add_bool( "avoid_div_traps", + "Generate explicit checks around native division instructions to avoid their trapping.", r#" - Generate explicit checks around native division instructions to avoid - their trapping. - This is primarily used by SpiderMonkey which doesn't install a signal handler for SIGFPE, but expects a SIGILL trap for division by zero. On ISAs like ARM where the native division instructions don't trap, this setting has no effect - explicit checks are always inserted. - "#, + "#, false, ); settings.add_bool( "enable_float", + "Enable the use of floating-point instructions.", r#" - Enable the use of floating-point instructions - Disabling use of floating-point instructions is not yet implemented. - "#, + "#, true, ); settings.add_bool( "enable_nan_canonicalization", + "Enable NaN canonicalization.", r#" - Enable NaN canonicalization - This replaces NaNs with a single canonical value, for users requiring entirely deterministic WebAssembly computation. This is not required by the WebAssembly spec, so it is not enabled by default. - "#, + "#, false, ); settings.add_bool( "enable_pinned_reg", - r#"Enable the use of the pinned register. - - This register is excluded from register allocation, and is completely under the control of - the end-user. It is possible to read it via the get_pinned_reg instruction, and to set it - with the set_pinned_reg instruction. + "Enable the use of the pinned register.", + r#" + This register is excluded from register allocation, and is completely under the control of + the end-user. It is possible to read it via the get_pinned_reg instruction, and to set it + with the set_pinned_reg instruction. "#, false, ); settings.add_bool( "use_pinned_reg_as_heap_base", - r#"Use the pinned register as the heap base. - - Enabling this requires the enable_pinned_reg setting to be set to true. It enables a custom - legalization of the `heap_addr` instruction so it will use the pinned register as the heap - base, instead of fetching it from a global value. + "Use the pinned register as the heap base.", + r#" + Enabling this requires the enable_pinned_reg setting to be set to true. It enables a custom + legalization of the `heap_addr` instruction so it will use the pinned register as the heap + base, instead of fetching it from a global value. - Warning! Enabling this means that the pinned register *must* be maintained to contain the - heap base address at all times, during the lifetime of a function. Using the pinned - register for other purposes when this is set is very likely to cause crashes. + Warning! Enabling this means that the pinned register *must* be maintained to contain the + heap base address at all times, during the lifetime of a function. Using the pinned + register for other purposes when this is set is very likely to cause crashes. "#, false, ); - settings.add_bool("enable_simd", "Enable the use of SIMD instructions.", false); + settings.add_bool( + "enable_simd", + "Enable the use of SIMD instructions.", + "", + false, + ); settings.add_bool( "enable_atomics", "Enable the use of atomic instructions", + "", true, ); settings.add_bool( "enable_safepoints", + "Enable safepoint instruction insertions.", r#" - Enable safepoint instruction insertions. - This will allow the emit_stack_maps() function to insert the safepoint instruction on top of calls and interrupt traps in order to display the live reference values at that point in the program. - "#, + "#, false, ); settings.add_enum( "tls_model", - r#" - Defines the model used to perform TLS accesses. - "#, + "Defines the model used to perform TLS accesses.", + "", vec!["none", "elf_gd", "macho", "coff"], ); @@ -176,9 +175,9 @@ pub(crate) fn define() -> SettingGroup { settings.add_enum( "libcall_call_conv", + "Defines the calling convention to use for LibCalls call expansion.", r#" - Defines the calling convention to use for LibCalls call expansion, - since it may be different from the ISA default calling convention. + This may be different from the ISA default calling convention. The default value is to use the same calling convention as the ISA default calling convention. @@ -202,9 +201,8 @@ pub(crate) fn define() -> SettingGroup { settings.add_num( "baldrdash_prologue_words", + "Number of pointer-sized words pushed by the baldrdash prologue.", r#" - Number of pointer-sized words pushed by the baldrdash prologue. - Functions with the `baldrdash` calling convention don't generate their own prologue and epilogue. They depend on externally generated code that pushes a fixed number of words in the prologue and restores them @@ -213,15 +211,14 @@ pub(crate) fn define() -> SettingGroup { This setting configures the number of pointer-sized words pushed on the stack when the Cranelift-generated code is entered. This includes the pushed return address on x86. - "#, + "#, 0, ); settings.add_bool( "enable_llvm_abi_extensions", + "Enable various ABI extensions defined by LLVM's behavior.", r#" - Enable various ABI extensions defined by LLVM's behavior. - In some cases, LLVM's implementation of an ABI (calling convention) goes beyond a standard and supports additional argument types or behavior. This option instructs Cranelift codegen to follow LLVM's @@ -232,18 +229,18 @@ pub(crate) fn define() -> SettingGroup { registers. The Fastcall implementation otherwise does not support `i128` arguments, and will panic if they are present and this option is not set. - "#, + "#, false, ); settings.add_bool( "unwind_info", + "Generate unwind information.", r#" - Generate unwind info. This increases metadata size and compile time, - but allows for the debugger to trace frames, is needed for GC tracing - that relies on libunwind (such as in Wasmtime), and is - unconditionally needed on certain platforms (such as Windows) that - must always be able to unwind. + This increases metadata size and compile time, but allows for the + debugger to trace frames, is needed for GC tracing that relies on + libunwind (such as in Wasmtime), and is unconditionally needed on + certain platforms (such as Windows) that must always be able to unwind. "#, true, ); @@ -253,6 +250,7 @@ pub(crate) fn define() -> SettingGroup { settings.add_bool( "emit_all_ones_funcaddrs", "Emit not-yet-relocated function addresses as all-ones bit patterns.", + "", false, ); @@ -260,32 +258,27 @@ pub(crate) fn define() -> SettingGroup { settings.add_bool( "enable_probestack", - r#" - Enable the use of stack probes, for calling conventions which support this - functionality. - "#, + "Enable the use of stack probes for supported calling conventions.", + "", true, ); settings.add_bool( "probestack_func_adjusts_sp", - r#" - Set this to true of the stack probe function modifies the stack pointer - itself. - "#, + "Enable if the stack probe adjusts the stack pointer.", + "", false, ); settings.add_num( "probestack_size_log2", + "The log2 of the size of the stack guard region.", r#" - The log2 of the size of the stack guard region. - Stack frames larger than this size will have stack overflow checked by calling the probestack function. The default is 12, which translates to a size of 4096. - "#, + "#, 12, ); @@ -294,6 +287,7 @@ pub(crate) fn define() -> SettingGroup { settings.add_bool( "enable_jump_tables", "Enable the use of jump tables in generated machine code.", + "", true, ); @@ -301,16 +295,15 @@ pub(crate) fn define() -> SettingGroup { settings.add_bool( "enable_heap_access_spectre_mitigation", + "Enable Spectre mitigation on heap bounds checks.", r#" - Enable Spectre mitigation on heap bounds checks. - - This is a no-op for any heap that needs no bounds checks; e.g., - if the limit is static and the guard region is large enough that - the index cannot reach past it. + This is a no-op for any heap that needs no bounds checks; e.g., + if the limit is static and the guard region is large enough that + the index cannot reach past it. - This option is enabled by default because it is highly - recommended for secure sandboxing. The embedder should consider - the security implications carefully before disabling this option. + This option is enabled by default because it is highly + recommended for secure sandboxing. The embedder should consider + the security implications carefully before disabling this option. "#, true, ); diff --git a/cranelift/codegen/src/isa/mod.rs b/cranelift/codegen/src/isa/mod.rs index 44fbf564d33a..519f061f91b6 100644 --- a/cranelift/codegen/src/isa/mod.rs +++ b/cranelift/codegen/src/isa/mod.rs @@ -200,6 +200,16 @@ pub struct Builder { } impl Builder { + /// Gets the triple for the builder. + pub fn triple(&self) -> &Triple { + &self.triple + } + + /// Iterates the available settings in the builder. + pub fn iter(&self) -> impl Iterator { + self.setup.iter() + } + /// 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 { diff --git a/cranelift/codegen/src/settings.rs b/cranelift/codegen/src/settings.rs index 11c3639d6f5e..ecdb17050b19 100644 --- a/cranelift/codegen/src/settings.rs +++ b/cranelift/codegen/src/settings.rs @@ -44,6 +44,34 @@ pub trait Configurable { fn enable(&mut self, name: &str) -> SetResult<()>; } +/// Represents the kind of setting. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SettingKind { + /// The setting is an enumeration. + Enum, + /// The setting is a number. + Num, + /// The setting is a boolean. + Bool, + /// The setting is a preset. + Preset, +} + +/// Represents an available builder setting. +/// +/// This is used for iterating settings in a builder. +#[derive(Clone, Copy, Debug)] +pub struct Setting { + /// The name of the setting. + pub name: &'static str, + /// The description of the setting. + pub description: &'static str, + /// The kind of the setting. + pub kind: SettingKind, + /// The supported values of the setting (for enum values). + pub values: Option<&'static [&'static str]>, +} + /// Collect settings values based on a template. #[derive(Clone, Hash)] pub struct Builder { @@ -66,6 +94,30 @@ impl Builder { self.bytes } + /// Iterates the available settings in the builder. + pub fn iter(&self) -> impl Iterator { + let template = self.template; + + template.descriptors.iter().map(move |d| { + let (kind, values) = match d.detail { + detail::Detail::Enum { last, enumerators } => { + let values = template.enums(last, enumerators); + (SettingKind::Enum, Some(values)) + } + detail::Detail::Num => (SettingKind::Num, None), + detail::Detail::Bool { .. } => (SettingKind::Bool, None), + detail::Detail::Preset => (SettingKind::Preset, None), + }; + + Setting { + name: d.name, + description: d.description, + kind, + values, + } + }) + } + /// Set the value of a single bit. fn set_bit(&mut self, offset: usize, bit: u8, value: bool) { let byte = &mut self.bytes[offset]; @@ -288,6 +340,9 @@ pub mod detail { /// Lower snake-case name of setting as defined in meta. pub name: &'static str, + /// The description of the setting. + pub description: &'static str, + /// Offset of byte containing this setting. pub offset: u32, diff --git a/crates/environ/src/data_structures.rs b/crates/environ/src/data_structures.rs index bbe6c1e1b9aa..de13ac0e499f 100644 --- a/crates/environ/src/data_structures.rs +++ b/crates/environ/src/data_structures.rs @@ -11,7 +11,7 @@ pub mod ir { pub mod settings { pub use cranelift_codegen::settings::{ - builder, Builder, Configurable, Flags, OptLevel, SetError, + builder, Builder, Configurable, Flags, OptLevel, SetError, Setting, SettingKind, }; } diff --git a/crates/wasmtime/src/module.rs b/crates/wasmtime/src/module.rs index bb3fcf0da663..de0e29c12a6b 100644 --- a/crates/wasmtime/src/module.rs +++ b/crates/wasmtime/src/module.rs @@ -271,6 +271,15 @@ impl Module { return Self::deserialize(engine, &binary[COMPILED_MODULE_HEADER.len()..]); } + // Check to see that the config's target matches the host + let target = engine.config().isa_flags.triple(); + if *target != target_lexicon::Triple::host() { + bail!( + "target '{}' specified in the configuration does not match the host", + target + ); + } + const USE_PAGED_MEM_INIT: bool = cfg!(all(feature = "uffd", target_os = "linux")); cfg_if::cfg_if! { diff --git a/src/bin/wasmtime.rs b/src/bin/wasmtime.rs index 46490227ef16..475f2c607bd9 100644 --- a/src/bin/wasmtime.rs +++ b/src/bin/wasmtime.rs @@ -6,7 +6,7 @@ use anyhow::Result; use structopt::{clap::AppSettings, clap::ErrorKind, StructOpt}; use wasmtime_cli::commands::{ - CompileCommand, ConfigCommand, RunCommand, WasmToObjCommand, WastCommand, + CompileCommand, ConfigCommand, RunCommand, SettingsCommand, WasmToObjCommand, WastCommand, }; /// Wasmtime WebAssembly Runtime @@ -42,6 +42,8 @@ enum WasmtimeApp { Compile(CompileCommand), /// Runs a WebAssembly module Run(RunCommand), + /// Displays available Cranelift settings for a target. + Settings(SettingsCommand), /// Translates a WebAssembly module to native object file #[structopt(name = "wasm2obj")] WasmToObj(WasmToObjCommand), @@ -56,6 +58,7 @@ impl WasmtimeApp { Self::Config(c) => c.execute(), Self::Compile(c) => c.execute(), Self::Run(c) => c.execute(), + Self::Settings(c) => c.execute(), Self::WasmToObj(c) => c.execute(), Self::Wast(c) => c.execute(), } diff --git a/src/commands.rs b/src/commands.rs index 1f9bc601f9ce..ebed3d732bdd 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -3,7 +3,8 @@ mod compile; mod config; mod run; +mod settings; mod wasm2obj; mod wast; -pub use self::{compile::*, config::*, run::*, wasm2obj::*, wast::*}; +pub use self::{compile::*, config::*, run::*, settings::*, wasm2obj::*, wast::*}; diff --git a/src/commands/compile.rs b/src/commands/compile.rs index b6656e1eca73..8401dd77058e 100644 --- a/src/commands/compile.rs +++ b/src/commands/compile.rs @@ -1,7 +1,7 @@ -//! The module that implements the `wasmtime wast` command. +//! The module that implements the `wasmtime compile` command. use crate::CommonOptions; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{bail, Context, Result}; use std::fs::{self, File}; use std::io::BufWriter; use std::path::PathBuf; @@ -10,7 +10,7 @@ use structopt::{ StructOpt, }; use target_lexicon::Triple; -use wasmtime::{Config, Engine, Module}; +use wasmtime::{Engine, Module}; lazy_static::lazy_static! { static ref AFTER_HELP: String = { @@ -31,7 +31,7 @@ lazy_static::lazy_static! { \n\ Compiling for a specific platform (Linux) and CPU preset (Skylake):\n\ \n \ - wasmtime compile --target x86_64-unknown-linux --skylake foo.wasm\n", + wasmtime compile --target x86_64-unknown-linux --cranelift-enable skylake foo.wasm\n", crate::WASM_FEATURES.as_str() ) }; @@ -57,90 +57,6 @@ pub struct CompileCommand { #[structopt(long)] interruptable: bool, - /// Enable SSE3 support (for x86-64 targets). - #[structopt(long, group = "x64")] - sse3: bool, - - /// Enable SSSE3 support (for x86-64 targets). - #[structopt(long, group = "x64")] - ssse3: bool, - - /// Enable SSE41 support (for x86-64 targets). - #[structopt(long, group = "x64")] - sse41: bool, - - /// Enable SSE42 support (for x86-64 targets). - #[structopt(long, group = "x64")] - sse42: bool, - - /// Enable AVX support (for x86-64 targets). - #[structopt(long, group = "x64")] - avx: bool, - - /// Enable AVX2 support (for x86-64 targets). - #[structopt(long, group = "x64")] - avx2: bool, - - /// Enable AVX512DQ support (for x86-64 targets). - #[structopt(long, group = "x64")] - avx512dq: bool, - - /// Enable AVX512VL support (for x86-64 targets). - #[structopt(long, group = "x64")] - avx512vl: bool, - - /// Enable AVX512F support (for x86-64 targets). - #[structopt(long, group = "x64")] - avx512f: bool, - - /// Enable POPCNT support (for x86-64 targets). - #[structopt(long, group = "x64")] - popcnt: bool, - - /// Enable BMI1 support (for x86-64 targets). - #[structopt(long, group = "x64")] - bmi1: bool, - - /// Enable BMI2 support (for x86-64 targets). - #[structopt(long, group = "x64")] - bmi2: bool, - - /// Enable LZCNT support (for x86-64 targets). - #[structopt(long, group = "x64")] - lzcnt: bool, - - /// Enable LSE support (for aarch64 targets). - #[structopt(long, group = "aarch64")] - lse: bool, - - /// Enable Nehalem preset (for x86-64 targets). - #[structopt(long, group = "x64", group = "preset-x64")] - nehalem: bool, - - /// Enable Haswell preset (for x86-64 targets). - #[structopt(long, group = "x64", group = "preset-x64")] - haswell: bool, - - /// Enable Broadwell preset (for x86-64 targets). - #[structopt(long, group = "x64", group = "preset-x64")] - broadwell: bool, - - /// Enable Skylake preset (for x86-64 targets). - #[structopt(long, group = "x64", group = "preset-x64")] - skylake: bool, - - /// Enable Cannonlake preset (for x86-64 targets). - #[structopt(long, group = "x64", group = "preset-x64")] - cannonlake: bool, - - /// Enable Icelake preset (for x86-64 targets). - #[structopt(long, group = "x64", group = "preset-x64")] - icelake: bool, - - /// Enable Zen preset (for x86-64 targets). - #[structopt(long, group = "x64", group = "preset-x64")] - znver1: bool, - /// The target triple; default is the host triple #[structopt(long, value_name = "TARGET")] target: Option, @@ -167,8 +83,6 @@ impl CompileCommand { let mut config = self.common.config(Some(&target))?; config.interruptable(self.interruptable); - self.set_flags(&mut config, &target)?; - let engine = Engine::new(&config)?; if self.module.file_name().is_none() { @@ -191,48 +105,6 @@ impl CompileCommand { Ok(()) } - - fn set_flags(&self, c: &mut Config, target: &str) -> Result<()> { - use std::str::FromStr; - - macro_rules! set_flag { - ($config:expr, $arch:expr, $flag:expr, $name:literal, $display:literal) => { - if $flag { - unsafe { - $config.cranelift_flag_enable($name).map_err(|_| { - anyhow!("{} is not supported for architecture '{}'", $display, $arch) - })?; - } - } - }; - } - - let arch = Triple::from_str(target).unwrap().architecture; - - set_flag!(c, arch, self.sse3, "has_sse3", "SSE3"); - set_flag!(c, arch, self.ssse3, "has_ssse3", "SSSE3"); - set_flag!(c, arch, self.sse41, "has_sse41", "SSE41"); - set_flag!(c, arch, self.sse42, "has_sse42", "SSE42"); - set_flag!(c, arch, self.avx, "has_avx", "AVX"); - set_flag!(c, arch, self.avx2, "has_avx2", "AVX2"); - set_flag!(c, arch, self.avx512dq, "has_avx512dq", "AVX512DQ"); - set_flag!(c, arch, self.avx512vl, "has_avx512vl", "AVX512VL"); - set_flag!(c, arch, self.avx512f, "has_avx512f", "AVX512F"); - set_flag!(c, arch, self.popcnt, "has_popcnt", "POPCNT"); - set_flag!(c, arch, self.bmi1, "has_bmi1", "BMI1"); - set_flag!(c, arch, self.bmi2, "has_bmi2", "BMI2"); - set_flag!(c, arch, self.lzcnt, "has_lzcnt", "LZCNT"); - set_flag!(c, arch, self.lse, "has_lse", "LSE"); - set_flag!(c, arch, self.nehalem, "nehalem", "Nehalem preset"); - set_flag!(c, arch, self.haswell, "haswell", "Haswell preset"); - set_flag!(c, arch, self.broadwell, "broadwell", "Broadwell preset"); - set_flag!(c, arch, self.skylake, "skylake", "Skylake preset"); - set_flag!(c, arch, self.cannonlake, "cannonlake", "Cannonlake preset"); - set_flag!(c, arch, self.icelake, "icelake", "Icelake preset"); - set_flag!(c, arch, self.znver1, "znver1", "Zen preset"); - - Ok(()) - } } #[cfg(test)] @@ -285,19 +157,32 @@ mod test { let command = CompileCommand::from_iter_safe(vec![ "compile", "--disable-logging", - "--sse3", - "--ssse3", - "--sse41", - "--sse42", - "--avx", - "--avx2", - "--avx512dq", - "--avx512vl", - "--avx512f", - "--popcnt", - "--bmi1", - "--bmi2", - "--lzcnt", + "--cranelift-enable", + "has_sse3", + "--cranelift-enable", + "has_ssse3", + "--cranelift-enable", + "has_sse41", + "--cranelift-enable", + "has_sse42", + "--cranelift-enable", + "has_avx", + "--cranelift-enable", + "has_avx2", + "--cranelift-enable", + "has_avx512dq", + "--cranelift-enable", + "has_avx512vl", + "--cranelift-enable", + "has_avx512f", + "--cranelift-enable", + "has_popcnt", + "--cranelift-enable", + "has_bmi1", + "--cranelift-enable", + "has_bmi2", + "--cranelift-enable", + "has_lzcnt", "-o", output_path.to_str().unwrap(), input_path.to_str().unwrap(), @@ -321,7 +206,8 @@ mod test { let command = CompileCommand::from_iter_safe(vec![ "compile", "--disable-logging", - "--lse", + "--cranelift-enable", + "has_lse", "-o", output_path.to_str().unwrap(), input_path.to_str().unwrap(), @@ -334,30 +220,28 @@ mod test { #[cfg(target_arch = "x86_64")] #[test] - fn test_incompatible_flags_compile() -> Result<()> { + fn test_unsupported_flags_compile() -> Result<()> { let (mut input, input_path) = NamedTempFile::new()?.into_parts(); input.write_all("(module)".as_bytes())?; drop(input); let output_path = NamedTempFile::new()?.into_temp_path(); - // x64 and aarch64 flags should conflict - match CompileCommand::from_iter_safe(vec![ + // aarch64 flags should not be supported + let command = CompileCommand::from_iter_safe(vec![ "compile", "--disable-logging", - "--sse3", - "--lse", + "--cranelift-enable", + "has_lse", "-o", output_path.to_str().unwrap(), input_path.to_str().unwrap(), - ]) { - Ok(_) => unreachable!(), - Err(e) => { - assert!(e - .to_string() - .contains("cannot be used with one or more of the other specified arguments")); - } - } + ])?; + + assert_eq!( + command.execute().unwrap_err().to_string(), + "No existing setting named 'has_lse'" + ); Ok(()) } @@ -372,17 +256,18 @@ mod test { let output_path = NamedTempFile::new()?.into_temp_path(); for preset in &[ - "--nehalem", - "--haswell", - "--broadwell", - "--skylake", - "--cannonlake", - "--icelake", - "--znver1", + "nehalem", + "haswell", + "broadwell", + "skylake", + "cannonlake", + "icelake", + "znver1", ] { let command = CompileCommand::from_iter_safe(vec![ "compile", "--disable-logging", + "--cranelift-enable", preset, "-o", output_path.to_str().unwrap(), @@ -392,24 +277,6 @@ mod test { command.execute()?; } - // Two presets should conflict - match CompileCommand::from_iter_safe(vec![ - "compile", - "--disable-logging", - "--broadwell", - "--cannonlake", - "-o", - output_path.to_str().unwrap(), - input_path.to_str().unwrap(), - ]) { - Ok(_) => unreachable!(), - Err(e) => { - assert!(e - .to_string() - .contains("cannot be used with one or more of the other specified arguments")); - } - } - Ok(()) } } diff --git a/src/commands/settings.rs b/src/commands/settings.rs new file mode 100644 index 000000000000..e791c7ae722a --- /dev/null +++ b/src/commands/settings.rs @@ -0,0 +1,103 @@ +//! The module that implements the `wasmtime settings` command. + +use anyhow::{anyhow, Result}; +use std::str::FromStr; +use structopt::StructOpt; +use wasmtime_environ::settings::{self, Setting, SettingKind}; +use wasmtime_jit::native; + +/// Displays available Cranelift settings for a target. +#[derive(StructOpt)] +#[structopt(name = "run")] +pub struct SettingsCommand { + /// The target triple to get the settings for; defaults to the host triple. + #[structopt(long, value_name = "TARGET")] + target: Option, +} + +impl SettingsCommand { + /// Executes the command. + pub fn execute(self) -> Result<()> { + let settings = match &self.target { + Some(target) => { + native::lookup(target_lexicon::Triple::from_str(target).map_err(|e| anyhow!(e))?)? + } + None => native::builder(), + }; + + let mut enums = (Vec::new(), 0); + let mut nums = (Vec::new(), 0); + let mut bools = (Vec::new(), 0); + let mut presets = (Vec::new(), 0); + + for setting in settings.iter() { + let (collection, max) = match setting.kind { + SettingKind::Enum => &mut enums, + SettingKind::Num => &mut nums, + SettingKind::Bool => &mut bools, + SettingKind::Preset => &mut presets, + }; + + if setting.name.len() > *max { + *max = setting.name.len(); + } + + collection.push(setting); + } + + if enums.0.is_empty() && nums.0.is_empty() && bools.0.is_empty() && presets.0.is_empty() { + println!("Target '{}' has no settings.", settings.triple()); + return Ok(()); + } + + println!("Cranelift settings for target '{}':", settings.triple()); + + if !enums.0.is_empty() { + println!(); + Self::print_settings("Enum settings:", enums.0, enums.1); + } + + if !nums.0.is_empty() { + println!(); + Self::print_settings("Numerical settings:", nums.0, nums.1); + } + + if !bools.0.is_empty() { + println!(); + Self::print_settings("Boolean settings:", bools.0, bools.1); + } + + if !presets.0.is_empty() { + println!(); + Self::print_settings("Presets:", presets.0, presets.1); + } + + if self.target.is_none() { + let isa = settings.finish(settings::Flags::new(settings::builder())); + println!(); + println!("Settings enabled for this host:"); + + for flag in isa.enabled_isa_flags() { + println!(" - {}", flag); + } + } + + Ok(()) + } + + fn print_settings(header: &str, settings: impl IntoIterator, width: usize) { + println!("{}", header); + for setting in settings { + println!( + " {:width$} {}{}", + setting.name, + setting.description, + setting + .values + .map(|v| format!(" Supported values: {}.", v.join(", "))) + .unwrap_or("".to_string()), + width = width + 2 + ); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index a89a7e783595..3485b6288975 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -212,12 +212,19 @@ struct CommonOptions { )] opt_level: Option, - /// Cranelift common flags to set. - #[structopt(long = "cranelift-set", value_name = "NAME=VALUE", parse(try_from_str = parse_cranelift_flag))] + /// Set a Cranelift setting to a given value. + /// Use `wasmtime settings` to list Cranelift settings for a target. + #[structopt(long = "cranelift-set", value_name = "NAME=VALUE", number_of_values = 1, verbatim_doc_comment, parse(try_from_str = parse_cranelift_flag))] cranelift_set: Vec<(String, String)>, - /// The Cranelift boolean setting or preset to enable. - #[structopt(long, value_name = "SETTING")] + /// Enable a Cranelift boolean setting or preset. + /// Use `wasmtime settings` to list Cranelift settings for a target. + #[structopt( + long, + value_name = "SETTING", + number_of_values = 1, + verbatim_doc_comment + )] cranelift_enable: Vec, /// Maximum size in bytes of wasm memory before it becomes dynamically diff --git a/tests/all/module.rs b/tests/all/module.rs index 0f811ac0857b..08996debac2c 100644 --- a/tests/all/module.rs +++ b/tests/all/module.rs @@ -2,6 +2,23 @@ use anyhow::Result; use std::io::BufWriter; use wasmtime::*; +#[test] +fn checks_incompatible_target() -> Result<()> { + let mut target = target_lexicon::Triple::host(); + target.operating_system = target_lexicon::OperatingSystem::Unknown; + match Module::new( + &Engine::new(Config::new().target(&target.to_string())?)?, + "(module)", + ) { + Ok(_) => unreachable!(), + Err(e) => assert!(e + .to_string() + .contains("configuration does not match the host")), + } + + Ok(()) +} + #[test] fn caches_across_engines() { let c = Config::new();