diff options
author | Alon Zakai <azakai@google.com> | 2019-04-25 17:10:06 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-25 17:10:06 -0700 |
commit | 78a4f9ef1afd6c209a5c69a8e7906ffe33575f58 (patch) | |
tree | 0f7443f2ca32ce1a6131a6cc95212de9b5ececb1 /src | |
parent | 21f014f4bd0ea1086895d8674f1473af222eb416 (diff) | |
download | binaryen-78a4f9ef1afd6c209a5c69a8e7906ffe33575f58.tar.gz binaryen-78a4f9ef1afd6c209a5c69a8e7906ffe33575f58.tar.bz2 binaryen-78a4f9ef1afd6c209a5c69a8e7906ffe33575f58.zip |
wasm2js2: optimize call_indirect and select operands (#2056)
Don't use temp vars to reorder them unless we need to.
Diffstat (limited to 'src')
-rw-r--r-- | src/ir/effects.h | 14 | ||||
-rw-r--r-- | src/wasm2js.h | 131 |
2 files changed, 96 insertions, 49 deletions
diff --git a/src/ir/effects.h b/src/ir/effects.h index 1c33964a7..e1fa77af0 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -59,13 +59,13 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe // Helper functions to check for various effect types - bool accessesLocal() { return localsRead.size() + localsWritten.size() > 0; } - bool accessesGlobal() { return globalsRead.size() + globalsWritten.size() > 0; } - bool accessesMemory() { return calls || readsMemory || writesMemory; } + bool accessesLocal() const { return localsRead.size() + localsWritten.size() > 0; } + bool accessesGlobal() const { return globalsRead.size() + globalsWritten.size() > 0; } + bool accessesMemory() const { return calls || readsMemory || writesMemory; } - bool hasGlobalSideEffects() { return calls || globalsWritten.size() > 0 || writesMemory || isAtomic; } - bool hasSideEffects() { return hasGlobalSideEffects() || localsWritten.size() > 0 || branches || implicitTrap; } - bool hasAnything() { return branches || calls || accessesLocal() || readsMemory || writesMemory || accessesGlobal() || implicitTrap || isAtomic; } + bool hasGlobalSideEffects() const { return calls || globalsWritten.size() > 0 || writesMemory || isAtomic; } + bool hasSideEffects() const { return hasGlobalSideEffects() || localsWritten.size() > 0 || branches || implicitTrap; } + bool hasAnything() const { return branches || calls || accessesLocal() || readsMemory || writesMemory || accessesGlobal() || implicitTrap || isAtomic; } bool noticesGlobalSideEffects() { return calls || readsMemory || isAtomic || globalsRead.size(); } @@ -73,7 +73,7 @@ struct EffectAnalyzer : public PostWalker<EffectAnalyzer, OverriddenVisitor<Effe bool hasExternalBreakTargets() { return !breakNames.empty(); } // checks if these effects would invalidate another set (e.g., if we write, we invalidate someone that reads, they can't be moved past us) - bool invalidates(EffectAnalyzer& other) { + bool invalidates(const EffectAnalyzer& other) { if ((branches && other.hasSideEffects()) || (other.branches && hasSideEffects()) || ((writesMemory || calls) && other.accessesMemory()) || diff --git a/src/wasm2js.h b/src/wasm2js.h index 6c97c5a6b..be794decb 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -35,6 +35,7 @@ #include "mixed_arena.h" #include "asm_v_wasm.h" #include "abi/js.h" +#include "ir/effects.h" #include "ir/find_all.h" #include "ir/import-utils.h" #include "ir/load-utils.h" @@ -884,33 +885,54 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } Ref visitCallIndirect(CallIndirect* curr) { - // TODO: the codegen here is a pessimization of what the ideal codegen - // looks like. Eventually if necessary this should be tightened up in the - // case that the argument expression doesn't have any side effects. - Ref ret; - ScopedTemp idx(i32, parent, func); - std::vector<ScopedTemp*> temps; // TODO: utility class, with destructor? - for (auto& operand : curr->operands) { - temps.push_back(new ScopedTemp(operand->type, parent, func)); - IString temp = temps.back()->temp; - sequenceAppend(ret, visitAndAssign(operand, temp)); - } - sequenceAppend(ret, visitAndAssign(curr->target, idx)); - Ref theCall = ValueBuilder::makeCall(ValueBuilder::makeSub( - ValueBuilder::makeName(FUNCTION_TABLE), - idx.getAstName() - )); - for (size_t i = 0; i < temps.size(); i++) { - IString temp = temps[i]->temp; - auto &operand = curr->operands[i]; - theCall[2]->push_back(makeAsmCoercion(ValueBuilder::makeName(temp), wasmToAsmType(operand->type))); + // If the target has effects that interact with the operands, we must reorder it to the start. + bool mustReorder = false; + EffectAnalyzer targetEffects(parent->options, curr->target); + if (targetEffects.hasAnything()) { + for (auto* operand : curr->operands) { + if (targetEffects.invalidates(EffectAnalyzer(parent->options, operand))) { + mustReorder = true; + break; + } + } } - theCall = makeAsmCoercion(theCall, wasmToAsmType(curr->type)); - sequenceAppend(ret, theCall); - for (auto temp : temps) { - delete temp; + if (mustReorder) { + Ref ret; + ScopedTemp idx(i32, parent, func); + std::vector<ScopedTemp*> temps; // TODO: utility class, with destructor? + for (auto* operand : curr->operands) { + temps.push_back(new ScopedTemp(operand->type, parent, func)); + IString temp = temps.back()->temp; + sequenceAppend(ret, visitAndAssign(operand, temp)); + } + sequenceAppend(ret, visitAndAssign(curr->target, idx)); + Ref theCall = ValueBuilder::makeCall(ValueBuilder::makeSub( + ValueBuilder::makeName(FUNCTION_TABLE), + idx.getAstName() + )); + for (size_t i = 0; i < temps.size(); i++) { + IString temp = temps[i]->temp; + auto &operand = curr->operands[i]; + theCall[2]->push_back(makeAsmCoercion(ValueBuilder::makeName(temp), wasmToAsmType(operand->type))); + } + theCall = makeAsmCoercion(theCall, wasmToAsmType(curr->type)); + sequenceAppend(ret, theCall); + for (auto temp : temps) { + delete temp; + } + return ret; + } else { + // Target has no side effects, emit simple code + Ref theCall = ValueBuilder::makeCall(ValueBuilder::makeSub( + ValueBuilder::makeName(FUNCTION_TABLE), + visit(curr->target, EXPRESSION_RESULT) + )); + for (auto* operand : curr->operands) { + theCall[2]->push_back(visit(operand, EXPRESSION_RESULT)); + } + theCall = makeAsmCoercion(theCall, wasmToAsmType(curr->type)); + return theCall; } - return ret; } // TODO: remove @@ -1526,28 +1548,49 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, Function* func, bool standalo } Ref visitSelect(Select* curr) { - // normal select - ScopedTemp tempIfTrue(curr->type, parent, func), - tempIfFalse(curr->type, parent, func), - tempCondition(i32, parent, func); - Ref ifTrue = visit(curr->ifTrue, EXPRESSION_RESULT); - Ref ifFalse = visit(curr->ifFalse, EXPRESSION_RESULT); - Ref condition = visit(curr->condition, EXPRESSION_RESULT); - return - ValueBuilder::makeSeq( - ValueBuilder::makeBinary(tempIfTrue.getAstName(), SET, ifTrue), + // If the condition has effects that interact with the operands, we must reorder it to the start. + // We must also use locals if the values have side effects, as a JS conditional does not + // visit both sides. + bool useLocals = false; + EffectAnalyzer conditionEffects(parent->options, curr->condition); + EffectAnalyzer ifTrueEffects(parent->options, curr->ifTrue); + EffectAnalyzer ifFalseEffects(parent->options, curr->ifFalse); + if (conditionEffects.invalidates(ifTrueEffects) || + conditionEffects.invalidates(ifFalseEffects) || + ifTrueEffects.hasSideEffects() || + ifFalseEffects.hasSideEffects()) { + useLocals = true; + } + if (useLocals) { + ScopedTemp tempIfTrue(curr->type, parent, func), + tempIfFalse(curr->type, parent, func), + tempCondition(i32, parent, func); + Ref ifTrue = visit(curr->ifTrue, EXPRESSION_RESULT); + Ref ifFalse = visit(curr->ifFalse, EXPRESSION_RESULT); + Ref condition = visit(curr->condition, EXPRESSION_RESULT); + return ValueBuilder::makeSeq( - ValueBuilder::makeBinary(tempIfFalse.getAstName(), SET, ifFalse), + ValueBuilder::makeBinary(tempIfTrue.getAstName(), SET, ifTrue), ValueBuilder::makeSeq( - ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition), - ValueBuilder::makeConditional( - tempCondition.getAstName(), - tempIfTrue.getAstName(), - tempIfFalse.getAstName() + ValueBuilder::makeBinary(tempIfFalse.getAstName(), SET, ifFalse), + ValueBuilder::makeSeq( + ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition), + ValueBuilder::makeConditional( + tempCondition.getAstName(), + tempIfTrue.getAstName(), + tempIfFalse.getAstName() + ) ) ) - ) + ); + } else { + // Simple case without reordering. + return ValueBuilder::makeConditional( + visit(curr->condition, EXPRESSION_RESULT), + visit(curr->ifTrue, EXPRESSION_RESULT), + visit(curr->ifFalse, EXPRESSION_RESULT) ); + } } Ref visitReturn(Return* curr) { @@ -1838,6 +1881,10 @@ void Wasm2JSGlue::emitPreES6() { noteImport(import->module, import->base); }); + if (wasm.table.exists && wasm.table.imported()) { + out << "import { FUNCTION_TABLE } from 'env';\n"; + } + out << '\n'; } |