| /* |
| * Copyright 2015 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // |
| // Misc optimizations that are useful for and/or are only valid for |
| // emscripten output. |
| // |
| |
| #include <asmjs/shared-constants.h> |
| #include <ir/import-utils.h> |
| #include <ir/localize.h> |
| #include <ir/memory-utils.h> |
| #include <ir/module-utils.h> |
| #include <ir/table-utils.h> |
| #include <pass.h> |
| #include <shared-constants.h> |
| #include <wasm-builder.h> |
| #include <wasm.h> |
| |
| namespace wasm { |
| |
| namespace { |
| |
| static bool isInvoke(Function* F) { |
| return F->imported() && F->module == ENV && F->base.startsWith("invoke_"); |
| } |
| |
| struct OptimizeCalls : public WalkerPass<PostWalker<OptimizeCalls>> { |
| bool isFunctionParallel() override { return true; } |
| |
| Pass* create() override { return new OptimizeCalls; } |
| |
| void visitCall(Call* curr) { |
| // special asm.js imports can be optimized |
| auto* func = getModule()->getFunction(curr->target); |
| if (!func->imported()) { |
| return; |
| } |
| if (func->module == GLOBAL_MATH) { |
| if (func->base == POW) { |
| if (auto* exponent = curr->operands[1]->dynCast<Const>()) { |
| if (exponent->value == Literal(double(2.0))) { |
| // This is just a square operation, do a multiply |
| Localizer localizer(curr->operands[0], getFunction(), getModule()); |
| Builder builder(*getModule()); |
| replaceCurrent(builder.makeBinary( |
| MulFloat64, |
| localizer.expr, |
| builder.makeLocalGet(localizer.index, localizer.expr->type))); |
| } else if (exponent->value == Literal(double(0.5))) { |
| // This is just a square root operation |
| replaceCurrent( |
| Builder(*getModule()).makeUnary(SqrtFloat64, curr->operands[0])); |
| } |
| } |
| } |
| } |
| } |
| }; |
| |
| } // namespace |
| |
| struct PostEmscripten : public Pass { |
| void run(PassRunner* runner, Module* module) override { |
| // Apply the sbrk ptr, if it was provided. |
| auto sbrkPtrStr = |
| runner->options.getArgumentOrDefault("emscripten-sbrk-ptr", ""); |
| if (sbrkPtrStr != "") { |
| auto sbrkPtr = std::stoi(sbrkPtrStr); |
| if (sbrkPtr == 0 || sbrkPtr == -1) { |
| Fatal() << "Invalid value for emscripten-sbrk-ptr"; |
| } |
| ImportInfo imports(*module); |
| auto* func = imports.getImportedFunction(ENV, "emscripten_get_sbrk_ptr"); |
| if (func) { |
| Builder builder(*module); |
| func->body = builder.makeConst(Literal(int32_t(sbrkPtr))); |
| func->module = func->base = Name(); |
| } |
| // Apply the sbrk ptr value, if it was provided. This lets emscripten set |
| // up sbrk entirely in wasm, without depending on the JS side to init |
| // anything; this is necessary for standalone wasm mode, in which we do |
| // not have any JS. Otherwise, the JS would set this value during |
| // startup. |
| auto sbrkValStr = |
| runner->options.getArgumentOrDefault("emscripten-sbrk-val", ""); |
| if (sbrkValStr != "") { |
| uint32_t sbrkVal = std::stoi(sbrkValStr); |
| auto end = sbrkPtr + sizeof(sbrkVal); |
| // Flatten memory to make it simple to write to. Later passes can |
| // re-optimize it. |
| MemoryUtils::ensureExists(module->memory); |
| if (!MemoryUtils::flatten(module->memory, end, module)) { |
| Fatal() << "cannot apply sbrk-val since memory is not flattenable\n"; |
| } |
| auto& segment = module->memory.segments[0]; |
| assert(segment.offset->cast<Const>()->value.geti32() == 0); |
| assert(end <= segment.data.size()); |
| memcpy(segment.data.data() + sbrkPtr, &sbrkVal, sizeof(sbrkVal)); |
| } |
| } |
| |
| // Optimize calls |
| OptimizeCalls().run(runner, module); |
| |
| // Optimize exceptions |
| optimizeExceptions(runner, module); |
| } |
| |
| // Optimize exceptions (and setjmp) by removing unnecessary invoke* calls. |
| // An invoke is a call to JS with a function pointer; JS does a try-catch |
| // and calls the pointer, catching and reporting any error. If we know no |
| // exception will be thrown, we can simply skip the invoke. |
| void optimizeExceptions(PassRunner* runner, Module* module) { |
| // First, check if this code even uses invokes. |
| bool hasInvokes = false; |
| for (auto& imp : module->functions) { |
| if (isInvoke(imp.get())) { |
| hasInvokes = true; |
| } |
| } |
| if (!hasInvokes) { |
| return; |
| } |
| // Next, see if the Table is flat, which we need in order to see where |
| // invokes go statically. (In dynamic linking, the table is not flat, |
| // and we can't do this.) |
| TableUtils::FlatTable flatTable(module->table); |
| if (!flatTable.valid) { |
| return; |
| } |
| // This code has exceptions. Find functions that definitely cannot throw, |
| // and remove invokes to them. |
| struct Info |
| : public ModuleUtils::CallGraphPropertyAnalysis<Info>::FunctionInfo { |
| bool canThrow = false; |
| }; |
| ModuleUtils::CallGraphPropertyAnalysis<Info> analyzer( |
| *module, [&](Function* func, Info& info) { |
| if (func->imported()) { |
| // Assume any import can throw. We may want to reduce this to just |
| // longjmp/cxa_throw/etc. |
| info.canThrow = true; |
| } |
| }); |
| |
| // Assume an indirect call might throw. |
| analyzer.propagateBack([](const Info& info) { return info.canThrow; }, |
| [](const Info& info) { return true; }, |
| [](Info& info) { info.canThrow = true; }, |
| analyzer.IndirectCallsHaveProperty); |
| |
| // Apply the information. |
| struct OptimizeInvokes : public WalkerPass<PostWalker<OptimizeInvokes>> { |
| bool isFunctionParallel() override { return true; } |
| |
| Pass* create() override { return new OptimizeInvokes(map, flatTable); } |
| |
| std::map<Function*, Info>& map; |
| TableUtils::FlatTable& flatTable; |
| |
| OptimizeInvokes(std::map<Function*, Info>& map, |
| TableUtils::FlatTable& flatTable) |
| : map(map), flatTable(flatTable) {} |
| |
| void visitCall(Call* curr) { |
| auto* target = getModule()->getFunction(curr->target); |
| if (isInvoke(target)) { |
| // The first operand is the function pointer index, which must be |
| // constant if we are to optimize it statically. |
| if (auto* index = curr->operands[0]->dynCast<Const>()) { |
| auto actualTarget = flatTable.names.at(index->value.geti32()); |
| if (!map[getModule()->getFunction(actualTarget)].canThrow) { |
| // This invoke cannot throw! Make it a direct call. |
| curr->target = actualTarget; |
| for (Index i = 0; i < curr->operands.size() - 1; i++) { |
| curr->operands[i] = curr->operands[i + 1]; |
| } |
| curr->operands.resize(curr->operands.size() - 1); |
| } |
| } |
| } |
| } |
| }; |
| OptimizeInvokes(analyzer.map, flatTable).run(runner, module); |
| } |
| }; |
| |
| Pass* createPostEmscriptenPass() { return new PostEmscripten(); } |
| |
| } // namespace wasm |