From f62a01197e07531a09e11354c9b94c21e06dbfd8 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Tue, 22 Sep 2026 18:56:57 -0700 Subject: [PATCH 1/4] [MakeSharedObjects] Wrap externref global imports Keep externref global imports as externrefs at the module boundary and insert their values into the externref table during instantiation via an active element segment. Replace the original globals with internal shared i31 globals initialized to their corresponding table indices. This keeps e.g. imported string constants working as expected without assuming that they can be converted to `(ref (shared extern))`, which would not work if they flowed to the same locations as other externrefs, which would have been lowered to i31ref instead. Now that both the function and externref tables have initial content, move the logic for creating the element segment to LazyTable. --- src/passes/MakeSharedObjects.cpp | 114 ++++++++---- test/lit/passes/make-shared-objects.wast | 215 +++++++++++++++++++++-- 2 files changed, 281 insertions(+), 48 deletions(-) diff --git a/src/passes/MakeSharedObjects.cpp b/src/passes/MakeSharedObjects.cpp index 8a52fb79948..f4ff53fe53e 100644 --- a/src/passes/MakeSharedObjects.cpp +++ b/src/passes/MakeSharedObjects.cpp @@ -79,6 +79,8 @@ struct LazyTable { // The names of conversion functions, if ever used. Name refToIndexName; Name indexToRefName; + // Initial elements used to populate the table via an active element segment. + std::vector init; LazyTable(Name base, Type type) : base(base), type(type) {} @@ -190,16 +192,16 @@ struct LazyTable { wasm->addFunction(std::move(func)); } - // Returns true if the table was added. (If it is never used, it will not be - // added.) - bool maybeAdd(Address initial, Address max) { + // Add the table, conversion functions, and active element segment if the + // table was ever used. + void maybeAdd(Address max) { assert(wasm); if (!name) { - return false; + return; } Table* table = wasm->addTable(Builder::makeTable(name)); table->type = type; - table->initial = initial; + table->initial = init.size(); table->max = max; if (refToIndexName) { addRefToIndexFunction(); @@ -207,11 +209,19 @@ struct LazyTable { if (indexToRefName) { addIndexToRefFunction(); } - return true; + if (!init.empty()) { + Builder builder(*wasm); + Name segName = Names::getValidElementSegmentName(*wasm, base); + auto* offset = builder.makeConst(Literal(int32_t(0))); + auto* segment = wasm->addElementSegment( + Builder::makeElementSegment(segName, name, offset)); + segment->type = type; + segment->data = std::move(init); + } } bool isTableType(Type t) const { - return t.isRef() && Type::isSubType(t, type); + return t.isRef() && !t.isNull() && Type::isSubType(t, type); } bool hasTableType(Type t) const { @@ -264,10 +274,16 @@ struct MakeSharedObjects Type funcref = Type(HeapTypes::func, Nullable); Type externref = Type(HeapTypes::ext, Nullable); + // Tables storing unshared funcrefs and externrefs that are lowered to shared + // i31ref indices inside the module. LazyTable funcTable{"funcs", funcref}; LazyTable externTable{"externs", externref}; - std::vector funcs; + + // Map from function name to index in `funcTable`. std::unordered_map funcIndices; + + // Imported helper functions used to lower `extern.convert_any` and + // `any.convert_extern`, if needed. Name anyToExternName; Name externToAnyName; @@ -287,10 +303,11 @@ struct MakeSharedObjects return externToAnyName; } - Index getIndex(Name func) { - auto [it, inserted] = funcIndices.insert({func, funcs.size()}); + Index getIndex(RefFunc* curr) { + auto [it, inserted] = + funcIndices.insert({curr->func, funcTable.init.size()}); if (inserted) { - funcs.push_back(func); + funcTable.init.push_back(curr); } return it->second; } @@ -488,8 +505,8 @@ struct MakeSharedObjects void visitRefFunc(RefFunc* curr) { Builder builder(*getModule()); - replaceCurrent(builder.makeRefI31( - builder.makeConst(Literal(getIndex(curr->func))), Shared)); + replaceCurrent( + builder.makeRefI31(builder.makeConst(Literal(getIndex(curr))), Shared)); } std::unordered_map callRefTypes; @@ -718,7 +735,47 @@ struct MakeSharedObjects void visitElementSegment(ElementSegment* curr) { updateType(curr->type); } - void visitGlobal(Global* curr) { updateType(curr->type); } + // Create a new imported global with the original externref type and add it + // to the externref table at instantiation time. The original global ceases to + // be an import and is initialized to the shared i31 table index so internal + // uses of the global receive the table index. + void wrapGlobalImport(Global* global, Type origType) { + Builder builder(*getModule()); + Name origName = global->name; + Name importName = + Names::getValidGlobalName(*getModule(), origName.toString() + "$import"); + + auto importGlobal = std::make_unique(); + importGlobal->name = importName; + importGlobal->module = global->module; + importGlobal->base = global->base; + importGlobal->type = origType; + importGlobal->mutable_ = false; + importGlobal->hasExplicitName = true; + + Index index = externTable.init.size(); + externTable.init.push_back(builder.makeGlobalGet(importName, origType)); + + global->module = Name(); + global->base = Name(); + global->init = + builder.makeRefI31(builder.makeConst(Literal(int32_t(index))), Shared); + + getModule()->addGlobal(std::move(importGlobal)); + } + + struct GlobalImportToWrap { + Global* global; + Type origType; + }; + std::vector globalImportsToWrap; + + void visitGlobal(Global* curr) { + if (curr->imported() && externTable.isTableType(curr->type)) { + globalImportsToWrap.push_back({curr, curr->type}); + } + updateType(curr->type); + } void doWalkModule(Module* wasm) { funcTable.wasm = wasm; @@ -759,6 +816,9 @@ struct MakeSharedObjects rewriteTypes(); + for (auto& info : globalImportsToWrap) { + wrapGlobalImport(info.global, info.origType); + } for (auto& info : importsToWrap) { wrapImport(info.func, info.origType); } @@ -773,9 +833,10 @@ struct MakeSharedObjects } } - addFunctionTable(); + funcTable.maybeAdd(funcTable.init.size()); addExternTable(); ReFinalize().run(getPassRunner(), wasm); + ReFinalize().walkModuleCode(wasm); } void rewriteTypes() { @@ -822,27 +883,6 @@ struct MakeSharedObjects rewriter.mapTypeNamesAndIndices(oldToNew); } - void addFunctionTable() { - if (!funcTable.maybeAdd(funcs.size(), funcs.size())) { - return; - } - if (funcs.empty()) { - return; - } - Builder builder(*getModule()); - Name segName = Names::getValidElementSegmentName(*getModule(), "funcs"); - auto* offset = builder.makeConst(Literal(int32_t(0))); - auto* segment = getModule()->addElementSegment( - Builder::makeElementSegment(segName, funcTable.getName(), offset)); - segment->type = funcTable.type; - - segment->data.reserve(funcs.size()); - for (auto func : funcs) { - segment->data.push_back( - builder.makeRefFunc(func, getModule()->getFunction(func)->type)); - } - } - void addAnyToExternFunction() { Type sharedAnyNullable = Type(HeapTypes::any.getBasic(Shared), Nullable); Type externrefNullable = Type(HeapTypes::ext, Nullable); @@ -866,7 +906,7 @@ struct MakeSharedObjects } void addExternTable() { - externTable.maybeAdd(0, Table::kUnlimitedSize); + externTable.maybeAdd(Table::kUnlimitedSize); if (anyToExternName) { addAnyToExternFunction(); } diff --git a/test/lit/passes/make-shared-objects.wast b/test/lit/passes/make-shared-objects.wast index f9713a4b4c3..a51a20cd5ff 100644 --- a/test/lit/passes/make-shared-objects.wast +++ b/test/lit/passes/make-shared-objects.wast @@ -361,18 +361,20 @@ ;; CHECK: (type $shared-sig (func (param i32) (result i32))) (type $shared-sig (shared (func (param i32) (result i32)))) - ;; CHECK: (type $2 (func)) - ;; CHECK: (type $struct (shared (struct (field (ref null (shared i31)))))) (type $struct (struct (field (ref null func)))) (func $effect (import "" "") (result i32)) + ;; CHECK: (type $3 (func)) + ;; CHECK: (type $4 (func (param (ref (shared i31)) i32) (result i32))) - ;; CHECK: (type $5 (func (param (ref (shared i31))) (result i32))) + ;; CHECK: (type $5 (func (param (ref $struct)))) + + ;; CHECK: (type $6 (func (param (ref (shared i31))) (result i32))) - ;; CHECK: (type $6 (func (param i32 i32) (result i32))) + ;; CHECK: (type $7 (func (param i32 i32) (result i32))) ;; CHECK: (import "" "" (func $effect (type $0) (result i32))) @@ -407,16 +409,16 @@ ;; CHECK: (elem $funcs (table $funcs) (i32.const 0) func $a $b $d $c) - ;; CHECK: (func $a (type $2) + ;; CHECK: (func $a (type $5) (param $0 (ref $struct)) ;; CHECK-NEXT: ) - (func $a) - ;; CHECK: (func $b (type $2) + (func $a (param (ref $struct))) + ;; CHECK: (func $b (type $3) ;; CHECK-NEXT: ) (func $b) - ;; CHECK: (func $c (type $2) + ;; CHECK: (func $c (type $3) ;; CHECK-NEXT: ) (func $c) - ;; CHECK: (func $d (type $2) + ;; CHECK: (func $d (type $3) ;; CHECK-NEXT: ) (func $d) @@ -468,7 +470,7 @@ (call_ref $sig (call $effect) (ref.null nofunc)) ) - ;; CHECK: (func $shared-call-ref (type $5) (param $0 (ref (shared i31))) (result i32) + ;; CHECK: (func $shared-call-ref (type $6) (param $0 (ref (shared i31))) (result i32) ;; CHECK-NEXT: (call_indirect $funcs (type $shared-sig) ;; CHECK-NEXT: (i32.const 42) ;; CHECK-NEXT: (i31.get_u @@ -529,7 +531,7 @@ ) ;; call_indirect lowering - ;; CHECK: (func $call-indirect (type $6) (param $0 i32) (param $1 i32) (result i32) + ;; CHECK: (func $call-indirect (type $7) (param $0 i32) (param $1 i32) (result i32) ;; CHECK-NEXT: (call_indirect $funcs (type $shared-sig) ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: (i31.get_u @@ -2418,6 +2420,7 @@ (call $im_mixed (local.get 0) (local.get 1)) ) ) + ;; CHECK: (func $test_mixed$export (type $2) (param $0 (ref $struct)) (param $1 externref) (result (ref $struct) externref) ;; CHECK-NEXT: (local $2 (tuple (ref $struct) (ref null (shared i31)))) ;; CHECK-NEXT: (local.set $2 @@ -2490,3 +2493,193 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) +(module + ;; Imported externref globals should remain externref imports, be inserted + ;; into the externs table at instantiation, and have internal uses replaced + ;; with i31 globals referring to their table indices. + ;; CHECK: (type $struct (shared (struct (field (ref null (shared i31))) (field (ref (shared i31)))))) + (type $struct (struct (field externref) (field (ref extern)))) + (import "env" "g_nullable" (global $g_nullable externref)) + (import "env" "g_non_null" (global $g_non_null (ref extern))) + + ;; CHECK: (type $1 (func (result externref (ref extern)))) + + ;; CHECK: (type $2 (func (result (ref null (shared i31)) (ref (shared i31))))) + + ;; CHECK: (type $3 (func (param (ref null (shared i31))) (result externref))) + + ;; CHECK: (import "env" "g_nullable" (global $g_nullable$import externref)) + + ;; CHECK: (import "env" "g_non_null" (global $g_non_null$import (ref extern))) + + ;; CHECK: (global $g_nullable (ref null (shared i31)) (ref.i31_shared + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: )) + + ;; CHECK: (global $g_non_null (ref (shared i31)) (ref.i31_shared + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: )) + + ;; CHECK: (global $user (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (global.get $g_nullable) + ;; CHECK-NEXT: (global.get $g_non_null) + ;; CHECK-NEXT: )) + (global $user (ref $struct) (struct.new $struct + (global.get $g_nullable) + (global.get $g_non_null) + )) + + ;; CHECK: (table $externs 2 externref) + + ;; CHECK: (elem $externs (table $externs) (i32.const 0) externref (item (global.get $g_nullable$import)) (item (global.get $g_non_null$import))) + + ;; CHECK: (export "get-globals" (func $get-globals$export)) + + ;; CHECK: (func $get-globals (type $2) (result (ref null (shared i31)) (ref (shared i31))) + ;; CHECK-NEXT: (tuple.make 2 + ;; CHECK-NEXT: (global.get $g_nullable) + ;; CHECK-NEXT: (global.get $g_non_null) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $get-globals (export "get-globals") (result externref (ref extern)) + ;; Read both imported externref globals inside an exported function. + (tuple.make 2 + (global.get $g_nullable) + (global.get $g_non_null) + ) + ) +) + +;; CHECK: (func $get-globals$export (type $1) (result externref (ref extern)) +;; CHECK-NEXT: (local $0 (tuple (ref null (shared i31)) (ref (shared i31)))) +;; CHECK-NEXT: (local.set $0 +;; CHECK-NEXT: (call $get-globals) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (tuple.make 2 +;; CHECK-NEXT: (call $index_to_extern +;; CHECK-NEXT: (tuple.extract 2 0 +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (ref.cast (ref extern) +;; CHECK-NEXT: (call $index_to_extern +;; CHECK-NEXT: (tuple.extract 2 1 +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) + +;; CHECK: (func $index_to_extern (type $3) (param $0 (ref null (shared i31))) (result externref) +;; CHECK-NEXT: (if (result externref) +;; CHECK-NEXT: (ref.is_null +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (ref.null noextern) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (else +;; CHECK-NEXT: (table.get $externs +;; CHECK-NEXT: (i31.get_u +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +(module + ;; If imported externref globals are never used in a way that requires the + ;; externs table, they still get table indices, but no table is emitted. + (import "env" "g_nullable" (global $g_nullable externref)) + (import "env" "g_non_null" (global $g_non_null (ref extern))) +) + +;; CHECK: (import "env" "g_nullable" (global $g_nullable$import externref)) + +;; CHECK: (import "env" "g_non_null" (global $g_non_null$import (ref extern))) + +;; CHECK: (global $g_nullable (ref null (shared i31)) (ref.i31_shared +;; CHECK-NEXT: (i32.const 0) +;; CHECK-NEXT: )) + +;; CHECK: (global $g_non_null (ref (shared i31)) (ref.i31_shared +;; CHECK-NEXT: (i32.const 1) +;; CHECK-NEXT: )) +(module + ;; Imported string constants should remain externref/stringref imports, be + ;; inserted into the externs table, and be converted back when passed to + ;; string builtins. + (import "'" "hello" (global $hello (ref extern))) + (import "'" "world" (global $world (ref string))) + (import "wasm:js-string" "equals" (func $equals (param externref externref) (result i32))) + + ;; CHECK: (type $0 (func (param (ref null (shared i31)) (ref null (shared i31))) (result i32))) + + ;; CHECK: (type $1 (func (result i32))) + + ;; CHECK: (type $2 (func (param externref externref) (result i32))) + + ;; CHECK: (type $3 (func (param (ref null (shared i31))) (result externref))) + + ;; CHECK: (import "\'" "hello" (global $hello$import (ref extern))) + + ;; CHECK: (import "\'" "world" (global $world$import (ref string))) + + ;; CHECK: (import "wasm:js-string" "equals" (func $equals$import (type $2) (param externref externref) (result i32))) + + ;; CHECK: (global $hello (ref (shared i31)) (ref.i31_shared + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: )) + + ;; CHECK: (global $world (ref (shared i31)) (ref.i31_shared + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: )) + + ;; CHECK: (table $externs 2 externref) + + ;; CHECK: (elem $externs (table $externs) (i32.const 0) externref (item (global.get $hello$import)) (item (global.get $world$import))) + + ;; CHECK: (func $equals (type $0) (param $0 (ref null (shared i31))) (param $1 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (call $equals$import + ;; CHECK-NEXT: (call $index_to_extern + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $index_to_extern + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + + ;; CHECK: (func $compare-strings (type $1) (result i32) + ;; CHECK-NEXT: (call $equals + ;; CHECK-NEXT: (global.get $hello) + ;; CHECK-NEXT: (global.get $world) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $compare-strings (result i32) + ;; Compare two imported string constants using wasm:js-string equals. + (call $equals + (global.get $hello) + (global.get $world) + ) + ) +) + +;; CHECK: (func $index_to_extern (type $3) (param $0 (ref null (shared i31))) (result externref) +;; CHECK-NEXT: (if (result externref) +;; CHECK-NEXT: (ref.is_null +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (then +;; CHECK-NEXT: (ref.null noextern) +;; CHECK-NEXT: ) +;; CHECK-NEXT: (else +;; CHECK-NEXT: (table.get $externs +;; CHECK-NEXT: (i31.get_u +;; CHECK-NEXT: (local.get $0) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) +;; CHECK-NEXT: ) From ad1cfbc90065e40ceecd76209001f2780e561ce8 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 25 Sep 2026 13:40:28 -0700 Subject: [PATCH 2/4] comment and error on mutable globals --- src/passes/MakeSharedObjects.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/passes/MakeSharedObjects.cpp b/src/passes/MakeSharedObjects.cpp index f4ff53fe53e..73da1174613 100644 --- a/src/passes/MakeSharedObjects.cpp +++ b/src/passes/MakeSharedObjects.cpp @@ -740,6 +740,13 @@ struct MakeSharedObjects // be an import and is initialized to the shared i31 table index so internal // uses of the global receive the table index. void wrapGlobalImport(Global* global, Type origType) { + // Mutable imported/exported externref globals are not supported because all + // accesses to them would have to be rewritten to be function calls that + // accessed the externref table. We cannot update such accesses outside this + // module. + if (global->mutable_ == Mutable) { + Fatal() << "Cannot wrap mutable global " << global->name; + } Builder builder(*getModule()); Name origName = global->name; Name importName = From cc2e278715e685ed585aa235a6f40f63e775b964 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 25 Sep 2026 13:43:04 -0700 Subject: [PATCH 3/4] isTableType => canHold, hasTableType => hasHoldable --- src/passes/MakeSharedObjects.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/passes/MakeSharedObjects.cpp b/src/passes/MakeSharedObjects.cpp index 73da1174613..b6729cb2b17 100644 --- a/src/passes/MakeSharedObjects.cpp +++ b/src/passes/MakeSharedObjects.cpp @@ -220,29 +220,29 @@ struct LazyTable { } } - bool isTableType(Type t) const { + bool canHold(Type t) const { return t.isRef() && !t.isNull() && Type::isSubType(t, type); } - bool hasTableType(Type t) const { + bool hasHoldable(Type t) const { if (t.isTuple()) { for (Type elem : t) { - if (hasTableType(elem)) { + if (hasHoldable(elem)) { return true; } } return false; } - return isTableType(t); + return canHold(t); } bool funcHasTableType(Function* func) const { Signature sig = func->type.getHeapType().getSignature(); - return hasTableType(sig.params) || hasTableType(sig.results); + return hasHoldable(sig.params) || hasHoldable(sig.results); } Expression* convertToRef(Expression* arg, Type origType) { - if (!isTableType(origType)) { + if (!canHold(origType)) { return arg; } Builder builder(*wasm); @@ -254,7 +254,7 @@ struct LazyTable { } Expression* convertToIndex(Expression* arg, Type origType, Type targetType) { - if (!isTableType(origType)) { + if (!canHold(origType)) { return arg; } Builder builder(*wasm); @@ -359,7 +359,7 @@ struct MakeSharedObjects std::vector params; Index i = 0; for (Type param : origSig.params) { - if (externTable.isTableType(param)) { + if (externTable.canHold(param)) { params.push_back(param); } else { params.push_back(rewrittenSig.params[i]); @@ -370,7 +370,7 @@ struct MakeSharedObjects std::vector results; Index j = 0; for (Type result : origSig.results) { - if (externTable.isTableType(result)) { + if (externTable.canHold(result)) { results.push_back(result); } else { results.push_back(rewrittenSig.results[j]); @@ -421,7 +421,7 @@ struct MakeSharedObjects auto* call = builder.makeCall(importName, callArgs, extResults); // Convert externrefs received as results to indices. - if (!externTable.hasTableType(extResults)) { + if (!externTable.hasHoldable(extResults)) { func->body = call; } else if (extResults.isSingle()) { Type targetType = func->getResults(); @@ -479,7 +479,7 @@ struct MakeSharedObjects // Forward results, converting indices to externrefs. Type extResults = boundarySig.results; - if (!externTable.hasTableType(extResults)) { + if (!externTable.hasHoldable(extResults)) { exportWrapper->body = call; } else if (extResults.isSingle()) { exportWrapper->body = externTable.convertToRef(call, extResults); @@ -778,7 +778,7 @@ struct MakeSharedObjects std::vector globalImportsToWrap; void visitGlobal(Global* curr) { - if (curr->imported() && externTable.isTableType(curr->type)) { + if (curr->imported() && externTable.canHold(curr->type)) { globalImportsToWrap.push_back({curr, curr->type}); } updateType(curr->type); From 52b354bee757707d6847ec7cb963f6f6d70e879d Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 25 Sep 2026 13:43:47 -0700 Subject: [PATCH 4/4] ImportToWrap => FuncImportToWrap --- src/passes/MakeSharedObjects.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/passes/MakeSharedObjects.cpp b/src/passes/MakeSharedObjects.cpp index b6729cb2b17..e1242a3b24b 100644 --- a/src/passes/MakeSharedObjects.cpp +++ b/src/passes/MakeSharedObjects.cpp @@ -791,7 +791,7 @@ struct MakeSharedObjects WalkerPass::doWalkModule(wasm); } - struct ImportToWrap { + struct FuncImportToWrap { Function* func; Type origType; }; @@ -802,7 +802,7 @@ struct MakeSharedObjects }; void visitModule(Module* wasm) { - std::vector importsToWrap; + std::vector importsToWrap; for (auto& func : wasm->functions) { if (func->imported() && externTable.funcHasTableType(func.get())) { importsToWrap.push_back({func.get(), func->type});