Home | History | Annotate | Download | only in opt
      1 // Copyright (c) 2017 The Khronos Group Inc.
      2 // Copyright (c) 2017 Valve Corporation
      3 // Copyright (c) 2017 LunarG Inc.
      4 //
      5 // Licensed under the Apache License, Version 2.0 (the "License");
      6 // you may not use this file except in compliance with the License.
      7 // You may obtain a copy of the License at
      8 //
      9 //     http://www.apache.org/licenses/LICENSE-2.0
     10 //
     11 // Unless required by applicable law or agreed to in writing, software
     12 // distributed under the License is distributed on an "AS IS" BASIS,
     13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 // See the License for the specific language governing permissions and
     15 // limitations under the License.
     16 
     17 #ifndef LIBSPIRV_OPT_INLINE_PASS_H_
     18 #define LIBSPIRV_OPT_INLINE_PASS_H_
     19 
     20 #include <algorithm>
     21 #include <memory>
     22 #include <unordered_map>
     23 #include <vector>
     24 #include <list>
     25 
     26 #include "def_use_manager.h"
     27 #include "module.h"
     28 #include "pass.h"
     29 
     30 namespace spvtools {
     31 namespace opt {
     32 
     33 // See optimizer.hpp for documentation.
     34 class InlinePass : public Pass {
     35 
     36   using cbb_ptr = const ir::BasicBlock*;
     37 
     38  public:
     39    using GetBlocksFunction =
     40      std::function<std::vector<ir::BasicBlock*>*(const ir::BasicBlock*)>;
     41 
     42   InlinePass();
     43   virtual ~InlinePass() = default;
     44 
     45  protected:
     46   // Return the next available Id and increment it.
     47   inline uint32_t TakeNextId() { return next_id_++; }
     48 
     49   // Write the next available Id back to the module.
     50   inline void FinalizeNextId(ir::Module* module) {
     51     module->SetIdBound(next_id_);
     52   }
     53 
     54   // Find pointer to type and storage in module, return its resultId,
     55   // 0 if not found. TODO(greg-lunarg): Move this into type manager.
     56   uint32_t FindPointerToType(uint32_t type_id, SpvStorageClass storage_class);
     57 
     58   // Add pointer to type to module and return resultId.
     59   uint32_t AddPointerToType(uint32_t type_id, SpvStorageClass storage_class);
     60 
     61   // Add unconditional branch to labelId to end of block block_ptr.
     62   void AddBranch(uint32_t labelId, std::unique_ptr<ir::BasicBlock>* block_ptr);
     63 
     64   // Add conditional branch to end of block |block_ptr|.
     65   void AddBranchCond(uint32_t cond_id, uint32_t true_id,
     66     uint32_t false_id, std::unique_ptr<ir::BasicBlock>* block_ptr);
     67 
     68   // Add unconditional branch to labelId to end of block block_ptr.
     69   void AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
     70                     std::unique_ptr<ir::BasicBlock>* block_ptr);
     71 
     72   // Add store of valId to ptrId to end of block block_ptr.
     73   void AddStore(uint32_t ptrId, uint32_t valId,
     74                 std::unique_ptr<ir::BasicBlock>* block_ptr);
     75 
     76   // Add load of ptrId into resultId to end of block block_ptr.
     77   void AddLoad(uint32_t typeId, uint32_t resultId, uint32_t ptrId,
     78                std::unique_ptr<ir::BasicBlock>* block_ptr);
     79 
     80   // Return new label.
     81   std::unique_ptr<ir::Instruction> NewLabel(uint32_t label_id);
     82 
     83   // Returns the id for the boolean false value. Looks in the module first
     84   // and creates it if not found. Remembers it for future calls.
     85   uint32_t GetFalseId();
     86 
     87   // Map callee params to caller args
     88   void MapParams(ir::Function* calleeFn,
     89                  ir::UptrVectorIterator<ir::Instruction> call_inst_itr,
     90                  std::unordered_map<uint32_t, uint32_t>* callee2caller);
     91 
     92   // Clone and map callee locals
     93   void CloneAndMapLocals(
     94       ir::Function* calleeFn,
     95       std::vector<std::unique_ptr<ir::Instruction>>* new_vars,
     96       std::unordered_map<uint32_t, uint32_t>* callee2caller);
     97 
     98   // Create return variable for callee clone code if needed. Return id
     99   // if created, otherwise 0.
    100   uint32_t CreateReturnVar(
    101       ir::Function* calleeFn,
    102       std::vector<std::unique_ptr<ir::Instruction>>* new_vars);
    103 
    104   // Return true if instruction must be in the same block that its result
    105   // is used.
    106   bool IsSameBlockOp(const ir::Instruction* inst) const;
    107 
    108   // Clone operands which must be in same block as consumer instructions.
    109   // Look in preCallSB for instructions that need cloning. Look in
    110   // postCallSB for instructions already cloned. Add cloned instruction
    111   // to postCallSB.
    112   void CloneSameBlockOps(
    113       std::unique_ptr<ir::Instruction>* inst,
    114       std::unordered_map<uint32_t, uint32_t>* postCallSB,
    115       std::unordered_map<uint32_t, ir::Instruction*>* preCallSB,
    116       std::unique_ptr<ir::BasicBlock>* block_ptr);
    117 
    118   // Return in new_blocks the result of inlining the call at call_inst_itr
    119   // within its block at call_block_itr. The block at call_block_itr can
    120   // just be replaced with the blocks in new_blocks. Any additional branches
    121   // are avoided. Debug instructions are cloned along with their callee
    122   // instructions. Early returns are replaced by a store to a local return
    123   // variable and a branch to a (created) exit block where the local variable
    124   // is returned. Formal parameters are trivially mapped to their actual
    125   // parameters. Note that the first block in new_blocks retains the label
    126   // of the original calling block. Also note that if an exit block is
    127   // created, it is the last block of new_blocks.
    128   //
    129   // Also return in new_vars additional OpVariable instructions required by
    130   // and to be inserted into the caller function after the block at
    131   // call_block_itr is replaced with new_blocks.
    132   void GenInlineCode(std::vector<std::unique_ptr<ir::BasicBlock>>* new_blocks,
    133                      std::vector<std::unique_ptr<ir::Instruction>>* new_vars,
    134                      ir::UptrVectorIterator<ir::Instruction> call_inst_itr,
    135                      ir::UptrVectorIterator<ir::BasicBlock> call_block_itr);
    136 
    137   // Return true if |inst| is a function call that can be inlined.
    138   bool IsInlinableFunctionCall(const ir::Instruction* inst);
    139 
    140   // Returns the id of the merge block declared by a merge instruction in
    141   // this block, if any.  If none, returns zero.
    142   uint32_t MergeBlockIdIfAny(const ir::BasicBlock& blk);
    143 
    144   // Compute structured successors for function |func|.
    145   // A block's structured successors are the blocks it branches to
    146   // together with its declared merge block if it has one.
    147   // When order matters, the merge block always appears first.
    148   // This assures correct depth first search in the presence of early
    149   // returns and kills. If the successor vector contain duplicates
    150   // if the merge block, they are safely ignored by DFS.
    151   void ComputeStructuredSuccessors(ir::Function* func);
    152 
    153   // Return function to return ordered structure successors for a given block
    154   // Assumes ComputeStructuredSuccessors() has been called.
    155   GetBlocksFunction StructuredSuccessorsFunction();
    156 
    157   // Return true if |func| has multiple returns
    158   bool HasMultipleReturns(ir::Function* func);
    159 
    160   // Return true if |func| has no return in a loop. The current analysis
    161   // requires structured control flow, so return false if control flow not
    162   // structured ie. module is not a shader.
    163   bool HasNoReturnInLoop(ir::Function* func);
    164 
    165   // Find all functions with multiple returns and no returns in loops
    166   void AnalyzeReturns(ir::Function* func);
    167 
    168   // Return true if |func| is a function that can be inlined.
    169   bool IsInlinableFunction(ir::Function* func);
    170 
    171   // Update phis in succeeding blocks to point to new last block
    172   void UpdateSucceedingPhis(
    173       std::vector<std::unique_ptr<ir::BasicBlock>>& new_blocks);
    174 
    175   // Initialize state for optimization of |module|
    176   void InitializeInline(ir::Module* module);
    177 
    178   // Module being processed by this pass
    179   ir::Module* module_;
    180 
    181   // Def/Use database
    182   std::unique_ptr<analysis::DefUseManager> def_use_mgr_;
    183 
    184   // Map from function's result id to function.
    185   std::unordered_map<uint32_t, ir::Function*> id2function_;
    186 
    187   // Map from block's label id to block.
    188   std::unordered_map<uint32_t, ir::BasicBlock*> id2block_;
    189 
    190   // Set of ids of functions with multiple returns.
    191   std::set<uint32_t> multi_return_funcs_;
    192 
    193   // Set of ids of functions with no returns in loop
    194   std::set<uint32_t> no_return_in_loop_;
    195 
    196   // Set of ids of inlinable functions
    197   std::set<uint32_t> inlinable_;
    198 
    199   // Map from block to its structured successor blocks. See
    200   // ComputeStructuredSuccessors() for definition.
    201   std::unordered_map<const ir::BasicBlock*, std::vector<ir::BasicBlock*>>
    202       block2structured_succs_;
    203 
    204   // result id for OpConstantFalse
    205   uint32_t false_id_;
    206 
    207   // Next unused ID
    208   uint32_t next_id_;
    209 };
    210 
    211 }  // namespace opt
    212 }  // namespace spvtools
    213 
    214 #endif  // LIBSPIRV_OPT_INLINE_PASS_H_
    215