| /* | |
| * Copyright 2022 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. | |
| */ | |
| #ifndef wasm_ir_function_h | |
| #define wasm_ir_function_h | |
| #include <variant> | |
| #include "ir/debuginfo.h" | |
| #include "ir/type-updating.h" | |
| #include "wasm.h" | |
| namespace wasm::CallUtils { | |
| // Define a variant to describe the information we know about an indirect call, | |
| // which is one of three things: | |
| // * Unknown: Nothing is known this call. | |
| // * Trap: This call target is invalid and will trap at runtime. | |
| // * Known: This call goes to a known static call target, which is provided. | |
| struct Unknown : public std::monostate {}; | |
| struct Trap : public std::monostate {}; | |
| struct Known { | |
| Name target; | |
| }; | |
| using IndirectCallInfo = std::variant<Unknown, Trap, Known>; | |
| // Converts indirect calls that target selects between values into ifs over | |
| // direct calls. For example, consider this input: | |
| // | |
| // (call_ref | |
| // (select | |
| // (ref.func A) | |
| // (ref.func B) | |
| // (..condition..) | |
| // ) | |
| // ) | |
| // | |
| // We'll check if the input falls into such a pattern, and if so, return the new | |
| // form: | |
| // | |
| // (if | |
| // (..condition..) | |
| // (call $A) | |
| // (call $B) | |
| // ) | |
| // | |
| // If we fail to find the expected pattern, or we decide it is not worth | |
| // optimizing it for some reason, we return nullptr. | |
| // | |
| // If this returns the new form, it will modify the function as necessary, | |
| // adding new locals etc., which later passes should optimize. | |
| // | |
| // |getCallInfo| is given one of the arms of the select and should return an | |
| // IndirectCallInfo that says what we know about it. We may know nothing, or | |
| // that it will trap, or that it will go to a known static target. | |
| template<typename T> | |
| inline Expression* | |
| convertToDirectCalls(T* curr, | |
| std::function<IndirectCallInfo(Expression*)> getCallInfo, | |
| Function& func, | |
| Module& wasm) { | |
| auto* select = curr->target->template dynCast<Select>(); | |
| if (!select) { | |
| return nullptr; | |
| } | |
| if (select->type == Type::unreachable) { | |
| // Leave this for DCE. | |
| return nullptr; | |
| } | |
| // Check if we can find useful info for both arms: either known call targets, | |
| // or traps. | |
| // TODO: support more than 2 targets (with nested selects) | |
| auto ifTrueCallInfo = getCallInfo(select->ifTrue); | |
| auto ifFalseCallInfo = getCallInfo(select->ifFalse); | |
| if (std::get_if<Unknown>(&ifTrueCallInfo) || | |
| std::get_if<Unknown>(&ifFalseCallInfo)) { | |
| // We know nothing about at least one arm, so give up. | |
| // TODO: Perhaps emitting a direct call for one arm is enough even if the | |
| // other remains indirect? | |
| return nullptr; | |
| } | |
| auto& operands = curr->operands; | |
| // We must use the operands twice, and also must move the condition to | |
| // execute first, so we'll use locals for them all. First, see if any are | |
| // unreachable, and if so stop trying to optimize and leave this for DCE. | |
| for (auto* operand : operands) { | |
| if (operand->type == Type::unreachable) { | |
| return nullptr; | |
| } | |
| } | |
| Builder builder(wasm); | |
| std::vector<Expression*> blockContents; | |
| // None of the types are a problem, so we can proceed to add new vars as | |
| // needed and perform this optimization. | |
| std::vector<Index> operandLocals; | |
| for (auto* operand : operands) { | |
| auto currLocal = builder.addVar(&func, operand->type); | |
| operandLocals.push_back(currLocal); | |
| blockContents.push_back(builder.makeLocalSet(currLocal, operand)); | |
| } | |
| // Build the calls. | |
| auto numOperands = operands.size(); | |
| auto getOperands = [&]() { | |
| std::vector<Expression*> newOperands(numOperands); | |
| for (Index i = 0; i < numOperands; i++) { | |
| newOperands[i] = | |
| builder.makeLocalGet(operandLocals[i], operands[i]->type); | |
| } | |
| return newOperands; | |
| }; | |
| auto makeCall = [&](IndirectCallInfo info) -> Expression* { | |
| Expression* ret; | |
| if (std::get_if<Trap>(&info)) { | |
| ret = builder.makeUnreachable(); | |
| } else { | |
| ret = builder.makeCall(std::get<Known>(info).target, | |
| getOperands(), | |
| curr->type, | |
| curr->isReturn); | |
| } | |
| debuginfo::copyOriginalToReplacement(curr, ret, &func); | |
| return ret; | |
| }; | |
| auto* ifTrueCall = makeCall(ifTrueCallInfo); | |
| auto* ifFalseCall = makeCall(ifFalseCallInfo); | |
| // Create the if to pick the calls, and emit the final block. | |
| auto* iff = builder.makeIf(select->condition, ifTrueCall, ifFalseCall); | |
| blockContents.push_back(iff); | |
| return builder.makeBlock(blockContents); | |
| } | |
| } // namespace wasm::CallUtils | |
| #endif // wasm_ir_function_h |