Skip to content

Commit 3e6ea6e

Browse files
Add ImportResolver interface and use it for global imports (#8166)
Add an ImportResolver interface with some implementations to handle the spec interpreter and ctor-eval * Removes coupling of imported names from the module they come from. This will allows us to provide special imports like the spec test module better which have some 'magic' e.g. for printing functions which can't be implemented in Wasm (globals remain the same because they still come from a real module which is better). * Removes pointer chasing logic when resolving imported globals in both cases. * Also allows us to relax ctor-eval and allow programs that import globals to be optimized more (as long as they don't try to evaluate imports) #8168 * Fixes #8167 * Part of #8180
1 parent 158ee9b commit 3e6ea6e

File tree

8 files changed

+173
-91
lines changed

8 files changed

+173
-91
lines changed

src/ir/import-name.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2026 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef wasm_ir_import_name_h
18+
#define wasm_ir_import_name_h
19+
20+
#include <ostream>
21+
22+
#include "support/name.h"
23+
24+
namespace wasm {
25+
26+
struct ImportNames {
27+
Name module;
28+
Name name;
29+
};
30+
31+
} // namespace wasm
32+
33+
#endif // wasm_ir_import_name_h

src/ir/import-utils.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef wasm_ir_import_h
1818
#define wasm_ir_import_h
1919

20+
#include "ir/import-name.h"
2021
#include "literal.h"
2122
#include "wasm.h"
2223

@@ -123,6 +124,37 @@ struct ImportInfo {
123124
Index getNumDefinedTags() { return wasm.tags.size() - getNumImportedTags(); }
124125
};
125126

127+
class ImportResolver {
128+
public:
129+
virtual ~ImportResolver() = default;
130+
131+
// Returns null if the `name` wasn't found. The returned Literals* lives as
132+
// long as the ImportResolver instance.
133+
virtual Literals* getGlobalOrNull(ImportNames name, Type type) const = 0;
134+
};
135+
136+
// Looks up imports from the given `linkedInstances`.
137+
template<typename ModuleRunnerType>
138+
class LinkedInstancesImportResolver : public ImportResolver {
139+
public:
140+
LinkedInstancesImportResolver(
141+
std::map<Name, std::shared_ptr<ModuleRunnerType>> linkedInstances)
142+
: linkedInstances(std::move(linkedInstances)) {}
143+
144+
Literals* getGlobalOrNull(ImportNames name, Type type) const override {
145+
auto it = linkedInstances.find(name.module);
146+
if (it == linkedInstances.end()) {
147+
return nullptr;
148+
}
149+
150+
ModuleRunnerType* instance = it->second.get();
151+
return instance->getExportedGlobalOrNull(name.name);
152+
}
153+
154+
private:
155+
const std::map<Name, std::shared_ptr<ModuleRunnerType>> linkedInstances;
156+
};
157+
126158
} // namespace wasm
127159

128160
#endif // wasm_ir_import_h

src/passes/Print.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3944,4 +3944,9 @@ std::ostream& operator<<(std::ostream& o, wasm::ModuleHeapType pair) {
39443944
return o << "(unnamed)";
39453945
}
39463946

3947+
std::ostream& operator<<(std::ostream& o,
3948+
const wasm::ImportNames& importNames) {
3949+
return o << importNames.module << "." << importNames.name;
3950+
}
3951+
39473952
} // namespace std

src/shell-interface.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -129,20 +129,6 @@ struct ShellExternalInterface : ModuleRunner::ExternalInterface {
129129
wasm, [&](Table* table) { tables[table->name].resize(table->initial); });
130130
}
131131

132-
void importGlobals(std::map<Name, Literals>& globals, Module& wasm) override {
133-
ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) {
134-
auto inst = getImportInstance(import);
135-
auto* exportedGlobal = inst->wasm.getExportOrNull(import->base);
136-
if (!exportedGlobal || exportedGlobal->kind != ExternalKind::Global) {
137-
trap((std::stringstream()
138-
<< "importGlobals: unknown import: " << import->module.str << "."
139-
<< import->name.str)
140-
.str());
141-
}
142-
globals[import->name] = inst->globals[*exportedGlobal->getInternalName()];
143-
});
144-
}
145-
146132
Literal getImportedFunction(Function* import) override {
147133
// TODO: We should perhaps restrict the types with which the well-known
148134
// functions can be imported.

src/tools/wasm-ctor-eval.cpp

Lines changed: 20 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,27 @@ bool isNullableAndMutable(Expression* ref, Index fieldIndex) {
6767
// the output.
6868
#define RECOMMENDATION "\n recommendation: "
6969

70+
class EvallingModuleRunner;
71+
72+
class EvallingImportResolver : public ImportResolver {
73+
public:
74+
EvallingImportResolver() = default;
75+
76+
Literals* getGlobalOrNull(ImportNames name, Type type) const override {
77+
throw FailToEvalException("Accessed imported global");
78+
}
79+
};
80+
7081
class EvallingModuleRunner : public ModuleRunnerBase<EvallingModuleRunner> {
7182
public:
7283
EvallingModuleRunner(
7384
Module& wasm,
7485
ExternalInterface* externalInterface,
7586
std::map<Name, std::shared_ptr<EvallingModuleRunner>> linkedInstances_ = {})
76-
: ModuleRunnerBase(wasm, externalInterface, linkedInstances_) {}
87+
: ModuleRunnerBase(wasm,
88+
externalInterface,
89+
std::make_shared<EvallingImportResolver>(),
90+
linkedInstances_) {}
7791

7892
Flow visitGlobalGet(GlobalGet* curr) {
7993
// Error on reads of imported globals.
@@ -147,19 +161,6 @@ std::unique_ptr<Module> buildEnvModule(Module& wasm) {
147161
}
148162
});
149163

150-
ModuleUtils::iterImportedGlobals(wasm, [&](Global* global) {
151-
if (global->module == env->name) {
152-
auto* copied = ModuleUtils::copyGlobal(global, *env);
153-
copied->module = Name();
154-
copied->base = Name();
155-
156-
Builder builder(*env);
157-
copied->init = builder.makeConst(Literal::makeZero(global->type));
158-
env->addExport(
159-
builder.makeExport(global->base, copied->name, ExternalKind::Global));
160-
}
161-
});
162-
163164
// create an exported memory with the same initial and max size
164165
ModuleUtils::iterImportedMemories(wasm, [&](Memory* memory) {
165166
if (memory->module == env->name) {
@@ -231,26 +232,6 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
231232
}
232233
}
233234

234-
void importGlobals(GlobalValueSet& globals, Module& wasm_) override {
235-
ModuleUtils::iterImportedGlobals(wasm_, [&](Global* global) {
236-
auto it = linkedInstances.find(global->module);
237-
if (it != linkedInstances.end()) {
238-
auto* inst = it->second.get();
239-
auto* globalExport = inst->wasm.getExportOrNull(global->base);
240-
if (!globalExport || globalExport->kind != ExternalKind::Global) {
241-
throw FailToEvalException(std::string("importGlobals: ") +
242-
global->module.toString() + "." +
243-
global->base.toString());
244-
}
245-
globals[global->name] = inst->globals[*globalExport->getInternalName()];
246-
} else {
247-
throw FailToEvalException(std::string("importGlobals: ") +
248-
global->module.toString() + "." +
249-
global->base.toString());
250-
}
251-
});
252-
}
253-
254235
Literal getImportedFunction(Function* import) override {
255236
auto f = [import, this](const Literals& arguments) -> Flow {
256237
Name WASI("wasi_snapshot_preview1");
@@ -558,8 +539,8 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
558539
void applyGlobalsToModule() {
559540
if (!wasm->features.hasGC()) {
560541
// Without GC, we can simply serialize the globals in place as they are.
561-
for (const auto& [name, values] : instance->globals) {
562-
wasm->getGlobal(name)->init = getSerialization(values);
542+
for (const auto& [name, values] : instance->allGlobals) {
543+
wasm->getGlobal(name)->init = getSerialization(*values);
563544
}
564545
return;
565546
}
@@ -590,9 +571,9 @@ struct CtorEvalExternalInterface : EvallingModuleRunner::ExternalInterface {
590571
// for it. (If there is no value, then this is a new global we've added
591572
// during execution, for whom we've already set up a proper serialized
592573
// value when we created it.)
593-
auto iter = instance->globals.find(oldGlobal->name);
594-
if (iter != instance->globals.end()) {
595-
oldGlobal->init = getSerialization(iter->second, name);
574+
auto iter = instance->allGlobals.find(oldGlobal->name);
575+
if (iter != instance->allGlobals.end()) {
576+
oldGlobal->init = getSerialization(*iter->second, name);
596577
}
597578

598579
// Add the global back to the module.

src/tools/wasm-shell.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ struct Shell {
280280
}
281281
auto& instance = it->second;
282282
try {
283-
return instance->getExportedGlobal(get->name);
283+
return instance->getExportedGlobalOrTrap(get->name);
284284
} catch (TrapException&) {
285285
return TrapResult{};
286286
} catch (...) {

0 commit comments

Comments
 (0)