@@ -126,7 +126,7 @@ class Wasm2JSBuilder {
126126 bool emscripten = false ;
127127 };
128128
129- Wasm2JSBuilder (Flags f) : flags(f) {}
129+ Wasm2JSBuilder (Flags f, PassOptions options ) : flags(f), options(options ) {}
130130
131131 Ref processWasm (Module* wasm, Name funcName = ASM_FUNC);
132132 Ref processFunction (Module* wasm, Function* func, bool standalone=false );
@@ -136,7 +136,7 @@ class Wasm2JSBuilder {
136136
137137 // The second pass on an expression: process it fully, generating
138138 // JS
139- Ref processFunctionBody (Module* m, Function* func);
139+ Ref processFunctionBody (Module* m, Function* func, bool standalone );
140140
141141 // Get a temp var.
142142 IString getTemp (Type type, Function* func) {
@@ -225,6 +225,7 @@ class Wasm2JSBuilder {
225225
226226private:
227227 Flags flags;
228+ PassOptions options;
228229
229230 // How many temp vars we need
230231 std::vector<size_t > temps; // type => num temps
@@ -238,6 +239,11 @@ class Wasm2JSBuilder {
238239
239240 size_t tableSize;
240241
242+ // If a function is callable from outside, we'll need to cast the inputs
243+ // and our return value. Otherwise, internally, casts are only needed
244+ // on operations.
245+ std::unordered_set<Name> functionsCallableFromOutside;
246+
241247 void addBasics (Ref ast);
242248 void addFunctionImport (Ref ast, Function* import );
243249 void addGlobalImport (Ref ast, Global* import );
@@ -252,6 +258,18 @@ class Wasm2JSBuilder {
252258};
253259
254260Ref Wasm2JSBuilder::processWasm (Module* wasm, Name funcName) {
261+ // Scan the wasm for important things.
262+ for (auto & exp : wasm->exports ) {
263+ if (exp->kind == ExternalKind::Function) {
264+ functionsCallableFromOutside.insert (exp->value );
265+ }
266+ }
267+ for (auto & segment : wasm->table .segments ) {
268+ for (auto name : segment.data ) {
269+ functionsCallableFromOutside.insert (name);
270+ }
271+ }
272+
255273 // Ensure the scratch memory helpers.
256274 // If later on they aren't needed, we'll clean them up.
257275 ABI::wasm2js::ensureScratchMemoryHelpers (wasm);
@@ -645,26 +663,29 @@ Ref Wasm2JSBuilder::processFunction(Module* m, Function* func, bool standaloneFu
645663 temps.resize (std::max (i32 , std::max (f32 , f64 )) + 1 );
646664 temps[i32 ] = temps[f32 ] = temps[f64 ] = 0 ;
647665 // arguments
666+ bool needCoercions = options.optimizeLevel == 0 || standaloneFunction || functionsCallableFromOutside.count (func->name );
648667 for (Index i = 0 ; i < func->getNumParams (); i++) {
649668 IString name = fromName (func->getLocalNameOrGeneric (i), NameScope::Local);
650669 ValueBuilder::appendArgumentToFunction (ret, name);
651- ret[3 ]->push_back (
652- ValueBuilder::makeStatement (
653- ValueBuilder::makeBinary (
654- ValueBuilder::makeName (name), SET,
655- makeAsmCoercion (
656- ValueBuilder::makeName (name),
657- wasmToAsmType (func->getLocalType (i))
670+ if (needCoercions) {
671+ ret[3 ]->push_back (
672+ ValueBuilder::makeStatement (
673+ ValueBuilder::makeBinary (
674+ ValueBuilder::makeName (name), SET,
675+ makeAsmCoercion (
676+ ValueBuilder::makeName (name),
677+ wasmToAsmType (func->getLocalType (i))
678+ )
658679 )
659680 )
660- )
661- );
681+ );
682+ }
662683 }
663684 Ref theVar = ValueBuilder::makeVar ();
664685 size_t theVarIndex = ret[3 ]->size ();
665686 ret[3 ]->push_back (theVar);
666687 // body
667- flattenAppend (ret, processFunctionBody (m, func));
688+ flattenAppend (ret, processFunctionBody (m, func, standaloneFunction ));
668689 // vars, including new temp vars
669690 for (Index i = func->getVarIndexBase (); i < func->getNumLocals (); i++) {
670691 ValueBuilder::appendToVar (
@@ -683,15 +704,17 @@ Ref Wasm2JSBuilder::processFunction(Module* m, Function* func, bool standaloneFu
683704 return ret;
684705}
685706
686- Ref Wasm2JSBuilder::processFunctionBody (Module* m, Function* func) {
707+ Ref Wasm2JSBuilder::processFunctionBody (Module* m, Function* func, bool standaloneFunction ) {
687708 struct ExpressionProcessor : public Visitor <ExpressionProcessor, Ref> {
688709 Wasm2JSBuilder* parent;
689710 IString result; // TODO: remove
690711 Function* func;
691712 Module* module ;
713+ bool standaloneFunction;
692714 MixedArena allocator;
693- ExpressionProcessor (Wasm2JSBuilder* parent, Module* m, Function* func)
694- : parent(parent), func(func), module (m) {}
715+
716+ ExpressionProcessor (Wasm2JSBuilder* parent, Module* m, Function* func, bool standaloneFunction)
717+ : parent(parent), func(func), module (m), standaloneFunction(standaloneFunction) {}
695718
696719 // A scoped temporary variable.
697720 struct ScopedTemp {
@@ -847,12 +870,19 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func) {
847870
848871 Ref visitCall (Call* curr) {
849872 Ref theCall = ValueBuilder::makeCall (fromName (curr->target , NameScope::Top));
873+ // For wasm => wasm calls, we don't need coercions. TODO: even imports might be safe?
874+ bool needCoercions = parent->options .optimizeLevel == 0 || standaloneFunction || module ->getFunction (curr->target )->imported ();
850875 for (auto operand : curr->operands ) {
851- theCall[2 ]->push_back (
852- makeAsmCoercion (visit (operand, EXPRESSION_RESULT),
853- wasmToAsmType (operand->type )));
876+ auto value = visit (operand, EXPRESSION_RESULT);
877+ if (needCoercions) {
878+ value = makeAsmCoercion (value, wasmToAsmType (operand->type ));
879+ }
880+ theCall[2 ]->push_back (value);
881+ }
882+ if (needCoercions) {
883+ theCall = makeAsmCoercion (theCall, wasmToAsmType (curr->type ));
854884 }
855- return makeAsmCoercion ( theCall, wasmToAsmType (curr-> type )) ;
885+ return theCall;
856886 }
857887
858888 Ref visitCallIndirect (CallIndirect* curr) {
@@ -992,7 +1022,14 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func) {
9921022 abort ();
9931023 }
9941024 }
995- return makeAsmCoercion (ret, wasmToAsmType (curr->type ));
1025+ // Coercions are not actually needed, as if the user reads beyond valid memory, it's
1026+ // undefined behavior anyhow, and so we don't care much about slowness of undefined
1027+ // values etc.
1028+ bool needCoercions = parent->options .optimizeLevel == 0 || standaloneFunction;
1029+ if (needCoercions) {
1030+ ret = makeAsmCoercion (ret, wasmToAsmType (curr->type ));
1031+ }
1032+ return ret;
9961033 }
9971034
9981035 Ref visitStore (Store* curr) {
@@ -1521,10 +1558,11 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func) {
15211558 if (!curr->value ) {
15221559 return ValueBuilder::makeReturn (Ref ());
15231560 }
1524- Ref val = makeAsmCoercion (
1525- visit (curr->value , EXPRESSION_RESULT),
1526- wasmToAsmType (curr->value ->type )
1527- );
1561+ Ref val = visit (curr->value , EXPRESSION_RESULT);
1562+ bool needCoercion = parent->options .optimizeLevel == 0 || standaloneFunction || parent->functionsCallableFromOutside .count (func->name );
1563+ if (needCoercion) {
1564+ val = makeAsmCoercion (val, wasmToAsmType (curr->value ->type ));
1565+ }
15281566 return ValueBuilder::makeReturn (val);
15291567 }
15301568
@@ -1564,7 +1602,7 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func) {
15641602 }
15651603 };
15661604
1567- return ExpressionProcessor (this , m, func).visit (func->body , NO_RESULT);
1605+ return ExpressionProcessor (this , m, func, standaloneFunction ).visit (func->body , NO_RESULT);
15681606}
15691607
15701608void Wasm2JSBuilder::addMemoryGrowthFuncs (Ref ast, Module* wasm) {
0 commit comments