Skip to content

Commit 0b8851e

Browse files
authored
Intrinsic: js.called (#8324)
When a function is annotated with this: ```wat (@binaryen.js.called) (func $foo ..) ``` then it is assumed to be called from JS (if a reference is taken). In closed world, we assume such references are not actually called from the outside, and this annotation can be used to mark the exceptions that are. Such functions are not normally exported, but "js called" in that JS may call them, but not other wasm code (so rec group type identity does not matter).
1 parent da18c0d commit 0b8851e

20 files changed

+167
-36
lines changed

src/ir/intrinsics.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,29 +101,29 @@ std::vector<Name> Intrinsics::getConfigureAllFunctions(Call* call) {
101101
return ret;
102102
}
103103

104-
std::vector<Name> Intrinsics::getConfigureAllFunctions() {
105-
// ConfigureAll in a start function makes its functions callable.
104+
std::vector<Name> Intrinsics::getJSCalledFunctions() {
105+
std::vector<Name> ret;
106+
for (auto& func : module.functions) {
107+
if (getAnnotations(func.get()).jsCalled) {
108+
ret.push_back(func->name);
109+
}
110+
}
111+
112+
// ConfigureAlls in a start function make their functions callable.
106113
if (module.start) {
107114
auto* start = module.getFunction(module.start);
108115
if (!start->imported()) {
109116
FindAll<Call> calls(start->body);
110-
// Look for the (single) configureAll.
111-
Call* configureAll = nullptr;
112117
for (auto* call : calls.list) {
113118
if (isConfigureAll(call)) {
114-
if (configureAll) {
115-
Fatal() << "Multiple configureAlls";
116-
} else {
117-
configureAll = call;
119+
for (auto name : getConfigureAllFunctions(call)) {
120+
ret.push_back(name);
118121
}
119122
}
120123
}
121-
if (configureAll) {
122-
return getConfigureAllFunctions(configureAll);
123-
}
124124
}
125125
}
126-
return {};
126+
return ret;
127127
}
128128

129129
} // namespace wasm

src/ir/intrinsics.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,11 @@ class Intrinsics {
109109
//
110110
// where the segment $seg is of size N.
111111
std::vector<Name> getConfigureAllFunctions(Call* call);
112-
// As above, but looks through the module to find the configureAll.
113-
std::vector<Name> getConfigureAllFunctions();
112+
113+
// Returns the names of all functions that are JS-called. That includes ones
114+
// in configureAll (which we look through the module for), and also those
115+
// annotated with @binaryen.js.called.
116+
std::vector<Name> getJSCalledFunctions();
114117

115118
// Get the code annotations for an expression in a function.
116119
CodeAnnotation getAnnotations(Expression* curr, Function* func) {
@@ -149,6 +152,9 @@ class Intrinsics {
149152
if (!ret.removableIfUnused) {
150153
ret.removableIfUnused = funcAnnotations.removableIfUnused;
151154
}
155+
if (!ret.jsCalled) {
156+
ret.jsCalled = funcAnnotations.jsCalled;
157+
}
152158
}
153159

154160
return ret;

src/ir/possible-contents.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2454,8 +2454,8 @@ Flower::Flower(Module& wasm, const PassOptions& options)
24542454
}
24552455
}
24562456

2457-
// configureAll functions are called from outside the module, as if exported.
2458-
for (auto func : Intrinsics(wasm).getConfigureAllFunctions()) {
2457+
// JS-called functions are called from outside the module, as if exported.
2458+
for (auto func : Intrinsics(wasm).getJSCalledFunctions()) {
24592459
calledFromOutside.insert(func);
24602460
}
24612461

src/parser/contexts.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,6 +1317,8 @@ struct AnnotationParserCtx {
13171317
inlineHint = &a;
13181318
} else if (a.kind == Annotations::RemovableIfUnusedHint) {
13191319
ret.removableIfUnused = true;
1320+
} else if (a.kind == Annotations::JSCalledHint) {
1321+
ret.jsCalled = true;
13201322
}
13211323
}
13221324

src/passes/GlobalTypeOptimization.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ struct GlobalTypeOptimization : public Pass {
420420
if (!wasm.features.hasCustomDescriptors()) {
421421
return;
422422
}
423-
for (auto func : Intrinsics(wasm).getConfigureAllFunctions()) {
423+
for (auto func : Intrinsics(wasm).getJSCalledFunctions()) {
424424
// Look at the result types being returned to JS and make sure we preserve
425425
// any configured prototypes they might expose.
426426
for (auto type : wasm.getFunction(func)->getResults()) {

src/passes/Print.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2797,6 +2797,12 @@ void PrintSExpression::printCodeAnnotations(Expression* curr) {
27972797
restoreNormalColor(o);
27982798
doIndent(o, indent);
27992799
}
2800+
if (annotation.jsCalled) {
2801+
Colors::grey(o);
2802+
o << "(@" << Annotations::JSCalledHint << ")\n";
2803+
restoreNormalColor(o);
2804+
doIndent(o, indent);
2805+
}
28002806
}
28012807
}
28022808

src/passes/RemoveUnusedModuleElements.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,16 @@ struct Noter : public PostWalker<Noter, UnifiedExpressionVisitor<Noter>> {
184184
noteCallRef(curr->target->type.getHeapType());
185185
}
186186

187-
void visitRefFunc(RefFunc* curr) { noteRefFunc(curr->func); }
187+
void visitRefFunc(RefFunc* curr) {
188+
// If the target is js-called then a reference is as strong as a use.
189+
auto target = curr->func;
190+
Intrinsics intrinsics(*getModule());
191+
if (intrinsics.getAnnotations(getModule()->getFunction(target)).jsCalled) {
192+
use({ModuleElementKind::Function, target});
193+
} else {
194+
noteRefFunc(target);
195+
}
196+
}
188197

189198
void visitStructGet(StructGet* curr) {
190199
if (curr->ref->type == Type::unreachable || curr->ref->type.isNull()) {

src/passes/SignaturePruning.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,10 @@ struct SignaturePruning : public Pass {
176176
allInfo[tag->type].optimizable = false;
177177
}
178178

179-
// configureAll functions are signature-called, and must also not be
180-
// modified.
181-
for (auto func : Intrinsics(*module).getConfigureAllFunctions()) {
179+
// Signature-called functions must also not be modified.
180+
// TODO: Explore whether removing parameters from the end could be
181+
// beneficial (check if it does not regress call performance with JS).
182+
for (auto func : Intrinsics(*module).getJSCalledFunctions()) {
182183
allInfo[module->getFunction(func)->type.getHeapType()].optimizable =
183184
false;
184185
}

src/passes/SignatureRefining.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,8 @@ struct SignatureRefining : public Pass {
162162
}
163163
}
164164

165-
// configureAll functions are signature-called, which means their params
166-
// must not be refined.
167-
for (auto func : Intrinsics(*module).getConfigureAllFunctions()) {
165+
// Signature-called functions must not have params refined.
166+
for (auto func : Intrinsics(*module).getJSCalledFunctions()) {
168167
allInfo[module->getFunction(func)->type.getHeapType()].canModifyParams =
169168
false;
170169
}

src/passes/StripToolchainAnnotations.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct StripToolchainAnnotations
4343
// Remove the toolchain-specific annotations.
4444
auto& annotation = iter->second;
4545
annotation.removableIfUnused = false;
46+
annotation.jsCalled = false;
4647

4748
// If nothing remains, remove the entire annotation.
4849
if (annotation == CodeAnnotation()) {

0 commit comments

Comments
 (0)