Resolve libcall relocations for older CPUs (#5567)
* Resolve libcall relocations for older CPUs Long ago Wasmtime used to have logic for resolving relocations post-compilation for libcalls which I ended up removing during refactorings last year. As #5563 points out, however, it's possible to get Wasmtime to panic by disabling SSE features which forces Cranelift to use libcalls for some floating-point operations instead. Note that this also requires disabling SIMD because SIMD support has a baseline of SSE 4.2. This commit pulls back the old implementations of various libcalls and reimplements logic necessary to have them work on CPUs without SSE 4.2 Closes #5563 * Fix log message in `wast` support * Fix offset listed in relocations Be sure to factor in the offset of the function itself * Review comments
This commit is contained in:
@@ -492,3 +492,85 @@ unsafe fn out_of_gas(vmctx: *mut VMContext) -> Result<()> {
|
||||
unsafe fn new_epoch(vmctx: *mut VMContext) -> Result<u64> {
|
||||
(*(*vmctx).instance().store()).new_epoch()
|
||||
}
|
||||
|
||||
/// This module contains functions which are used for resolving relocations at
|
||||
/// runtime if necessary.
|
||||
///
|
||||
/// These functions are not used by default and currently the only platform
|
||||
/// they're used for is on x86_64 when SIMD is disabled and then SSE features
|
||||
/// are further disabled. In these configurations Cranelift isn't allowed to use
|
||||
/// native CPU instructions so it falls back to libcalls and we rely on the Rust
|
||||
/// standard library generally for implementing these.
|
||||
#[allow(missing_docs)]
|
||||
pub mod relocs {
|
||||
pub extern "C" fn floorf32(f: f32) -> f32 {
|
||||
f.floor()
|
||||
}
|
||||
|
||||
pub extern "C" fn floorf64(f: f64) -> f64 {
|
||||
f.floor()
|
||||
}
|
||||
|
||||
pub extern "C" fn ceilf32(f: f32) -> f32 {
|
||||
f.ceil()
|
||||
}
|
||||
|
||||
pub extern "C" fn ceilf64(f: f64) -> f64 {
|
||||
f.ceil()
|
||||
}
|
||||
|
||||
pub extern "C" fn truncf32(f: f32) -> f32 {
|
||||
f.trunc()
|
||||
}
|
||||
|
||||
pub extern "C" fn truncf64(f: f64) -> f64 {
|
||||
f.trunc()
|
||||
}
|
||||
|
||||
const TOINT_32: f32 = 1.0 / f32::EPSILON;
|
||||
const TOINT_64: f64 = 1.0 / f64::EPSILON;
|
||||
|
||||
// NB: replace with `round_ties_even` from libstd when it's stable as
|
||||
// tracked by rust-lang/rust#96710
|
||||
pub extern "C" fn nearestf32(x: f32) -> f32 {
|
||||
// Rust doesn't have a nearest function; there's nearbyint, but it's not
|
||||
// stabilized, so do it manually.
|
||||
// Nearest is either ceil or floor depending on which is nearest or even.
|
||||
// This approach exploited round half to even default mode.
|
||||
let i = x.to_bits();
|
||||
let e = i >> 23 & 0xff;
|
||||
if e >= 0x7f_u32 + 23 {
|
||||
// Check for NaNs.
|
||||
if e == 0xff {
|
||||
// Read the 23-bits significand.
|
||||
if i & 0x7fffff != 0 {
|
||||
// Ensure it's arithmetic by setting the significand's most
|
||||
// significant bit to 1; it also works for canonical NaNs.
|
||||
return f32::from_bits(i | (1 << 22));
|
||||
}
|
||||
}
|
||||
x
|
||||
} else {
|
||||
(x.abs() + TOINT_32 - TOINT_32).copysign(x)
|
||||
}
|
||||
}
|
||||
|
||||
pub extern "C" fn nearestf64(x: f64) -> f64 {
|
||||
let i = x.to_bits();
|
||||
let e = i >> 52 & 0x7ff;
|
||||
if e >= 0x3ff_u64 + 52 {
|
||||
// Check for NaNs.
|
||||
if e == 0x7ff {
|
||||
// Read the 52-bits significand.
|
||||
if i & 0xfffffffffffff != 0 {
|
||||
// Ensure it's arithmetic by setting the significand's most
|
||||
// significant bit to 1; it also works for canonical NaNs.
|
||||
return f64::from_bits(i | (1 << 51));
|
||||
}
|
||||
}
|
||||
x
|
||||
} else {
|
||||
(x.abs() + TOINT_64 - TOINT_64).copysign(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user