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
|
// Copyright (c) 2021 Google LLC
//
// 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_REPLACE_DESC_VAR_INDEX_ACCESS_H_
#define SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
#include <cstdio>
#include <memory>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "source/opt/function.h"
#include "source/opt/pass.h"
#include "source/opt/type_manager.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
class ReplaceDescArrayAccessUsingVarIndex : public Pass {
public:
ReplaceDescArrayAccessUsingVarIndex() {}
const char* name() const override {
return "replace-desc-array-access-using-var-index";
}
Status Process() override;
IRContext::Analysis GetPreservedAnalyses() override {
return IRContext::kAnalysisDefUse |
IRContext::kAnalysisInstrToBlockMapping |
IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
}
private:
// Replaces all accesses to |var| using variable indices with constant
// elements of the array |var|. Creates switch-case statements to determine
// the value of the variable index for all the possible cases.
Status ReplaceVariableAccessesWithConstantElements(Instruction* var) const;
// Replaces the OpAccessChain or OpInBoundsAccessChain instruction |use| that
// uses the descriptor variable |var| with the OpAccessChain or
// OpInBoundsAccessChain instruction with a constant Indexes operand.
bool ReplaceAccessChain(Instruction* var, Instruction* use) const;
// Updates the first Indexes operand of the OpAccessChain or
// OpInBoundsAccessChain instruction |access_chain| to let it use a constant
// index |const_element_idx|.
void UseConstIndexForAccessChain(Instruction* access_chain,
uint32_t const_element_idx) const;
// Replaces users of the OpAccessChain or OpInBoundsAccessChain instruction
// |access_chain| that accesses an array descriptor variable using variable
// indices with constant elements. |number_of_elements| is the number
// of array elements.
bool ReplaceUsersOfAccessChain(Instruction* access_chain,
uint32_t number_of_elements) const;
// Puts all the recursive users of |access_chain| with concrete result types
// or the ones without result it in |final_users|.
void CollectRecursiveUsersWithConcreteType(
Instruction* access_chain, std::vector<Instruction*>* final_users) const;
// Recursively collects the operands of |user| (and operands of the operands)
// whose result types are images/samplers (or pointers/arrays/ structs of
// them) and access chains instructions and returns them. The returned
// collection includes |user|.
std::deque<Instruction*> CollectRequiredImageAndAccessInsts(
Instruction* user) const;
// Returns whether result type of |inst| is an image/sampler/pointer of image
// or sampler or not.
bool HasImageOrImagePtrType(const Instruction* inst) const;
// Returns whether |type_inst| is an image/sampler or pointer/array/struct of
// image or sampler or not.
bool IsImageOrImagePtrType(const Instruction* type_inst) const;
// Returns whether the type with |type_id| is a concrete type or not.
bool IsConcreteType(uint32_t type_id) const;
// Replaces the non-uniform access to a descriptor variable
// |access_chain_final_user| with OpSwitch instruction and case blocks. Each
// case block will contain a clone of |access_chain| and clones of
// |non_uniform_accesses_to_clone| that are recursively used by
// |access_chain_final_user|. The clone of |access_chain| (or
// OpInBoundsAccessChain) will have a constant index for its first index. The
// OpSwitch instruction will have the cases for the variable index of
// |access_chain| from 0 to |number_of_elements| - 1.
bool ReplaceNonUniformAccessWithSwitchCase(
Instruction* access_chain_final_user, Instruction* access_chain,
uint32_t number_of_elements,
const std::deque<Instruction*>& non_uniform_accesses_to_clone) const;
// Creates and returns a new basic block that contains all instructions of
// |block| after |separation_begin_inst|. The new basic block is added to the
// function in this method.
BasicBlock* SeparateInstructionsIntoNewBlock(
BasicBlock* block, Instruction* separation_begin_inst) const;
// Creates and returns a new block.
BasicBlock* CreateNewBlock() const;
// Returns the first operand id of the OpAccessChain or OpInBoundsAccessChain
// instruction |access_chain|.
uint32_t GetFirstIndexOfAccessChain(Instruction* access_chain) const;
// Adds a clone of the OpAccessChain or OpInBoundsAccessChain instruction
// |access_chain| to |case_block|. The clone of |access_chain| will use
// |const_element_idx| for its first index. |old_ids_to_new_ids| keeps the
// mapping from the result id of |access_chain| to the result of its clone.
bool AddConstElementAccessToCaseBlock(
BasicBlock* case_block, Instruction* access_chain,
uint32_t const_element_idx,
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
// Clones all instructions in |insts_to_be_cloned| and put them to |block|.
// |old_ids_to_new_ids| keeps the mapping from the result id of each
// instruction of |insts_to_be_cloned| to the result of their clones.
bool CloneInstsToBlock(
BasicBlock* block, Instruction* inst_to_skip_cloning,
const std::deque<Instruction*>& insts_to_be_cloned,
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
// Adds OpBranch to |branch_destination| at the end of |parent_block|.
void AddBranchToBlock(BasicBlock* parent_block,
uint32_t branch_destination) const;
// Replaces in-operands of all instructions in the basic block |block| using
// |old_ids_to_new_ids|. It conducts the replacement only if the in-operand
// id is a key of |old_ids_to_new_ids|.
void UseNewIdsInBlock(
BasicBlock* block,
const std::unordered_map<uint32_t, uint32_t>& old_ids_to_new_ids) const;
// Creates a case block for |element_index| case. It adds clones of
// |insts_to_be_cloned| and a clone of |access_chain| with |element_index| as
// its first index. The termination instruction of the created case block will
// be a branch to |branch_target_id|. Puts old ids to new ids map for the
// cloned instructions in |old_ids_to_new_ids|.
BasicBlock* CreateCaseBlock(
Instruction* access_chain, uint32_t element_index,
const std::deque<Instruction*>& insts_to_be_cloned,
uint32_t branch_target_id,
std::unordered_map<uint32_t, uint32_t>* old_ids_to_new_ids) const;
// Creates a default block for switch-case statement that has only a single
// instruction OpBranch whose target is a basic block with |merge_block_id|.
// If |null_const_for_phi_is_needed| is true, gets or creates a default null
// constant value for a phi instruction whose operands are |phi_operands| and
// puts it in |phi_operands|.
BasicBlock* CreateDefaultBlock(bool null_const_for_phi_is_needed,
std::vector<uint32_t>* phi_operands,
uint32_t merge_block_id) const;
// Creates and adds an OpSwitch used for the selection of OpAccessChain whose
// first Indexes operand is |access_chain_index_var_id|. The OpSwitch will be
// added at the end of |parent_block|. It will jump to |default_id| for the
// default case and jumps to one of case blocks whose ids are |case_block_ids|
// if |access_chain_index_var_id| matches the case number. |merge_id| is the
// merge block id.
void AddSwitchForAccessChain(
BasicBlock* parent_block, uint32_t access_chain_index_var_id,
uint32_t default_id, uint32_t merge_id,
const std::vector<uint32_t>& case_block_ids) const;
// Creates a phi instruction with |phi_operands| as values and
// |case_block_ids| and |default_block_id| as incoming blocks. The size of
// |phi_operands| must be exactly 1 larger than the size of |case_block_ids|.
// The last element of |phi_operands| will be used for |default_block_id|. It
// adds the phi instruction to the beginning of |parent_block|. Returns 0 if
// it fails to create the Phi instruction.
uint32_t CreatePhiInstruction(BasicBlock* parent_block,
const std::vector<uint32_t>& phi_operands,
const std::vector<uint32_t>& case_block_ids,
uint32_t default_block_id) const;
// Replaces the incoming block operand of OpPhi instructions with
// |new_incoming_block_id| if the incoming block operand is
// |old_incoming_block_id|.
void ReplacePhiIncomingBlock(uint32_t old_incoming_block_id,
uint32_t new_incoming_block_id) const;
// Create an OpConstantNull instruction whose result type id is |type_id|.
Instruction* GetConstNull(uint32_t type_id) const;
};
} // namespace opt
} // namespace spvtools
#endif // SOURCE_OPT_REPLACE_DESC_VAR_INDEX_ACCESS_H_
|