diff --git a/src/wasm/wasm-interpreter.cc b/src/wasm/wasm-interpreter.cc index 765ee49fd3..796e6519a0 100644 --- a/src/wasm/wasm-interpreter.cc +++ b/src/wasm/wasm-interpreter.cc @@ -893,6 +893,19 @@ class SideTable : public ZoneObject { stack_height = c->end_label->target_stack_height + kCatchInArity; break; } + case kExprBrOnExn: { + BranchOnExceptionImmediate imm(&i, i.pc()); + uint32_t depth = imm.depth.depth; // Extracted for convenience. + imm.index.exception = &module->exceptions[imm.index.index]; + DCHECK_EQ(0, imm.index.exception->sig->return_count()); + size_t params = imm.index.exception->sig->parameter_count(); + // Taken branches pop the exception and push the encoded values. + uint32_t height = stack_height - 1 + static_cast(params); + TRACE("control @%u: BrOnExn[depth=%u]\n", i.pc_offset(), depth); + Control* c = &control_stack[control_stack.size() - depth - 1]; + if (!unreachable) c->end_label->Ref(i.pc(), height); + break; + } case kExprEnd: { Control* c = &control_stack.back(); TRACE("control @%u: End\n", i.pc_offset()); @@ -1253,10 +1266,7 @@ class ThreadImpl { InterpreterCode* code = frame.code; if (code->side_table->HasEntryAt(frame.pc)) { TRACE("----- HANDLE -----\n"); - // TODO(mstarzinger): Push a reference to the pending exception instead - // of a bogus {int32_t(0)} value here once the interpreter supports it. - USE(isolate->pending_exception()); - Push(WasmValue(int32_t{0})); + Push(WasmValue(handle(isolate->pending_exception(), isolate))); isolate->clear_pending_exception(); frame.pc += JumpToHandlerDelta(code, frame.pc); TRACE(" => handler #%zu (#%u @%zu)\n", frames_.size() - 1, @@ -1291,6 +1301,8 @@ class ThreadImpl { CodeMap* codemap_; Handle instance_object_; + // TODO(mstarzinger): The operand stack will need to be changed so that the + // value lifetime of {WasmValue} is not coupled to a {HandleScope}. std::unique_ptr stack_; WasmValue* stack_limit_ = nullptr; // End of allocated stack space. WasmValue* sp_ = nullptr; // Current stack pointer. @@ -1349,6 +1361,13 @@ class ThreadImpl { break; WASM_CTYPES(CASE_TYPE) #undef CASE_TYPE + case kWasmAnyRef: + case kWasmAnyFunc: + case kWasmExceptRef: { + Isolate* isolate = instance_object_->GetIsolate(); + val = WasmValue(isolate->factory()->null_value()); + break; + } default: UNREACHABLE(); break; @@ -2410,14 +2429,90 @@ class ThreadImpl { // Throw a given existing exception. Returns true if the exception is being // handled locally by the interpreter, false otherwise (interpreter exits). - bool DoRethrowException(WasmValue* exception) { + bool DoRethrowException(WasmValue exception) { Isolate* isolate = instance_object_->GetIsolate(); - // TODO(mstarzinger): Use the passed {exception} here once reference types - // as values on the operand stack are supported by the interpreter. - isolate->ReThrow(*isolate->factory()->undefined_value()); + isolate->ReThrow(*exception.to_anyref()); return HandleException(isolate) == WasmInterpreter::Thread::HANDLED; } + // Determines whether the given exception has a tag matching the expected tag + // for the given index within the exception table of the current instance. + bool MatchingExceptionTag(Handle exception_object, uint32_t index) { + Isolate* isolate = instance_object_->GetIsolate(); + Handle caught_tag = + WasmExceptionPackage::GetExceptionTag(isolate, exception_object); + Handle expected_tag = + handle(instance_object_->exceptions_table()->get(index), isolate); + DCHECK(expected_tag->IsWasmExceptionTag()); + return expected_tag.is_identical_to(caught_tag); + } + + void DecodeI32ExceptionValue(Handle encoded_values, + uint32_t* encoded_index, uint32_t* value) { + uint32_t msb = Smi::cast(encoded_values->get((*encoded_index)++)).value(); + uint32_t lsb = Smi::cast(encoded_values->get((*encoded_index)++)).value(); + *value = (msb << 16) | (lsb & 0xffff); + } + + void DecodeI64ExceptionValue(Handle encoded_values, + uint32_t* encoded_index, uint64_t* value) { + uint32_t lsb = 0, msb = 0; + DecodeI32ExceptionValue(encoded_values, encoded_index, &msb); + DecodeI32ExceptionValue(encoded_values, encoded_index, &lsb); + *value = (static_cast(msb) << 32) | static_cast(lsb); + } + + // Unpack the values encoded in the given exception. The exception values are + // pushed onto the operand stack. Callers must perform a tag check to ensure + // the encoded values match the expected signature of the exception. + void DoUnpackException(const WasmException* exception, + Handle exception_object) { + Isolate* isolate = instance_object_->GetIsolate(); + Handle encoded_values = Handle::cast( + WasmExceptionPackage::GetExceptionValues(isolate, exception_object)); + // Decode the exception values from the given exception package and push + // them onto the operand stack. This encoding has to be in sync with other + // backends so that exceptions can be passed between them. + const WasmExceptionSig* sig = exception->sig; + uint32_t encoded_index = 0; + for (size_t i = 0; i < sig->parameter_count(); ++i) { + WasmValue value; + switch (sig->GetParam(i)) { + case kWasmI32: { + uint32_t u32 = 0; + DecodeI32ExceptionValue(encoded_values, &encoded_index, &u32); + value = WasmValue(u32); + break; + } + case kWasmF32: { + uint32_t f32_bits = 0; + DecodeI32ExceptionValue(encoded_values, &encoded_index, &f32_bits); + value = WasmValue(Float32::FromBits(f32_bits)); + break; + } + case kWasmI64: { + uint64_t u64 = 0; + DecodeI64ExceptionValue(encoded_values, &encoded_index, &u64); + value = WasmValue(u64); + break; + } + case kWasmF64: { + uint64_t f64_bits = 0; + DecodeI64ExceptionValue(encoded_values, &encoded_index, &f64_bits); + value = WasmValue(Float64::FromBits(f64_bits)); + break; + } + case kWasmAnyRef: + UNIMPLEMENTED(); + break; + default: + UNREACHABLE(); + } + Push(value); + } + DCHECK_EQ(WasmExceptionPackage::GetEncodedSize(exception), encoded_index); + } + void Execute(InterpreterCode* code, pc_t pc, int max) { DCHECK_NOT_NULL(code->side_table); DCHECK(!frames_.empty()); @@ -2531,10 +2626,27 @@ class ThreadImpl { case kExprRethrow: { WasmValue ex = Pop(); CommitPc(pc); // Needed for local unwinding. - if (!DoRethrowException(&ex)) return; + if (!DoRethrowException(ex)) return; ReloadFromFrameOnException(&decoder, &code, &pc, &limit); continue; // Do not bump pc. } + case kExprBrOnExn: { + BranchOnExceptionImmediate imm(&decoder, + code->at(pc)); + WasmValue ex = Pop(); + Handle exception = ex.to_anyref(); + if (MatchingExceptionTag(exception, imm.index.index)) { + imm.index.exception = &module()->exceptions[imm.index.index]; + DoUnpackException(imm.index.exception, exception); + len = DoBreak(code, pc, imm.depth.depth); + TRACE(" match => @%zu\n", pc + len); + } else { + Push(ex); // Exception remains on stack. + TRACE(" false => fallthrough\n"); + len = 1 + imm.length; + } + break; + } case kExprSelect: { WasmValue cond = Pop(); WasmValue fval = Pop(); @@ -2614,6 +2726,11 @@ class ThreadImpl { len = 1 + imm.length; break; } + case kExprRefNull: { + Isolate* isolate = instance_object_->GetIsolate(); + Push(WasmValue(isolate->factory()->null_value())); + break; + } case kExprGetLocal: { LocalIndexImmediate imm(&decoder, code->at(pc)); Push(GetStackValue(frames_.back().sp + imm.index)); @@ -2968,6 +3085,11 @@ class ThreadImpl { SIGN_EXTENSION_CASE(I64SExtendI16, int64_t, int16_t); SIGN_EXTENSION_CASE(I64SExtendI32, int64_t, int32_t); #undef SIGN_EXTENSION_CASE + case kExprRefIsNull: { + uint32_t result = Pop().to_anyref()->IsNull() ? 1 : 0; + Push(WasmValue(result)); + break; + } case kNumericPrefix: { ++len; if (!ExecuteNumericOp(opcode, &decoder, code, pc, len)) return; @@ -3149,6 +3271,15 @@ class ThreadImpl { PrintF("i32x4:%d %d %d %d", s.val[0], s.val[1], s.val[2], s.val[3]); break; } + case kWasmAnyRef: { + Handle ref = val.to_anyref(); + if (ref->IsNull()) { + PrintF("ref:null"); + } else { + PrintF("ref:0x%" V8PRIxPTR, ref->ptr()); + } + break; + } case kWasmStmt: PrintF("void"); break; diff --git a/src/wasm/wasm-value.h b/src/wasm/wasm-value.h index c1538e8523..4201c14ae4 100644 --- a/src/wasm/wasm-value.h +++ b/src/wasm/wasm-value.h @@ -6,6 +6,7 @@ #define V8_WASM_WASM_VALUE_H_ #include "src/boxed-float.h" +#include "src/handles.h" #include "src/v8memory.h" #include "src/wasm/wasm-opcodes.h" #include "src/zone/zone-containers.h" @@ -62,7 +63,10 @@ class Simd128 { V(f32_boxed, kWasmF32, Float32) \ V(f64, kWasmF64, double) \ V(f64_boxed, kWasmF64, Float64) \ - V(s128, kWasmS128, Simd128) + V(s128, kWasmS128, Simd128) \ + V(anyref, kWasmAnyRef, Handle) + +ASSERT_TRIVIALLY_COPYABLE(Handle); // A wasm value with type information. class WasmValue { diff --git a/test/cctest/wasm/test-run-wasm-interpreter.cc b/test/cctest/wasm/test-run-wasm-interpreter.cc index 20471ef4ee..b90c2cc3f2 100644 --- a/test/cctest/wasm/test-run-wasm-interpreter.cc +++ b/test/cctest/wasm/test-run-wasm-interpreter.cc @@ -430,6 +430,21 @@ TEST(MemoryGrowInvalidSize) { CHECK_EQ(-1, r.Call(1048575)); } +TEST(ReferenceTypeLocals) { + { + WasmRunner r(ExecutionTier::kInterpreter); + BUILD(r, WASM_REF_IS_NULL(WASM_REF_NULL)); + CHECK_EQ(1, r.Call()); + } + { + WasmRunner r(ExecutionTier::kInterpreter); + r.AllocateLocal(kWasmAnyRef); + BUILD(r, WASM_REF_IS_NULL(WASM_GET_LOCAL(0))); + CHECK_EQ(1, r.Call()); + } + // TODO(mstarzinger): Test and support global anyref variables. +} + TEST(TestPossibleNondeterminism) { { WasmRunner r(ExecutionTier::kInterpreter); diff --git a/test/common/wasm/wasm-macro-gen.h b/test/common/wasm/wasm-macro-gen.h index c45b907bc2..cdeb3d3cdf 100644 --- a/test/common/wasm/wasm-macro-gen.h +++ b/test/common/wasm/wasm-macro-gen.h @@ -346,6 +346,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { static_cast(bit_cast(static_cast(val)) >> 56) #define WASM_REF_NULL kExprRefNull +#define WASM_REF_IS_NULL(val) val, kExprRefIsNull #define WASM_GET_LOCAL(index) kExprGetLocal, static_cast(index) #define WASM_SET_LOCAL(index, val) val, kExprSetLocal, static_cast(index)