diff options
-rwxr-xr-x | scripts/gen-s-parser.py | 5 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 3 | ||||
-rw-r--r-- | src/ir/ReFinalize.cpp | 1 | ||||
-rw-r--r-- | src/ir/cost.h | 1 | ||||
-rw-r--r-- | src/ir/effects.h | 5 | ||||
-rw-r--r-- | src/passes/Print.cpp | 4 | ||||
-rw-r--r-- | src/passes/RemoveUnusedModuleElements.cpp | 19 | ||||
-rw-r--r-- | src/shell-interface.h | 16 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 4 | ||||
-rw-r--r-- | src/wasm-binary.h | 3 | ||||
-rw-r--r-- | src/wasm-builder.h | 8 | ||||
-rw-r--r-- | src/wasm-delegations-fields.def | 7 | ||||
-rw-r--r-- | src/wasm-delegations.def | 1 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 69 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 1 | ||||
-rw-r--r-- | src/wasm.h | 12 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 19 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 10 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 5 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 15 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 7 | ||||
-rw-r--r-- | src/wasm2js.h | 4 | ||||
-rw-r--r-- | test/binaryen.js/exception-handling.js.txt | 8 | ||||
-rw-r--r-- | test/binaryen.js/kitchen-sink.js.txt | 42 | ||||
-rw-r--r-- | test/lit/table-operations.wast | 108 | ||||
-rw-r--r-- | test/spec/table_get.wast | 79 |
26 files changed, 404 insertions, 52 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index cc9ffc7c3..0c71644aa 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -516,10 +516,12 @@ instructions = [ ("f64x2.promote_low_f32x4", "makeUnary(s, UnaryOp::PromoteLowVecF32x4ToVecF64x2)"), # reference types instructions - # TODO Add table instructions ("ref.null", "makeRefNull(s)"), ("ref.is_null", "makeRefIs(s, RefIsNull)"), ("ref.func", "makeRefFunc(s)"), + ("ref.eq", "makeRefEq(s)"), + # TODO Add table instructions + ("table.get", "makeTableGet(s)"), # exception handling instructions ("try", "makeTry(s)"), ("throw", "makeThrow(s)"), @@ -532,7 +534,6 @@ instructions = [ ("call_ref", "makeCallRef(s, /*isReturn=*/false)"), ("return_call_ref", "makeCallRef(s, /*isReturn=*/true)"), # GC - ("ref.eq", "makeRefEq(s)"), ("i31.new", "makeI31New(s)"), ("i31.get_s", "makeI31Get(s, true)"), ("i31.get_u", "makeI31Get(s, false)"), diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 69ce0dbbc..984ebf6b2 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -3041,6 +3041,9 @@ switch (op[0]) { } case 't': { switch (op[1]) { + case 'a': + if (strcmp(op, "table.get") == 0) { return makeTableGet(s); } + goto parse_error; case 'h': { switch (op[2]) { case 'e': diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 0d089a7ce..4f9c7c849 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -135,6 +135,7 @@ void ReFinalize::visitRefFunc(RefFunc* curr) { // this class has been meant to do. } void ReFinalize::visitRefEq(RefEq* curr) { curr->finalize(); } +void ReFinalize::visitTableGet(TableGet* curr) { curr->finalize(); } void ReFinalize::visitTry(Try* curr) { curr->finalize(); } void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); } void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); } diff --git a/src/ir/cost.h b/src/ir/cost.h index 29fcb8dd4..12797b0b5 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -540,6 +540,7 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, CostType> { CostType visitRefEq(RefEq* curr) { return 1 + visit(curr->left) + visit(curr->right); } + CostType visitTableGet(TableGet* curr) { return 1 + visit(curr->index); } CostType visitTry(Try* curr) { // We assume no exception will be thrown in most cases return visit(curr->body); diff --git a/src/ir/effects.h b/src/ir/effects.h index ee45d8205..22e1e8149 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -589,6 +589,11 @@ private: void visitRefIs(RefIs* curr) {} void visitRefFunc(RefFunc* curr) {} void visitRefEq(RefEq* curr) {} + void visitTableGet(TableGet* curr) { + // TODO: track readsTable/writesTable, like memory? + // Traps when the index is out of bounds for the table. + parent.implicitTrap = true; + } void visitTry(Try* curr) {} void visitThrow(Throw* curr) { if (parent.tryDepth == 0) { diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 6a41961a2..4d6207b01 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -1842,6 +1842,10 @@ struct PrintExpressionContents printName(curr->func, o); } void visitRefEq(RefEq* curr) { printMedium(o, "ref.eq"); } + void visitTableGet(TableGet* curr) { + printMedium(o, "table.get "); + printName(curr->table, o); + } void visitTry(Try* curr) { printMedium(o, "try"); if (curr->name.is()) { diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 2b460a0fd..7436676ef 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -92,18 +92,18 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { } } + // Add a reference to a table and all its segments and elements. + void maybeAddTable(Name name) { + maybeAdd(ModuleElement(ModuleElementKind::Table, name)); + ModuleUtils::iterTableSegments(*module, name, [&](ElementSegment* segment) { + maybeAdd(ModuleElement(ModuleElementKind::ElementSegment, segment->name)); + }); + } + void visitCall(Call* curr) { maybeAdd(ModuleElement(ModuleElementKind::Function, curr->target)); } - void visitCallIndirect(CallIndirect* curr) { - assert(!module->tables.empty() && "call-indirect to undefined table."); - maybeAdd(ModuleElement(ModuleElementKind::Table, curr->table)); - ModuleUtils::iterTableSegments( - *module, curr->table, [&](ElementSegment* segment) { - maybeAdd( - ModuleElement(ModuleElementKind::ElementSegment, segment->name)); - }); - } + void visitCallIndirect(CallIndirect* curr) { maybeAddTable(curr->table); } void visitGlobalGet(GlobalGet* curr) { maybeAdd(ModuleElement(ModuleElementKind::Global, curr->name)); @@ -128,6 +128,7 @@ struct ReachabilityAnalyzer : public PostWalker<ReachabilityAnalyzer> { void visitRefFunc(RefFunc* curr) { maybeAdd(ModuleElement(ModuleElementKind::Function, curr->func)); } + void visitTableGet(TableGet* curr) { maybeAddTable(curr->table); } void visitThrow(Throw* curr) { maybeAdd(ModuleElement(ModuleElementKind::Tag, curr->tag)); } diff --git a/src/shell-interface.h b/src/shell-interface.h index 96b96cf15..66608c37c 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -163,7 +163,6 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { } auto& table = it->second; - if (index >= table.size()) { trap("callTable overflow"); } @@ -233,6 +232,21 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { } } + Literal tableLoad(Name tableName, Address addr) override { + + auto it = tables.find(tableName); + if (it == tables.end()) { + trap("tableGet on non-existing table"); + } + + auto& table = it->second; + if (addr >= table.size()) { + trap("tableGet overflow"); + } + + return table[addr]; + } + bool growMemory(Address /*oldSize*/, Address newSize) override { // Apply a reasonable limit on memory size, 1GB, to avoid DOS on the // interpreter. diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 04ef10f57..61897dc60 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -343,6 +343,10 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { std::to_string(index)); } + Literal tableGet(Name tableName, Index index) { + throw FailToEvalException("table.get: TODO"); + } + int8_t load8s(Address addr) override { return doLoad<int8_t>(addr); } uint8_t load8u(Address addr) override { return doLoad<uint8_t>(addr); } int16_t load16s(Address addr) override { return doLoad<int16_t>(addr); } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index aea9e56d8..c0225c270 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -474,6 +474,8 @@ enum ASTNodes { GlobalGet = 0x23, GlobalSet = 0x24, + TableGet = 0x25, + I32LoadMem = 0x28, I64LoadMem = 0x29, F32LoadMem = 0x2a, @@ -1659,6 +1661,7 @@ public: void visitRefIs(RefIs* curr, uint8_t code); void visitRefFunc(RefFunc* curr); void visitRefEq(RefEq* curr); + void visitTableGet(TableGet* curr); void visitTryOrTryInBlock(Expression*& out); void visitThrow(Throw* curr); void visitRethrow(Rethrow* curr); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index f992b1e03..d1cb5f16d 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -648,6 +648,14 @@ public: ret->finalize(); return ret; } + TableGet* makeTableGet(Name table, Expression* index, Type type) { + auto* ret = wasm.allocator.alloc<TableGet>(); + ret->table = table; + ret->index = index; + ret->type = type; + ret->finalize(); + return ret; + } private: Try* makeTry(Name name, diff --git a/src/wasm-delegations-fields.def b/src/wasm-delegations-fields.def index f5524d181..19a7df3a2 100644 --- a/src/wasm-delegations-fields.def +++ b/src/wasm-delegations-fields.def @@ -502,6 +502,13 @@ switch (DELEGATE_ID) { DELEGATE_END(RefEq); break; } + case Expression::Id::TableGetId: { + DELEGATE_START(TableGet); + DELEGATE_FIELD_CHILD(TableGet, index); + DELEGATE_FIELD_NAME(TableGet, table); + DELEGATE_END(TableGet); + break; + } case Expression::Id::TryId: { DELEGATE_START(Try); DELEGATE_FIELD_SCOPE_NAME_USE(Try, delegateTarget); diff --git a/src/wasm-delegations.def b/src/wasm-delegations.def index 2579ac621..873eb2902 100644 --- a/src/wasm-delegations.def +++ b/src/wasm-delegations.def @@ -58,6 +58,7 @@ DELEGATE(RefNull); DELEGATE(RefIs); DELEGATE(RefFunc); DELEGATE(RefEq); +DELEGATE(TableGet); DELEGATE(Try); DELEGATE(Throw); DELEGATE(Rethrow); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 4e2d40e08..6ea9f9c9e 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1358,6 +1358,7 @@ public: NOTE_EVAL2(left, right); return Literal(int32_t(left == right)); } + Flow visitTableGet(TableGet* curr) { WASM_UNREACHABLE("unimp"); } Flow visitTry(Try* curr) { WASM_UNREACHABLE("unimp"); } Flow visitThrow(Throw* curr) { NOTE_ENTER("Throw"); @@ -2157,6 +2158,10 @@ public: NOTE_ENTER("CallRef"); return Flow(NONCONSTANT_FLOW); } + Flow visitTableGet(TableGet* curr) { + NOTE_ENTER("TableGet"); + return Flow(NONCONSTANT_FLOW); + } Flow visitLoad(Load* curr) { NOTE_ENTER("Load"); return Flow(NONCONSTANT_FLOW); @@ -2441,6 +2446,9 @@ public: tableStore(Name tableName, Address addr, const Literal& entry) { WASM_UNREACHABLE("unimp"); } + virtual Literal tableLoad(Name tableName, Address addr) { + WASM_UNREACHABLE("unimp"); + } }; SubType* self() { return static_cast<SubType*>(this); } @@ -2528,7 +2536,37 @@ private: std::unordered_set<size_t> droppedSegments; + struct TableInterfaceInfo { + // The external interface in which the table is defined. + ExternalInterface* interface; + // The name the table has in that interface. + Name name; + }; + + TableInterfaceInfo getTableInterfaceInfo(Name name) { + auto* table = wasm.getTable(name); + if (table->imported()) { + auto& importedInstance = linkedInstances.at(table->module); + auto* tableExport = importedInstance->wasm.getExport(table->base); + return TableInterfaceInfo{importedInstance->externalInterface, + tableExport->value}; + } else { + return TableInterfaceInfo{externalInterface, name}; + } + } + void initializeTableContents() { + for (auto& table : wasm.tables) { + if (table->type.isNullable()) { + // Initial with nulls in a nullable table. + auto info = getTableInterfaceInfo(table->name); + auto null = Literal::makeNull(table->type); + for (Address i = 0; i < table->initial; i++) { + info.interface->tableStore(info.name, i, null); + } + } + } + ModuleUtils::iterActiveElementSegments(wasm, [&](ElementSegment* segment) { Function dummyFunc; dummyFunc.type = Signature(Type::none, Type::none); @@ -2546,6 +2584,7 @@ private: extInterface = inst->externalInterface; tableName = inst->wasm.getExport(table->base)->value; } + for (Index i = 0; i < segment->data.size(); ++i) { Flow ret = runner.visit(segment->data[i]); extInterface->tableStore(tableName, offset + i, ret.getSingleValue()); @@ -2686,6 +2725,7 @@ private: } return ret; } + Flow visitCallIndirect(CallIndirect* curr) { NOTE_ENTER("CallIndirect"); LiteralList arguments; @@ -2701,21 +2741,9 @@ private: Index index = target.getSingleValue().geti32(); Type type = curr->isReturn ? scope.function->getResults() : curr->type; - Flow ret; - auto* table = instance.wasm.getTable(curr->table); - if (table->imported()) { - auto inst = instance.linkedInstances.at(table->module); - Export* tableExport = inst->wasm.getExport(table->base); - ret = inst->externalInterface->callTable(tableExport->value, - index, - curr->sig, - arguments, - type, - *instance.self()); - } else { - ret = instance.externalInterface->callTable( - curr->table, index, curr->sig, arguments, type, *instance.self()); - } + auto info = instance.getTableInterfaceInfo(curr->table); + Flow ret = info.interface->callTable( + info.name, index, curr->sig, arguments, type, *instance.self()); // TODO: make this a proper tail call (return first) if (curr->isReturn) { @@ -2755,6 +2783,17 @@ private: return ret; } + Flow visitTableGet(TableGet* curr) { + NOTE_ENTER("TableGet"); + Flow index = this->visit(curr->index); + if (index.breaking()) { + return index; + } + auto info = instance.getTableInterfaceInfo(curr->table); + return info.interface->tableLoad(info.name, + index.getSingleValue().geti32()); + } + Flow visitLocalGet(LocalGet* curr) { NOTE_ENTER("LocalGet"); auto index = curr->index; diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index ef677cbf7..693b15ea2 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -265,6 +265,7 @@ private: Expression* makeRefIs(Element& s, RefIsOp op); Expression* makeRefFunc(Element& s); Expression* makeRefEq(Element& s); + Expression* makeTableGet(Element& s); Expression* makeTry(Element& s); Expression* makeTryOrCatchBody(Element& s, Type type, bool isTry); Expression* makeThrow(Element& s); diff --git a/src/wasm.h b/src/wasm.h index 9c6062e48..697931082 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -624,6 +624,7 @@ public: RefIsId, RefFuncId, RefEqId, + TableGetId, TryId, ThrowId, RethrowId, @@ -1264,6 +1265,17 @@ public: void finalize(); }; +class TableGet : public SpecificExpression<Expression::TableGetId> { +public: + TableGet(MixedArena& allocator) {} + + Name table; + + Expression* index; + + void finalize(); +}; + class Try : public SpecificExpression<Expression::TryId> { public: Try(MixedArena& allocator) : catchTags(allocator), catchBodies(allocator) {} diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index cd1cfaa0f..a8eea2808 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2761,6 +2761,8 @@ void WasmBinaryBuilder::processNames() { for (auto* ref : refs) { if (auto* callIndirect = ref->dynCast<CallIndirect>()) { callIndirect->table = getTableName(index); + } else if (auto* get = ref->dynCast<TableGet>()) { + get->table = getTableName(index); } else { WASM_UNREACHABLE("Invalid type in table references"); } @@ -3491,6 +3493,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { case BinaryConsts::BrOnNonNull: maybeVisitBrOn(curr, code); break; + case BinaryConsts::TableGet: + visitTableGet((curr = allocator.alloc<TableGet>())->cast<TableGet>()); + break; case BinaryConsts::Try: visitTryOrTryInBlock(curr); break; @@ -4016,6 +4021,7 @@ void WasmBinaryBuilder::visitCallIndirect(CallIndirect* curr) { for (size_t i = 0; i < num; i++) { curr->operands[num - i - 1] = popNonVoidExpression(); } + // Defer setting the table name for later, when we know it. tableRefs[tableIdx].push_back(curr); curr->finalize(); } @@ -6170,6 +6176,19 @@ void WasmBinaryBuilder::visitRefEq(RefEq* curr) { curr->finalize(); } +void WasmBinaryBuilder::visitTableGet(TableGet* curr) { + BYN_TRACE("zz node: TableGet\n"); + Index tableIdx = getU32LEB(); + if (tableIdx >= tables.size()) { + throwError("bad table index"); + } + curr->index = popNonVoidExpression(); + curr->type = tables[tableIdx]->type; + curr->finalize(); + // Defer setting the table name for later, when we know it. + tableRefs[tableIdx].push_back(curr); +} + void WasmBinaryBuilder::visitTryOrTryInBlock(Expression*& out) { BYN_TRACE("zz node: Try\n"); auto* curr = allocator.alloc<Try>(); diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index e9ea6aaa5..573699eae 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -2417,6 +2417,16 @@ Expression* SExpressionWasmBuilder::makeRefEq(Element& s) { return ret; } +Expression* SExpressionWasmBuilder::makeTableGet(Element& s) { + auto tableName = s[1]->str(); + auto* index = parseExpression(s[2]); + auto* table = wasm.getTableOrNull(tableName); + if (!table) { + throw ParseException("invalid table name in table.get", s.line, s.col); + } + return Builder(wasm).makeTableGet(tableName, index, table->type); +} + // try can be either in the form of try-catch or try-delegate. // try-catch is written in the folded wast format as // (try diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 70b52cfbb..58bd29f6e 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1847,6 +1847,11 @@ void BinaryInstWriter::visitRefEq(RefEq* curr) { o << int8_t(BinaryConsts::RefEq); } +void BinaryInstWriter::visitTableGet(TableGet* curr) { + o << int8_t(BinaryConsts::TableGet); + o << U32LEB(parent.getTableIndex(curr->table)); +} + void BinaryInstWriter::visitTry(Try* curr) { breakStack.push_back(curr->name); o << int8_t(BinaryConsts::Try); diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index d03fc2446..fae95980f 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -364,6 +364,7 @@ public: void visitRefIs(RefIs* curr); void visitRefFunc(RefFunc* curr); void visitRefEq(RefEq* curr); + void visitTableGet(TableGet* curr); void noteDelegate(Name name, Expression* curr); void noteRethrow(Name name, Expression* curr); void visitTry(Try* curr); @@ -2031,6 +2032,20 @@ void FunctionValidator::visitRefEq(RefEq* curr) { "ref.eq's right argument should be a subtype of eqref"); } +void FunctionValidator::visitTableGet(TableGet* curr) { + shouldBeTrue(getModule()->features.hasReferenceTypes(), + curr, + "table.get requires reference types to be enabled"); + shouldBeEqualOrFirstIsUnreachable( + curr->index->type, Type(Type::i32), curr, "table.get index must be an i32"); + auto* table = getModule()->getTableOrNull(curr->table); + if (shouldBeTrue(!!table, curr, "table.get table must exist") && + curr->type != Type::unreachable) { + shouldBeEqual( + curr->type, table->type, curr, "table.get must have same type as table."); + } +} + void FunctionValidator::noteDelegate(Name name, Expression* curr) { if (name != DELEGATE_CALLER_TARGET) { shouldBeTrue(delegateTargetNames.count(name) != 0, diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 8565bfaa5..19ba5f265 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -818,6 +818,13 @@ void RefEq::finalize() { } } +void TableGet::finalize() { + if (index->type == Type::unreachable) { + type = Type::unreachable; + } + // Otherwise, the type should have been set already. +} + void Try::finalize() { // If none of the component bodies' type is a supertype of the others, assume // the current type is already correct. TODO: Calculate a proper LUB. diff --git a/src/wasm2js.h b/src/wasm2js.h index 370546134..ef6c9af36 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2167,6 +2167,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitTableGet(TableGet* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } Ref visitTry(Try* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); diff --git a/test/binaryen.js/exception-handling.js.txt b/test/binaryen.js/exception-handling.js.txt index 823b638f5..a1a1879bd 100644 --- a/test/binaryen.js/exception-handling.js.txt +++ b/test/binaryen.js/exception-handling.js.txt @@ -34,7 +34,7 @@ ) ) -getExpressionInfo(throw) = {"id":46,"type":1,"tag":"e"} -getExpressionInfo(rethrow) = {"id":47,"type":1,"target":"l0"} -getExpressionInfo(try_catch) = {"id":45,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0} -getExpressionInfo(try_delegate) = {"id":45,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0} +getExpressionInfo(throw) = {"id":47,"type":1,"tag":"e"} +getExpressionInfo(rethrow) = {"id":48,"type":1,"target":"l0"} +getExpressionInfo(try_catch) = {"id":46,"type":1,"name":"l0","hasCatchAll":0,"delegateTarget":"","isDelegate":0} +getExpressionInfo(try_delegate) = {"id":46,"type":0,"name":"try_outer","hasCatchAll":1,"delegateTarget":"","isDelegate":0} diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 21413d87a..f971e1a07 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -88,27 +88,27 @@ RefNullId: 41 RefIsId: 42 RefFuncId: 43 RefEqId: 44 -TryId: 45 -ThrowId: 46 -RethrowId: 47 -TupleMakeId: 48 -TupleExtractId: 49 -I31NewId: 50 -I31GetId: 51 -CallRefId: 52 -RefTestId: 53 -RefCastId: 54 -BrOnId: 55 -RttCanonId: 56 -RttSubId: 57 -StructNewId: 58 -StructGetId: 59 -StructSetId: 60 -ArrayNewId: 61 -ArrayInitId: 62 -ArrayGetId: 63 -ArraySetId: 64 -ArrayLenId: 65 +TryId: 46 +ThrowId: 47 +RethrowId: 48 +TupleMakeId: 49 +TupleExtractId: 50 +I31NewId: 51 +I31GetId: 52 +CallRefId: 53 +RefTestId: 54 +RefCastId: 55 +BrOnId: 56 +RttCanonId: 57 +RttSubId: 58 +StructNewId: 59 +StructGetId: 60 +StructSetId: 61 +ArrayNewId: 62 +ArrayInitId: 63 +ArrayGetId: 64 +ArraySetId: 65 +ArrayLenId: 66 getExpressionInfo={"id":15,"type":4,"op":6} (f32.neg (f32.const -33.61199951171875) diff --git a/test/lit/table-operations.wast b/test/lit/table-operations.wast new file mode 100644 index 000000000..6e303b495 --- /dev/null +++ b/test/lit/table-operations.wast @@ -0,0 +1,108 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-as %s -all -g -o %t.wasm +;; RUN: wasm-dis %t.wasm -all -o %t.wast +;; RUN: wasm-as %s -all -o %t.nodebug.wasm +;; RUN: wasm-dis %t.nodebug.wasm -all -o %t.nodebug.wast +;; RUN: wasm-opt %t.wast -all -o %t.text.wast -g -S +;; RUN: cat %t.wast | filecheck %s --check-prefix=CHECK-BINARY +;; RUN: cat %t.nodebug.wast | filecheck %s --check-prefix=CHECK-NODEBUG +;; RUN: cat %t.text.wast | filecheck %s --check-prefix=CHECK-TEXT + +(module + ;; CHECK-BINARY: (type $none_=>_none (func)) + + ;; CHECK-BINARY: (table $table-1 1 1 funcref) + ;; CHECK-TEXT: (type $none_=>_none (func)) + + ;; CHECK-TEXT: (table $table-1 1 1 funcref) + (table $table-1 funcref + (elem $foo) + ) + + ;; CHECK-BINARY: (table $table-2 3 3 funcref) + ;; CHECK-TEXT: (table $table-2 3 3 funcref) + (table $table-2 funcref + (elem $bar $bar $bar) + ) + + ;; CHECK-BINARY: (elem $0 (table $table-1) (i32.const 0) func $foo) + + ;; CHECK-BINARY: (elem $1 (table $table-2) (i32.const 0) func $bar $bar $bar) + + ;; CHECK-BINARY: (func $foo + ;; CHECK-BINARY-NEXT: (nop) + ;; CHECK-BINARY-NEXT: ) + ;; CHECK-TEXT: (elem $0 (table $table-1) (i32.const 0) func $foo) + + ;; CHECK-TEXT: (elem $1 (table $table-2) (i32.const 0) func $bar $bar $bar) + + ;; CHECK-TEXT: (func $foo + ;; CHECK-TEXT-NEXT: (nop) + ;; CHECK-TEXT-NEXT: ) + (func $foo + (nop) + ) + ;; CHECK-BINARY: (func $bar + ;; CHECK-BINARY-NEXT: (drop + ;; CHECK-BINARY-NEXT: (table.get $table-1 + ;; CHECK-BINARY-NEXT: (i32.const 0) + ;; CHECK-BINARY-NEXT: ) + ;; CHECK-BINARY-NEXT: ) + ;; CHECK-BINARY-NEXT: (drop + ;; CHECK-BINARY-NEXT: (table.get $table-2 + ;; CHECK-BINARY-NEXT: (i32.const 100) + ;; CHECK-BINARY-NEXT: ) + ;; CHECK-BINARY-NEXT: ) + ;; CHECK-BINARY-NEXT: ) + ;; CHECK-TEXT: (func $bar + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (table.get $table-1 + ;; CHECK-TEXT-NEXT: (i32.const 0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (drop + ;; CHECK-TEXT-NEXT: (table.get $table-2 + ;; CHECK-TEXT-NEXT: (i32.const 100) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + (func $bar + (drop + (table.get $table-1 + (i32.const 0) + ) + ) + (drop + (table.get $table-2 + (i32.const 100) + ) + ) + ) +) +;; CHECK-NODEBUG: (type $none_=>_none (func)) + +;; CHECK-NODEBUG: (table $0 1 1 funcref) + +;; CHECK-NODEBUG: (table $1 3 3 funcref) + +;; CHECK-NODEBUG: (elem $0 (table $0) (i32.const 0) func $0) + +;; CHECK-NODEBUG: (elem $1 (table $1) (i32.const 0) func $1 $1 $1) + +;; CHECK-NODEBUG: (func $0 +;; CHECK-NODEBUG-NEXT: (nop) +;; CHECK-NODEBUG-NEXT: ) + +;; CHECK-NODEBUG: (func $1 +;; CHECK-NODEBUG-NEXT: (drop +;; CHECK-NODEBUG-NEXT: (table.get $0 +;; CHECK-NODEBUG-NEXT: (i32.const 0) +;; CHECK-NODEBUG-NEXT: ) +;; CHECK-NODEBUG-NEXT: ) +;; CHECK-NODEBUG-NEXT: (drop +;; CHECK-NODEBUG-NEXT: (table.get $1 +;; CHECK-NODEBUG-NEXT: (i32.const 100) +;; CHECK-NODEBUG-NEXT: ) +;; CHECK-NODEBUG-NEXT: ) +;; CHECK-NODEBUG-NEXT: ) diff --git a/test/spec/table_get.wast b/test/spec/table_get.wast new file mode 100644 index 000000000..de5f6622a --- /dev/null +++ b/test/spec/table_get.wast @@ -0,0 +1,79 @@ +(module + (table $t2 2 externref) + (table $t3 3 funcref) + (elem (table $t3) (i32.const 1) func $dummy) + (func $dummy) + + (func (export "get-externref") (param $i i32) (result externref) + (table.get $t2 (local.get $i)) + ) + (func $f3 (export "get-funcref") (param $i i32) (result funcref) + (table.get $t3 (local.get $i)) + ) + + (func (export "is_null-funcref") (param $i i32) (result i32) + (ref.is_null (call $f3 (local.get $i))) + ) +) + +(assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern)) + +(assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) +(assert_return (invoke "is_null-funcref" (i32.const 1)) (i32.const 0)) + +(assert_trap (invoke "get-externref" (i32.const 2)) "out of bounds") +(assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds") +(assert_trap (invoke "get-externref" (i32.const -1)) "out of bounds") +(assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds") + + +;; Type errors + +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-empty-vs-i32 (result externref) + (table.get $t) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-index-f32-vs-i32 (result externref) + (table.get $t (f32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t 10 externref) + (func $type-result-externref-vs-empty + (table.get $t (i32.const 0)) + ) + ) + "type mismatch" +) +(assert_invalid + (module + (table $t 10 externref) + (func $type-result-externref-vs-funcref (result funcref) + (table.get $t (i32.const 1)) + ) + ) + "type mismatch" +) + +(assert_invalid + (module + (table $t1 1 funcref) + (table $t2 1 externref) + (func $type-result-externref-vs-funcref-multi (result funcref) + (table.get $t2 (i32.const 0)) + ) + ) + "type mismatch" +) |