forked from bytecodealliance/wasmtime
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for
v128
to the typed function API (bytecodealliance#7010)
* Add support for `v128` to the typed function API This commit adds a Rust type `V128` which corresponds to the wasm `v128` type. This is intended to perhaps one day have accessors for lanes of various sizes but in the meantime only supports conversion back and forth between `u128`. The intention of this type is to allow platforms to perform typed between to functions that take or return `v128` wasm values. Previously this was not implemented because it's a bit tricky ABI-wise. Typed functions work by passing arguments in registers which requires the calling convention to match in both Cranelift and in Rust. This should be the case for supported platforms and the default calling convention, especially now that the wasm calling convention is separate from the platform calling convention. This does mean, however, that this feature can only be supported on x86_64 and AArch64. Currently neither s390x nor RISC-V have a means of supporting the vector calling convention since the vector types aren't available on stable in Rust itself. This means that it's now unfortunately possible to write a Wasmtime embedding that compiles on x86_64 that doesn't compile on s390x for example, but given how niche this feature is that seems like an ok tradeoff for now and by the time it's a problem Rust might have native stable support for vector types on these platforms. prtest:full * Fix compile of C API * Conditionally enable typed v128 tests * Review comments * Fix compiler warnings
- Loading branch information
1 parent
5250cfa
commit b4a6948
Showing
13 changed files
with
356 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
#![cfg_attr( | ||
not(any(target_arch = "x86_64", target_arch = "aarch64")), | ||
allow(unused_imports) | ||
)] | ||
|
||
use crate::store::StoreOpaque; | ||
use crate::{ValRaw, ValType, WasmTy}; | ||
use std::cmp::Ordering; | ||
use std::fmt; | ||
|
||
/// Representation of a 128-bit vector type, `v128`, for WebAssembly. | ||
/// | ||
/// This type corresponds to the `v128` type in WebAssembly and can be used with | ||
/// the [`TypedFunc`] API for example. This is additionally | ||
/// the payload of [`Val::V128`](crate::Val). | ||
/// | ||
/// # Platform specifics | ||
/// | ||
/// This type can currently only be used on x86_64 and AArch64 with the | ||
/// [`TypedFunc`] API. Rust does not have stable support on other platforms for | ||
/// this type so invoking functions with `v128` parameters requires the | ||
/// [`Func::call`](crate::Func::call) API (or perhaps | ||
/// [`Func::call_unchecked`](crate::Func::call_unchecked). | ||
/// | ||
/// [`TypedFunc`]: crate::TypedFunc | ||
#[derive(Copy, Clone)] | ||
#[repr(transparent)] | ||
pub struct V128(Abi); | ||
|
||
// NB: this is why this type is only suitable with `TypedFunc` on some platforms | ||
// and no other. See the documentation for each platform for why each ABI is | ||
// chosen. | ||
cfg_if::cfg_if! { | ||
if #[cfg(target_arch = "x86_64")] { | ||
// x86 vectors are represented with XMM registers which are represented | ||
// with the `__m128i` type. This type is considered a vector type for | ||
// ABI purposes which is implemented by Cranelift. | ||
type Abi = std::arch::x86_64::__m128i; | ||
} else if #[cfg(target_arch = "aarch64")] { | ||
// AArch64 uses vector registered which here is used with a vector type. | ||
// Note that the specific type shouldn't matter too much but the choice | ||
// of using a vector is the significant part. | ||
type Abi = std::arch::aarch64::uint8x16_t; | ||
} else if #[cfg(target_arch = "riscv64")] { | ||
// RISC-V currently always passes all vector arguments indirectly in the | ||
// ABI. Currently Rust has no stable means of representing this meaning | ||
// that a 128-bit representation is chosen here but it can't be passed | ||
// directly to WebAssembly, for example, and must instead be passed | ||
// through an array-call trampoline. | ||
type Abi = u128; | ||
} else if #[cfg(target_arch = "s390x")] { | ||
// Currently Rust has no stable means of representing vector registers | ||
// so like RISC-V at this time this uses a bland 128-bit representation. | ||
type Abi = u128; | ||
} else { | ||
compile_error!("unsupported platform"); | ||
} | ||
} | ||
|
||
union Reinterpret { | ||
abi: Abi, | ||
u128: u128, | ||
} | ||
|
||
impl V128 { | ||
/// Returns the representation of this `v128` as a 128-bit integer in Rust. | ||
pub fn as_u128(&self) -> u128 { | ||
unsafe { Reinterpret { abi: self.0 }.u128 } | ||
} | ||
} | ||
|
||
/// Primary constructor of a `V128` type. | ||
impl From<u128> for V128 { | ||
fn from(val: u128) -> V128 { | ||
unsafe { V128(Reinterpret { u128: val }.abi) } | ||
} | ||
} | ||
|
||
impl From<V128> for u128 { | ||
fn from(val: V128) -> u128 { | ||
val.as_u128() | ||
} | ||
} | ||
|
||
impl fmt::Debug for V128 { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
self.as_u128().fmt(f) | ||
} | ||
} | ||
|
||
impl PartialEq for V128 { | ||
fn eq(&self, other: &V128) -> bool { | ||
self.as_u128() == other.as_u128() | ||
} | ||
} | ||
|
||
impl Eq for V128 {} | ||
|
||
impl PartialOrd for V128 { | ||
fn partial_cmp(&self, other: &V128) -> Option<Ordering> { | ||
Some(self.cmp(other)) | ||
} | ||
} | ||
|
||
impl Ord for V128 { | ||
fn cmp(&self, other: &V128) -> Ordering { | ||
self.as_u128().cmp(&other.as_u128()) | ||
} | ||
} | ||
|
||
// Note that this trait is conditionally implemented which is intentional. See | ||
// the documentation above in the `cfg_if!` for why this is conditional. | ||
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] | ||
unsafe impl WasmTy for V128 { | ||
type Abi = Abi; | ||
|
||
#[inline] | ||
fn valtype() -> ValType { | ||
ValType::V128 | ||
} | ||
|
||
#[inline] | ||
fn compatible_with_store(&self, _: &StoreOpaque) -> bool { | ||
true | ||
} | ||
|
||
#[inline] | ||
fn is_externref(&self) -> bool { | ||
false | ||
} | ||
|
||
#[inline] | ||
unsafe fn abi_from_raw(raw: *mut ValRaw) -> Self::Abi { | ||
V128::from((*raw).get_v128()).0 | ||
} | ||
|
||
#[inline] | ||
unsafe fn abi_into_raw(abi: Self::Abi, raw: *mut ValRaw) { | ||
*raw = ValRaw::v128(V128(abi).as_u128()); | ||
} | ||
|
||
#[inline] | ||
fn into_abi(self, _store: &mut StoreOpaque) -> Self::Abi { | ||
self.0 | ||
} | ||
|
||
#[inline] | ||
unsafe fn from_abi(abi: Self::Abi, _store: &mut StoreOpaque) -> Self { | ||
V128(abi) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.