| /* | |
| * Copyright 2021 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_intrinsics_h | |
| #define wasm_ir_intrinsics_h | |
| #include "pass.h" | |
| #include "wasm-traversal.h" | |
| // | |
| // Intrinsics include Binaryen intrinsics, and Wasm spec builtins. See the | |
| // README.md for more. | |
| // | |
| // Intrinsics can be recognized by Intrinsics::isFoo() methods, that check if a | |
| // function is a particular intrinsic, or if a call to a function is so. The | |
| // latter returns nullptr if the input is not that intrinsic, and otherwise the | |
| // intrinsic itself cast to a Call*. | |
| // | |
| namespace wasm { | |
| class Intrinsics { | |
| const Module& module; | |
| public: | |
| Intrinsics(const Module& module) : module(module) {} | |
| // Check if an instruction is the Binaryen call.without.effects intrinsic. | |
| // | |
| // (import "binaryen-intrinsics" "call.without.effects" | |
| // (func (..params..) (param $target funcref) (..results..))) | |
| // | |
| // call.without.effects can take any parameters, and in addition a funcref, | |
| // and return any result. | |
| // | |
| // Precise semantics: | |
| // | |
| // * The optimizer will assume this instruction has no side effects. | |
| // * Final lowering turns a call.without.effects into a call of the given | |
| // function with the given parameters. (This will either be a direct call, | |
| // or a call_ref; note that either way, the function reference that appears | |
| // here must have the proper type - if not, you will get an error.) | |
| // | |
| // call.without.effects is useful to be able to get rid of an unused result | |
| // that has side effects. For example, | |
| // | |
| // (drop (call $get-something)) | |
| // | |
| // cannot be removed, as a call has side effects. But if a code generator | |
| // knows that it is fine to not make the call given that the result is | |
| // dropped (perhaps the side effects are to initialize a global cache, for | |
| // example) then instead of emitting | |
| // | |
| // (call $get-something) | |
| // | |
| // it can emit | |
| // | |
| // (call $call.without.effects (ref.func $get-something)) | |
| // | |
| // which will have this behavior in the optimizer if it is dropped: | |
| // | |
| // (drop (call $call.without.effects (ref.func $get-something))) | |
| // => | |
| // (drop (ref.func $get-something)) | |
| // | |
| // Later optimizations can remove the dropped ref.func. Or, if the result is | |
| // actually used, | |
| // | |
| // (local.set $x (call $call.without.effects (ref.func $get-something))) | |
| // => | |
| // (local.set $x (call $get-something)) | |
| // | |
| // Later passes will then turn that into a direct call and further optimize | |
| // things. | |
| bool isCallWithoutEffects(Function* func); | |
| Call* isCallWithoutEffects(Expression* curr); | |
| // Check if an instruction is the wasm/JS interop configureAll builtin. Such | |
| // calls have a second parameter which is an array of function references, | |
| // which must be assumed to be "signature-called": Called from outside the | |
| // module, using that signature. "Signature-called" is less powerful than the | |
| // reference to the function generally escaping, as we do not care about the | |
| // heap type of the function (no call_refs or casts will happen), all we know | |
| // is that a JS-style call of the methods can occur (and only the signature | |
| // matters then). | |
| bool isConfigureAll(Function* func); | |
| Call* isConfigureAll(Expression* curr); | |
| // Given a configureAll, return all the functions it refers to. We error if it | |
| // is not in the canonical form | |
| // | |
| // (call $configureAll | |
| // ..arg0.. | |
| // (array.new_elem $seg (0) (N) | |
| // .. | |
| // ) | |
| // | |
| // where the segment $seg is of size N. | |
| std::vector<Name> getConfigureAllFunctions(Call* call); | |
| // Returns the names of all functions that are JS-called. That includes ones | |
| // in configureAll (which we look through the module for), and also those | |
| // annotated with @binaryen.js.called. | |
| std::vector<Name> getJSCalledFunctions(); | |
| // Get the code annotations for an expression in a function. | |
| static CodeAnnotation getAnnotations(Expression* curr, Function* func) { | |
| auto& annotations = func->codeAnnotations; | |
| auto iter = annotations.find(curr); | |
| if (iter != annotations.end()) { | |
| return iter->second; | |
| } | |
| return {}; | |
| } | |
| // Get the code annotations for a function itself. | |
| static CodeAnnotation getAnnotations(Function* func) { | |
| return func->funcAnnotations; | |
| } | |
| void setAnnotations(Function* func, | |
| Expression* curr, | |
| const CodeAnnotation& value) { | |
| func->codeAnnotations[curr] = value; | |
| } | |
| void setAnnotations(Function* func, const CodeAnnotation& value) { | |
| func->funcAnnotations = value; | |
| } | |
| // Given a call in a function, return all the annotations for it. The call may | |
| // be annotated itself (which takes precedence), or the function it calls may | |
| // be annotated. | |
| CodeAnnotation getCallAnnotations(Call* call, Function* func) { | |
| // Combine annotations from the call itself and from the called function. | |
| auto ret = getAnnotations(call, func); | |
| // Check on the called function, if it exists (it may not if the IR is still | |
| // being built up). | |
| if (auto* target = module.getFunctionOrNull(call->target)) { | |
| auto funcAnnotations = getAnnotations(target); | |
| // Merge them, giving precedence for the call annotation. | |
| if (!ret.branchLikely) { | |
| ret.branchLikely = funcAnnotations.branchLikely; | |
| } | |
| if (!ret.inline_) { | |
| ret.inline_ = funcAnnotations.inline_; | |
| } | |
| if (!ret.removableIfUnused) { | |
| ret.removableIfUnused = funcAnnotations.removableIfUnused; | |
| } | |
| if (!ret.jsCalled) { | |
| ret.jsCalled = funcAnnotations.jsCalled; | |
| } | |
| if (!ret.idempotent) { | |
| ret.idempotent = funcAnnotations.idempotent; | |
| } | |
| if (!ret.toolchainInline) { | |
| ret.toolchainInline = funcAnnotations.toolchainInline; | |
| } | |
| } | |
| return ret; | |
| } | |
| }; | |
| } // namespace wasm | |
| #endif // wasm_ir_intrinsics_h |