| /* |
| * 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 { |
| Module& module; |
| |
| public: |
| Intrinsics(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 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; |
| } |
| } |
| |
| return ret; |
| } |
| }; |
| |
| } // namespace wasm |
| |
| #endif // wasm_ir_intrinsics_h |