diff options
Diffstat (limited to 'src/wasm')
-rw-r--r-- | src/wasm/wasm-binary.cpp | 84 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 61 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 101 | ||||
-rw-r--r-- | src/wasm/wasm-type.cpp | 22 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 68 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 18 |
6 files changed, 305 insertions, 49 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 278a9ef44..3b48d9781 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1087,6 +1087,11 @@ int64_t WasmBinaryBuilder::getS64LEB() { Type WasmBinaryBuilder::getType() { int type = getS32LEB(); + // Single value types are negative; signature indices are non-negative + if (type >= 0) { + // TODO: Handle block input types properly + return signatures[type].results; + } switch (type) { // None only used for block signatures. TODO: Separate out? case BinaryConsts::EncodedType::Empty: @@ -1699,7 +1704,7 @@ void WasmBinaryBuilder::processExpressions() { BYN_TRACE("== processExpressions finished\n"); return; } - expressionStack.push_back(curr); + pushExpression(curr); if (curr->type == Type::unreachable) { // Once we see something unreachable, we don't want to add anything else // to the stack, as it could be stacky code that is non-representable in @@ -1759,6 +1764,22 @@ void WasmBinaryBuilder::skipUnreachableCode() { expressionStack = savedStack; return; } + pushExpression(curr); + } +} + +void WasmBinaryBuilder::pushExpression(Expression* curr) { + if (curr->type.isMulti()) { + // Store tuple to local and push individual extracted values + Builder builder(wasm); + Index tuple = builder.addVar(currFunction, curr->type); + expressionStack.push_back(builder.makeLocalSet(tuple, curr)); + const std::vector<Type> types = curr->type.expand(); + for (Index i = 0; i < types.size(); ++i) { + expressionStack.push_back( + builder.makeTupleExtract(builder.makeLocalGet(tuple, curr->type), i)); + } + } else { expressionStack.push_back(curr); } } @@ -1778,6 +1799,7 @@ Expression* WasmBinaryBuilder::popExpression() { } // the stack is not empty, and we would not be going out of the current block auto ret = expressionStack.back(); + assert(!ret->type.isMulti()); expressionStack.pop_back(); return ret; } @@ -1818,6 +1840,35 @@ Expression* WasmBinaryBuilder::popNonVoidExpression() { return block; } +Expression* WasmBinaryBuilder::popTuple(size_t numElems) { + Builder builder(wasm); + std::vector<Expression*> elements; + elements.resize(numElems); + for (size_t i = 0; i < numElems; i++) { + auto* elem = popNonVoidExpression(); + if (elem->type == Type::unreachable) { + // All the previously-popped items cannot be reached, so ignore them. We + // cannot continue popping because there might not be enough items on the + // expression stack after an unreachable expression. Any remaining + // elements can stay unperturbed on the stack and will be explicitly + // dropped by some parent call to pushBlockElements. + return elem; + } + elements[numElems - i - 1] = elem; + } + return Builder(wasm).makeTupleMake(std::move(elements)); +} + +Expression* WasmBinaryBuilder::popTypedExpression(Type type) { + if (type.isSingle()) { + return popNonVoidExpression(); + } else if (type.isMulti()) { + return popTuple(type.size()); + } else { + WASM_UNREACHABLE("Invalid popped type"); + } +} + void WasmBinaryBuilder::validateBinary() { if (hasDataCount && wasm.memory.segments.size() != dataCount) { throwError("Number of segments does not agree with DataCount section"); @@ -2386,8 +2437,8 @@ void WasmBinaryBuilder::pushBlockElements(Block* curr, assert(start <= expressionStack.size()); // The results of this block are the last values pushed to the expressionStack Expression* results = nullptr; - if (type != Type::none) { - results = popNonVoidExpression(); + if (type.isConcrete()) { + results = popTypedExpression(type); } if (expressionStack.size() < start) { throwError("Block requires more values than are available"); @@ -2429,7 +2480,7 @@ void WasmBinaryBuilder::visitBlock(Block* curr) { while (1) { curr->type = getType(); curr->name = getNextLabel(); - breakStack.push_back({curr->name, curr->type != Type::none}); + breakStack.push_back({curr->name, curr->type}); stack.push_back(curr); if (more() && input[pos] == BinaryConsts::Block) { // a recursion @@ -2454,7 +2505,7 @@ void WasmBinaryBuilder::visitBlock(Block* curr) { size_t start = expressionStack.size(); if (last) { // the previous block is our first-position element - expressionStack.push_back(last); + pushExpression(last); } last = curr; processExpressions(); @@ -2478,14 +2529,13 @@ void WasmBinaryBuilder::visitBlock(Block* curr) { Expression* WasmBinaryBuilder::getBlockOrSingleton(Type type, unsigned numPops) { Name label = getNextLabel(); - breakStack.push_back( - {label, type != Type::none && type != Type::unreachable}); + breakStack.push_back({label, type}); auto start = expressionStack.size(); Builder builder(wasm); for (unsigned i = 0; i < numPops; i++) { auto* pop = builder.makePop(Type::exnref); - expressionStack.push_back(pop); + pushExpression(pop); } processExpressions(); @@ -2529,7 +2579,7 @@ void WasmBinaryBuilder::visitLoop(Loop* curr) { startControlFlow(curr); curr->type = getType(); curr->name = getNextLabel(); - breakStack.push_back({curr->name, 0}); + breakStack.push_back({curr->name, Type::none}); // find the expressions in the block, and create the body // a loop may have a list of instructions in wasm, much like // a block, but it only has a label at the top of the loop, @@ -2564,8 +2614,8 @@ WasmBinaryBuilder::getBreakTarget(int32_t offset) { if (index >= breakStack.size()) { throwError("bad breakindex (high)"); } - BYN_TRACE("breaktarget " << breakStack[index].name << " arity " - << breakStack[index].arity << std::endl); + BYN_TRACE("breaktarget " << breakStack[index].name << " type " + << breakStack[index].type << std::endl); auto& ret = breakStack[index]; // if the break is in literally unreachable code, then we will not emit it // anyhow, so do not note that the target has breaks to it @@ -2582,8 +2632,8 @@ void WasmBinaryBuilder::visitBreak(Break* curr, uint8_t code) { if (code == BinaryConsts::BrIf) { curr->condition = popNonVoidExpression(); } - if (target.arity) { - curr->value = popNonVoidExpression(); + if (target.type.isConcrete()) { + curr->value = popTypedExpression(target.type); } curr->finalize(); } @@ -2599,8 +2649,8 @@ void WasmBinaryBuilder::visitSwitch(Switch* curr) { auto defaultTarget = getBreakTarget(getU32LEB()); curr->default_ = defaultTarget.name; BYN_TRACE("default: " << curr->default_ << "\n"); - if (defaultTarget.arity) { - curr->value = popNonVoidExpression(); + if (defaultTarget.type.isConcrete()) { + curr->value = popTypedExpression(defaultTarget.type); } curr->finalize(); } @@ -4464,8 +4514,8 @@ void WasmBinaryBuilder::visitSelect(Select* curr, uint8_t code) { void WasmBinaryBuilder::visitReturn(Return* curr) { BYN_TRACE("zz node: Return\n"); requireFunctionContext("return"); - if (currFunction->sig.results != Type::none) { - curr->value = popNonVoidExpression(); + if (currFunction->sig.results.isConcrete()) { + curr->value = popTypedExpression(currFunction->sig.results); } curr->finalize(); } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 573df9dfa..4354b747a 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -538,19 +538,34 @@ SExpressionWasmBuilder::parseParamOrLocal(Element& s, size_t& localIndex) { name = Name::fromInt(localIndex); } localIndex++; - Type type = stringToType(s[i]->str()); + Type type; + if (s[i]->isStr()) { + type = stringToType(s[i]->str()); + } else { + if (elementStartsWith(s, PARAM)) { + throw ParseException( + "params may not have tuple types", s[i]->line, s[i]->col); + } + auto& tuple = s[i]->list(); + std::vector<Type> types; + for (size_t j = 0; j < s[i]->size(); ++j) { + types.push_back(stringToType(tuple[j]->str())); + } + type = Type(types); + } namedParams.emplace_back(name, type); } return namedParams; } // Parses (result type) element. (e.g. (result i32)) -Type SExpressionWasmBuilder::parseResults(Element& s) { +std::vector<Type> SExpressionWasmBuilder::parseResults(Element& s) { assert(elementStartsWith(s, RESULT)); - if (s.size() != 2) { - throw ParseException("invalid result arity", s.line, s.col); + std::vector<Type> types; + for (size_t i = 1; i < s.size(); i++) { + types.push_back(stringToType(s[i]->str())); } - return stringToType(s[1]->str()); + return types; } // Parses an element that references an entry in the type section. The element @@ -599,8 +614,8 @@ SExpressionWasmBuilder::parseTypeUse(Element& s, while (i < s.size() && elementStartsWith(*s[i], RESULT)) { paramsOrResultsExist = true; - // TODO: make parseResults return a vector - results.push_back(parseResults(*s[i++])); + auto newResults = parseResults(*s[i++]); + results.insert(results.end(), newResults.begin(), newResults.end()); } auto inlineSig = Signature(Type(params), Type(results)); @@ -1635,14 +1650,13 @@ Type SExpressionWasmBuilder::parseOptionalResultType(Element& s, Index& i) { return stringToType(s[i++]->str()); } - Element& params = *s[i]; - IString id = params[0]->str(); - if (id != RESULT) { - return Type::none; + Element& results = *s[i]; + IString id = results[0]->str(); + if (id == RESULT) { + i++; + return Type(parseResults(results)); } - - i++; - return stringToType(params[1]->str()); + return Type::none; } Expression* SExpressionWasmBuilder::makeLoop(Element& s) { @@ -1882,6 +1896,21 @@ Expression* SExpressionWasmBuilder::makeBrOnExn(Element& s) { return ret; } +Expression* SExpressionWasmBuilder::makeTupleMake(Element& s) { + auto ret = allocator.alloc<TupleMake>(); + parseCallOperands(s, 1, s.size(), ret); + ret->finalize(); + return ret; +} + +Expression* SExpressionWasmBuilder::makeTupleExtract(Element& s) { + auto ret = allocator.alloc<TupleExtract>(); + ret->index = atoi(s[1]->str().c_str()); + ret->tuple = parseExpression(s[2]); + ret->finalize(); + return ret; +} + // converts an s-expression string representing binary data into an output // sequence of raw bytes this appends to data, which may already contain // content. @@ -2440,8 +2469,8 @@ void SExpressionWasmBuilder::parseType(Element& s) { auto newParams = parseParamOrLocal(curr); params.insert(params.end(), newParams.begin(), newParams.end()); } else if (elementStartsWith(curr, RESULT)) { - // TODO: Parse multiple results at once - results.push_back(parseResults(curr)); + auto newResults = parseResults(curr); + results.insert(results.end(), newResults.begin(), newResults.end()); } } signatures.emplace_back(Type(params), Type(results)); diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 2e4cd4344..df9ad8620 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -15,13 +15,24 @@ */ #include "wasm-stack.h" +#include "ir/find_all.h" namespace wasm { +void BinaryInstWriter::emitResultType(Type type) { + if (type == Type::unreachable) { + o << binaryType(Type::none); + } else if (type.isMulti()) { + o << U32LEB(parent.getTypeIndex(Signature(Type::none, type))); + } else { + o << binaryType(type); + } +} + void BinaryInstWriter::visitBlock(Block* curr) { breakStack.push_back(curr->name); o << int8_t(BinaryConsts::Block); - o << binaryType(curr->type != Type::unreachable ? curr->type : Type::none); + emitResultType(curr->type); } void BinaryInstWriter::visitIf(If* curr) { @@ -30,7 +41,7 @@ void BinaryInstWriter::visitIf(If* curr) { // instead) breakStack.emplace_back(IMPOSSIBLE_CONTINUE); o << int8_t(BinaryConsts::If); - o << binaryType(curr->type != Type::unreachable ? curr->type : Type::none); + emitResultType(curr->type); } void BinaryInstWriter::emitIfElse(If* curr) { @@ -46,7 +57,7 @@ void BinaryInstWriter::emitIfElse(If* curr) { void BinaryInstWriter::visitLoop(Loop* curr) { breakStack.push_back(curr->name); o << int8_t(BinaryConsts::Loop); - o << binaryType(curr->type != Type::unreachable ? curr->type : Type::none); + emitResultType(curr->type); } void BinaryInstWriter::visitBreak(Break* curr) { @@ -76,13 +87,30 @@ void BinaryInstWriter::visitCallIndirect(CallIndirect* curr) { } void BinaryInstWriter::visitLocalGet(LocalGet* curr) { - o << int8_t(BinaryConsts::LocalGet) - << U32LEB(mappedLocals[std::make_pair(curr->index, 0)]); + size_t numValues = func->getLocalType(curr->index).size(); + for (Index i = 0; i < numValues; ++i) { + o << int8_t(BinaryConsts::LocalGet) + << U32LEB(mappedLocals[std::make_pair(curr->index, i)]); + } } void BinaryInstWriter::visitLocalSet(LocalSet* curr) { - o << int8_t(curr->isTee() ? BinaryConsts::LocalTee : BinaryConsts::LocalSet) - << U32LEB(mappedLocals[std::make_pair(curr->index, 0)]); + size_t numValues = func->getLocalType(curr->index).size(); + for (Index i = numValues - 1; i >= 1; --i) { + o << int8_t(BinaryConsts::LocalSet) + << U32LEB(mappedLocals[std::make_pair(curr->index, i)]); + } + if (!curr->isTee()) { + o << int8_t(BinaryConsts::LocalSet) + << U32LEB(mappedLocals[std::make_pair(curr->index, 0)]); + } else { + o << int8_t(BinaryConsts::LocalTee) + << U32LEB(mappedLocals[std::make_pair(curr->index, 0)]); + for (Index i = 1; i < numValues; ++i) { + o << int8_t(BinaryConsts::LocalGet) + << U32LEB(mappedLocals[std::make_pair(curr->index, i)]); + } + } } void BinaryInstWriter::visitGlobalGet(GlobalGet* curr) { @@ -1596,7 +1624,7 @@ void BinaryInstWriter::visitRefFunc(RefFunc* curr) { void BinaryInstWriter::visitTry(Try* curr) { breakStack.emplace_back(IMPOSSIBLE_CONTINUE); o << int8_t(BinaryConsts::Try); - o << binaryType(curr->type != Type::unreachable ? curr->type : Type::none); + emitResultType(curr->type); } void BinaryInstWriter::emitCatch(Try* curr) { @@ -1629,7 +1657,10 @@ void BinaryInstWriter::visitUnreachable(Unreachable* curr) { } void BinaryInstWriter::visitDrop(Drop* curr) { - o << int8_t(BinaryConsts::Drop); + size_t numValues = curr->value->type.size(); + for (size_t i = 0; i < numValues; i++) { + o << int8_t(BinaryConsts::Drop); + } } void BinaryInstWriter::visitPush(Push* curr) { @@ -1640,6 +1671,30 @@ void BinaryInstWriter::visitPop(Pop* curr) { // Turns into nothing in the binary format } +void BinaryInstWriter::visitTupleMake(TupleMake* curr) { + // Turns into nothing in the binary format +} + +void BinaryInstWriter::visitTupleExtract(TupleExtract* curr) { + size_t numVals = curr->tuple->type.size(); + // Drop all values after the one we want + for (size_t i = curr->index + 1; i < numVals; ++i) { + o << int8_t(BinaryConsts::Drop); + } + // If the extracted value is the only one left, we're done + if (curr->index == 0) { + return; + } + // Otherwise, save it to a scratch local, drop the others, then retrieve it + assert(scratchLocals.find(curr->type) != scratchLocals.end()); + auto scratch = scratchLocals[curr->type]; + o << int8_t(BinaryConsts::LocalSet) << U32LEB(scratch); + for (size_t i = 0; i < curr->index; ++i) { + o << int8_t(BinaryConsts::Drop); + } + o << int8_t(BinaryConsts::LocalGet) << U32LEB(scratch); +} + void BinaryInstWriter::emitScopeEnd(Expression* curr) { assert(!breakStack.empty()); breakStack.pop_back(); @@ -1667,13 +1722,14 @@ void BinaryInstWriter::mapLocalsAndEmitHeader() { numLocalsByType[t]++; } } + countScratchLocals(); std::map<Type, size_t> currLocalsByType; for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) { const std::vector<Type> types = func->getLocalType(i).expand(); for (Index j = 0; j < types.size(); j++) { Type type = types[j]; auto fullIndex = std::make_pair(i, j); - size_t index = func->getVarIndexBase(); + Index index = func->getVarIndexBase(); for (auto& typeCount : numLocalsByType) { if (type == typeCount.first) { mappedLocals[fullIndex] = index + currLocalsByType[typeCount.first]; @@ -1684,12 +1740,37 @@ void BinaryInstWriter::mapLocalsAndEmitHeader() { } } } + setScratchLocals(); o << U32LEB(numLocalsByType.size()); for (auto& typeCount : numLocalsByType) { o << U32LEB(typeCount.second) << binaryType(typeCount.first); } } +void BinaryInstWriter::countScratchLocals() { + // Add a scratch register in `numLocalsByType` for each type of + // tuple.extract with nonzero index present. + FindAll<TupleExtract> extracts(func->body); + for (auto* extract : extracts.list) { + if (extract->type != Type::unreachable && extract->index != 0) { + scratchLocals[extract->type] = 0; + } + } + for (auto t : scratchLocals) { + numLocalsByType[t.first]++; + } +} + +void BinaryInstWriter::setScratchLocals() { + Index index = func->getVarIndexBase(); + for (auto& typeCount : numLocalsByType) { + index += typeCount.second; + if (scratchLocals.find(typeCount.first) != scratchLocals.end()) { + scratchLocals[typeCount.first] = index - 1; + } + } +} + void BinaryInstWriter::emitMemoryAccess(size_t alignment, size_t bytes, uint32_t offset) { diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 3542f5d69..148b55716 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -229,7 +229,7 @@ Type Type::get(unsigned byteSize, bool float_) { WASM_UNREACHABLE("invalid size"); } -bool Type::Type::isSubType(Type left, Type right) { +bool Type::isSubType(Type left, Type right) { if (left == right) { return true; } @@ -240,7 +240,7 @@ bool Type::Type::isSubType(Type left, Type right) { return false; } -Type Type::Type::getLeastUpperBound(Type a, Type b) { +Type Type::getLeastUpperBound(Type a, Type b) { if (a == b) { return a; } @@ -250,8 +250,24 @@ Type Type::Type::getLeastUpperBound(Type a, Type b) { if (b == Type::unreachable) { return a; } + if (a.size() != b.size()) { + return Type::none; // a poison value that must not be consumed + } + if (a.isMulti()) { + std::vector<Type> types; + types.resize(a.size()); + const auto& as = a.expand(); + const auto& bs = b.expand(); + for (size_t i = 0; i < types.size(); ++i) { + types[i] = getLeastUpperBound(as[i], bs[i]); + if (types[i] == Type::none) { + return Type::none; + } + } + return Type(types); + } if (!a.isRef() || !b.isRef()) { - return none; // a poison value that must not be consumed + return Type::none; } if (a == Type::nullref) { return b; diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 484376358..1db8ed7c0 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -319,6 +319,8 @@ public: void visitThrow(Throw* curr); void visitRethrow(Rethrow* curr); void visitBrOnExn(BrOnExn* curr); + void visitTupleMake(TupleMake* curr); + void visitTupleExtract(TupleExtract* curr); void visitFunction(Function* curr); // helpers @@ -386,6 +388,11 @@ void FunctionValidator::noteLabelName(Name name) { } void FunctionValidator::visitBlock(Block* curr) { + if (!getModule()->features.hasMultivalue()) { + shouldBeTrue(!curr->type.isMulti(), + curr, + "Multivalue block type (multivalue is not enabled)"); + } // if we are break'ed to, then the value must be right for us if (curr->name.is()) { noteLabelName(curr->name); @@ -1744,6 +1751,14 @@ void FunctionValidator::visitSelect(Select* curr) { curr->condition->type == Type::i32, curr, "select condition must be valid"); + if (curr->ifTrue->type != Type::unreachable) { + shouldBeTrue( + curr->ifTrue->type.isSingle(), curr, "select value may not be a tuple"); + } + if (curr->ifFalse->type != Type::unreachable) { + shouldBeTrue( + curr->ifFalse->type.isSingle(), curr, "select value may not be a tuple"); + } if (curr->type != Type::unreachable) { shouldBeTrue(Type::isSubType(curr->ifTrue->type, curr->type), curr, @@ -1887,10 +1902,46 @@ void FunctionValidator::visitBrOnExn(BrOnExn* curr) { } } +void FunctionValidator::visitTupleMake(TupleMake* curr) { + std::vector<Type> types; + for (auto* op : curr->operands) { + if (op->type == Type::unreachable) { + shouldBeTrue( + curr->type == Type::unreachable, + curr, + "If tuple.make has an unreachable operand, it must be unreachable"); + return; + } + types.push_back(op->type); + } + shouldBeTrue(Type(types) == curr->type, + curr, + "Type of tuple.make does not match types of its operands"); +} + +void FunctionValidator::visitTupleExtract(TupleExtract* curr) { + if (curr->tuple->type == Type::unreachable) { + shouldBeTrue( + curr->type == Type::unreachable, + curr, + "If tuple.extract has an unreachable operand, it must be unreachable"); + } else { + shouldBeTrue(curr->index < curr->tuple->type.size(), + curr, + "tuple.extract index out of bounds"); + shouldBeTrue( + curr->type == curr->tuple->type.expand()[curr->index], + curr, + "tuple.extract type does not match the type of the extracted element"); + } +} + void FunctionValidator::visitFunction(Function* curr) { - shouldBeTrue(!curr->sig.results.isMulti(), - curr->body, - "Multivalue functions not allowed yet"); + if (curr->sig.results.isMulti()) { + shouldBeTrue(getModule()->features.hasMultivalue(), + curr->body, + "Multivalue function results (multivalue is not enabled)"); + } FeatureSet features; for (auto type : curr->sig.params.expand()) { features |= type.getFeatures(); @@ -2053,6 +2104,12 @@ static void validateBinaryenIR(Module& wasm, ValidationInfo& info) { static void validateImports(Module& module, ValidationInfo& info) { ModuleUtils::iterImportedFunctions(module, [&](Function* curr) { + if (curr->sig.results.isMulti()) { + info.shouldBeTrue(module.features.hasMultivalue(), + curr->name, + "Imported multivalue function " + "(multivalue is not enabled)"); + } if (info.validateWeb) { for (Type param : curr->sig.params.expand()) { info.shouldBeUnequal(param, @@ -2252,6 +2309,11 @@ static void validateEvents(Module& module, ValidationInfo& info) { Type(Type::none), curr->name, "Event type's result type should be none"); + if (curr->sig.params.isMulti()) { + info.shouldBeTrue(module.features.hasMultivalue(), + curr->name, + "Multivalue event type (multivalue is not enabled)"); + } for (auto type : curr->sig.params.expand()) { info.shouldBeTrue(type.isConcrete(), curr->name, diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index f8874da01..64bc5615f 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -188,6 +188,10 @@ const char* getExpressionName(Expression* curr) { return "rethrow"; case Expression::Id::BrOnExnId: return "br_on_exn"; + case Expression::Id::TupleMakeId: + return "tuple.make"; + case Expression::Id::TupleExtractId: + return "tuple.extract"; case Expression::Id::NumExpressionIds: WASM_UNREACHABLE("invalid expr id"); } @@ -898,6 +902,20 @@ void Push::finalize() { } } +void TupleMake::finalize() { + std::vector<Type> types; + for (auto* op : operands) { + if (op->type == Type::unreachable) { + type = Type::unreachable; + return; + } + types.push_back(op->type); + } + type = Type(types); +} + +void TupleExtract::finalize() { type = tuple->type.expand()[index]; } + size_t Function::getNumParams() { return sig.params.size(); } size_t Function::getNumVars() { return vars.size(); } |