Commit 491a94b0 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Implement 'let' opcode.

Changes:
- Implement the 'let' opcode, as per
https://github.com/WebAssembly/function-references/blob/master/proposals/function-references/Overview.md#local-bindings
- Use a WasmDecoder in place of a plain decoder in OpcodeLength and
AnalyzeLoopAssignment.
- Change ControlBase to accept an additional 'locals_count' parameter.
- Implement required test infrastructure and write some simple tests.

Bug: v8:7748
Change-Id: I39d60d1f0c26016c8f89c009dc5f4119b0c73c87
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2204107
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67937}
parent cf7731e2
...@@ -1617,6 +1617,16 @@ class LiftoffCompiler { ...@@ -1617,6 +1617,16 @@ class LiftoffCompiler {
LocalSet(imm.index, true); LocalSet(imm.index, true);
} }
void AllocateLocals(FullDecoder* decoder, Vector<Value> local_values) {
// TODO(7748): Introduce typed functions bailout reason
unsupported(decoder, kGC, "let");
}
void DeallocateLocals(FullDecoder* decoder, uint32_t count) {
// TODO(7748): Introduce typed functions bailout reason
unsupported(decoder, kGC, "let");
}
Register GetGlobalBaseAndOffset(const WasmGlobal* global, Register GetGlobalBaseAndOffset(const WasmGlobal* global,
LiftoffRegList* pinned, uint32_t* offset) { LiftoffRegList* pinned, uint32_t* offset) {
Register addr = pinned->set(__ GetUnusedRegister(kGpReg, {})).gp(); Register addr = pinned->set(__ GetUnusedRegister(kGpReg, {})).gp();
......
...@@ -729,7 +729,7 @@ struct Merge { ...@@ -729,7 +729,7 @@ struct Merge {
// Reachability::kReachable. // Reachability::kReachable.
bool reached; bool reached;
Merge(bool reached = false) : reached(reached) {} explicit Merge(bool reached = false) : reached(reached) {}
Value& operator[](uint32_t i) { Value& operator[](uint32_t i) {
DCHECK_GT(arity, i); DCHECK_GT(arity, i);
...@@ -742,6 +742,7 @@ enum ControlKind : uint8_t { ...@@ -742,6 +742,7 @@ enum ControlKind : uint8_t {
kControlIfElse, kControlIfElse,
kControlBlock, kControlBlock,
kControlLoop, kControlLoop,
kControlLet,
kControlTry, kControlTry,
kControlTryCatch kControlTryCatch
}; };
...@@ -759,6 +760,7 @@ enum Reachability : uint8_t { ...@@ -759,6 +760,7 @@ enum Reachability : uint8_t {
template <typename Value> template <typename Value>
struct ControlBase { struct ControlBase {
ControlKind kind = kControlBlock; ControlKind kind = kControlBlock;
uint32_t locals_count = 0;
uint32_t stack_depth = 0; // stack height at the beginning of the construct. uint32_t stack_depth = 0; // stack height at the beginning of the construct.
const uint8_t* pc = nullptr; const uint8_t* pc = nullptr;
Reachability reachability = kReachable; Reachability reachability = kReachable;
...@@ -769,13 +771,16 @@ struct ControlBase { ...@@ -769,13 +771,16 @@ struct ControlBase {
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(ControlBase); MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(ControlBase);
ControlBase(ControlKind kind, uint32_t stack_depth, const uint8_t* pc, ControlBase(ControlKind kind, uint32_t locals_count, uint32_t stack_depth,
Reachability reachability) const uint8_t* pc, Reachability reachability)
: kind(kind), : kind(kind),
locals_count(locals_count),
stack_depth(stack_depth), stack_depth(stack_depth),
pc(pc), pc(pc),
reachability(reachability), reachability(reachability),
start_merge(reachability == kReachable) {} start_merge(reachability == kReachable) {
DCHECK(kind == kControlLet || locals_count == 0);
}
// Check whether the current block is reachable. // Check whether the current block is reachable.
bool reachable() const { return reachability == kReachable; } bool reachable() const { return reachability == kReachable; }
...@@ -795,6 +800,7 @@ struct ControlBase { ...@@ -795,6 +800,7 @@ struct ControlBase {
bool is_onearmed_if() const { return kind == kControlIf; } bool is_onearmed_if() const { return kind == kControlIf; }
bool is_if_else() const { return kind == kControlIfElse; } bool is_if_else() const { return kind == kControlIfElse; }
bool is_block() const { return kind == kControlBlock; } bool is_block() const { return kind == kControlBlock; }
bool is_let() const { return kind == kControlLet; }
bool is_loop() const { return kind == kControlLoop; } bool is_loop() const { return kind == kControlLoop; }
bool is_incomplete_try() const { return kind == kControlTry; } bool is_incomplete_try() const { return kind == kControlTry; }
bool is_try_catch() const { return kind == kControlTryCatch; } bool is_try_catch() const { return kind == kControlTryCatch; }
...@@ -841,6 +847,8 @@ struct ControlBase { ...@@ -841,6 +847,8 @@ struct ControlBase {
F(LocalSet, const Value& value, const LocalIndexImmediate<validate>& imm) \ F(LocalSet, const Value& value, const LocalIndexImmediate<validate>& imm) \
F(LocalTee, const Value& value, Value* result, \ F(LocalTee, const Value& value, Value* result, \
const LocalIndexImmediate<validate>& imm) \ const LocalIndexImmediate<validate>& imm) \
F(AllocateLocals, Vector<Value> local_values) \
F(DeallocateLocals, uint32_t count) \
F(GlobalGet, Value* result, const GlobalIndexImmediate<validate>& imm) \ F(GlobalGet, Value* result, const GlobalIndexImmediate<validate>& imm) \
F(GlobalSet, const Value& value, const GlobalIndexImmediate<validate>& imm) \ F(GlobalSet, const Value& value, const GlobalIndexImmediate<validate>& imm) \
F(TableGet, const Value& index, Value* result, \ F(TableGet, const Value& index, Value* result, \
...@@ -1010,6 +1018,7 @@ class WasmDecoder : public Decoder { ...@@ -1010,6 +1018,7 @@ class WasmDecoder : public Decoder {
} }
*total_length += length; *total_length += length;
if (insert_position.has_value()) { if (insert_position.has_value()) {
// Move the insertion iterator to the end of the newly inserted locals.
insert_iterator = insert_iterator =
local_types_->insert(insert_iterator, count, type) + count; local_types_->insert(insert_iterator, count, type) + count;
} }
...@@ -1018,7 +1027,7 @@ class WasmDecoder : public Decoder { ...@@ -1018,7 +1027,7 @@ class WasmDecoder : public Decoder {
return true; return true;
} }
static BitVector* AnalyzeLoopAssignment(Decoder* decoder, const byte* pc, static BitVector* AnalyzeLoopAssignment(WasmDecoder* decoder, const byte* pc,
uint32_t locals_count, Zone* zone) { uint32_t locals_count, Zone* zone) {
if (pc >= decoder->end()) return nullptr; if (pc >= decoder->end()) return nullptr;
if (*pc != kExprLoop) return nullptr; if (*pc != kExprLoop) return nullptr;
...@@ -1385,7 +1394,7 @@ class WasmDecoder : public Decoder { ...@@ -1385,7 +1394,7 @@ class WasmDecoder : public Decoder {
return true; return true;
} }
static uint32_t OpcodeLength(Decoder* decoder, const byte* pc) { static uint32_t OpcodeLength(WasmDecoder* decoder, const byte* pc) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc); WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
switch (opcode) { switch (opcode) {
#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: #define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
...@@ -1430,6 +1439,15 @@ class WasmDecoder : public Decoder { ...@@ -1430,6 +1439,15 @@ class WasmDecoder : public Decoder {
return 1 + imm.length; return 1 + imm.length;
} }
case kExprLet: {
BlockTypeImmediate<validate> imm(WasmFeatures::All(), decoder, pc);
uint32_t locals_length;
bool locals_result =
decoder->DecodeLocals(decoder->pc() + 1 + imm.length,
&locals_length, base::Optional<uint32_t>());
return 1 + imm.length + (locals_result ? locals_length : 0);
}
case kExprThrow: { case kExprThrow: {
ExceptionIndexImmediate<validate> imm(decoder, pc); ExceptionIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length; return 1 + imm.length;
...@@ -1719,6 +1737,9 @@ class WasmDecoder : public Decoder { ...@@ -1719,6 +1737,9 @@ class WasmDecoder : public Decoder {
case kExprReturnCallIndirect: case kExprReturnCallIndirect:
case kExprUnreachable: case kExprUnreachable:
return {0, 0}; return {0, 0};
case kExprLet:
// TODO(7748): Implement
return {0, 0};
case kNumericPrefix: case kNumericPrefix:
case kAtomicPrefix: case kAtomicPrefix:
case kSimdPrefix: { case kSimdPrefix: {
...@@ -2117,6 +2138,33 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2117,6 +2138,33 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
break; break;
} }
case kExprLet: {
CHECK_PROTOTYPE_OPCODE(typed_funcref);
BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
if (!this->Validate(imm)) break;
uint32_t current_local_count =
static_cast<uint32_t>(local_type_vec_.size());
// Temporarily add the let-defined values
// to the beginning of the function locals.
uint32_t locals_length;
if (!this->DecodeLocals(this->pc() + 1 + imm.length, &locals_length,
0)) {
break;
}
len = 1 + imm.length + locals_length;
uint32_t locals_count = static_cast<uint32_t>(local_type_vec_.size() -
current_local_count);
ArgVector let_local_values =
PopArgs(VectorOf(local_type_vec_.data(), locals_count));
ArgVector args = PopArgs(imm.sig);
Control* let_block = PushControl(kControlLet, locals_count);
SetBlockType(let_block, imm, args.begin());
CALL_INTERFACE_IF_REACHABLE(Block, let_block);
PushMergeValues(let_block, &let_block->start_merge);
CALL_INTERFACE_IF_REACHABLE(AllocateLocals,
VectorOf(let_local_values));
break;
}
case kExprLoop: { case kExprLoop: {
BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_); BlockTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
if (!this->Validate(imm)) break; if (!this->Validate(imm)) break;
...@@ -2182,6 +2230,12 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2182,6 +2230,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
if (!TypeCheckOneArmedIf(c)) break; if (!TypeCheckOneArmedIf(c)) break;
} }
if (c->is_let()) {
this->local_types_->erase(
this->local_types_->begin(),
this->local_types_->begin() + c->locals_count);
CALL_INTERFACE_IF_REACHABLE(DeallocateLocals, c->locals_count);
}
if (!TypeCheckFallThru()) break; if (!TypeCheckFallThru()) break;
if (control_.size() == 1) { if (control_.size() == 1) {
...@@ -2716,6 +2770,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2716,6 +2770,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break; break;
case kControlIfElse: case kControlIfElse:
case kControlTryCatch: case kControlTryCatch:
case kControlLet: // TODO(7748): Implement
break; break;
} }
if (c.start_merge.arity) TRACE_PART("%u-", c.start_merge.arity); if (c.start_merge.arity) TRACE_PART("%u-", c.start_merge.arity);
...@@ -2818,15 +2873,24 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2818,15 +2873,24 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return args; return args;
} }
V8_INLINE ArgVector PopArgs(Vector<ValueType> arg_types) {
ArgVector args(arg_types.size());
for (int i = static_cast<int>(arg_types.size()) - 1; i >= 0; i--) {
args[i] = Pop(i, arg_types[i]);
}
return args;
}
ValueType GetReturnType(const FunctionSig* sig) { ValueType GetReturnType(const FunctionSig* sig) {
DCHECK_GE(1, sig->return_count()); DCHECK_GE(1, sig->return_count());
return sig->return_count() == 0 ? kWasmStmt : sig->GetReturn(); return sig->return_count() == 0 ? kWasmStmt : sig->GetReturn();
} }
Control* PushControl(ControlKind kind) { Control* PushControl(ControlKind kind, uint32_t locals_count = 0) {
Reachability reachability = Reachability reachability =
control_.empty() ? kReachable : control_.back().innerReachability(); control_.empty() ? kReachable : control_.back().innerReachability();
control_.emplace_back(kind, stack_size(), this->pc_, reachability); control_.emplace_back(kind, locals_count, stack_size(), this->pc_,
reachability);
return &control_.back(); return &control_.back();
} }
......
...@@ -64,7 +64,9 @@ DecodeResult VerifyWasmCode(AccountingAllocator* allocator, ...@@ -64,7 +64,9 @@ DecodeResult VerifyWasmCode(AccountingAllocator* allocator,
} }
unsigned OpcodeLength(const byte* pc, const byte* end) { unsigned OpcodeLength(const byte* pc, const byte* end) {
Decoder decoder(pc, end); WasmFeatures no_features = WasmFeatures::None();
WasmDecoder<Decoder::kNoValidate> decoder(nullptr, no_features, &no_features,
nullptr, pc, end, 0);
return WasmDecoder<Decoder::kNoValidate>::OpcodeLength(&decoder, pc); return WasmDecoder<Decoder::kNoValidate>::OpcodeLength(&decoder, pc);
} }
...@@ -293,7 +295,9 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body, ...@@ -293,7 +295,9 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals, BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals,
const byte* start, const byte* end) { const byte* start, const byte* end) {
Decoder decoder(start, end); WasmFeatures no_features = WasmFeatures::None();
WasmDecoder<Decoder::kValidate> decoder(nullptr, no_features, &no_features,
nullptr, start, end, 0);
return WasmDecoder<Decoder::kValidate>::AnalyzeLoopAssignment( return WasmDecoder<Decoder::kValidate>::AnalyzeLoopAssignment(
&decoder, start, static_cast<uint32_t>(num_locals), zone); &decoder, start, static_cast<uint32_t>(num_locals), zone);
} }
......
...@@ -296,6 +296,19 @@ class WasmGraphBuildingInterface { ...@@ -296,6 +296,19 @@ class WasmGraphBuildingInterface {
ssa_env_->locals[imm.index] = value.node; ssa_env_->locals[imm.index] = value.node;
} }
void AllocateLocals(FullDecoder* decoder, Vector<Value> local_values) {
ZoneVector<TFNode*>* locals = &ssa_env_->locals;
locals->insert(locals->begin(), local_values.size(), nullptr);
for (uint32_t i = 0; i < local_values.size(); i++) {
(*locals)[i] = local_values[i].node;
}
}
void DeallocateLocals(FullDecoder* decoder, uint32_t count) {
ZoneVector<TFNode*>* locals = &ssa_env_->locals;
locals->erase(locals->begin(), locals->begin() + count);
}
void GlobalGet(FullDecoder* decoder, Value* result, void GlobalGet(FullDecoder* decoder, Value* result,
const GlobalIndexImmediate<validate>& imm) { const GlobalIndexImmediate<validate>& imm) {
result->node = BUILD(GlobalGet, imm.index); result->node = BUILD(GlobalGet, imm.index);
......
...@@ -367,6 +367,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { ...@@ -367,6 +367,7 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(RefCast, "ref.cast") CASE_OP(RefCast, "ref.cast")
CASE_OP(BrOnCast, "br_on_cast") CASE_OP(BrOnCast, "br_on_cast")
CASE_OP(RefEq, "ref.eq") CASE_OP(RefEq, "ref.eq")
CASE_OP(Let, "let")
case kNumericPrefix: case kNumericPrefix:
......
...@@ -38,7 +38,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&); ...@@ -38,7 +38,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&);
V(BrIf, 0x0d, _) \ V(BrIf, 0x0d, _) \
V(BrTable, 0x0e, _) \ V(BrTable, 0x0e, _) \
V(Return, 0x0f, _) \ V(Return, 0x0f, _) \
V(BrOnNull, 0xd4, _) /* gc prototype */ V(Let, 0x17, _ /* gc prototype */) \
V(BrOnNull, 0xd4, _ /* gc prototype */)
// Constants, locals, globals, and calls. // Constants, locals, globals, and calls.
#define FOREACH_MISC_OPCODE(V) \ #define FOREACH_MISC_OPCODE(V) \
......
...@@ -158,6 +158,8 @@ WASM_EXEC_TEST(BasicStruct) { ...@@ -158,6 +158,8 @@ WASM_EXEC_TEST(BasicStruct) {
n->EmitCode(n_code, sizeof(n_code)); n->EmitCode(n_code, sizeof(n_code));
// Result: 0b1001 // Result: 0b1001
/************************* End of test definitions *************************/
ZoneBuffer buffer(&zone); ZoneBuffer buffer(&zone);
builder->WriteTo(&buffer); builder->WriteTo(&buffer);
...@@ -165,10 +167,11 @@ WASM_EXEC_TEST(BasicStruct) { ...@@ -165,10 +167,11 @@ WASM_EXEC_TEST(BasicStruct) {
HandleScope scope(isolate); HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate); testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "Test"); ErrorThrower thrower(isolate, "Test");
Handle<WasmInstanceObject> instance = MaybeHandle<WasmInstanceObject> maybe_instance =
testing::CompileAndInstantiateForTesting( testing::CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end())) isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
.ToHandleChecked(); if (thrower.error()) FATAL("%s", thrower.error_msg());
Handle<WasmInstanceObject> instance = maybe_instance.ToHandleChecked();
CHECK_EQ(42, testing::CallWasmFunctionForTesting(isolate, instance, &thrower, CHECK_EQ(42, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"f", 0, nullptr)); "f", 0, nullptr));
...@@ -202,6 +205,106 @@ WASM_EXEC_TEST(BasicStruct) { ...@@ -202,6 +205,106 @@ WASM_EXEC_TEST(BasicStruct) {
isolate, instance, &thrower, "n", 0, nullptr)); isolate, instance, &thrower, "n", 0, nullptr));
} }
WASM_EXEC_TEST(LetInstruction) {
// TODO(7748): Implement support in other tiers.
if (execution_tier == ExecutionTier::kLiftoff) return;
if (execution_tier == ExecutionTier::kInterpreter) return;
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(typed_funcref);
EXPERIMENTAL_FLAG_SCOPE(anyref);
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
StructType::Builder type_builder(&zone, 2);
type_builder.AddField(kWasmI32);
type_builder.AddField(kWasmI32);
int32_t type_index = builder->AddStructType(type_builder.Build());
ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)};
FunctionSig sig_q_v(1, 0, kRefTypes);
WasmFunctionBuilder* let_test_1 = builder->AddFunction(sigs.i_v());
let_test_1->builder()->AddExport(CStrVector("let_test_1"), let_test_1);
uint32_t let_local_index = 0;
uint32_t let_field_index = 0;
byte let_code[] = {
WASM_LET_1_I(WASM_REF_TYPE(type_index),
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(52)),
WASM_STRUCT_GET(type_index, let_field_index,
WASM_GET_LOCAL(let_local_index))),
kExprEnd};
let_test_1->EmitCode(let_code, sizeof(let_code));
WasmFunctionBuilder* let_test_2 = builder->AddFunction(sigs.i_v());
let_test_2->builder()->AddExport(CStrVector("let_test_2"), let_test_2);
uint32_t let_2_field_index = 0;
byte let_code_2[] = {
WASM_LET_2_I(kLocalI32, WASM_I32_ADD(WASM_I32V(42), WASM_I32V(-32)),
WASM_REF_TYPE(type_index),
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(52)),
WASM_I32_MUL(WASM_STRUCT_GET(type_index, let_2_field_index,
WASM_GET_LOCAL(1)),
WASM_GET_LOCAL(0))),
kExprEnd};
let_test_2->EmitCode(let_code_2, sizeof(let_code_2));
WasmFunctionBuilder* let_test_locals = builder->AddFunction(sigs.i_i());
let_test_locals->builder()->AddExport(CStrVector("let_test_locals"),
let_test_locals);
let_test_locals->AddLocal(kWasmI32);
byte let_code_locals[] = {
WASM_SET_LOCAL(1, WASM_I32V(100)),
WASM_LET_2_I(
kLocalI32, WASM_I32V(1), kLocalI32, WASM_I32V(10),
WASM_I32_SUB(WASM_I32_ADD(WASM_GET_LOCAL(0), // 1st let-local
WASM_GET_LOCAL(2)), // Parameter
WASM_I32_ADD(WASM_GET_LOCAL(1), // 2nd let-local
WASM_GET_LOCAL(3)))), // Function local
kExprEnd};
// Result: (1 + 1000) - (10 + 100) = 891
let_test_locals->EmitCode(let_code_locals, sizeof(let_code_locals));
WasmFunctionBuilder* let_test_erase = builder->AddFunction(sigs.i_v());
let_test_erase->builder()->AddExport(CStrVector("let_test_erase"),
let_test_erase);
uint32_t let_erase_local_index = let_test_erase->AddLocal(kWasmI32);
byte let_code_erase[] = {WASM_SET_LOCAL(let_erase_local_index, WASM_I32V(0)),
WASM_LET_1_V(kLocalI32, WASM_I32V(1), WASM_NOP),
WASM_GET_LOCAL(let_erase_local_index), kExprEnd};
// The result should be 0 and not 1, as local_get(0) refers to the original
// local.
let_test_erase->EmitCode(let_code_erase, sizeof(let_code_erase));
/************************* End of test definitions *************************/
ZoneBuffer buffer(&zone);
builder->WriteTo(&buffer);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "Test");
MaybeHandle<WasmInstanceObject> maybe_instance =
testing::CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
if (thrower.error()) FATAL("%s", thrower.error_msg());
Handle<WasmInstanceObject> instance = maybe_instance.ToHandleChecked();
CHECK_EQ(42, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"let_test_1", 0, nullptr));
CHECK_EQ(420, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"let_test_2", 0, nullptr));
Handle<Object> let_local_args[] = {handle(Smi::FromInt(1000), isolate)};
CHECK_EQ(891, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"let_test_locals", 1,
let_local_args));
CHECK_EQ(0, testing::CallWasmFunctionForTesting(
isolate, instance, &thrower, "let_test_erase", 0, nullptr));
}
WASM_EXEC_TEST(BasicArray) { WASM_EXEC_TEST(BasicArray) {
// TODO(7748): Implement support in other tiers. // TODO(7748): Implement support in other tiers.
if (execution_tier == ExecutionTier::kLiftoff) return; if (execution_tier == ExecutionTier::kLiftoff) return;
......
...@@ -458,6 +458,23 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -458,6 +458,23 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_RETURN_CALL_INDIRECT(sig_index, ...) \ #define WASM_RETURN_CALL_INDIRECT(sig_index, ...) \
__VA_ARGS__, kExprReturnCallIndirect, static_cast<byte>(sig_index), TABLE_ZERO __VA_ARGS__, kExprReturnCallIndirect, static_cast<byte>(sig_index), TABLE_ZERO
#define WASM_REF_TYPE(typeidx) kLocalRef, U32V_1(typeidx)
// shift locals by 1; let (locals[0]: local_type) = value in ...
#define WASM_LET_1_V(local_type, value, ...) \
value, kExprLet, kLocalVoid, U32V_1(1), U32V_1(1), local_type, __VA_ARGS__, \
kExprEnd
#define WASM_LET_1_I(local_type, value, ...) \
value, kExprLet, kLocalI32, U32V_1(1), U32V_1(1), local_type, __VA_ARGS__, \
kExprEnd
// shift locals by 2;
// let (locals[0]: local_type_1) = value_1,
// (locals[1]: local_type_2) = value_2
// in ...
#define WASM_LET_2_I(local_type_1, value_1, local_type_2, value_2, ...) \
value_1, value_2, kExprLet, kLocalI32, U32V_1(2), U32V_1(1), local_type_1, \
U32V_1(1), local_type_2, __VA_ARGS__, kExprEnd
#define WASM_NOT(x) x, kExprI32Eqz #define WASM_NOT(x) x, kExprI32Eqz
#define WASM_SEQ(...) __VA_ARGS__ #define WASM_SEQ(...) __VA_ARGS__
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment