* x64: Take SIGFPE signals for divide traps Prior to this commit Wasmtime would configure `avoid_div_traps=true` unconditionally for Cranelift. This, for the division-based instructions, would change emitted code to explicitly trap on trap conditions instead of letting the `div` x86 instruction trap. There's no specific reason for Wasmtime, however, to specifically avoid traps in the `div` instruction. This means that the extra generated branches on x86 aren't necessary since the `div` and `idiv` instructions already trap for similar conditions as wasm requires. This commit instead disables the `avoid_div_traps` setting for Wasmtime's usage of Cranelift. Subsequently the codegen rules were updated slightly: * When `avoid_div_traps=true`, traps are no longer emitted for `div` instructions. * The `udiv`/`urem` instructions now list their trap as divide-by-zero instead of integer overflow. * The lowering for `sdiv` was updated to still explicitly check for zero but the integer overflow case is deferred to the instruction itself. * The lowering of `srem` no longer checks for zero and the listed trap for the `div` instruction is a divide-by-zero. This means that the codegen for `udiv` and `urem` no longer have any branches. The codegen for `sdiv` removes one branch but keeps the zero-check to differentiate the two kinds of traps. The codegen for `srem` removes one branch but keeps the -1 check since the semantics of `srem` mismatch with the semantics of `idiv` with a -1 divisor (specifically for INT_MIN). This is unlikely to have really all that much of a speedup but was something I noticed during #6008 which seemed like it'd be good to clean up. Plus Wasmtime's signal handling was already set up to catch `SIGFPE`, it was just never firing. * Remove the `avoid_div_traps` cranelift setting With no known users currently removing this should be possible and helps simplify the x64 backend. * x64: GC more support for avoid_div_traps Remove the `validate_sdiv_divisor*` pseudo-instructions and clean up some of the ISLE rules now that `div` is allowed to itself trap unconditionally. * x64: Store div trap code in instruction itself * Keep divisors in registers, not in memory Don't accidentally fold multiple traps together * Handle EXC_ARITHMETIC on macos * Update emit tests * Update winch and tests
101 lines
3.2 KiB
Rust
101 lines
3.2 KiB
Rust
use anyhow::Result;
|
|
use cranelift_codegen::isa::IsaBuilder as Builder;
|
|
use cranelift_codegen::settings::{self, Configurable, Flags, SetError};
|
|
use target_lexicon::Triple;
|
|
use wasmtime_environ::{Setting, SettingKind};
|
|
|
|
/// A helper to build an Isa for a compiler implementation.
|
|
/// Compiler builders can wrap this to provide better flexibility when setting flags.
|
|
///
|
|
/// Most methods are mirrored from the `wasmtime_environ::CompilerBuilder` trait, so look there for more
|
|
/// information.
|
|
pub struct IsaBuilder<T> {
|
|
/// The shared flags that all targets share.
|
|
shared_flags: settings::Builder,
|
|
/// The internal ISA builder for the current target.
|
|
inner: Builder<T>,
|
|
/// A callback to lookup a new ISA builder for a target.
|
|
pub lookup: fn(Triple) -> Result<Builder<T>>,
|
|
}
|
|
|
|
impl<T> IsaBuilder<T> {
|
|
/// Create a new ISA builder with the given lookup function.
|
|
pub fn new(lookup: fn(Triple) -> Result<Builder<T>>) -> Self {
|
|
let mut flags = settings::builder();
|
|
|
|
// We don't use probestack as a stack limit mechanism
|
|
flags
|
|
.set("enable_probestack", "false")
|
|
.expect("should be valid flag");
|
|
|
|
let mut isa_flags = lookup(Triple::host()).expect("host machine is not a supported target");
|
|
cranelift_native::infer_native_flags(&mut isa_flags).unwrap();
|
|
|
|
Self {
|
|
shared_flags: flags,
|
|
inner: isa_flags,
|
|
lookup,
|
|
}
|
|
}
|
|
|
|
pub fn triple(&self) -> &target_lexicon::Triple {
|
|
self.inner.triple()
|
|
}
|
|
|
|
pub fn target(&mut self, target: target_lexicon::Triple) -> Result<()> {
|
|
self.inner = (self.lookup)(target)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn settings(&self) -> Vec<Setting> {
|
|
self.inner
|
|
.iter()
|
|
.map(|s| Setting {
|
|
description: s.description,
|
|
name: s.name,
|
|
values: s.values,
|
|
kind: match s.kind {
|
|
settings::SettingKind::Preset => SettingKind::Preset,
|
|
settings::SettingKind::Enum => SettingKind::Enum,
|
|
settings::SettingKind::Num => SettingKind::Num,
|
|
settings::SettingKind::Bool => SettingKind::Bool,
|
|
},
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
pub fn set(&mut self, name: &str, value: &str) -> Result<()> {
|
|
if let Err(err) = self.shared_flags.set(name, value) {
|
|
match err {
|
|
SetError::BadName(_) => {
|
|
self.inner.set(name, value)?;
|
|
}
|
|
_ => return Err(err.into()),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn enable(&mut self, name: &str) -> Result<()> {
|
|
if let Err(err) = self.shared_flags.enable(name) {
|
|
match err {
|
|
SetError::BadName(_) => {
|
|
// Try the target-specific flags.
|
|
self.inner.enable(name)?;
|
|
}
|
|
_ => return Err(err.into()),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn build(&self) -> T {
|
|
self.inner
|
|
.finish(settings::Flags::new(self.shared_flags.clone()))
|
|
}
|
|
|
|
pub fn shared_flags(&self) -> Flags {
|
|
settings::Flags::new(self.shared_flags.clone())
|
|
}
|
|
}
|