Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

winch: Gracefully handle unsupported Wasm types #9949

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 35 additions & 28 deletions winch/codegen/src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
use crate::codegen::ptr_type_from_ptr_size;
use crate::isa::{reg::Reg, CallingConvention};
use crate::masm::SPOffset;
use anyhow::Result;
use smallvec::SmallVec;
use std::collections::HashSet;
use std::ops::{Add, BitAnd, Not, Sub};
Expand Down Expand Up @@ -86,7 +87,7 @@ pub(crate) use scratch;
pub(crate) use vmctx;

/// Constructs an [ABISig] using Winch's ABI.
pub(crate) fn wasm_sig<A: ABI>(ty: &WasmFuncType) -> ABISig {
pub(crate) fn wasm_sig<A: ABI>(ty: &WasmFuncType) -> Result<ABISig> {
// 6 is used semi-arbitrarily here, we can modify as we see fit.
let mut params: SmallVec<[WasmValType; 6]> = SmallVec::new();
params.extend_from_slice(&vmctx_types::<A>());
Expand Down Expand Up @@ -116,7 +117,7 @@ pub(crate) trait ABI {
/// Construct the ABI-specific signature from a WebAssembly
/// function type.
#[cfg(test)]
fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> ABISig {
fn sig(wasm_sig: &WasmFuncType, call_conv: &CallingConvention) -> Result<ABISig> {
Self::sig_from(wasm_sig.params(), wasm_sig.returns(), call_conv)
}

Expand All @@ -125,10 +126,10 @@ pub(crate) trait ABI {
params: &[WasmValType],
returns: &[WasmValType],
call_conv: &CallingConvention,
) -> ABISig;
) -> Result<ABISig>;

/// Construct [`ABIResults`] from a slice of [`WasmType`].
fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> ABIResults;
fn abi_results(returns: &[WasmValType], call_conv: &CallingConvention) -> Result<ABIResults>;

/// Returns the number of bits in a word.
fn word_bits() -> u8;
Expand Down Expand Up @@ -315,24 +316,30 @@ impl ABIResults {
/// representation, according to the calling convention. In the case of
/// results, one result is stored in registers and the rest at particular
/// offsets in the stack.
pub fn from<F>(returns: &[WasmValType], call_conv: &CallingConvention, mut map: F) -> Self
pub fn from<F>(
returns: &[WasmValType],
call_conv: &CallingConvention,
mut map: F,
) -> Result<Self>
where
F: FnMut(&WasmValType, u32) -> (ABIOperand, u32),
F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,
{
if returns.len() == 0 {
return Self::default();
return Ok(Self::default());
}

type FoldTuple = (SmallVec<[ABIOperand; 6]>, HashSet<Reg>, u32);

let fold_impl = |(mut operands, mut regs, stack_bytes): FoldTuple, arg| {
let (operand, bytes) = map(arg, stack_bytes);
if operand.is_reg() {
regs.insert(operand.unwrap_reg());
}
operands.push(operand);
(operands, regs, bytes)
};
type FoldTupleResult = Result<FoldTuple>;

let fold_impl =
|(mut operands, mut regs, stack_bytes): FoldTuple, arg| -> FoldTupleResult {
let (operand, bytes) = map(arg, stack_bytes)?;
if operand.is_reg() {
regs.insert(operand.unwrap_reg());
}
operands.push(operand);
Ok((operands, regs, bytes))
};

// When dealing with multiple results, Winch's calling convention stores the
// last return value in a register rather than the first one. In that
Expand All @@ -342,15 +349,15 @@ impl ABIResults {
// * Spilled memory values always precede register values
// * Spilled values are stored from oldest to newest, matching their
// respective locations on the machine stack.
let (mut operands, regs, bytes): FoldTuple = if call_conv.is_default() {
let (mut operands, regs, bytes) = if call_conv.is_default() {
returns
.iter()
.rev()
.fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)
.try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?
} else {
returns
.iter()
.fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)
.try_fold((SmallVec::new(), HashSet::with_capacity(1), 0), fold_impl)?
};

// Similar to above, we reverse the result of the operands calculation
Expand All @@ -359,11 +366,11 @@ impl ABIResults {
operands.reverse();
}

Self::new(ABIOperands {
Ok(Self::new(ABIOperands {
inner: operands,
regs,
bytes,
})
}))
}

/// Create a new [`ABIResults`] from [`ABIOperands`].
Expand Down Expand Up @@ -464,12 +471,12 @@ impl ABIParams {
initial_bytes: u32,
needs_stack_results: bool,
mut map: F,
) -> Self
) -> Result<Self>
where
F: FnMut(&WasmValType, u32) -> (ABIOperand, u32),
F: FnMut(&WasmValType, u32) -> Result<(ABIOperand, u32)>,
{
if params.len() == 0 && !needs_stack_results {
return Self::with_bytes(initial_bytes);
return Ok(Self::with_bytes(initial_bytes));
}

let register_capacity = params.len().min(6);
Expand All @@ -480,7 +487,7 @@ impl ABIParams {
let ptr_type = ptr_type_from_ptr_size(<A as ABI>::word_bytes());
// Handle stack results by specifying an extra, implicit first argument.
let stack_results = if needs_stack_results {
let (operand, bytes) = map(&ptr_type, stack_bytes);
let (operand, bytes) = map(&ptr_type, stack_bytes)?;
if operand.is_reg() {
regs.insert(operand.unwrap_reg());
}
Expand All @@ -491,7 +498,7 @@ impl ABIParams {
};

for arg in params.iter() {
let (operand, bytes) = map(arg, stack_bytes);
let (operand, bytes) = map(arg, stack_bytes)?;
if operand.is_reg() {
regs.insert(operand.unwrap_reg());
}
Expand All @@ -505,14 +512,14 @@ impl ABIParams {
operands.push(operand);
}

Self {
Ok(Self {
operands: ABIOperands {
inner: operands,
regs,
bytes: stack_bytes,
},
has_retptr: needs_stack_results,
}
})
}

/// Creates new [`ABIParams`], with the specified amount of stack bytes.
Expand Down
59 changes: 30 additions & 29 deletions winch/codegen/src/codegen/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
codegen::env::ptr_type_from_ptr_size,
CallingConvention,
};
use anyhow::Result;
use cranelift_codegen::ir::LibCall;
use std::sync::Arc;
use wasmtime_environ::{BuiltinFunctionIndex, PtrSize, VMOffsets, WasmValType};
Expand Down Expand Up @@ -154,117 +155,117 @@ macro_rules! declare_function_sig {
WasmValType::I32
}

fn over_f64<A: ABI>(&self) -> ABISig {
fn over_f64<A: ABI>(&self) -> Result<ABISig> {
A::sig_from(&[self.f64()], &[self.f64()], &self.host_call_conv)
}

fn over_f32<A: ABI>(&self) -> ABISig {
fn over_f32<A: ABI>(&self) -> Result<ABISig> {
A::sig_from(&[self.f64()], &[self.f64()], &self.host_call_conv)
}

pub(crate) fn ceil_f32<A: ABI>(&mut self) -> BuiltinFunction {
pub(crate) fn ceil_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
if self.ceil_f32.is_none() {
let sig = self.over_f32::<A>();
let sig = self.over_f32::<A>()?;
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::CeilF32) });
self.ceil_f32 = Some(BuiltinFunction {
inner,
});
}
self.ceil_f32.as_ref().unwrap().clone()
Ok(self.ceil_f32.as_ref().unwrap().clone())
}

pub(crate) fn ceil_f64<A: ABI>(&mut self) -> BuiltinFunction {
pub(crate) fn ceil_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
if self.ceil_f64.is_none() {
let sig = self.over_f64::<A>();
let sig = self.over_f64::<A>()?;
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::CeilF64) });
self.ceil_f64 = Some(BuiltinFunction {
inner,
});
}
self.ceil_f64.as_ref().unwrap().clone()
Ok(self.ceil_f64.as_ref().unwrap().clone())
}

pub(crate) fn floor_f32<A: ABI>(&mut self) -> BuiltinFunction {
pub(crate) fn floor_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
if self.floor_f32.is_none() {
let sig = self.over_f32::<A>();
let sig = self.over_f32::<A>()?;
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::FloorF32) });
self.floor_f32 = Some(BuiltinFunction {
inner,
});
}
self.floor_f32.as_ref().unwrap().clone()
Ok(self.floor_f32.as_ref().unwrap().clone())
}

pub(crate) fn floor_f64<A: ABI>(&mut self) -> BuiltinFunction {
pub(crate) fn floor_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
if self.floor_f64.is_none() {
let sig = self.over_f64::<A>();
let sig = self.over_f64::<A>()?;
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::FloorF64) });
self.floor_f64 = Some(BuiltinFunction {
inner,
});
}
self.floor_f64.as_ref().unwrap().clone()
Ok(self.floor_f64.as_ref().unwrap().clone())
}

pub(crate) fn trunc_f32<A: ABI>(&mut self) -> BuiltinFunction {
pub(crate) fn trunc_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
if self.trunc_f32.is_none() {
let sig = self.over_f32::<A>();
let sig = self.over_f32::<A>()?;
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::TruncF32) });
self.trunc_f32 = Some(BuiltinFunction {
inner,
});
}
self.trunc_f32.as_ref().unwrap().clone()
Ok(self.trunc_f32.as_ref().unwrap().clone())
}

pub(crate) fn trunc_f64<A: ABI>(&mut self) -> BuiltinFunction {
pub(crate) fn trunc_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
if self.trunc_f64.is_none() {
let sig = self.over_f64::<A>();
let sig = self.over_f64::<A>()?;
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::TruncF64) });
self.trunc_f64 = Some(BuiltinFunction {
inner,
});
}
self.trunc_f64.as_ref().unwrap().clone()
Ok(self.trunc_f64.as_ref().unwrap().clone())
}

pub(crate) fn nearest_f32<A: ABI>(&mut self) -> BuiltinFunction {
pub(crate) fn nearest_f32<A: ABI>(&mut self) -> Result<BuiltinFunction> {
if self.nearest_f32.is_none() {
let sig = self.over_f32::<A>();
let sig = self.over_f32::<A>()?;
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::NearestF32) });
self.nearest_f32 = Some(BuiltinFunction {
inner,
});
}
self.nearest_f32.as_ref().unwrap().clone()
Ok(self.nearest_f32.as_ref().unwrap().clone())
}

pub(crate) fn nearest_f64<A: ABI>(&mut self) -> BuiltinFunction {
pub(crate) fn nearest_f64<A: ABI>(&mut self) -> Result<BuiltinFunction> {
if self.nearest_f64.is_none() {
let sig = self.over_f64::<A>();
let sig = self.over_f64::<A>()?;
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::libcall(LibCall::NearestF64) });
self.nearest_f64 = Some(BuiltinFunction {
inner,
});
}
self.nearest_f64.as_ref().unwrap().clone()
Ok(self.nearest_f64.as_ref().unwrap().clone())
}

$(
$( #[ $attr ] )*
pub(crate) fn $name<A: ABI, P: PtrSize>(&mut self) -> BuiltinFunction {
pub(crate) fn $name<A: ABI, P: PtrSize>(&mut self) -> Result<BuiltinFunction> {
if self.$name.is_none() {
let params = vec![ $(self.$param() ),* ];
let result = vec![ $(self.$result() )?];
let sig = A::sig_from(&params, &result, &self.wasm_call_conv);
let sig = A::sig_from(&params, &result, &self.wasm_call_conv)?;
let index = BuiltinFunctionIndex::$name();
let inner = Arc::new(BuiltinFunctionInner { sig, ty: BuiltinType::builtin(index) });
self.$name = Some(BuiltinFunction {
inner,
});
}

self.$name.as_ref().unwrap().clone()
Ok(self.$name.as_ref().unwrap().clone())
}
)*
}
Expand Down
6 changes: 3 additions & 3 deletions winch/codegen/src/codegen/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl FnCall {
) -> Result<()> {
let (kind, callee_context) = Self::lower(env, context.vmoffsets, &callee, context, masm)?;

let sig = env.callee_sig::<M::ABI>(&callee);
let sig = env.callee_sig::<M::ABI>(&callee)?;
context.spill(masm)?;
let ret_area = Self::make_ret_area(&sig, masm)?;
let arg_stack_space = sig.params_stack_size();
Expand Down Expand Up @@ -142,11 +142,11 @@ impl FnCall {
match callee {
Callee::Builtin(b) => Ok(Self::lower_builtin(env, b)),
Callee::FuncRef(_) => {
Self::lower_funcref(env.callee_sig::<M::ABI>(callee), ptr, context, masm)
Self::lower_funcref(env.callee_sig::<M::ABI>(callee)?, ptr, context, masm)
}
Callee::Local(i) => Ok(Self::lower_local(env, *i)),
Callee::Import(i) => {
let sig = env.callee_sig::<M::ABI>(callee);
let sig = env.callee_sig::<M::ABI>(callee)?;
Self::lower_import(*i, sig, context, masm, vmoffsets)
}
}
Expand Down
Loading
Loading