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 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
|
// Copyright (c) 2022 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_INTERFACE_VAR_SROA_H_
#define SOURCE_OPT_INTERFACE_VAR_SROA_H_
#include <optional>
#include <unordered_set>
#include "source/opt/pass.h"
namespace spvtools {
namespace opt {
// See optimizer.hpp for documentation.
//
// Note that the current implementation of this pass covers only store, load,
// access chain instructions for the interface variables. Supporting other types
// of instructions is a future work.
class InterfaceVariableScalarReplacement : public Pass {
public:
InterfaceVariableScalarReplacement() {}
const char* name() const override {
return "interface-variable-scalar-replacement";
}
Status Process() override;
IRContext::Analysis GetPreservedAnalyses() override {
return IRContext::kAnalysisDecorations | IRContext::kAnalysisDefUse |
IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
}
private:
// A struct containing components of a composite variable. If the composite
// consists of multiple or recursive components, |component_variable| is
// nullptr and |nested_composite_components| keeps the components. If it has a
// single component, |nested_composite_components| is empty and
// |component_variable| is the component. Note that each element of
// |nested_composite_components| has the NestedCompositeComponents struct as
// its type that can recursively keep the components.
struct NestedCompositeComponents {
NestedCompositeComponents() : component_variable(nullptr) {}
bool HasMultipleComponents() const {
return !nested_composite_components.empty();
}
const std::vector<NestedCompositeComponents>& GetComponents() const {
return nested_composite_components;
}
void AddComponent(const NestedCompositeComponents& component) {
nested_composite_components.push_back(component);
}
Instruction* GetComponentVariable() const { return component_variable; }
void SetSingleComponentVariable(Instruction* var) {
component_variable = var;
}
private:
std::vector<NestedCompositeComponents> nested_composite_components;
Instruction* component_variable;
};
// Collects all interface variables used by the |entry_point|.
std::vector<Instruction*> CollectInterfaceVariables(Instruction& entry_point);
// Returns whether |var| has the extra arrayness for the entry point
// |entry_point| or not.
bool HasExtraArrayness(Instruction& entry_point, Instruction* var);
// Finds a Location BuiltIn decoration of |var| and returns it via
// |location|. Returns true whether the location exists or not.
bool GetVariableLocation(Instruction* var, uint32_t* location);
// Finds a Component BuiltIn decoration of |var| and returns it via
// |component|. Returns true whether the component exists or not.
bool GetVariableComponent(Instruction* var, uint32_t* component);
// Returns the type of |var| as an instruction.
Instruction* GetTypeOfVariable(Instruction* var);
// Replaces an interface variable |interface_var| whose type is
// |interface_var_type| with scalars and returns whether it succeeds or not.
// |location| is the value of Location Decoration for |interface_var|.
// |component| is the value of Component Decoration for |interface_var|.
// If |extra_array_length| is 0, it means |interface_var| has a Patch
// decoration. Otherwise, |extra_array_length| denotes the length of the extra
// array of |interface_var|.
Status ReplaceInterfaceVariableWithScalars(Instruction* interface_var,
Instruction* interface_var_type,
uint32_t location,
uint32_t component,
uint32_t extra_array_length);
// Creates scalar variables with the storage classe |storage_class| to replace
// an interface variable whose type is |interface_var_type|. If
// |extra_array_length| is not zero, adds the extra arrayness to the created
// scalar variables.
std::optional<NestedCompositeComponents>
CreateScalarInterfaceVarsForReplacement(Instruction* interface_var_type,
spv::StorageClass storage_class,
uint32_t extra_array_length);
// Creates scalar variables with the storage classe |storage_class| to replace
// the interface variable whose type is OpTypeArray |interface_var_type| with.
// If |extra_array_length| is not zero, adds the extra arrayness to all the
// scalar variables.
std::optional<NestedCompositeComponents> CreateScalarInterfaceVarsForArray(
Instruction* interface_var_type, spv::StorageClass storage_class,
uint32_t extra_array_length);
// Creates scalar variables with the storage classe |storage_class| to replace
// the interface variable whose type is OpTypeMatrix |interface_var_type|
// with. If |extra_array_length| is not zero, adds the extra arrayness to all
// the scalar variables.
std::optional<NestedCompositeComponents> CreateScalarInterfaceVarsForMatrix(
Instruction* interface_var_type, spv::StorageClass storage_class,
uint32_t extra_array_length);
// Recursively adds Location and Component decorations to variables in
// |vars| with |location| and |component|. Increases |location| by one after
// it actually adds Location and Component decorations for a variable.
void AddLocationAndComponentDecorations(const NestedCompositeComponents& vars,
uint32_t* location,
uint32_t component);
// Replaces the interface variable |interface_var| with
// |scalar_interface_vars| and returns whether it succeeds or not.
// |extra_arrayness| is the extra arrayness of the interface variable.
// |scalar_interface_vars| contains the nested variables to replace the
// interface variable with.
Status ReplaceInterfaceVarWith(
Instruction* interface_var, uint32_t extra_arrayness,
const NestedCompositeComponents& scalar_interface_vars);
// Replaces |interface_var| in the operands of instructions
// |interface_var_users| with |scalar_interface_vars|. This is a recursive
// method and |interface_var_component_indices| is used to specify which
// recursive component of |interface_var| is replaced. Returns composite
// construct instructions to be replaced with load instructions of
// |interface_var_users| via |loads_to_composites|. Returns composite
// construct instructions to be replaced with load instructions of access
// chain instructions in |interface_var_users| via
// |loads_for_access_chain_to_composites|.
Status ReplaceComponentsOfInterfaceVarWith(
Instruction* interface_var,
const std::vector<Instruction*>& interface_var_users,
const NestedCompositeComponents& scalar_interface_vars,
std::vector<uint32_t>& interface_var_component_indices,
const uint32_t* extra_array_index,
std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
std::unordered_map<Instruction*, Instruction*>*
loads_for_access_chain_to_composites);
// Replaces |interface_var| in the operands of instructions
// |interface_var_users| with |components| that is a vector of components for
// the interface variable |interface_var|. This is a recursive method and
// |interface_var_component_indices| is used to specify which recursive
// component of |interface_var| is replaced. Returns composite construct
// instructions to be replaced with load instructions of |interface_var_users|
// via |loads_to_composites|. Returns composite construct instructions to be
// replaced with load instructions of access chain instructions in
// |interface_var_users| via |loads_for_access_chain_to_composites|.
Status ReplaceMultipleComponentsOfInterfaceVarWith(
Instruction* interface_var,
const std::vector<Instruction*>& interface_var_users,
const std::vector<NestedCompositeComponents>& components,
std::vector<uint32_t>& interface_var_component_indices,
const uint32_t* extra_array_index,
std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
std::unordered_map<Instruction*, Instruction*>*
loads_for_access_chain_to_composites);
// Replaces a component of |interface_var| that is used as an operand of
// instruction |interface_var_user| with |scalar_var|.
// |interface_var_component_indices| is a vector of recursive indices for
// which recursive component of |interface_var| is replaced. If
// |interface_var_user| is a load, returns the component value via
// |loads_to_component_values|. If |interface_var_user| is an access chain,
// returns the component value for loads of |interface_var_user| via
// |loads_for_access_chain_to_component_values|.
Status ReplaceComponentOfInterfaceVarWith(
Instruction* interface_var, Instruction* interface_var_user,
Instruction* scalar_var,
const std::vector<uint32_t>& interface_var_component_indices,
const uint32_t* extra_array_index,
std::unordered_map<Instruction*, Instruction*>* loads_to_component_values,
std::unordered_map<Instruction*, Instruction*>*
loads_for_access_chain_to_component_values);
// Creates instructions to load |scalar_var| and inserts them before
// |insert_before|. If |extra_array_index| is not null, they load
// |extra_array_index| th component of |scalar_var| instead of |scalar_var|
// itself.
Instruction* LoadScalarVar(Instruction* scalar_var,
const uint32_t* extra_array_index,
Instruction* insert_before);
// Creates instructions to load an access chain to |var| and inserts them
// before |insert_before|. |Indexes| will be Indexes operand of the access
// chain.
Instruction* LoadAccessChainToVar(Instruction* var,
const std::vector<uint32_t>& indexes,
Instruction* insert_before);
// Creates instructions to store a component of an aggregate whose id is
// |value_id| to an access chain to |scalar_var| and inserts the created
// instructions before |insert_before|. To get the component, recursively
// traverses the aggregate with |component_indices| as indexes.
// Numbers in |access_chain_indices| are the Indexes operand of the access
// chain to |scalar_var|
void StoreComponentOfValueToAccessChainToScalarVar(
uint32_t value_id, const std::vector<uint32_t>& component_indices,
Instruction* scalar_var,
const std::vector<uint32_t>& access_chain_indices,
Instruction* insert_before);
// Creates instructions to store a component of an aggregate whose id is
// |value_id| to |scalar_var| and inserts the created instructions before
// |insert_before|. To get the component, recursively traverses the aggregate
// using |extra_array_index| and |component_indices| as indexes.
void StoreComponentOfValueToScalarVar(
uint32_t value_id, const std::vector<uint32_t>& component_indices,
Instruction* scalar_var, const uint32_t* extra_array_index,
Instruction* insert_before);
// Creates instructions to store a component of an aggregate whose id is
// |value_id| to |ptr| and inserts the created instructions before
// |insert_before|. To get the component, recursively traverses the aggregate
// using |extra_array_index| and |component_indices| as indexes.
// |component_type_id| is the id of the type instruction of the component.
void StoreComponentOfValueTo(uint32_t component_type_id, uint32_t value_id,
const std::vector<uint32_t>& component_indices,
Instruction* ptr,
const uint32_t* extra_array_index,
Instruction* insert_before);
// Creates new OpCompositeExtract with |type_id| for Result Type,
// |composite_id| for Composite operand, and |indexes| for Indexes operands.
// If |extra_first_index| is not nullptr, uses it as the first Indexes
// operand.
Instruction* CreateCompositeExtract(uint32_t type_id, uint32_t composite_id,
const std::vector<uint32_t>& indexes,
const uint32_t* extra_first_index);
// Creates a new OpLoad whose Result Type is |type_id| and Pointer operand is
// |ptr|. Inserts the new instruction before |insert_before|.
Instruction* CreateLoad(uint32_t type_id, Instruction* ptr,
Instruction* insert_before);
// Clones an annotation instruction |annotation_inst| and sets the target
// operand of the new annotation instruction as |var_id|.
void CloneAnnotationForVariable(Instruction* annotation_inst,
uint32_t var_id);
// Replaces the interface variable |interface_var| in the operands of the
// entry point |entry_point| with |scalar_var_id|. If it cannot find
// |interface_var| from the operands of the entry point |entry_point|, adds
// |scalar_var_id| as an operand of the entry point |entry_point|.
bool ReplaceInterfaceVarInEntryPoint(Instruction* interface_var,
Instruction* entry_point,
uint32_t scalar_var_id);
// Creates an access chain instruction whose Base operand is |var| and Indexes
// operand is |index|. |component_type_id| is the id of the type instruction
// that is the type of component. Inserts the new access chain before
// |insert_before|.
Instruction* CreateAccessChainWithIndex(uint32_t component_type_id,
Instruction* var, uint32_t index,
Instruction* insert_before);
// Returns the pointee type of the type of variable |var|.
uint32_t GetPointeeTypeIdOfVar(Instruction* var);
// Replaces the access chain |access_chain| and its users with a new access
// chain that points |scalar_var| as the Base operand having
// |interface_var_component_indices| as Indexes operands and users of the new
// access chain. When some of the users are load instructions, returns the
// original load instruction to the new instruction that loads a component of
// the original load value via |loads_to_component_values|.
void ReplaceAccessChainWith(
Instruction* access_chain,
const std::vector<uint32_t>& interface_var_component_indices,
Instruction* scalar_var,
std::unordered_map<Instruction*, Instruction*>*
loads_to_component_values);
// Assuming that |access_chain| is an access chain instruction whose Base
// operand is |base_access_chain|, replaces the operands of |access_chain|
// with operands of |base_access_chain| and Indexes operands of
// |access_chain|.
void UseBaseAccessChainForAccessChain(Instruction* access_chain,
Instruction* base_access_chain);
// Creates composite construct instructions for load instructions that are the
// keys of |loads_to_component_values| if no such composite construct
// instructions exist. Adds a component of the composite as an operand of the
// created composite construct instruction. Each value of
// |loads_to_component_values| is the component. Returns the created composite
// construct instructions using |loads_to_composites|. |depth_to_component| is
// the number of recursive access steps to get the component from the
// composite.
void AddComponentsToCompositesForLoads(
const std::unordered_map<Instruction*, Instruction*>&
loads_to_component_values,
std::unordered_map<Instruction*, Instruction*>* loads_to_composites,
uint32_t depth_to_component);
// Creates a composite construct instruction for a component of the value of
// instruction |load| in |depth_to_component| th recursive depth and inserts
// it after |load|.
Instruction* CreateCompositeConstructForComponentOfLoad(
Instruction* load, uint32_t depth_to_component);
// Creates a new access chain instruction that points to variable |var| whose
// type is the instruction with |var_type_id| and inserts it before
// |insert_before|. The new access chain will have |index_ids| for Indexes
// operands. Returns the type id of the component that is pointed by the new
// access chain via |component_type_id|.
Instruction* CreateAccessChainToVar(uint32_t var_type_id, Instruction* var,
const std::vector<uint32_t>& index_ids,
Instruction* insert_before,
uint32_t* component_type_id);
// Returns the result id of OpTypeArray instrunction whose Element Type
// operand is |elem_type_id| and Length operand is |array_length|.
uint32_t GetArrayType(uint32_t elem_type_id, uint32_t array_length);
// Returns the result id of OpTypePointer instrunction whose Type
// operand is |type_id| and Storage Class operand is |storage_class|.
uint32_t GetPointerType(uint32_t type_id, spv::StorageClass storage_class);
// Kills an instrunction |inst| and its users.
void KillInstructionAndUsers(Instruction* inst);
// Kills a vector of instrunctions |insts| and their users.
void KillInstructionsAndUsers(const std::vector<Instruction*>& insts);
// Kills all OpDecorate instructions for Location and Component of the
// variable whose id is |var_id|.
void KillLocationAndComponentDecorations(uint32_t var_id);
// If |var| has the extra arrayness for an entry point, reports an error and
// returns true. Otherwise, returns false.
bool ReportErrorIfHasExtraArraynessForOtherEntry(Instruction* var);
// If |var| does not have the extra arrayness for an entry point, reports an
// error and returns true. Otherwise, returns false.
bool ReportErrorIfHasNoExtraArraynessForOtherEntry(Instruction* var);
// If |interface_var| has the extra arrayness for an entry point but it does
// not have one for another entry point, reports an error and returns false.
// Otherwise, returns true. |has_extra_arrayness| denotes whether it has an
// extra arrayness for an entry point or not.
bool CheckExtraArraynessConflictBetweenEntries(Instruction* interface_var,
bool has_extra_arrayness);
// Conducts the scalar replacement for the interface variables used by the
// |entry_point|.
Pass::Status ReplaceInterfaceVarsWithScalars(Instruction& entry_point);
// A set of interface variable ids that were already removed from operands of
// the entry point.
std::unordered_set<uint32_t>
interface_vars_removed_from_entry_point_operands_;
// A mapping from ids of new composite construct instructions that load
// instructions are replaced with to the recursive depth of the component of
// load that the new component construct instruction is used for.
std::unordered_map<uint32_t, uint32_t> composite_ids_to_component_depths;
// A set of interface variables with the extra arrayness for any of the entry
// points.
std::unordered_set<Instruction*> vars_with_extra_arrayness;
// A set of interface variables without the extra arrayness for any of the
// entry points.
std::unordered_set<Instruction*> vars_without_extra_arrayness;
// Returns the next available id, or 0 if the id overflows.
uint32_t TakeNextId() { return context()->TakeNextId(); }
};
} // namespace opt
} // namespace spvtools
#endif // SOURCE_OPT_INTERFACE_VAR_SROA_H_
|