diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ast_utils.h | 26 | ||||
-rw-r--r-- | src/passes/Precompute.cpp | 6 | ||||
-rw-r--r-- | src/passes/Print.cpp | 27 | ||||
-rw-r--r-- | src/wasm-builder.h | 13 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 42 | ||||
-rw-r--r-- | src/wasm-s-parser.h | 59 | ||||
-rw-r--r-- | src/wasm-traversal.h | 21 | ||||
-rw-r--r-- | src/wasm-validator.h | 5 | ||||
-rw-r--r-- | src/wasm.h | 66 |
9 files changed, 256 insertions, 9 deletions
diff --git a/src/ast_utils.h b/src/ast_utils.h index 77bfaf1f3..3e45d0e33 100644 --- a/src/ast_utils.h +++ b/src/ast_utils.h @@ -160,6 +160,8 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, Visitor<EffectAnalyzer void visitSetLocal(SetLocal *curr) { localsWritten.insert(curr->index); } + void visitGetGlobal(GetGlobal *curr) { readsMemory = true; } // TODO: global-specific + void visitSetGlobal(SetGlobal *curr) { writesMemory = true; } // stuff? void visitLoad(Load *curr) { readsMemory = true; } void visitStore(Store *curr) { writesMemory = true; } void visitReturn(Return *curr) { branches = true; } @@ -277,6 +279,12 @@ struct ExpressionManipulator { Expression* visitSetLocal(SetLocal *curr) { return builder.makeSetLocal(curr->index, copy(curr->value)); } + Expression* visitGetGlobal(GetGlobal *curr) { + return builder.makeGetGlobal(curr->index, curr->type); + } + Expression* visitSetGlobal(SetGlobal *curr) { + return builder.makeSetGlobal(curr->index, copy(curr->value)); + } Expression* visitLoad(Load *curr) { return builder.makeLoad(curr->bytes, curr->signed_, curr->offset, curr->align, copy(curr->ptr), curr->type); } @@ -476,6 +484,15 @@ struct ExpressionAnalyzer { PUSH(SetLocal, value); break; } + case Expression::Id::GetGlobalId: { + CHECK(GetGlobal, index); + break; + } + case Expression::Id::SetGlobalId: { + CHECK(SetGlobal, index); + PUSH(SetGlobal, value); + break; + } case Expression::Id::LoadId: { CHECK(Load, bytes); CHECK(Load, signed_); @@ -678,6 +695,15 @@ struct ExpressionAnalyzer { PUSH(SetLocal, value); break; } + case Expression::Id::GetGlobalId: { + HASH(GetGlobal, index); + break; + } + case Expression::Id::SetGlobalId: { + HASH(SetGlobal, index); + PUSH(SetGlobal, value); + break; + } case Expression::Id::LoadId: { HASH(Load, bytes); HASH(Load, signed_); diff --git a/src/passes/Precompute.cpp b/src/passes/Precompute.cpp index 4f0a0e22d..a341ba938 100644 --- a/src/passes/Precompute.cpp +++ b/src/passes/Precompute.cpp @@ -63,6 +63,12 @@ public: Flow visitSetLocal(SetLocal *curr) { return Flow(NONSTANDALONE); } + Flow visitGetGlobal(GetGlobal *curr) { + return Flow(NONSTANDALONE); + } + Flow visitSetGlobal(SetGlobal *curr) { + return Flow(NONSTANDALONE); + } Flow visitLoad(Load *curr) { return Flow(NONSTANDALONE); } diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 5fe7f93ac..e967d1850 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -35,6 +35,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { bool fullAST = false; // whether to not elide nodes in output when possible // (like implicit blocks) + Module* currModule = nullptr; Function* currFunction = nullptr; PrintSExpression(std::ostream& o) : o(o) { @@ -78,6 +79,10 @@ struct PrintSExpression : public Visitor<PrintSExpression> { return name; } + Name printableGlobal(Index index) { + return currModule->getGlobal(index)->name; + } + std::ostream& printName(Name name) { // we need to quote names if they have tricky chars if (strpbrk(name.str, "()")) { @@ -239,6 +244,15 @@ struct PrintSExpression : public Visitor<PrintSExpression> { printFullLine(curr->value); decIndent(); } + void visitGetGlobal(GetGlobal *curr) { + printOpening(o, "get_global ") << printableGlobal(curr->index) << ')'; + } + void visitSetGlobal(SetGlobal *curr) { + printOpening(o, "set_global ") << printableGlobal(curr->index); + incIndent(); + printFullLine(curr->value); + decIndent(); + } void visitLoad(Load *curr) { o << '('; prepareColor(o) << printWasmType(curr->type) << ".load"; @@ -519,6 +533,12 @@ struct PrintSExpression : public Visitor<PrintSExpression> { printText(o, curr->name.str) << ' '; printName(curr->value) << ')'; } + void visitGlobal(Global *curr) { + printOpening(o, "global "); + printName(curr->name) << ' ' << printWasmType(curr->type); + printFullLine(curr->init); + o << ')'; + } void visitFunction(Function *curr) { currFunction = curr; printOpening(o, "func ", true); @@ -563,6 +583,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { o << ')'; } void visitModule(Module *curr) { + currModule = curr; printOpening(o, "module", true); incIndent(); doIndent(o, indent); @@ -621,6 +642,11 @@ struct PrintSExpression : public Visitor<PrintSExpression> { visitExport(child.get()); o << maybeNewLine; } + for (auto& child : curr->globals) { + doIndent(o, indent); + visitGlobal(child.get()); + o << maybeNewLine; + } if (curr->table.names.size() > 0) { doIndent(o, indent); visitTable(&curr->table); @@ -633,6 +659,7 @@ struct PrintSExpression : public Visitor<PrintSExpression> { } decIndent(); o << maybeNewLine; + currModule = nullptr; } }; diff --git a/src/wasm-builder.h b/src/wasm-builder.h index e73b707b9..22e1f9a00 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -145,6 +145,19 @@ public: ret->type = value->type; return ret; } + GetGlobal* makeGetGlobal(Index index, WasmType type) { + auto* ret = allocator.alloc<GetGlobal>(); + ret->index = index; + ret->type = type; + return ret; + } + SetGlobal* makeSetGlobal(Index index, Expression* value) { + auto* ret = allocator.alloc<SetGlobal>(); + ret->index = index; + ret->value = value; + ret->type = value->type; + return ret; + } Load* makeLoad(unsigned bytes, bool signed_, uint32_t offset, unsigned align, Expression *ptr, WasmType type) { auto* ret = allocator.alloc<Load>(); ret->bytes = bytes; ret->signed_ = signed_; ret->offset = offset; ret->align = align; ret->ptr = ptr; diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index a8b4b5614..adaee0b6f 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -491,6 +491,22 @@ public: } }; +// Execute an expression in global init +class GlobalInitRunner : public ExpressionRunner<GlobalInitRunner> { +public: + Flow visitLoop(Loop* curr) { WASM_UNREACHABLE(); } + Flow visitCall(Call* curr) { WASM_UNREACHABLE(); } + Flow visitCallImport(CallImport* curr) { WASM_UNREACHABLE(); } + Flow visitCallIndirect(CallIndirect* curr) { WASM_UNREACHABLE(); } + Flow visitGetLocal(GetLocal *curr) { WASM_UNREACHABLE(); } + Flow visitSetLocal(SetLocal *curr) { WASM_UNREACHABLE(); } + Flow visitGetGlobal(GetGlobal *curr) { WASM_UNREACHABLE(); } + Flow visitSetGlobal(SetGlobal *curr) { WASM_UNREACHABLE(); } + Flow visitLoad(Load *curr) { WASM_UNREACHABLE(); } + Flow visitStore(Store *curr) { WASM_UNREACHABLE(); } + Flow visitHost(Host *curr) { WASM_UNREACHABLE(); } +}; + // // An instance of a WebAssembly module, which can execute it via AST interpretation. // @@ -519,8 +535,14 @@ public: Module& wasm; + // Values of globals + std::vector<Literal> globals; + ModuleInstance(Module& wasm, ExternalInterface* externalInterface) : wasm(wasm), externalInterface(externalInterface) { memorySize = wasm.memory.initial; + for (Index i = 0; i < wasm.globals.size(); i++) { + globals.push_back(GlobalInitRunner().visit(wasm.globals[i]->init).value); + } externalInterface->init(wasm); if (wasm.start.is()) { LiteralList arguments; @@ -676,6 +698,26 @@ private: scope.locals[index] = flow.value; return flow; } + + Flow visitGetGlobal(GetGlobal *curr) { + NOTE_ENTER("GetGlobal"); + auto index = curr->index; + NOTE_EVAL1(index); + NOTE_EVAL1(instance.globals[index]); + return instance.globals[index]; + } + Flow visitSetGlobal(SetGlobal *curr) { + NOTE_ENTER("SetGlobal"); + auto index = curr->index; + Flow flow = visit(curr->value); + if (flow.breaking()) return flow; + NOTE_EVAL1(index); + NOTE_EVAL1(flow.value); + assert(flow.value.type == curr->type); + instance.globals[index] = flow.value; + return flow; + } + Flow visitLoad(Load *curr) { NOTE_ENTER("Load"); Flow flow = visit(curr->ptr); diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index c6b3809f1..b93494c76 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -264,11 +264,12 @@ class SExpressionWasmBuilder { std::vector<Name> functionNames; int functionCounter; int importCounter; + int globalCounter; std::map<Name, WasmType> functionTypes; // we need to know function return types before we parse their contents public: // Assumes control of and modifies the input. - SExpressionWasmBuilder(Module& wasm, Element& module) : wasm(wasm), allocator(wasm.allocator), importCounter(0) { + SExpressionWasmBuilder(Module& wasm, Element& module) : wasm(wasm), allocator(wasm.allocator), importCounter(0), globalCounter(0) { assert(module[0]->str() == MODULE); if (module.size() > 1 && module[1]->isStr()) { // these s-expressions contain a binary module, actually @@ -340,6 +341,7 @@ private: if (id == MEMORY) return parseMemory(curr); if (id == EXPORT) return parseExport(curr); if (id == IMPORT) return; // already done + if (id == GLOBAL) return parseGlobal(curr); if (id == TABLE) return parseTable(curr); if (id == TYPE) return; // already done std::cerr << "bad module element " << id.str << '\n'; @@ -703,7 +705,10 @@ public: abort_on(str); } case 'g': { - if (str[1] == 'e') return makeGetLocal(s); + if (str[1] == 'e') { + if (str[4] == 'l') return makeGetLocal(s); + if (str[4] == 'g') return makeGetGlobal(s); + } if (str[1] == 'r') return makeHost(s, HostOp::GrowMemory); abort_on(str); } @@ -728,7 +733,10 @@ public: abort_on(str); } case 's': { - if (str[1] == 'e' && str[2] == 't') return makeSetLocal(s); + if (str[1] == 'e' && str[2] == 't') { + if (str[4] == 'l') return makeSetLocal(s); + if (str[4] == 'g') return makeSetGlobal(s); + } if (str[1] == 'e' && str[2] == 'l') return makeSelect(s); abort_on(str); } @@ -844,6 +852,7 @@ private: } Index getLocalIndex(Element& s) { + if (!currFunction) throw ParseException("local access in non-function scope", s.line, s.col); if (s.dollared()) { auto ret = s.str(); if (currFunction->localIndices.count(ret) == 0) throw ParseException("bad local name", s.line, s.col); @@ -870,6 +879,35 @@ private: return ret; } + Index getGlobalIndex(Element& s) { + if (s.dollared()) { + auto name = s.str(); + for (Index i = 0; i < wasm.globals.size(); i++) { + if (wasm.globals[i]->name == name) return i; + } + throw ParseException("bad global name", s.line, s.col); + } + // this is a numeric index + Index ret = atoi(s.c_str()); + if (!wasm.checkGlobal(ret)) throw ParseException("bad global index", s.line, s.col); + return ret; + } + + Expression* makeGetGlobal(Element& s) { + auto ret = allocator.alloc<GetGlobal>(); + ret->index = getGlobalIndex(*s[1]); + ret->type = wasm.getGlobal(ret->index)->type; + return ret; + } + + Expression* makeSetGlobal(Element& s) { + auto ret = allocator.alloc<SetGlobal>(); + ret->index = getGlobalIndex(*s[1]); + ret->value = parseExpression(s[2]); + ret->type = wasm.getGlobal(ret->index)->type; + return ret; + } + Expression* makeBlock(Element& s) { // special-case Block, because Block nesting (in their first element) can be incredibly deep auto curr = allocator.alloc<Block>(); @@ -1315,6 +1353,21 @@ private: wasm.addImport(im.release()); } + void parseGlobal(Element& s) { + std::unique_ptr<Global> global = make_unique<Global>(); + size_t i = 1; + if (s.size() == 4) { + global->name = s[i++]->str(); + } else { + global->name = Name::fromInt(globalCounter); + } + globalCounter++; + global->type = stringToWasmType(s[i++]->str()); + global->init = parseExpression(s[i++]); + assert(i == s.size()); + wasm.addGlobal(global.release()); + } + void parseTable(Element& s) { for (size_t i = 1; i < s.size(); i++) { wasm.table.names.push_back(getFunctionName(*s[i])); diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index cf023d3fa..b50ca0fb2 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -45,6 +45,8 @@ struct Visitor { ReturnType visitCallIndirect(CallIndirect *curr) {} ReturnType visitGetLocal(GetLocal *curr) {} ReturnType visitSetLocal(SetLocal *curr) {} + ReturnType visitGetGlobal(GetGlobal *curr) {} + ReturnType visitSetGlobal(SetGlobal *curr) {} ReturnType visitLoad(Load *curr) {} ReturnType visitStore(Store *curr) {} ReturnType visitConst(Const *curr) {} @@ -59,6 +61,7 @@ struct Visitor { ReturnType visitFunctionType(FunctionType *curr) {} ReturnType visitImport(Import *curr) {} ReturnType visitExport(Export *curr) {} + ReturnType visitGlobal(Global *curr) {} ReturnType visitFunction(Function *curr) {} ReturnType visitTable(Table *curr) {} ReturnType visitMemory(Memory *curr) {} @@ -82,6 +85,8 @@ struct Visitor { case Expression::Id::CallIndirectId: DELEGATE(CallIndirect); case Expression::Id::GetLocalId: DELEGATE(GetLocal); case Expression::Id::SetLocalId: DELEGATE(SetLocal); + case Expression::Id::GetGlobalId: DELEGATE(GetGlobal); + case Expression::Id::SetGlobalId: DELEGATE(SetGlobal); case Expression::Id::LoadId: DELEGATE(Load); case Expression::Id::StoreId: DELEGATE(Store); case Expression::Id::ConstId: DELEGATE(Const); @@ -119,6 +124,8 @@ struct UnifiedExpressionVisitor : public Visitor<SubType> { ReturnType visitCallIndirect(CallIndirect *curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitGetLocal(GetLocal *curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitSetLocal(SetLocal *curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitGetGlobal(GetGlobal *curr) { return static_cast<SubType*>(this)->visitExpression(curr); } + ReturnType visitSetGlobal(SetGlobal *curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitLoad(Load *curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitStore(Store *curr) { return static_cast<SubType*>(this)->visitExpression(curr); } ReturnType visitConst(Const *curr) { return static_cast<SubType*>(this)->visitExpression(curr); } @@ -192,6 +199,9 @@ struct Walker : public VisitorType { for (auto& curr : module->exports) { self->visitExport(curr.get()); } + for (auto& curr : module->globals) { + self->visitGlobal(curr.get()); + } for (auto& curr : module->functions) { self->walkFunction(curr.get()); } @@ -254,6 +264,8 @@ struct Walker : public VisitorType { static void doVisitCallIndirect(SubType* self, Expression** currp) { self->visitCallIndirect((*currp)->cast<CallIndirect>()); } static void doVisitGetLocal(SubType* self, Expression** currp) { self->visitGetLocal((*currp)->cast<GetLocal>()); } static void doVisitSetLocal(SubType* self, Expression** currp) { self->visitSetLocal((*currp)->cast<SetLocal>()); } + static void doVisitGetGlobal(SubType* self, Expression** currp) { self->visitGetGlobal((*currp)->cast<GetGlobal>()); } + static void doVisitSetGlobal(SubType* self, Expression** currp) { self->visitSetGlobal((*currp)->cast<SetGlobal>()); } static void doVisitLoad(SubType* self, Expression** currp) { self->visitLoad((*currp)->cast<Load>()); } static void doVisitStore(SubType* self, Expression** currp) { self->visitStore((*currp)->cast<Store>()); } static void doVisitConst(SubType* self, Expression** currp) { self->visitConst((*currp)->cast<Const>()); } @@ -357,6 +369,15 @@ struct PostWalker : public Walker<SubType, VisitorType> { self->pushTask(SubType::scan, &curr->cast<SetLocal>()->value); break; } + case Expression::Id::GetGlobalId: { + self->pushTask(SubType::doVisitGetGlobal, currp); + break; + } + case Expression::Id::SetGlobalId: { + self->pushTask(SubType::doVisitSetGlobal, currp); + self->pushTask(SubType::scan, &curr->cast<SetGlobal>()->value); + break; + } case Expression::Id::LoadId: { self->pushTask(SubType::doVisitLoad, currp); self->pushTask(SubType::scan, &curr->cast<Load>()->ptr); diff --git a/src/wasm-validator.h b/src/wasm-validator.h index 066ab57da..90930d8f4 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -260,6 +260,11 @@ public: } } + void visitGlobal(Global* curr) { + shouldBeTrue(curr->init->is<Const>(), curr->name, "global init must be valid"); + shouldBeEqual(curr->type, curr->init->type, curr, "global init must have correct type"); + } + void visitFunction(Function *curr) { // if function has no result, it is ignored // if body is unreachable, it might be e.g. a return diff --git a/src/wasm.h b/src/wasm.h index 2d007be65..f3eee7375 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -869,6 +869,8 @@ public: CallIndirectId, GetLocalId, SetLocalId, + GetGlobalId, + SetGlobalId, LoadId, StoreId, ConstId, @@ -919,6 +921,8 @@ inline const char *getExpressionName(Expression *curr) { case Expression::Id::CallIndirectId: return "call_indirect"; case Expression::Id::GetLocalId: return "get_local"; case Expression::Id::SetLocalId: return "set_local"; + case Expression::Id::GetGlobalId: return "get_global"; + case Expression::Id::SetGlobalId: return "set_global"; case Expression::Id::LoadId: return "load"; case Expression::Id::StoreId: return "store"; case Expression::Id::ConstId: return "const"; @@ -1105,6 +1109,27 @@ public: } }; +class GetGlobal : public SpecificExpression<Expression::GetGlobalId> { +public: + GetGlobal() {} + GetGlobal(MixedArena& allocator) {} + + Index index; +}; + +class SetGlobal : public SpecificExpression<Expression::SetGlobalId> { +public: + SetGlobal() {} + SetGlobal(MixedArena& allocator) {} + + Index index; + Expression *value; + + void finalize() { + type = value->type; + } +}; + class Load : public SpecificExpression<Expression::LoadId> { public: Load() {} @@ -1431,6 +1456,13 @@ public: Memory() : initial(0), max(kMaxSize) {} }; +class Global { +public: + Name name; + WasmType type; + Expression* init; +}; + class Module { public: // wasm contents (generally you shouldn't access these from outside, except maybe for iterating; use add*() and the get() functions) @@ -1438,6 +1470,7 @@ public: std::vector<std::unique_ptr<Import>> imports; std::vector<std::unique_ptr<Export>> exports; std::vector<std::unique_ptr<Function>> functions; + std::vector<std::unique_ptr<Global>> globals; Table table; Memory memory; @@ -1451,24 +1484,34 @@ private: std::map<Name, Import*> importsMap; std::map<Name, Export*> exportsMap; std::map<Name, Function*> functionsMap; + std::map<Name, Global*> globalsMap; public: - Module() : functionTypeIndex(0), importIndex(0), exportIndex(0), functionIndex(0) {} + Module() : functionTypeIndex(0), importIndex(0), exportIndex(0), functionIndex(0), globalIndex(0) {} - FunctionType* getFunctionType(size_t i) { assert(i < functionTypes.size()); return functionTypes[i].get(); } - Import* getImport(size_t i) { assert(i < imports.size()); return imports[i].get(); } - Export* getExport(size_t i) { assert(i < exports.size()); return exports[i].get(); } - Function* getFunction(size_t i) { assert(i < functions.size()); return functions[i].get(); } + FunctionType* getFunctionType(Index i) { assert(i < functionTypes.size()); return functionTypes[i].get(); } + Import* getImport(Index i) { assert(i < imports.size()); return imports[i].get(); } + Export* getExport(Index i) { assert(i < exports.size()); return exports[i].get(); } + Function* getFunction(Index i) { assert(i < functions.size()); return functions[i].get(); } + Global* getGlobal(Index i) { assert(i < globals.size()); return globals[i].get(); } FunctionType* getFunctionType(Name name) { assert(functionTypesMap[name]); return functionTypesMap[name]; } Import* getImport(Name name) { assert(importsMap[name]); return importsMap[name]; } Export* getExport(Name name) { assert(exportsMap[name]); return exportsMap[name]; } Function* getFunction(Name name) { assert(functionsMap[name]); return functionsMap[name]; } + Global* getGlobal(Name name) { assert(globalsMap[name]); return globalsMap[name]; } FunctionType* checkFunctionType(Name name) { if (functionTypesMap.find(name) == functionTypesMap.end()) return nullptr; return functionTypesMap[name]; } Import* checkImport(Name name) { if (importsMap.find(name) == importsMap.end()) return nullptr; return importsMap[name]; } Export* checkExport(Name name) { if (exportsMap.find(name) == exportsMap.end()) return nullptr; return exportsMap[name]; } Function* checkFunction(Name name) { if (functionsMap.find(name) == functionsMap.end()) return nullptr; return functionsMap[name]; } + Global* checkGlobal(Name name) { if (globalsMap.find(name) == globalsMap.end()) return nullptr; return globalsMap[name]; } + + FunctionType* checkFunctionType(Index i) { if (i >= functionTypes.size()) return nullptr; return functionTypes[i].get(); } + Import* checkImport(Index i) { if (i >= imports.size()) return nullptr; return imports[i].get(); } + Export* checkExport(Index i) { if (i >= exports.size()) return nullptr; return exports[i].get(); } + Function* checkFunction(Index i) { if (i >= functions.size()) return nullptr; return functions[i].get(); } + Global* checkGlobal(Index i) { if (i >= globals.size()) return nullptr; return globals[i].get(); } void addFunctionType(FunctionType* curr) { Name numericName = Name::fromInt(functionTypeIndex); // TODO: remove all these, assert on names already existing, do numeric stuff in wasm-s-parser etc. @@ -1510,6 +1553,17 @@ public: functionsMap[numericName] = curr; functionIndex++; } + void addGlobal(Global* curr) { + Name numericName = Name::fromInt(globalIndex); + if (curr->name.isNull()) { + curr->name = numericName; + } + globals.push_back(std::unique_ptr<Global>(curr)); + globalsMap[curr->name] = curr; + globalsMap[numericName] = curr; + globalIndex++; + } + void addStart(const Name &s) { start = s; } @@ -1533,7 +1587,7 @@ public: } private: - size_t functionTypeIndex, importIndex, exportIndex, functionIndex; + size_t functionTypeIndex, importIndex, exportIndex, functionIndex, globalIndex; }; } // namespace wasm |