Merge branch 'pch/wiggle_flags_bitflags' into pch/wasi_common_cap_std
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -2091,9 +2091,9 @@ checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.5.1"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "souper-ir"
|
||||
@@ -2983,6 +2983,7 @@ dependencies = [
|
||||
name = "wiggle"
|
||||
version = "0.22.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"proptest",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
|
||||
1
build.rs
1
build.rs
@@ -32,6 +32,7 @@ fn main() -> anyhow::Result<()> {
|
||||
test_directory_module(out, "tests/misc_testsuite/reference-types", strategy)?;
|
||||
test_directory_module(out, "tests/misc_testsuite/multi-memory", strategy)?;
|
||||
test_directory_module(out, "tests/misc_testsuite/module-linking", strategy)?;
|
||||
test_directory_module(out, "tests/misc_testsuite/threads", strategy)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ log = { version = "0.4.6", default-features = false }
|
||||
serde = { version = "1.0.94", features = ["derive"], optional = true }
|
||||
bincode = { version = "1.2.1", optional = true }
|
||||
gimli = { version = "0.23.0", default-features = false, features = ["write"], optional = true }
|
||||
smallvec = { version = "1.0.0" }
|
||||
smallvec = { version = "1.6.1" }
|
||||
thiserror = "1.0.4"
|
||||
byteorder = { version = "1.3.2", default-features = false }
|
||||
peepmatic = { path = "../peepmatic", optional = true, version = "0.69.0" }
|
||||
|
||||
@@ -145,7 +145,7 @@ impl PrettyPrint for Amode {
|
||||
/// A Memory Address. These denote a 64-bit value only.
|
||||
/// Used for usual addressing modes as well as addressing modes used during compilation, when the
|
||||
/// moving SP offset is not known.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SyntheticAmode {
|
||||
/// A real amode.
|
||||
Real(Amode),
|
||||
@@ -286,7 +286,7 @@ impl PrettyPrintSized for RegMemImm {
|
||||
|
||||
/// An operand which is either an integer Register or a value in Memory. This can denote an 8, 16,
|
||||
/// 32, 64, or 128 bit value.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum RegMem {
|
||||
Reg { reg: Reg },
|
||||
Mem { addr: SyntheticAmode },
|
||||
|
||||
@@ -2716,16 +2716,41 @@ pub(crate) fn emit(
|
||||
}
|
||||
|
||||
Inst::LoadExtName { dst, name, offset } => {
|
||||
// The full address can be encoded in the register, with a relocation.
|
||||
// Generates: movabsq $name, %dst
|
||||
let enc_dst = int_reg_enc(dst.to_reg());
|
||||
sink.put1(0x48 | ((enc_dst >> 3) & 1));
|
||||
sink.put1(0xB8 | (enc_dst & 7));
|
||||
emit_reloc(sink, state, Reloc::Abs8, name, *offset);
|
||||
if info.flags().emit_all_ones_funcaddrs() {
|
||||
sink.put8(u64::max_value());
|
||||
if info.flags().is_pic() {
|
||||
// Generates: movq symbol@GOTPCREL(%rip), %dst
|
||||
let enc_dst = int_reg_enc(dst.to_reg());
|
||||
sink.put1(0x48 | ((enc_dst >> 3) & 1) << 2);
|
||||
sink.put1(0x8B);
|
||||
sink.put1(0x05 | ((enc_dst & 7) << 3));
|
||||
emit_reloc(sink, state, Reloc::X86GOTPCRel4, name, -4);
|
||||
sink.put4(0);
|
||||
// Offset in the relocation above applies to the address of the *GOT entry*, not
|
||||
// the loaded address; so we emit a separate add or sub instruction if needed.
|
||||
if *offset < 0 {
|
||||
assert!(*offset >= -i32::MAX as i64);
|
||||
sink.put1(0x48 | ((enc_dst >> 3) & 1));
|
||||
sink.put1(0x81);
|
||||
sink.put1(0xe8 | (enc_dst & 7));
|
||||
sink.put4((-*offset) as u32);
|
||||
} else if *offset > 0 {
|
||||
assert!(*offset <= i32::MAX as i64);
|
||||
sink.put1(0x48 | ((enc_dst >> 3) & 1));
|
||||
sink.put1(0x81);
|
||||
sink.put1(0xc0 | (enc_dst & 7));
|
||||
sink.put4(*offset as u32);
|
||||
}
|
||||
} else {
|
||||
sink.put8(0);
|
||||
// The full address can be encoded in the register, with a relocation.
|
||||
// Generates: movabsq $name, %dst
|
||||
let enc_dst = int_reg_enc(dst.to_reg());
|
||||
sink.put1(0x48 | ((enc_dst >> 3) & 1));
|
||||
sink.put1(0xB8 | (enc_dst & 7));
|
||||
emit_reloc(sink, state, Reloc::Abs8, name, *offset);
|
||||
if info.flags().emit_all_ones_funcaddrs() {
|
||||
sink.put8(u64::max_value());
|
||||
} else {
|
||||
sink.put8(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2860,6 +2860,46 @@ fn test_x64_emit() {
|
||||
"call *321(%r10,%rdx,4)",
|
||||
));
|
||||
|
||||
// ========================================================
|
||||
// LoadExtName
|
||||
// N.B.: test harness below sets is_pic.
|
||||
insns.push((
|
||||
Inst::LoadExtName {
|
||||
dst: Writable::from_reg(r11),
|
||||
name: Box::new(ExternalName::User {
|
||||
namespace: 0,
|
||||
index: 0,
|
||||
}),
|
||||
offset: 0,
|
||||
},
|
||||
"4C8B1D00000000",
|
||||
"load_ext_name u0:0+0, %r11",
|
||||
));
|
||||
insns.push((
|
||||
Inst::LoadExtName {
|
||||
dst: Writable::from_reg(r11),
|
||||
name: Box::new(ExternalName::User {
|
||||
namespace: 0,
|
||||
index: 0,
|
||||
}),
|
||||
offset: 0x12345678,
|
||||
},
|
||||
"4C8B1D000000004981C378563412",
|
||||
"load_ext_name u0:0+305419896, %r11",
|
||||
));
|
||||
insns.push((
|
||||
Inst::LoadExtName {
|
||||
dst: Writable::from_reg(r11),
|
||||
name: Box::new(ExternalName::User {
|
||||
namespace: 0,
|
||||
index: 0,
|
||||
}),
|
||||
offset: -0x12345678,
|
||||
},
|
||||
"4C8B1D000000004981EB78563412",
|
||||
"load_ext_name u0:0+-305419896, %r11",
|
||||
));
|
||||
|
||||
// ========================================================
|
||||
// Ret
|
||||
insns.push((Inst::ret(), "C3", "ret"));
|
||||
@@ -3781,7 +3821,9 @@ fn test_x64_emit() {
|
||||
|
||||
// ========================================================
|
||||
// Actually run the tests!
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
let mut flag_builder = settings::builder();
|
||||
flag_builder.enable("is_pic").unwrap();
|
||||
let flags = settings::Flags::new(flag_builder);
|
||||
|
||||
use crate::settings::Configurable;
|
||||
let mut isa_flag_builder = x64::settings::builder();
|
||||
|
||||
@@ -397,7 +397,10 @@ pub enum Inst {
|
||||
/// An instruction that will always trigger the illegal instruction exception.
|
||||
Ud2 { trap_code: TrapCode },
|
||||
|
||||
/// Loads an external symbol in a register, with a relocation: movabsq $name, dst
|
||||
/// Loads an external symbol in a register, with a relocation:
|
||||
///
|
||||
/// movq $name@GOTPCREL(%rip), dst if PIC is enabled, or
|
||||
/// movabsq $name, dst otherwise.
|
||||
LoadExtName {
|
||||
dst: Writable<Reg>,
|
||||
name: Box<ExternalName>,
|
||||
@@ -1726,7 +1729,7 @@ impl PrettyPrint for Inst {
|
||||
dst, name, offset, ..
|
||||
} => format!(
|
||||
"{} {}+{}, {}",
|
||||
ljustify("movaps".into()),
|
||||
ljustify("load_ext_name".into()),
|
||||
name,
|
||||
offset,
|
||||
show_ireg_sized(dst.to_reg(), mb_rru, 8),
|
||||
|
||||
@@ -4095,6 +4095,53 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
||||
emit_extract_lane(ctx, src, dst, lane, ty);
|
||||
}
|
||||
|
||||
Opcode::ScalarToVector => {
|
||||
// When moving a scalar value to a vector register, we must be handle several
|
||||
// situations:
|
||||
// 1. a scalar float is already in an XMM register, so we simply move it
|
||||
// 2. a scalar of any other type resides in a GPR register: MOVD moves the bits to an
|
||||
// XMM register and zeroes the upper bits
|
||||
// 3. a scalar (float or otherwise) that has previously been loaded from memory (e.g.
|
||||
// the default lowering of Wasm's `load[32|64]_zero`) can be lowered to a single
|
||||
// MOVSS/MOVSD instruction; to do this, we rely on `input_to_reg_mem` to sink the
|
||||
// unused load.
|
||||
let src = input_to_reg_mem(ctx, inputs[0]);
|
||||
let src_ty = ctx.input_ty(insn, 0);
|
||||
let dst = get_output_reg(ctx, outputs[0]).only_reg().unwrap();
|
||||
let dst_ty = ty.unwrap();
|
||||
assert!(src_ty == dst_ty.lane_type() && dst_ty.bits() == 128);
|
||||
match src {
|
||||
RegMem::Reg { reg } => {
|
||||
if src_ty.is_float() {
|
||||
// Case 1: when moving a scalar float, we simply move from one XMM register
|
||||
// to another, expecting the register allocator to elide this. Here we
|
||||
// assume that the upper bits of a scalar float have not been munged with
|
||||
// (the same assumption the old backend makes).
|
||||
ctx.emit(Inst::gen_move(dst, reg, dst_ty));
|
||||
} else {
|
||||
// Case 2: when moving a scalar value of any other type, use MOVD to zero
|
||||
// the upper lanes.
|
||||
let src_size = match src_ty.bits() {
|
||||
32 => OperandSize::Size32,
|
||||
64 => OperandSize::Size64,
|
||||
_ => unimplemented!("invalid source size for type: {}", src_ty),
|
||||
};
|
||||
ctx.emit(Inst::gpr_to_xmm(SseOpcode::Movd, src, src_size, dst));
|
||||
}
|
||||
}
|
||||
RegMem::Mem { .. } => {
|
||||
// Case 3: when presented with `load + scalar_to_vector`, coalesce into a single
|
||||
// MOVSS/MOVSD instruction.
|
||||
let opcode = match src_ty.bits() {
|
||||
32 => SseOpcode::Movss,
|
||||
64 => SseOpcode::Movsd,
|
||||
_ => unimplemented!("unable to move scalar to vector for type: {}", src_ty),
|
||||
};
|
||||
ctx.emit(Inst::xmm_mov(opcode, src, dst));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Opcode::Splat => {
|
||||
let ty = ty.unwrap();
|
||||
assert_eq!(ty.bits(), 128);
|
||||
|
||||
@@ -683,10 +683,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
has_side_effect,
|
||||
value_needed,
|
||||
);
|
||||
// Skip lowering branches; these are handled separately.
|
||||
if self.f.dfg[inst].opcode().is_branch() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update scan state to color prior to this inst (as we are scanning
|
||||
// backward).
|
||||
@@ -699,6 +695,12 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
self.cur_scan_entry_color = Some(entry_color);
|
||||
}
|
||||
|
||||
// Skip lowering branches; these are handled separately
|
||||
// (see `lower_clif_branches()` below).
|
||||
if self.f.dfg[inst].opcode().is_branch() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Normal instruction: codegen if the instruction is side-effecting
|
||||
// or any of its outputs its used.
|
||||
if has_side_effect || value_needed {
|
||||
|
||||
@@ -59,3 +59,14 @@ block0(v0: i64, v1: i64):
|
||||
; nextln: movq 0(%rax,%rdi,1), %rsi
|
||||
; nextln: movq %rsi, %rax
|
||||
}
|
||||
|
||||
function %merge_scalar_to_vector(i64) -> i32x4 {
|
||||
block0(v0: i64):
|
||||
v1 = load.i32 v0
|
||||
v2 = scalar_to_vector.i32x4 v1
|
||||
; check: movss 0(%rdi), %xmm0
|
||||
|
||||
jump block1
|
||||
block1:
|
||||
return v2
|
||||
}
|
||||
|
||||
@@ -91,3 +91,34 @@ block0(v0: f64):
|
||||
; check: uninit %xmm1
|
||||
; nextln: movsd %xmm0, %xmm1
|
||||
; nextln: movlhps %xmm0, %xmm1
|
||||
|
||||
|
||||
|
||||
;; load*_zero
|
||||
|
||||
; Verify that a `load` followed by a `scalar_to_vector` (the CLIF translation of `load32_zero`) is
|
||||
; lowered to a single MOVSS instruction.
|
||||
function %load32_zero_coalesced(i64) -> i32x4 {
|
||||
block0(v0: i64):
|
||||
v1 = load.i32 v0
|
||||
v2 = scalar_to_vector.i32x4 v1
|
||||
; check: movss 0(%rdi), %xmm0
|
||||
return v2
|
||||
}
|
||||
|
||||
;; Verify that `scalar_to_vector` (used by `load32_zero`), lowers as expected.
|
||||
function %load32_zero_int(i32) -> i32x4 {
|
||||
block0(v0: i32):
|
||||
v1 = scalar_to_vector.i32x4 v0
|
||||
; check: movd %edi, %xmm0
|
||||
return v1
|
||||
}
|
||||
function %load32_zero_float(f32) -> f32x4 {
|
||||
block0(v0: f32):
|
||||
v1 = scalar_to_vector.f32x4 v0
|
||||
; regex: MOV=movap*
|
||||
; check: pushq
|
||||
; not: $MOV
|
||||
; check: ret
|
||||
return v1
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ cranelift-codegen = { path = "../codegen", version = "0.69.0", default-features
|
||||
target-lexicon = "0.11"
|
||||
log = { version = "0.4.6", default-features = false }
|
||||
hashbrown = { version = "0.9.1", optional = true }
|
||||
smallvec = { version = "1.0.0" }
|
||||
smallvec = { version = "1.6.1" }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
||||
@@ -15,7 +15,7 @@ cranelift-codegen = { path = "../codegen", version = "0.69.0", features = ["all-
|
||||
cranelift-entity = { path = "../entity", version = "0.69.0" }
|
||||
cranelift-reader = { path = "../reader", version = "0.69.0" }
|
||||
log = { version = "0.4.8", default-features = false }
|
||||
smallvec = "1.4.2"
|
||||
smallvec = "1.6.1"
|
||||
thiserror = "1.0.15"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
[package]
|
||||
name = "peepmatic-test-operator"
|
||||
description = "Operator for usage in peepmatic tests"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
version = "0.69.0"
|
||||
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
@@ -11,7 +11,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cranelift-codegen = { path = "../codegen", version = "0.69.0" }
|
||||
smallvec = "1.0.0"
|
||||
smallvec = "1.6.1"
|
||||
target-lexicon = "0.11"
|
||||
thiserror = "1.0.15"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ hashbrown = { version = "0.9.1", optional = true }
|
||||
itertools = "0.9.0"
|
||||
log = { version = "0.4.6", default-features = false }
|
||||
serde = { version = "1.0.94", features = ["derive"], optional = true }
|
||||
smallvec = "1.0.0"
|
||||
smallvec = "1.6.1"
|
||||
thiserror = "1.0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -201,14 +201,26 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
state.pop1();
|
||||
}
|
||||
Operator::Select => {
|
||||
let (arg1, arg2, cond) = state.pop3();
|
||||
let (mut arg1, mut arg2, cond) = state.pop3();
|
||||
if builder.func.dfg.value_type(arg1).is_vector() {
|
||||
arg1 = optionally_bitcast_vector(arg1, I8X16, builder);
|
||||
}
|
||||
if builder.func.dfg.value_type(arg2).is_vector() {
|
||||
arg2 = optionally_bitcast_vector(arg2, I8X16, builder);
|
||||
}
|
||||
state.push1(builder.ins().select(cond, arg1, arg2));
|
||||
}
|
||||
Operator::TypedSelect { ty: _ } => {
|
||||
// We ignore the explicit type parameter as it is only needed for
|
||||
// validation, which we require to have been performed before
|
||||
// translation.
|
||||
let (arg1, arg2, cond) = state.pop3();
|
||||
let (mut arg1, mut arg2, cond) = state.pop3();
|
||||
if builder.func.dfg.value_type(arg1).is_vector() {
|
||||
arg1 = optionally_bitcast_vector(arg1, I8X16, builder);
|
||||
}
|
||||
if builder.func.dfg.value_type(arg2).is_vector() {
|
||||
arg2 = optionally_bitcast_vector(arg2, I8X16, builder);
|
||||
}
|
||||
state.push1(builder.ins().select(cond, arg1, arg2));
|
||||
}
|
||||
Operator::Nop => {
|
||||
@@ -1052,6 +1064,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
let timeout = state.pop1(); // 64 (fixed)
|
||||
let expected = state.pop1(); // 32 or 64 (per the `Ixx` in `IxxAtomicWait`)
|
||||
let addr = state.pop1(); // 32 (fixed)
|
||||
let addr = fold_atomic_mem_addr(addr, memarg, implied_ty, builder);
|
||||
assert!(builder.func.dfg.value_type(expected) == implied_ty);
|
||||
// `fn translate_atomic_wait` can inspect the type of `expected` to figure out what
|
||||
// code it needs to generate, if it wants.
|
||||
@@ -1070,6 +1083,7 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
let heap = state.get_heap(builder.func, memarg.memory, environ)?;
|
||||
let count = state.pop1(); // 32 (fixed)
|
||||
let addr = state.pop1(); // 32 (fixed)
|
||||
let addr = fold_atomic_mem_addr(addr, memarg, I32, builder);
|
||||
let res =
|
||||
environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?;
|
||||
state.push1(res);
|
||||
@@ -2142,6 +2156,42 @@ fn translate_icmp(cc: IntCC, builder: &mut FunctionBuilder, state: &mut FuncTran
|
||||
state.push1(builder.ins().bint(I32, val));
|
||||
}
|
||||
|
||||
fn fold_atomic_mem_addr(
|
||||
linear_mem_addr: Value,
|
||||
memarg: &MemoryImmediate,
|
||||
access_ty: Type,
|
||||
builder: &mut FunctionBuilder,
|
||||
) -> Value {
|
||||
let access_ty_bytes = access_ty.bytes();
|
||||
let final_lma = if memarg.offset > 0 {
|
||||
assert!(builder.func.dfg.value_type(linear_mem_addr) == I32);
|
||||
let linear_mem_addr = builder.ins().uextend(I64, linear_mem_addr);
|
||||
let a = builder
|
||||
.ins()
|
||||
.iadd_imm(linear_mem_addr, i64::from(memarg.offset));
|
||||
let cflags = builder.ins().ifcmp_imm(a, 0x1_0000_0000i64);
|
||||
builder.ins().trapif(
|
||||
IntCC::UnsignedGreaterThanOrEqual,
|
||||
cflags,
|
||||
ir::TrapCode::HeapOutOfBounds,
|
||||
);
|
||||
builder.ins().ireduce(I32, a)
|
||||
} else {
|
||||
linear_mem_addr
|
||||
};
|
||||
assert!(access_ty_bytes == 4 || access_ty_bytes == 8);
|
||||
let final_lma_misalignment = builder
|
||||
.ins()
|
||||
.band_imm(final_lma, i64::from(access_ty_bytes - 1));
|
||||
let f = builder
|
||||
.ins()
|
||||
.ifcmp_imm(final_lma_misalignment, i64::from(0));
|
||||
builder
|
||||
.ins()
|
||||
.trapif(IntCC::NotEqual, f, ir::TrapCode::HeapMisaligned);
|
||||
final_lma
|
||||
}
|
||||
|
||||
// For an atomic memory operation, emit an alignment check for the linear memory address,
|
||||
// and then compute the final effective address.
|
||||
fn finalise_atomic_mem_addr<FE: FuncEnvironment + ?Sized>(
|
||||
|
||||
51
cranelift/wasmtests/pr2559.wat
Normal file
51
cranelift/wasmtests/pr2559.wat
Normal file
@@ -0,0 +1,51 @@
|
||||
(module
|
||||
(type (;0;) (func (result v128 v128 v128)))
|
||||
|
||||
(func $main1 (type 0) (result v128 v128 v128)
|
||||
call $main1
|
||||
i8x16.add
|
||||
call $main1
|
||||
i8x16.ge_u
|
||||
i16x8.ne
|
||||
i32.const 13
|
||||
br_if 0 (;@0;)
|
||||
i32.const 43
|
||||
br_if 0 (;@0;)
|
||||
i32.const 13
|
||||
br_if 0 (;@0;)
|
||||
i32.const 87
|
||||
select
|
||||
unreachable
|
||||
i32.const 0
|
||||
br_if 0 (;@0;)
|
||||
i32.const 13
|
||||
br_if 0 (;@0;)
|
||||
i32.const 43
|
||||
br_if 0 (;@0;)
|
||||
)
|
||||
(export "main1" (func $main1))
|
||||
|
||||
(func $main2 (type 0) (result v128 v128 v128)
|
||||
call $main2
|
||||
i8x16.add
|
||||
call $main2
|
||||
i8x16.ge_u
|
||||
i16x8.ne
|
||||
i32.const 13
|
||||
br_if 0 (;@0;)
|
||||
i32.const 43
|
||||
br_if 0 (;@0;)
|
||||
i32.const 13
|
||||
br_if 0 (;@0;)
|
||||
i32.const 87
|
||||
select (result v128)
|
||||
unreachable
|
||||
i32.const 0
|
||||
br_if 0 (;@0;)
|
||||
i32.const 13
|
||||
br_if 0 (;@0;)
|
||||
i32.const 43
|
||||
br_if 0 (;@0;)
|
||||
)
|
||||
(export "main2" (func $main2))
|
||||
)
|
||||
@@ -81,6 +81,10 @@ macro_rules! declare_function_signatures {
|
||||
AbiParam::new(I32).uext()
|
||||
}
|
||||
|
||||
fn i64(&self) -> AbiParam {
|
||||
AbiParam::new(I64)
|
||||
}
|
||||
|
||||
$(
|
||||
fn $name(&mut self, func: &mut Function) -> ir::SigRef {
|
||||
let sig = self.$name.unwrap_or_else(|| {
|
||||
@@ -258,6 +262,70 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_memory_atomic_notify(
|
||||
&mut self,
|
||||
func: &mut Function,
|
||||
memory_index: MemoryIndex,
|
||||
) -> (ir::SigRef, usize, BuiltinFunctionIndex) {
|
||||
if let Some(defined_memory_index) = self.module.defined_memory_index(memory_index) {
|
||||
(
|
||||
self.builtin_function_signatures.memory_atomic_notify(func),
|
||||
defined_memory_index.index(),
|
||||
BuiltinFunctionIndex::memory_atomic_notify(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
self.builtin_function_signatures
|
||||
.imported_memory_atomic_notify(func),
|
||||
memory_index.index(),
|
||||
BuiltinFunctionIndex::imported_memory_atomic_notify(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_memory_atomic_wait(
|
||||
&mut self,
|
||||
func: &mut Function,
|
||||
memory_index: MemoryIndex,
|
||||
ty: ir::Type,
|
||||
) -> (ir::SigRef, usize, BuiltinFunctionIndex) {
|
||||
match ty {
|
||||
I32 => {
|
||||
if let Some(defined_memory_index) = self.module.defined_memory_index(memory_index) {
|
||||
(
|
||||
self.builtin_function_signatures.memory_atomic_wait32(func),
|
||||
defined_memory_index.index(),
|
||||
BuiltinFunctionIndex::memory_atomic_wait32(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
self.builtin_function_signatures
|
||||
.imported_memory_atomic_wait32(func),
|
||||
memory_index.index(),
|
||||
BuiltinFunctionIndex::imported_memory_atomic_wait32(),
|
||||
)
|
||||
}
|
||||
}
|
||||
I64 => {
|
||||
if let Some(defined_memory_index) = self.module.defined_memory_index(memory_index) {
|
||||
(
|
||||
self.builtin_function_signatures.memory_atomic_wait64(func),
|
||||
defined_memory_index.index(),
|
||||
BuiltinFunctionIndex::memory_atomic_wait64(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
self.builtin_function_signatures
|
||||
.imported_memory_atomic_wait64(func),
|
||||
memory_index.index(),
|
||||
BuiltinFunctionIndex::imported_memory_atomic_wait64(),
|
||||
)
|
||||
}
|
||||
}
|
||||
x => panic!("get_memory_atomic_wait unsupported type: {:?}", x),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_memory_init_func(&mut self, func: &mut Function) -> (ir::SigRef, BuiltinFunctionIndex) {
|
||||
(
|
||||
self.builtin_function_signatures.memory_init(func),
|
||||
@@ -1368,29 +1436,50 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
|
||||
|
||||
fn translate_atomic_wait(
|
||||
&mut self,
|
||||
_pos: FuncCursor,
|
||||
_index: MemoryIndex,
|
||||
mut pos: FuncCursor,
|
||||
memory_index: MemoryIndex,
|
||||
_heap: ir::Heap,
|
||||
_addr: ir::Value,
|
||||
_expected: ir::Value,
|
||||
_timeout: ir::Value,
|
||||
addr: ir::Value,
|
||||
expected: ir::Value,
|
||||
timeout: ir::Value,
|
||||
) -> WasmResult<ir::Value> {
|
||||
Err(WasmError::Unsupported(
|
||||
"wasm atomics (fn translate_atomic_wait)".to_string(),
|
||||
))
|
||||
let implied_ty = pos.func.dfg.value_type(expected);
|
||||
let (func_sig, memory_index, func_idx) =
|
||||
self.get_memory_atomic_wait(&mut pos.func, memory_index, implied_ty);
|
||||
|
||||
let memory_index_arg = pos.ins().iconst(I32, memory_index as i64);
|
||||
|
||||
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
||||
|
||||
let call_inst = pos.ins().call_indirect(
|
||||
func_sig,
|
||||
func_addr,
|
||||
&[vmctx, memory_index_arg, addr, expected, timeout],
|
||||
);
|
||||
|
||||
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
|
||||
}
|
||||
|
||||
fn translate_atomic_notify(
|
||||
&mut self,
|
||||
_pos: FuncCursor,
|
||||
_index: MemoryIndex,
|
||||
mut pos: FuncCursor,
|
||||
memory_index: MemoryIndex,
|
||||
_heap: ir::Heap,
|
||||
_addr: ir::Value,
|
||||
_count: ir::Value,
|
||||
addr: ir::Value,
|
||||
count: ir::Value,
|
||||
) -> WasmResult<ir::Value> {
|
||||
Err(WasmError::Unsupported(
|
||||
"wasm atomics (fn translate_atomic_notify)".to_string(),
|
||||
))
|
||||
let (func_sig, memory_index, func_idx) =
|
||||
self.get_memory_atomic_notify(&mut pos.func, memory_index);
|
||||
|
||||
let memory_index_arg = pos.ins().iconst(I32, memory_index as i64);
|
||||
|
||||
let (vmctx, func_addr) = self.translate_load_builtin_function_address(&mut pos, func_idx);
|
||||
|
||||
let call_inst =
|
||||
pos.ins()
|
||||
.call_indirect(func_sig, func_addr, &[vmctx, memory_index_arg, addr, count]);
|
||||
|
||||
Ok(*pos.func.dfg.inst_results(call_inst).first().unwrap())
|
||||
}
|
||||
|
||||
fn translate_loop_header(&mut self, mut pos: FuncCursor) -> WasmResult<()> {
|
||||
|
||||
@@ -45,6 +45,18 @@ macro_rules! foreach_builtin_function {
|
||||
externref_global_get(vmctx, i32) -> (reference);
|
||||
/// Returns an index for Wasm's `global.get` instruction for `externref`s.
|
||||
externref_global_set(vmctx, i32, reference) -> ();
|
||||
/// Returns an index for wasm's `memory.atomic.notify` for locally defined memories.
|
||||
memory_atomic_notify(vmctx, i32, i32, i32) -> (i32);
|
||||
/// Returns an index for wasm's `memory.atomic.notify` for imported memories.
|
||||
imported_memory_atomic_notify(vmctx, i32, i32, i32) -> (i32);
|
||||
/// Returns an index for wasm's `memory.atomic.wait32` for locally defined memories.
|
||||
memory_atomic_wait32(vmctx, i32, i32, i32, i64) -> (i32);
|
||||
/// Returns an index for wasm's `memory.atomic.wait32` for imported memories.
|
||||
imported_memory_atomic_wait32(vmctx, i32, i32, i32, i64) -> (i32);
|
||||
/// Returns an index for wasm's `memory.atomic.wait64` for locally defined memories.
|
||||
memory_atomic_wait64(vmctx, i32, i32, i64, i64) -> (i32);
|
||||
/// Returns an index for wasm's `memory.atomic.wait64` for imported memories.
|
||||
imported_memory_atomic_wait64(vmctx, i32, i32, i64, i64) -> (i32);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ iter-enum = "0.2"
|
||||
itertools = "0.9.0"
|
||||
memoffset = "0.6.0"
|
||||
more-asserts = "0.2.1"
|
||||
smallvec = "1.0.0"
|
||||
smallvec = "1.6.1"
|
||||
thiserror = "1.0.9"
|
||||
typemap = "0.3"
|
||||
wasmparser = "0.71"
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
use crate::externref::VMExternRef;
|
||||
use crate::table::Table;
|
||||
use crate::traphandlers::raise_lib_trap;
|
||||
use crate::traphandlers::{raise_lib_trap, Trap};
|
||||
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMContext};
|
||||
use std::mem;
|
||||
use std::ptr::{self, NonNull};
|
||||
@@ -496,3 +496,88 @@ pub unsafe extern "C" fn wasmtime_externref_global_set(
|
||||
let old = mem::replace((*global).as_externref_mut(), externref);
|
||||
drop(old);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Unimplemented(&'static str);
|
||||
impl std::error::Error for Unimplemented {}
|
||||
impl std::fmt::Display for Unimplemented {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
write!(f, "unimplemented: {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `memory.atomic.notify` for locally defined memories.
|
||||
pub unsafe extern "C" fn wasmtime_memory_atomic_notify(
|
||||
_vmctx: *mut VMContext,
|
||||
_memory_index: u32,
|
||||
_addr: u32,
|
||||
_count: u32,
|
||||
) -> u32 {
|
||||
raise_lib_trap(Trap::User(Box::new(Unimplemented(
|
||||
"wasm atomics (fn wasmtime_memory_atomic_notify) unsupported",
|
||||
))));
|
||||
}
|
||||
|
||||
/// Implementation of `memory.atomic.notify` for imported memories.
|
||||
pub unsafe extern "C" fn wasmtime_imported_memory_atomic_notify(
|
||||
_vmctx: *mut VMContext,
|
||||
_memory_index: u32,
|
||||
_addr: u32,
|
||||
_count: u32,
|
||||
) -> u32 {
|
||||
raise_lib_trap(Trap::User(Box::new(Unimplemented(
|
||||
"wasm atomics (fn wasmtime_imported_memory_atomic_notify) unsupported",
|
||||
))));
|
||||
}
|
||||
|
||||
/// Implementation of `memory.atomic.wait32` for locally defined memories.
|
||||
pub unsafe extern "C" fn wasmtime_memory_atomic_wait32(
|
||||
_vmctx: *mut VMContext,
|
||||
_memory_index: u32,
|
||||
_addr: u32,
|
||||
_expected: u32,
|
||||
_timeout: u64,
|
||||
) -> u32 {
|
||||
raise_lib_trap(Trap::User(Box::new(Unimplemented(
|
||||
"wasm atomics (fn wasmtime_memory_atomic_wait32) unsupported",
|
||||
))));
|
||||
}
|
||||
|
||||
/// Implementation of `memory.atomic.wait32` for imported memories.
|
||||
pub unsafe extern "C" fn wasmtime_imported_memory_atomic_wait32(
|
||||
_vmctx: *mut VMContext,
|
||||
_memory_index: u32,
|
||||
_addr: u32,
|
||||
_expected: u32,
|
||||
_timeout: u64,
|
||||
) -> u32 {
|
||||
raise_lib_trap(Trap::User(Box::new(Unimplemented(
|
||||
"wasm atomics (fn wasmtime_imported_memory_atomic_wait32) unsupported",
|
||||
))));
|
||||
}
|
||||
|
||||
/// Implementation of `memory.atomic.wait64` for locally defined memories.
|
||||
pub unsafe extern "C" fn wasmtime_memory_atomic_wait64(
|
||||
_vmctx: *mut VMContext,
|
||||
_memory_index: u32,
|
||||
_addr: u32,
|
||||
_expected: u64,
|
||||
_timeout: u64,
|
||||
) -> u32 {
|
||||
raise_lib_trap(Trap::User(Box::new(Unimplemented(
|
||||
"wasm atomics (fn wasmtime_memory_atomic_wait32) unsupported",
|
||||
))));
|
||||
}
|
||||
|
||||
/// Implementation of `memory.atomic.wait32` for imported memories.
|
||||
pub unsafe extern "C" fn wasmtime_imported_memory_atomic_wait64(
|
||||
_vmctx: *mut VMContext,
|
||||
_memory_index: u32,
|
||||
_addr: u32,
|
||||
_expected: u64,
|
||||
_timeout: u64,
|
||||
) -> u32 {
|
||||
raise_lib_trap(Trap::User(Box::new(Unimplemented(
|
||||
"wasm atomics (fn wasmtime_imported_memory_atomic_wait64) unsupported",
|
||||
))));
|
||||
}
|
||||
|
||||
@@ -600,6 +600,18 @@ impl VMBuiltinFunctionsArray {
|
||||
wasmtime_table_fill as usize;
|
||||
ptrs[BuiltinFunctionIndex::table_fill_funcref().index() as usize] =
|
||||
wasmtime_table_fill as usize;
|
||||
ptrs[BuiltinFunctionIndex::memory_atomic_notify().index() as usize] =
|
||||
wasmtime_memory_atomic_notify as usize;
|
||||
ptrs[BuiltinFunctionIndex::imported_memory_atomic_notify().index() as usize] =
|
||||
wasmtime_imported_memory_atomic_notify as usize;
|
||||
ptrs[BuiltinFunctionIndex::memory_atomic_wait32().index() as usize] =
|
||||
wasmtime_memory_atomic_wait32 as usize;
|
||||
ptrs[BuiltinFunctionIndex::imported_memory_atomic_wait32().index() as usize] =
|
||||
wasmtime_imported_memory_atomic_wait32 as usize;
|
||||
ptrs[BuiltinFunctionIndex::memory_atomic_wait64().index() as usize] =
|
||||
wasmtime_memory_atomic_wait64 as usize;
|
||||
ptrs[BuiltinFunctionIndex::imported_memory_atomic_wait64().index() as usize] =
|
||||
wasmtime_imported_memory_atomic_wait64 as usize;
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
for i in 0..ptrs.len() {
|
||||
|
||||
@@ -78,7 +78,7 @@ impl Entry {
|
||||
/// `HandleRights` structure is a subset of rights attached to this `Entry`. The check is
|
||||
/// performed using `Entry::validate_rights` method. If the check fails, `Error::Notcapable`
|
||||
/// is returned.
|
||||
pub(crate) fn as_handle(&self, rights: &HandleRights) -> Result<EntryHandle> {
|
||||
pub(crate) fn as_handle(&self, rights: HandleRights) -> Result<EntryHandle> {
|
||||
self.validate_rights(rights)?;
|
||||
Ok(self.handle.get())
|
||||
}
|
||||
@@ -87,7 +87,7 @@ impl Entry {
|
||||
/// rights attached to this `Entry` object are a superset.
|
||||
///
|
||||
/// Upon unsuccessful check, `Error::Notcapable` is returned.
|
||||
pub(crate) fn validate_rights(&self, rights: &HandleRights) -> Result<()> {
|
||||
pub(crate) fn validate_rights(&self, rights: HandleRights) -> Result<()> {
|
||||
let this_rights = self.handle.get_rights();
|
||||
if this_rights.contains(rights) {
|
||||
Ok(())
|
||||
|
||||
@@ -39,8 +39,8 @@ impl HandleRights {
|
||||
}
|
||||
|
||||
/// Checks if `other` is a subset of those rights.
|
||||
pub fn contains(&self, other: &Self) -> bool {
|
||||
self.base.contains(&other.base) && self.inheriting.contains(&other.inheriting)
|
||||
pub fn contains(&self, other: Self) -> bool {
|
||||
self.base.contains(other.base) && self.inheriting.contains(other.inheriting)
|
||||
}
|
||||
|
||||
/// Returns base rights.
|
||||
@@ -100,7 +100,7 @@ pub trait Handle {
|
||||
let file_type = self.get_file_type();
|
||||
let rights = self.get_rights();
|
||||
let required_rights = HandleRights::from_base(Rights::FD_SEEK | Rights::FD_TELL);
|
||||
file_type == Filetype::CharacterDevice && rights.contains(&required_rights)
|
||||
file_type == Filetype::CharacterDevice && rights.contains(required_rights)
|
||||
}
|
||||
// TODO perhaps should be a separate trait?
|
||||
// FdOps
|
||||
|
||||
@@ -11,7 +11,7 @@ pub(crate) use crate::sys::path::{from_host, open_rights};
|
||||
/// This is a workaround for not having Capsicum support in the OS.
|
||||
pub(crate) fn get(
|
||||
entry: &Entry,
|
||||
required_rights: &HandleRights,
|
||||
required_rights: HandleRights,
|
||||
dirflags: Lookupflags,
|
||||
path: &str,
|
||||
needs_final_component: bool,
|
||||
@@ -140,7 +140,7 @@ pub(crate) fn get(
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if ends_with_slash || dirflags.contains(&Lookupflags::SYMLINK_FOLLOW)
|
||||
} else if ends_with_slash || dirflags.contains(Lookupflags::SYMLINK_FOLLOW)
|
||||
{
|
||||
// if there's a trailing slash, or if `LOOKUP_SYMLINK_FOLLOW` is set, attempt
|
||||
// symlink expansion
|
||||
|
||||
@@ -60,7 +60,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let required_rights = HandleRights::from_base(types::Rights::FD_ADVISE);
|
||||
let entry = self.get_entry(fd)?;
|
||||
entry
|
||||
.as_handle(&required_rights)?
|
||||
.as_handle(required_rights)?
|
||||
.advise(advice, offset, len)
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
) -> Result<()> {
|
||||
let required_rights = HandleRights::from_base(types::Rights::FD_ALLOCATE);
|
||||
let entry = self.get_entry(fd)?;
|
||||
entry.as_handle(&required_rights)?.allocate(offset, len)
|
||||
entry.as_handle(required_rights)?.allocate(offset, len)
|
||||
}
|
||||
|
||||
fn fd_close(&self, fd: types::Fd) -> Result<()> {
|
||||
@@ -89,12 +89,12 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
fn fd_datasync(&self, fd: types::Fd) -> Result<()> {
|
||||
let required_rights = HandleRights::from_base(types::Rights::FD_DATASYNC);
|
||||
let entry = self.get_entry(fd)?;
|
||||
entry.as_handle(&required_rights)?.datasync()
|
||||
entry.as_handle(required_rights)?.datasync()
|
||||
}
|
||||
|
||||
fn fd_fdstat_get(&self, fd: types::Fd) -> Result<types::Fdstat> {
|
||||
let entry = self.get_entry(fd)?;
|
||||
let file = entry.as_handle(&HandleRights::empty())?;
|
||||
let file = entry.as_handle(HandleRights::empty())?;
|
||||
let fs_flags = file.fdstat_get()?;
|
||||
let rights = entry.get_rights();
|
||||
let fdstat = types::Fdstat {
|
||||
@@ -109,7 +109,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
fn fd_fdstat_set_flags(&self, fd: types::Fd, flags: types::Fdflags) -> Result<()> {
|
||||
let required_rights = HandleRights::from_base(types::Rights::FD_FDSTAT_SET_FLAGS);
|
||||
let entry = self.get_entry(fd)?;
|
||||
entry.as_handle(&required_rights)?.fdstat_set_flags(flags)
|
||||
entry.as_handle(required_rights)?.fdstat_set_flags(flags)
|
||||
}
|
||||
|
||||
fn fd_fdstat_set_rights(
|
||||
@@ -120,7 +120,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
) -> Result<()> {
|
||||
let rights = HandleRights::new(fs_rights_base, fs_rights_inheriting);
|
||||
let entry = self.get_entry(fd)?;
|
||||
if !entry.get_rights().contains(&rights) {
|
||||
if !entry.get_rights().contains(rights) {
|
||||
return Err(Error::Notcapable);
|
||||
}
|
||||
entry.set_rights(rights);
|
||||
@@ -130,14 +130,14 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
fn fd_filestat_get(&self, fd: types::Fd) -> Result<types::Filestat> {
|
||||
let required_rights = HandleRights::from_base(types::Rights::FD_FILESTAT_GET);
|
||||
let entry = self.get_entry(fd)?;
|
||||
let host_filestat = entry.as_handle(&required_rights)?.filestat_get()?;
|
||||
let host_filestat = entry.as_handle(required_rights)?.filestat_get()?;
|
||||
Ok(host_filestat)
|
||||
}
|
||||
|
||||
fn fd_filestat_set_size(&self, fd: types::Fd, size: types::Filesize) -> Result<()> {
|
||||
let required_rights = HandleRights::from_base(types::Rights::FD_FILESTAT_SET_SIZE);
|
||||
let entry = self.get_entry(fd)?;
|
||||
entry.as_handle(&required_rights)?.filestat_set_size(size)
|
||||
entry.as_handle(required_rights)?.filestat_set_size(size)
|
||||
}
|
||||
|
||||
fn fd_filestat_set_times(
|
||||
@@ -150,7 +150,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let required_rights = HandleRights::from_base(types::Rights::FD_FILESTAT_SET_TIMES);
|
||||
let entry = self.get_entry(fd)?;
|
||||
entry
|
||||
.as_handle(&required_rights)?
|
||||
.as_handle(required_rights)?
|
||||
.filestat_set_times(atim, mtim, fst_flags)
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
.map(|s| io::IoSliceMut::new(&mut *s))
|
||||
.collect::<Vec<io::IoSliceMut<'_>>>();
|
||||
entry
|
||||
.as_handle(&required_rights)?
|
||||
.as_handle(required_rights)?
|
||||
.preadv(&mut buf, offset)?
|
||||
.try_into()?
|
||||
};
|
||||
@@ -255,7 +255,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let buf: Vec<io::IoSlice> =
|
||||
guest_slices.iter().map(|s| io::IoSlice::new(&*s)).collect();
|
||||
entry
|
||||
.as_handle(&required_rights)?
|
||||
.as_handle(required_rights)?
|
||||
.pwritev(&buf, offset)?
|
||||
.try_into()?
|
||||
};
|
||||
@@ -278,7 +278,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
.map(|s| io::IoSliceMut::new(&mut *s))
|
||||
.collect();
|
||||
entry
|
||||
.as_handle(&required_rights)?
|
||||
.as_handle(required_rights)?
|
||||
.read_vectored(&mut slices)?
|
||||
.try_into()?
|
||||
};
|
||||
@@ -298,7 +298,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
|
||||
let mut bufused = 0;
|
||||
let mut buf = buf.clone();
|
||||
for pair in entry.as_handle(&required_rights)?.readdir(cookie)? {
|
||||
for pair in entry.as_handle(required_rights)?.readdir(cookie)? {
|
||||
let (dirent, name) = pair?;
|
||||
let dirent_raw = dirent.as_bytes()?;
|
||||
let dirent_len: types::Size = dirent_raw.len().try_into()?;
|
||||
@@ -379,21 +379,21 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
types::Whence::End => SeekFrom::End(offset),
|
||||
types::Whence::Set => SeekFrom::Start(offset as u64),
|
||||
};
|
||||
let host_newoffset = entry.as_handle(&required_rights)?.seek(pos)?;
|
||||
let host_newoffset = entry.as_handle(required_rights)?.seek(pos)?;
|
||||
Ok(host_newoffset)
|
||||
}
|
||||
|
||||
fn fd_sync(&self, fd: types::Fd) -> Result<()> {
|
||||
let required_rights = HandleRights::from_base(types::Rights::FD_SYNC);
|
||||
let entry = self.get_entry(fd)?;
|
||||
entry.as_handle(&required_rights)?.sync()
|
||||
entry.as_handle(required_rights)?.sync()
|
||||
}
|
||||
|
||||
fn fd_tell(&self, fd: types::Fd) -> Result<types::Filesize> {
|
||||
let required_rights = HandleRights::from_base(types::Rights::FD_TELL);
|
||||
let entry = self.get_entry(fd)?;
|
||||
let host_offset = entry
|
||||
.as_handle(&required_rights)?
|
||||
.as_handle(required_rights)?
|
||||
.seek(SeekFrom::Current(0))?;
|
||||
Ok(host_offset)
|
||||
}
|
||||
@@ -411,7 +411,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let slices: Vec<io::IoSlice> =
|
||||
guest_slices.iter().map(|s| io::IoSlice::new(&*s)).collect();
|
||||
entry
|
||||
.as_handle(&required_rights)?
|
||||
.as_handle(required_rights)?
|
||||
.write_vectored(&slices)?
|
||||
.try_into()?
|
||||
};
|
||||
@@ -426,7 +426,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let path = path.as_str()?;
|
||||
let (dirfd, path) = path::get(
|
||||
&entry,
|
||||
&required_rights,
|
||||
required_rights,
|
||||
types::Lookupflags::empty(),
|
||||
path.deref(),
|
||||
false,
|
||||
@@ -443,9 +443,9 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let required_rights = HandleRights::from_base(types::Rights::PATH_FILESTAT_GET);
|
||||
let entry = self.get_entry(dirfd)?;
|
||||
let path = path.as_str()?;
|
||||
let (dirfd, path) = path::get(&entry, &required_rights, flags, path.deref(), false)?;
|
||||
let (dirfd, path) = path::get(&entry, required_rights, flags, path.deref(), false)?;
|
||||
let host_filestat =
|
||||
dirfd.filestat_get_at(&path, flags.contains(&types::Lookupflags::SYMLINK_FOLLOW))?;
|
||||
dirfd.filestat_get_at(&path, flags.contains(types::Lookupflags::SYMLINK_FOLLOW))?;
|
||||
Ok(host_filestat)
|
||||
}
|
||||
|
||||
@@ -461,13 +461,13 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let required_rights = HandleRights::from_base(types::Rights::PATH_FILESTAT_SET_TIMES);
|
||||
let entry = self.get_entry(dirfd)?;
|
||||
let path = path.as_str()?;
|
||||
let (dirfd, path) = path::get(&entry, &required_rights, flags, path.deref(), false)?;
|
||||
let (dirfd, path) = path::get(&entry, required_rights, flags, path.deref(), false)?;
|
||||
dirfd.filestat_set_times_at(
|
||||
&path,
|
||||
atim,
|
||||
mtim,
|
||||
fst_flags,
|
||||
flags.contains(&types::Lookupflags::SYMLINK_FOLLOW),
|
||||
flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -487,7 +487,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let old_path = old_path.as_str()?;
|
||||
path::get(
|
||||
&old_entry,
|
||||
&required_rights,
|
||||
required_rights,
|
||||
types::Lookupflags::empty(),
|
||||
old_path.deref(),
|
||||
false,
|
||||
@@ -500,7 +500,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let new_path = new_path.as_str()?;
|
||||
path::get(
|
||||
&new_entry,
|
||||
&required_rights,
|
||||
required_rights,
|
||||
types::Lookupflags::empty(),
|
||||
new_path.deref(),
|
||||
false,
|
||||
@@ -510,7 +510,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
&old_path,
|
||||
new_dirfd,
|
||||
&new_path,
|
||||
old_flags.contains(&types::Lookupflags::SYMLINK_FOLLOW),
|
||||
old_flags.contains(types::Lookupflags::SYMLINK_FOLLOW),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let path = path.as_str()?;
|
||||
path::get(
|
||||
&entry,
|
||||
&needed_rights,
|
||||
needed_rights,
|
||||
dirflags,
|
||||
path.deref(),
|
||||
oflags & types::Oflags::CREAT != types::Oflags::empty(),
|
||||
@@ -581,7 +581,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let path = path.as_str()?;
|
||||
path::get(
|
||||
&entry,
|
||||
&required_rights,
|
||||
required_rights,
|
||||
types::Lookupflags::empty(),
|
||||
path.deref(),
|
||||
false,
|
||||
@@ -599,7 +599,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let path = path.as_str()?;
|
||||
path::get(
|
||||
&entry,
|
||||
&required_rights,
|
||||
required_rights,
|
||||
types::Lookupflags::empty(),
|
||||
path.deref(),
|
||||
true,
|
||||
@@ -621,7 +621,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let old_path = old_path.as_str()?;
|
||||
path::get(
|
||||
&entry,
|
||||
&required_rights,
|
||||
required_rights,
|
||||
types::Lookupflags::empty(),
|
||||
old_path.deref(),
|
||||
true,
|
||||
@@ -633,7 +633,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let new_path = new_path.as_str()?;
|
||||
path::get(
|
||||
&entry,
|
||||
&required_rights,
|
||||
required_rights,
|
||||
types::Lookupflags::empty(),
|
||||
new_path.deref(),
|
||||
true,
|
||||
@@ -654,7 +654,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let new_path = new_path.as_str()?;
|
||||
path::get(
|
||||
&entry,
|
||||
&required_rights,
|
||||
required_rights,
|
||||
types::Lookupflags::empty(),
|
||||
new_path.deref(),
|
||||
true,
|
||||
@@ -672,7 +672,7 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
|
||||
let path = path.as_str()?;
|
||||
path::get(
|
||||
&entry,
|
||||
&required_rights,
|
||||
required_rights,
|
||||
types::Lookupflags::empty(),
|
||||
path.deref(),
|
||||
false,
|
||||
@@ -815,7 +815,7 @@ impl WasiCtx {
|
||||
}
|
||||
};
|
||||
fd_events.push(sched::FdEventData {
|
||||
handle: entry.as_handle(&required_rights)?,
|
||||
handle: entry.as_handle(required_rights)?,
|
||||
r#type: types::Eventtype::FdRead,
|
||||
userdata: subscription.userdata,
|
||||
});
|
||||
@@ -841,7 +841,7 @@ impl WasiCtx {
|
||||
}
|
||||
};
|
||||
fd_events.push(sched::FdEventData {
|
||||
handle: entry.as_handle(&required_rights)?,
|
||||
handle: entry.as_handle(required_rights)?,
|
||||
r#type: types::Eventtype::FdWrite,
|
||||
userdata: subscription.userdata,
|
||||
});
|
||||
|
||||
@@ -12,10 +12,10 @@ pub(crate) fn filestat_set_times(
|
||||
st_mtim: Timestamp,
|
||||
fst_flags: Fstflags,
|
||||
) -> Result<()> {
|
||||
let set_atim = fst_flags.contains(&Fstflags::ATIM);
|
||||
let set_atim_now = fst_flags.contains(&Fstflags::ATIM_NOW);
|
||||
let set_mtim = fst_flags.contains(&Fstflags::MTIM);
|
||||
let set_mtim_now = fst_flags.contains(&Fstflags::MTIM_NOW);
|
||||
let set_atim = fst_flags.contains(Fstflags::ATIM);
|
||||
let set_atim_now = fst_flags.contains(Fstflags::ATIM_NOW);
|
||||
let set_mtim = fst_flags.contains(Fstflags::MTIM);
|
||||
let set_mtim_now = fst_flags.contains(Fstflags::MTIM_NOW);
|
||||
|
||||
if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) {
|
||||
return Err(Error::Inval);
|
||||
|
||||
@@ -147,19 +147,19 @@ impl From<Clockid> for ClockId {
|
||||
impl From<Fdflags> for OFlags {
|
||||
fn from(fdflags: Fdflags) -> Self {
|
||||
let mut nix_flags = Self::empty();
|
||||
if fdflags.contains(&Fdflags::APPEND) {
|
||||
if fdflags.contains(Fdflags::APPEND) {
|
||||
nix_flags.insert(Self::APPEND);
|
||||
}
|
||||
if fdflags.contains(&Fdflags::DSYNC) {
|
||||
if fdflags.contains(Fdflags::DSYNC) {
|
||||
nix_flags.insert(Self::DSYNC);
|
||||
}
|
||||
if fdflags.contains(&Fdflags::NONBLOCK) {
|
||||
if fdflags.contains(Fdflags::NONBLOCK) {
|
||||
nix_flags.insert(Self::NONBLOCK);
|
||||
}
|
||||
if fdflags.contains(&Fdflags::RSYNC) {
|
||||
if fdflags.contains(Fdflags::RSYNC) {
|
||||
nix_flags.insert(O_RSYNC);
|
||||
}
|
||||
if fdflags.contains(&Fdflags::SYNC) {
|
||||
if fdflags.contains(Fdflags::SYNC) {
|
||||
nix_flags.insert(Self::SYNC);
|
||||
}
|
||||
nix_flags
|
||||
@@ -191,16 +191,16 @@ impl From<OFlags> for Fdflags {
|
||||
impl From<Oflags> for OFlags {
|
||||
fn from(oflags: Oflags) -> Self {
|
||||
let mut nix_flags = Self::empty();
|
||||
if oflags.contains(&Oflags::CREAT) {
|
||||
if oflags.contains(Oflags::CREAT) {
|
||||
nix_flags.insert(Self::CREAT);
|
||||
}
|
||||
if oflags.contains(&Oflags::DIRECTORY) {
|
||||
if oflags.contains(Oflags::DIRECTORY) {
|
||||
nix_flags.insert(Self::DIRECTORY);
|
||||
}
|
||||
if oflags.contains(&Oflags::EXCL) {
|
||||
if oflags.contains(Oflags::EXCL) {
|
||||
nix_flags.insert(Self::EXCL);
|
||||
}
|
||||
if oflags.contains(&Oflags::TRUNC) {
|
||||
if oflags.contains(Oflags::TRUNC) {
|
||||
nix_flags.insert(Self::TRUNC);
|
||||
}
|
||||
nix_flags
|
||||
|
||||
@@ -239,10 +239,10 @@ pub(crate) fn filestat_set_times_at(
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
use yanix::filetime::*;
|
||||
|
||||
let set_atim = fst_flags.contains(&Fstflags::ATIM);
|
||||
let set_atim_now = fst_flags.contains(&Fstflags::ATIM_NOW);
|
||||
let set_mtim = fst_flags.contains(&Fstflags::MTIM);
|
||||
let set_mtim_now = fst_flags.contains(&Fstflags::MTIM_NOW);
|
||||
let set_atim = fst_flags.contains(Fstflags::ATIM);
|
||||
let set_atim_now = fst_flags.contains(Fstflags::ATIM_NOW);
|
||||
let set_mtim = fst_flags.contains(Fstflags::MTIM);
|
||||
let set_mtim_now = fst_flags.contains(Fstflags::MTIM_NOW);
|
||||
|
||||
if (set_atim && set_atim_now) || (set_mtim && set_mtim_now) {
|
||||
return Err(Error::Inval);
|
||||
|
||||
@@ -299,7 +299,7 @@ impl Handle for InMemoryFile {
|
||||
trace!("write_vectored(iovs={:?})", iovs);
|
||||
let mut data = self.data.borrow_mut();
|
||||
|
||||
let append_mode = self.fd_flags.get().contains(&Fdflags::APPEND);
|
||||
let append_mode = self.fd_flags.get().contains(Fdflags::APPEND);
|
||||
trace!(" | fd_flags={}", self.fd_flags.get());
|
||||
|
||||
// If this file is in append mode, we write to the end.
|
||||
@@ -353,7 +353,7 @@ impl Handle for InMemoryFile {
|
||||
oflags: Oflags,
|
||||
_fd_flags: Fdflags,
|
||||
) -> Result<Box<dyn Handle>> {
|
||||
if oflags.contains(&Oflags::DIRECTORY) {
|
||||
if oflags.contains(Oflags::DIRECTORY) {
|
||||
tracing::trace!(
|
||||
"InMemoryFile::openat was passed oflags DIRECTORY, but {:?} is a file.",
|
||||
path
|
||||
@@ -652,7 +652,7 @@ impl Handle for VirtualDir {
|
||||
return Err(Error::Exist);
|
||||
}
|
||||
|
||||
if oflags.contains(&Oflags::DIRECTORY)
|
||||
if oflags.contains(Oflags::DIRECTORY)
|
||||
&& e.get().get_file_type() != Filetype::Directory
|
||||
{
|
||||
tracing::trace!(
|
||||
@@ -665,7 +665,7 @@ impl Handle for VirtualDir {
|
||||
e.get().try_clone().map_err(Into::into)
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
if oflags.contains(&Oflags::CREAT) {
|
||||
if oflags.contains(Oflags::CREAT) {
|
||||
if self.writable {
|
||||
// Enforce a hard limit at `u32::MAX - 2` files.
|
||||
// This is to have a constant limit (rather than target-dependent limit we
|
||||
|
||||
@@ -6,7 +6,7 @@ description = "Wasmtime implementation of the wasi-nn API"
|
||||
documentation = "https://docs.rs/wasmtime-wasi-nn"
|
||||
license = "Apache-2.0 WITH LLVM-exception"
|
||||
categories = ["wasm", "computer-vision"]
|
||||
keywords = ["webassembly", "wasm", "neural network"]
|
||||
keywords = ["webassembly", "wasm", "neural-network"]
|
||||
repository = "https://github.com/bytecodealliance/wasmtime"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
@@ -26,7 +26,7 @@ rustc-demangle = "0.1.16"
|
||||
cpp_demangle = "0.3.2"
|
||||
log = "0.4.8"
|
||||
wat = { version = "1.0.18", optional = true }
|
||||
smallvec = "1.4.0"
|
||||
smallvec = "1.6.1"
|
||||
serde = { version = "1.0.94", features = ["derive"] }
|
||||
bincode = "1.2.1"
|
||||
indexmap = "1.6"
|
||||
|
||||
@@ -15,6 +15,7 @@ thiserror = "1"
|
||||
witx = { path = "../wasi-common/WASI/tools/witx", version = "0.8.7", optional = true }
|
||||
wiggle-macro = { path = "macro", version = "0.22.0" }
|
||||
tracing = "0.1.15"
|
||||
bitflags = "1.2"
|
||||
|
||||
[badges]
|
||||
maintenance = { status = "actively-developed" }
|
||||
|
||||
@@ -169,7 +169,7 @@ pub fn define_func(
|
||||
let func_name = &func.name.as_str();
|
||||
|
||||
if func.noreturn {
|
||||
quote!(pub fn #ident(#abi_args) -> Result<#abi_ret, wiggle::Trap> {
|
||||
quote!(pub fn #ident(#abi_args) -> Result<#abi_ret, #rt::Trap> {
|
||||
let _span = #rt::tracing::span!(
|
||||
#rt::tracing::Level::TRACE,
|
||||
"wiggle abi",
|
||||
@@ -184,7 +184,7 @@ pub fn define_func(
|
||||
Err(trap)
|
||||
})
|
||||
} else {
|
||||
quote!(pub fn #ident(#abi_args) -> Result<#abi_ret, wiggle::Trap> {
|
||||
quote!(pub fn #ident(#abi_args) -> Result<#abi_ret, #rt::Trap> {
|
||||
let _span = #rt::tracing::span!(
|
||||
#rt::tracing::Level::TRACE,
|
||||
"wiggle abi",
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn generate(doc: &witx::Document, names: &Names, errs: &ErrorTransform) -> T
|
||||
let abi_typename = names.type_ref(&errtype.abi_type(), anon_lifetime());
|
||||
let user_typename = errtype.typename();
|
||||
let methodname = names.user_error_conversion_method(&errtype);
|
||||
quote!(fn #methodname(&self, e: super::#user_typename) -> Result<#abi_typename, wiggle::Trap>;)
|
||||
quote!(fn #methodname(&self, e: super::#user_typename) -> Result<#abi_typename, #rt::Trap>;)
|
||||
});
|
||||
let user_error_conversion = quote! {
|
||||
pub trait UserErrorConversion {
|
||||
|
||||
@@ -69,7 +69,8 @@ pub fn define_module_trait(names: &Names, m: &Module, errxform: &ErrorTransform)
|
||||
.unwrap_or(quote!(()));
|
||||
quote!( Result<(#(#rets),*), #err> )
|
||||
} else {
|
||||
quote!(wiggle::Trap)
|
||||
let rt = names.runtime_mod();
|
||||
quote!(#rt::Trap)
|
||||
};
|
||||
|
||||
if is_anonymous {
|
||||
|
||||
@@ -27,102 +27,31 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty
|
||||
}
|
||||
|
||||
quote! {
|
||||
#[repr(transparent)]
|
||||
#[derive(Copy, Clone, Debug, ::std::hash::Hash, Eq, PartialEq)]
|
||||
pub struct #ident(#repr);
|
||||
|
||||
impl #ident {
|
||||
#(pub const #names_: #ident = #ident(#values_);)*
|
||||
|
||||
#[inline]
|
||||
pub const fn empty() -> Self {
|
||||
#ident(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn all() -> Self {
|
||||
#ident(#(#values_)|*)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, other: &#ident) -> bool {
|
||||
!*self & *other == Self::empty()
|
||||
#rt::bitflags::bitflags! {
|
||||
pub struct #ident: #repr {
|
||||
#(const #names_ = #values_;)*
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for #ident {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
let mut first = true;
|
||||
#(
|
||||
if self.0 & #values_ == #values_ {
|
||||
if !first {
|
||||
f.write_str("|")?;
|
||||
}
|
||||
first = false;
|
||||
f.write_fmt(format_args!("{}", stringify!(#names_).to_lowercase()))?;
|
||||
}
|
||||
)*
|
||||
if first {
|
||||
f.write_str("empty")?;
|
||||
}
|
||||
f.write_fmt(format_args!(" ({:#x})", self.0))?;
|
||||
f.write_str(stringify!(#ident))?;
|
||||
f.write_str("(")?;
|
||||
::std::fmt::Debug::fmt(self, f)?;
|
||||
f.write_str(" (0x")?;
|
||||
::std::fmt::LowerHex::fmt(&self.bits, f)?;
|
||||
f.write_str("))")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitAnd for #ident {
|
||||
type Output = Self;
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
#ident(self.0 & rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitAndAssign for #ident {
|
||||
fn bitand_assign(&mut self, rhs: Self) {
|
||||
*self = *self & rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitOr for #ident {
|
||||
type Output = Self;
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
#ident(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitOrAssign for #ident {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
*self = *self | rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitXor for #ident {
|
||||
type Output = Self;
|
||||
fn bitxor(self, rhs: Self) -> Self::Output {
|
||||
#ident(self.0 ^ rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitXorAssign for #ident {
|
||||
fn bitxor_assign(&mut self, rhs: Self) {
|
||||
*self = *self ^ rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::Not for #ident {
|
||||
type Output = Self;
|
||||
fn not(self) -> Self::Output {
|
||||
#ident(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::convert::TryFrom<#repr> for #ident {
|
||||
type Error = #rt::GuestError;
|
||||
fn try_from(value: #repr) -> Result<Self, #rt::GuestError> {
|
||||
if #repr::from(!#ident::all()) & value != 0 {
|
||||
Err(#rt::GuestError::InvalidFlagValue(stringify!(#ident)))
|
||||
} else {
|
||||
Ok(#ident(value))
|
||||
Ok(#ident { bits: value })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,7 +65,7 @@ pub(super) fn define_flags(names: &Names, name: &witx::Id, f: &witx::FlagsDataty
|
||||
|
||||
impl From<#ident> for #repr {
|
||||
fn from(e: #ident) -> #repr {
|
||||
e.0
|
||||
e.bits
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::slice;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use bitflags;
|
||||
pub use wiggle_macro::from_witx;
|
||||
|
||||
#[cfg(feature = "wiggle_metadata")]
|
||||
|
||||
@@ -103,11 +103,11 @@ proptest! {
|
||||
#[test]
|
||||
fn flags_fmt() {
|
||||
let empty = format!("{}", types::CarConfig::empty());
|
||||
assert_eq!(empty, "empty (0x0)");
|
||||
assert_eq!(empty, "CarConfig((empty) (0x0))");
|
||||
let one_flag = format!("{}", types::CarConfig::AWD);
|
||||
assert_eq!(one_flag, "awd (0x2)");
|
||||
assert_eq!(one_flag, "CarConfig(AWD (0x2))");
|
||||
let two_flags = format!("{}", types::CarConfig::AUTOMATIC | types::CarConfig::SUV);
|
||||
assert_eq!(two_flags, "automatic|suv (0x5)");
|
||||
assert_eq!(two_flags, "CarConfig(AUTOMATIC | SUV (0x5))");
|
||||
let all = format!("{}", types::CarConfig::all());
|
||||
assert_eq!(all, "automatic|awd|suv (0x7)");
|
||||
assert_eq!(all, "CarConfig(AUTOMATIC | AWD | SUV (0x7))");
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ fn run_wast(wast: &str, strategy: Strategy) -> anyhow::Result<()> {
|
||||
|
||||
let multi_memory = wast.iter().any(|s| s == "multi-memory");
|
||||
let module_linking = wast.iter().any(|s| s == "module-linking");
|
||||
let threads = wast.iter().any(|s| s == "threads");
|
||||
let bulk_mem = multi_memory || wast.iter().any(|s| s == "bulk-memory-operations");
|
||||
|
||||
// Some simd tests assume support for multiple tables, which are introduced
|
||||
@@ -26,6 +27,7 @@ fn run_wast(wast: &str, strategy: Strategy) -> anyhow::Result<()> {
|
||||
.wasm_reference_types(reftypes || module_linking)
|
||||
.wasm_multi_memory(multi_memory || module_linking)
|
||||
.wasm_module_linking(module_linking)
|
||||
.wasm_threads(threads)
|
||||
.strategy(strategy)?
|
||||
.cranelift_debug_verifier(true);
|
||||
|
||||
|
||||
52
tests/misc_testsuite/threads/atomics_wait_address.wast
Normal file
52
tests/misc_testsuite/threads/atomics_wait_address.wast
Normal file
@@ -0,0 +1,52 @@
|
||||
;; From https://bugzilla.mozilla.org/show_bug.cgi?id=1684861.
|
||||
;;
|
||||
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(func $main (type 0)
|
||||
i32.const -64
|
||||
i32.const -63
|
||||
memory.atomic.notify offset=1
|
||||
unreachable)
|
||||
(memory (;0;) 4 4)
|
||||
(export "main" (func $main))
|
||||
)
|
||||
|
||||
(assert_trap (invoke "main") "misaligned memory access")
|
||||
|
||||
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(func $main (type 0)
|
||||
i32.const -64
|
||||
i32.const -63
|
||||
memory.atomic.notify offset=65536
|
||||
unreachable)
|
||||
(memory (;0;) 4 4)
|
||||
(export "main" (func $main))
|
||||
)
|
||||
|
||||
(assert_trap (invoke "main") "out of bounds memory access")
|
||||
|
||||
|
||||
(module
|
||||
(type (;0;) (func))
|
||||
(func $wait32 (type 0)
|
||||
i32.const -64
|
||||
i32.const 42
|
||||
i64.const 0
|
||||
memory.atomic.wait32 offset=1
|
||||
unreachable)
|
||||
(func $wait64 (type 0)
|
||||
i32.const -64
|
||||
i64.const 43
|
||||
i64.const 0
|
||||
memory.atomic.wait64 offset=3
|
||||
unreachable)
|
||||
(memory (;0;) 4 4)
|
||||
(export "wait32" (func $wait32))
|
||||
(export "wait64" (func $wait64))
|
||||
)
|
||||
|
||||
(assert_trap (invoke "wait32") "misaligned memory access")
|
||||
(assert_trap (invoke "wait64") "misaligned memory access")
|
||||
Reference in New Issue
Block a user