| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- // Copyright (c) 2017 The Khronos Group Inc.
- // Copyright (c) 2017 Valve Corporation
- // Copyright (c) 2017 LunarG Inc.
- //
- // 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 SOURCE_OPT_INLINE_PASS_H_
- #define SOURCE_OPT_INLINE_PASS_H_
- #include <algorithm>
- #include <list>
- #include <memory>
- #include <set>
- #include <unordered_map>
- #include <vector>
- #include "source/opt/debug_info_manager.h"
- #include "source/opt/decoration_manager.h"
- #include "source/opt/module.h"
- #include "source/opt/pass.h"
- namespace spvtools {
- namespace opt {
- // See optimizer.hpp for documentation.
- class InlinePass : public Pass {
- using cbb_ptr = const BasicBlock*;
- public:
- virtual ~InlinePass() override = default;
- protected:
- InlinePass();
- // Add pointer to type to module and return resultId. Returns 0 if the type
- // could not be created.
- uint32_t AddPointerToType(uint32_t type_id, spv::StorageClass storage_class);
- // Add unconditional branch to labelId to end of block block_ptr.
- void AddBranch(uint32_t labelId, std::unique_ptr<BasicBlock>* block_ptr);
- // Add conditional branch to end of block |block_ptr|.
- void AddBranchCond(uint32_t cond_id, uint32_t true_id, uint32_t false_id,
- std::unique_ptr<BasicBlock>* block_ptr);
- // Add unconditional branch to labelId to end of block block_ptr.
- void AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
- std::unique_ptr<BasicBlock>* block_ptr);
- // Add store of valId to ptrId to end of block block_ptr.
- void AddStore(uint32_t ptrId, uint32_t valId,
- std::unique_ptr<BasicBlock>* block_ptr,
- const Instruction* line_inst, const DebugScope& dbg_scope);
- // Add load of ptrId into resultId to end of block block_ptr.
- void AddLoad(uint32_t typeId, uint32_t resultId, uint32_t ptrId,
- std::unique_ptr<BasicBlock>* block_ptr,
- const Instruction* line_inst, const DebugScope& dbg_scope);
- // Return new label.
- std::unique_ptr<Instruction> NewLabel(uint32_t label_id);
- // Returns the id for the boolean false value. Looks in the module first
- // and creates it if not found. Remembers it for future calls. Returns 0 if
- // the value could not be created.
- uint32_t GetFalseId();
- // Map callee params to caller args
- void MapParams(Function* calleeFn, BasicBlock::iterator call_inst_itr,
- std::unordered_map<uint32_t, uint32_t>* callee2caller);
- // Clone and map callee locals. Return true if successful.
- bool CloneAndMapLocals(Function* calleeFn,
- std::vector<std::unique_ptr<Instruction>>* new_vars,
- std::unordered_map<uint32_t, uint32_t>* callee2caller,
- analysis::DebugInlinedAtContext* inlined_at_ctx);
- // Create return variable for callee clone code. The return type of
- // |calleeFn| must not be void. Returns the id of the return variable if
- // created. Returns 0 if the return variable could not be created.
- uint32_t CreateReturnVar(Function* calleeFn,
- std::vector<std::unique_ptr<Instruction>>* new_vars);
- // Return true if instruction must be in the same block that its result
- // is used.
- bool IsSameBlockOp(const Instruction* inst) const;
- // Clone operands which must be in same block as consumer instructions.
- // Look in preCallSB for instructions that need cloning. Look in
- // postCallSB for instructions already cloned. Add cloned instruction
- // to postCallSB.
- bool CloneSameBlockOps(std::unique_ptr<Instruction>* inst,
- std::unordered_map<uint32_t, uint32_t>* postCallSB,
- std::unordered_map<uint32_t, Instruction*>* preCallSB,
- std::unique_ptr<BasicBlock>* block_ptr);
- // Return in new_blocks the result of inlining the call at call_inst_itr
- // within its block at call_block_itr. The block at call_block_itr can
- // just be replaced with the blocks in new_blocks. Any additional branches
- // are avoided. Debug instructions are cloned along with their callee
- // instructions. Early returns are replaced by a store to a local return
- // variable and a branch to a (created) exit block where the local variable
- // is returned. Formal parameters are trivially mapped to their actual
- // parameters. Note that the first block in new_blocks retains the label
- // of the original calling block. Also note that if an exit block is
- // created, it is the last block of new_blocks.
- //
- // Also return in new_vars additional OpVariable instructions required by
- // and to be inserted into the caller function after the block at
- // call_block_itr is replaced with new_blocks.
- //
- // Returns true if successful.
- bool GenInlineCode(std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
- std::vector<std::unique_ptr<Instruction>>* new_vars,
- BasicBlock::iterator call_inst_itr,
- UptrVectorIterator<BasicBlock> call_block_itr);
- // Return true if |inst| is a function call that can be inlined.
- bool IsInlinableFunctionCall(const Instruction* inst);
- // Return true if |func| has no return in a loop. The current analysis
- // requires structured control flow, so return false if control flow not
- // structured ie. module is not a shader.
- bool HasNoReturnInLoop(Function* func);
- // Find all functions with multiple returns and no returns in loops
- void AnalyzeReturns(Function* func);
- // Return true if |func| is a function that can be inlined.
- bool IsInlinableFunction(Function* func);
- // Returns true if |func| contains an abort instruction that is not an
- // `OpUnreachable` instruction.
- bool ContainsAbortOtherThanUnreachable(Function* func) const;
- // Update phis in succeeding blocks to point to new last block
- void UpdateSucceedingPhis(
- std::vector<std::unique_ptr<BasicBlock>>& new_blocks);
- // Initialize state for optimization of |module|
- void InitializeInline();
- // Fixes invalid debug declare functions in `func` that were caused by
- // inlining. This function cannot be called while in the middle of inlining
- // because it needs to be able to find the instructions that define an
- // id.
- void FixDebugDeclares(Function* func);
- // Map from function's result id to function.
- std::unordered_map<uint32_t, Function*> id2function_;
- // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt
- // CFG. It has functionality not present in CFG. Consolidate.
- std::unordered_map<uint32_t, BasicBlock*> id2block_;
- // Set of ids of functions with early return.
- std::set<uint32_t> early_return_funcs_;
- // Set of ids of functions with no returns in loop
- std::set<uint32_t> no_return_in_loop_;
- // Set of ids of inlinable functions
- std::set<uint32_t> inlinable_;
- // result id for OpConstantFalse
- uint32_t false_id_;
- // Set of functions that are originally called directly or indirectly from a
- // continue construct.
- std::unordered_set<uint32_t> funcs_called_from_continue_;
- private:
- // Moves instructions of the caller function up to the call instruction
- // to |new_blk_ptr|.
- void MoveInstsBeforeEntryBlock(
- std::unordered_map<uint32_t, Instruction*>* preCallSB,
- BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr,
- UptrVectorIterator<BasicBlock> call_block_itr);
- // Returns a new guard block after adding a branch to the end of
- // |new_blocks|.
- std::unique_ptr<BasicBlock> AddGuardBlock(
- std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
- std::unordered_map<uint32_t, uint32_t>* callee2caller,
- std::unique_ptr<BasicBlock> new_blk_ptr, uint32_t entry_blk_label_id);
- // Add store instructions for initializers of variables.
- InstructionList::iterator AddStoresForVariableInitializers(
- const std::unordered_map<uint32_t, uint32_t>& callee2caller,
- analysis::DebugInlinedAtContext* inlined_at_ctx,
- std::unique_ptr<BasicBlock>* new_blk_ptr,
- UptrVectorIterator<BasicBlock> callee_block_itr);
- // Inlines a single instruction of the callee function.
- bool InlineSingleInstruction(
- const std::unordered_map<uint32_t, uint32_t>& callee2caller,
- BasicBlock* new_blk_ptr, const Instruction* inst,
- uint32_t dbg_inlined_at);
- // Inlines the return instruction of the callee function.
- std::unique_ptr<BasicBlock> InlineReturn(
- const std::unordered_map<uint32_t, uint32_t>& callee2caller,
- std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
- std::unique_ptr<BasicBlock> new_blk_ptr,
- analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn,
- const Instruction* inst, uint32_t returnVarId);
- // Inlines the entry block of the callee function.
- bool InlineEntryBlock(
- const std::unordered_map<uint32_t, uint32_t>& callee2caller,
- std::unique_ptr<BasicBlock>* new_blk_ptr,
- UptrVectorIterator<BasicBlock> callee_first_block,
- analysis::DebugInlinedAtContext* inlined_at_ctx);
- // Inlines basic blocks of the callee function other than the entry basic
- // block.
- std::unique_ptr<BasicBlock> InlineBasicBlocks(
- std::vector<std::unique_ptr<BasicBlock>>* new_blocks,
- const std::unordered_map<uint32_t, uint32_t>& callee2caller,
- std::unique_ptr<BasicBlock> new_blk_ptr,
- analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn);
- // Moves instructions of the caller function after the call instruction
- // to |new_blk_ptr|.
- bool MoveCallerInstsAfterFunctionCall(
- std::unordered_map<uint32_t, Instruction*>* preCallSB,
- std::unordered_map<uint32_t, uint32_t>* postCallSB,
- std::unique_ptr<BasicBlock>* new_blk_ptr,
- BasicBlock::iterator call_inst_itr, bool multiBlocks);
- // Move the OpLoopMerge from the last block back to the first.
- void MoveLoopMergeInstToFirstBlock(
- std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
- // Update the structure of single block loops so that the inlined code ends
- // up in the loop construct and a new continue target is added to satisfy
- // structural dominance.
- void UpdateSingleBlockLoopContinueTarget(
- uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks);
- // Replaces the `var` operand of `dbg_declare_inst` and updates the indexes
- // accordingly, if it is the id of an access chain in `access_chains`.
- void FixDebugDeclare(Instruction* dbg_declare_inst,
- const std::map<uint32_t, Instruction*>& access_chains);
- };
- } // namespace opt
- } // namespace spvtools
- #endif // SOURCE_OPT_INLINE_PASS_H_
|