1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
|
// 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_
|