| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957 |
- // Copyright (c) 2018 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.
- #include "source/opt/copy_prop_arrays.h"
- #include <utility>
- #include "source/opt/ir_builder.h"
- namespace spvtools {
- namespace opt {
- namespace {
- constexpr uint32_t kLoadPointerInOperand = 0;
- constexpr uint32_t kStorePointerInOperand = 0;
- constexpr uint32_t kStoreObjectInOperand = 1;
- constexpr uint32_t kCompositeExtractObjectInOperand = 0;
- constexpr uint32_t kTypePointerStorageClassInIdx = 0;
- constexpr uint32_t kTypePointerPointeeInIdx = 1;
- constexpr uint32_t kExtInstSetInIdx = 0;
- constexpr uint32_t kExtInstOpInIdx = 1;
- constexpr uint32_t kInterpolantInIdx = 2;
- bool IsDebugDeclareOrValue(Instruction* di) {
- auto dbg_opcode = di->GetCommonDebugOpcode();
- return dbg_opcode == CommonDebugInfoDebugDeclare ||
- dbg_opcode == CommonDebugInfoDebugValue;
- }
- // Returns the number of members in |type|. If |type| is not a composite type
- // or the number of components is not known at compile time, the return value
- // will be 0.
- uint32_t GetNumberOfMembers(const analysis::Type* type, IRContext* context) {
- if (const analysis::Struct* struct_type = type->AsStruct()) {
- return static_cast<uint32_t>(struct_type->element_types().size());
- } else if (const analysis::Array* array_type = type->AsArray()) {
- const analysis::Constant* length_const =
- context->get_constant_mgr()->FindDeclaredConstant(
- array_type->LengthId());
- if (length_const == nullptr) {
- // This can happen if the length is an OpSpecConstant.
- return 0;
- }
- assert(length_const->type()->AsInteger());
- return length_const->GetU32();
- } else if (const analysis::Vector* vector_type = type->AsVector()) {
- return vector_type->element_count();
- } else if (const analysis::Matrix* matrix_type = type->AsMatrix()) {
- return matrix_type->element_count();
- } else {
- return 0;
- }
- }
- } // namespace
- Pass::Status CopyPropagateArrays::Process() {
- bool modified = false;
- for (Function& function : *get_module()) {
- if (function.IsDeclaration()) {
- continue;
- }
- BasicBlock* entry_bb = &*function.begin();
- for (auto var_inst = entry_bb->begin();
- var_inst->opcode() == spv::Op::OpVariable; ++var_inst) {
- worklist_.push(&*var_inst);
- }
- }
- while (!worklist_.empty()) {
- Instruction* var_inst = worklist_.front();
- worklist_.pop();
- // Find the only store to the entire memory location, if it exists.
- Instruction* store_inst = FindStoreInstruction(&*var_inst);
- if (!store_inst) {
- continue;
- }
- std::unique_ptr<MemoryObject> source_object =
- FindSourceObjectIfPossible(&*var_inst, store_inst);
- if (source_object != nullptr) {
- if (!IsPointerToArrayType(var_inst->type_id()) &&
- source_object->GetStorageClass() != spv::StorageClass::Input) {
- continue;
- }
- if (CanUpdateUses(&*var_inst, source_object->GetPointerTypeId(this))) {
- modified = true;
- PropagateObject(&*var_inst, source_object.get(), store_inst);
- }
- }
- }
- return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
- }
- std::unique_ptr<CopyPropagateArrays::MemoryObject>
- CopyPropagateArrays::FindSourceObjectIfPossible(Instruction* var_inst,
- Instruction* store_inst) {
- assert(var_inst->opcode() == spv::Op::OpVariable && "Expecting a variable.");
- // Check that the variable is a composite object where |store_inst|
- // dominates all of its loads.
- if (!store_inst) {
- return nullptr;
- }
- // Look at the loads to ensure they are dominated by the store.
- if (!HasValidReferencesOnly(var_inst, store_inst)) {
- return nullptr;
- }
- // If so, look at the store to see if it is the copy of an object.
- std::unique_ptr<MemoryObject> source = GetSourceObjectIfAny(
- store_inst->GetSingleWordInOperand(kStoreObjectInOperand));
- if (!source) {
- return nullptr;
- }
- // Ensure that |source| does not change between the point at which it is
- // loaded, and the position in which |var_inst| is loaded.
- //
- // For now we will go with the easy to implement approach, and check that the
- // entire variable (not just the specific component) is never written to.
- if (!HasNoStores(source->GetVariable())) {
- return nullptr;
- }
- return source;
- }
- Instruction* CopyPropagateArrays::FindStoreInstruction(
- const Instruction* var_inst) const {
- Instruction* store_inst = nullptr;
- get_def_use_mgr()->WhileEachUser(
- var_inst, [&store_inst, var_inst](Instruction* use) {
- if (use->opcode() == spv::Op::OpStore &&
- use->GetSingleWordInOperand(kStorePointerInOperand) ==
- var_inst->result_id()) {
- if (store_inst == nullptr) {
- store_inst = use;
- } else {
- store_inst = nullptr;
- return false;
- }
- }
- return true;
- });
- return store_inst;
- }
- void CopyPropagateArrays::PropagateObject(Instruction* var_inst,
- MemoryObject* source,
- Instruction* insertion_point) {
- assert(var_inst->opcode() == spv::Op::OpVariable &&
- "This function propagates variables.");
- Instruction* new_access_chain = BuildNewAccessChain(insertion_point, source);
- context()->KillNamesAndDecorates(var_inst);
- UpdateUses(var_inst, new_access_chain);
- }
- Instruction* CopyPropagateArrays::BuildNewAccessChain(
- Instruction* insertion_point,
- CopyPropagateArrays::MemoryObject* source) const {
- InstructionBuilder builder(
- context(), insertion_point,
- IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
- if (source->AccessChain().size() == 0) {
- return source->GetVariable();
- }
- source->BuildConstants();
- std::vector<uint32_t> access_ids(source->AccessChain().size());
- std::transform(
- source->AccessChain().cbegin(), source->AccessChain().cend(),
- access_ids.begin(), [](const AccessChainEntry& entry) {
- assert(entry.is_result_id && "Constants needs to be built first.");
- return entry.result_id;
- });
- return builder.AddAccessChain(source->GetPointerTypeId(this),
- source->GetVariable()->result_id(), access_ids);
- }
- bool CopyPropagateArrays::HasNoStores(Instruction* ptr_inst) {
- return get_def_use_mgr()->WhileEachUser(ptr_inst, [this](Instruction* use) {
- if (use->opcode() == spv::Op::OpLoad) {
- return true;
- } else if (use->opcode() == spv::Op::OpAccessChain) {
- return HasNoStores(use);
- } else if (use->IsDecoration() || use->opcode() == spv::Op::OpName) {
- return true;
- } else if (use->opcode() == spv::Op::OpStore) {
- return false;
- } else if (use->opcode() == spv::Op::OpImageTexelPointer) {
- return true;
- } else if (use->opcode() == spv::Op::OpEntryPoint) {
- return true;
- } else if (IsInterpolationInstruction(use)) {
- return true;
- }
- // Some other instruction. Be conservative.
- return false;
- });
- }
- bool CopyPropagateArrays::HasValidReferencesOnly(Instruction* ptr_inst,
- Instruction* store_inst) {
- BasicBlock* store_block = context()->get_instr_block(store_inst);
- DominatorAnalysis* dominator_analysis =
- context()->GetDominatorAnalysis(store_block->GetParent());
- return get_def_use_mgr()->WhileEachUser(
- ptr_inst,
- [this, store_inst, dominator_analysis, ptr_inst](Instruction* use) {
- if (use->opcode() == spv::Op::OpLoad ||
- use->opcode() == spv::Op::OpImageTexelPointer) {
- // TODO: If there are many load in the same BB as |store_inst| the
- // time to do the multiple traverses can add up. Consider collecting
- // those loads and doing a single traversal.
- return dominator_analysis->Dominates(store_inst, use);
- } else if (IsInterpolationInstruction(use)) {
- // GLSL InterpolateAt* instructions work similarly to loads
- uint32_t interpolant = use->GetSingleWordInOperand(kInterpolantInIdx);
- if (interpolant !=
- store_inst->GetSingleWordInOperand(kStorePointerInOperand))
- return false;
- return dominator_analysis->Dominates(store_inst, use);
- } else if (use->opcode() == spv::Op::OpAccessChain) {
- return HasValidReferencesOnly(use, store_inst);
- } else if (use->IsDecoration() || use->opcode() == spv::Op::OpName) {
- return true;
- } else if (use->opcode() == spv::Op::OpStore) {
- // If we are storing to part of the object it is not an candidate.
- return ptr_inst->opcode() == spv::Op::OpVariable &&
- store_inst->GetSingleWordInOperand(kStorePointerInOperand) ==
- ptr_inst->result_id();
- } else if (IsDebugDeclareOrValue(use)) {
- return true;
- }
- // Some other instruction. Be conservative.
- return false;
- });
- }
- std::unique_ptr<CopyPropagateArrays::MemoryObject>
- CopyPropagateArrays::GetSourceObjectIfAny(uint32_t result) {
- Instruction* result_inst = context()->get_def_use_mgr()->GetDef(result);
- switch (result_inst->opcode()) {
- case spv::Op::OpLoad:
- return BuildMemoryObjectFromLoad(result_inst);
- case spv::Op::OpCompositeExtract:
- return BuildMemoryObjectFromExtract(result_inst);
- case spv::Op::OpCompositeConstruct:
- return BuildMemoryObjectFromCompositeConstruct(result_inst);
- case spv::Op::OpCopyObject:
- case spv::Op::OpCopyLogical:
- return GetSourceObjectIfAny(result_inst->GetSingleWordInOperand(0));
- case spv::Op::OpCompositeInsert:
- return BuildMemoryObjectFromInsert(result_inst);
- default:
- return nullptr;
- }
- }
- std::unique_ptr<CopyPropagateArrays::MemoryObject>
- CopyPropagateArrays::BuildMemoryObjectFromLoad(Instruction* load_inst) {
- std::vector<uint32_t> components_in_reverse;
- analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- Instruction* current_inst = def_use_mgr->GetDef(
- load_inst->GetSingleWordInOperand(kLoadPointerInOperand));
- // Build the access chain for the memory object by collecting the indices used
- // in the OpAccessChain instructions. If we find a variable index, then
- // return |nullptr| because we cannot know for sure which memory location is
- // used.
- //
- // It is built in reverse order because the different |OpAccessChain|
- // instructions are visited in reverse order from which they are applied.
- while (current_inst->opcode() == spv::Op::OpAccessChain) {
- for (uint32_t i = current_inst->NumInOperands() - 1; i >= 1; --i) {
- uint32_t element_index_id = current_inst->GetSingleWordInOperand(i);
- components_in_reverse.push_back(element_index_id);
- }
- current_inst = def_use_mgr->GetDef(current_inst->GetSingleWordInOperand(0));
- }
- // If the address in the load is not constructed from an |OpVariable|
- // instruction followed by a series of |OpAccessChain| instructions, then
- // return |nullptr| because we cannot identify the owner or access chain
- // exactly.
- if (current_inst->opcode() != spv::Op::OpVariable) {
- return nullptr;
- }
- // Build the memory object. Use |rbegin| and |rend| to put the access chain
- // back in the correct order.
- return std::unique_ptr<CopyPropagateArrays::MemoryObject>(
- new MemoryObject(current_inst, components_in_reverse.rbegin(),
- components_in_reverse.rend()));
- }
- std::unique_ptr<CopyPropagateArrays::MemoryObject>
- CopyPropagateArrays::BuildMemoryObjectFromExtract(Instruction* extract_inst) {
- assert(extract_inst->opcode() == spv::Op::OpCompositeExtract &&
- "Expecting an OpCompositeExtract instruction.");
- std::unique_ptr<MemoryObject> result = GetSourceObjectIfAny(
- extract_inst->GetSingleWordInOperand(kCompositeExtractObjectInOperand));
- if (!result) {
- return nullptr;
- }
- // Copy the indices of the extract instruction to |OpAccessChain| indices.
- std::vector<AccessChainEntry> components;
- for (uint32_t i = 1; i < extract_inst->NumInOperands(); ++i) {
- components.push_back({false, {extract_inst->GetSingleWordInOperand(i)}});
- }
- result->PushIndirection(components);
- return result;
- }
- std::unique_ptr<CopyPropagateArrays::MemoryObject>
- CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct(
- Instruction* conststruct_inst) {
- assert(conststruct_inst->opcode() == spv::Op::OpCompositeConstruct &&
- "Expecting an OpCompositeConstruct instruction.");
- // If every operand in the instruction are part of the same memory object, and
- // are being combined in the same order, then the result is the same as the
- // parent.
- std::unique_ptr<MemoryObject> memory_object =
- GetSourceObjectIfAny(conststruct_inst->GetSingleWordInOperand(0));
- if (!memory_object) {
- return nullptr;
- }
- if (!memory_object->IsMember()) {
- return nullptr;
- }
- AccessChainEntry last_access = memory_object->AccessChain().back();
- if (!IsAccessChainIndexValidAndEqualTo(last_access, 0)) {
- return nullptr;
- }
- memory_object->PopIndirection();
- if (memory_object->GetNumberOfMembers() !=
- conststruct_inst->NumInOperands()) {
- return nullptr;
- }
- for (uint32_t i = 1; i < conststruct_inst->NumInOperands(); ++i) {
- std::unique_ptr<MemoryObject> member_object =
- GetSourceObjectIfAny(conststruct_inst->GetSingleWordInOperand(i));
- if (!member_object) {
- return nullptr;
- }
- if (!member_object->IsMember()) {
- return nullptr;
- }
- if (!memory_object->Contains(member_object.get())) {
- return nullptr;
- }
- last_access = member_object->AccessChain().back();
- if (!IsAccessChainIndexValidAndEqualTo(last_access, i)) {
- return nullptr;
- }
- }
- return memory_object;
- }
- std::unique_ptr<CopyPropagateArrays::MemoryObject>
- CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) {
- assert(insert_inst->opcode() == spv::Op::OpCompositeInsert &&
- "Expecting an OpCompositeInsert instruction.");
- analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- const analysis::Type* result_type = type_mgr->GetType(insert_inst->type_id());
- uint32_t number_of_elements = GetNumberOfMembers(result_type, context());
- if (number_of_elements == 0) {
- return nullptr;
- }
- if (insert_inst->NumInOperands() != 3) {
- return nullptr;
- }
- if (insert_inst->GetSingleWordInOperand(2) != number_of_elements - 1) {
- return nullptr;
- }
- std::unique_ptr<MemoryObject> memory_object =
- GetSourceObjectIfAny(insert_inst->GetSingleWordInOperand(0));
- if (!memory_object) {
- return nullptr;
- }
- if (!memory_object->IsMember()) {
- return nullptr;
- }
- AccessChainEntry last_access = memory_object->AccessChain().back();
- if (!IsAccessChainIndexValidAndEqualTo(last_access, number_of_elements - 1)) {
- return nullptr;
- }
- memory_object->PopIndirection();
- Instruction* current_insert =
- def_use_mgr->GetDef(insert_inst->GetSingleWordInOperand(1));
- for (uint32_t i = number_of_elements - 1; i > 0; --i) {
- if (current_insert->opcode() != spv::Op::OpCompositeInsert) {
- return nullptr;
- }
- if (current_insert->NumInOperands() != 3) {
- return nullptr;
- }
- if (current_insert->GetSingleWordInOperand(2) != i - 1) {
- return nullptr;
- }
- std::unique_ptr<MemoryObject> current_memory_object =
- GetSourceObjectIfAny(current_insert->GetSingleWordInOperand(0));
- if (!current_memory_object) {
- return nullptr;
- }
- if (!current_memory_object->IsMember()) {
- return nullptr;
- }
- if (memory_object->AccessChain().size() + 1 !=
- current_memory_object->AccessChain().size()) {
- return nullptr;
- }
- if (!memory_object->Contains(current_memory_object.get())) {
- return nullptr;
- }
- AccessChainEntry current_last_access =
- current_memory_object->AccessChain().back();
- if (!IsAccessChainIndexValidAndEqualTo(current_last_access, i - 1)) {
- return nullptr;
- }
- current_insert =
- def_use_mgr->GetDef(current_insert->GetSingleWordInOperand(1));
- }
- return memory_object;
- }
- bool CopyPropagateArrays::IsAccessChainIndexValidAndEqualTo(
- const AccessChainEntry& entry, uint32_t value) const {
- if (!entry.is_result_id) {
- return entry.immediate == value;
- }
- analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
- const analysis::Constant* constant =
- const_mgr->FindDeclaredConstant(entry.result_id);
- if (!constant || !constant->type()->AsInteger()) {
- return false;
- }
- return constant->GetU32() == value;
- }
- bool CopyPropagateArrays::IsPointerToArrayType(uint32_t type_id) {
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- analysis::Pointer* pointer_type = type_mgr->GetType(type_id)->AsPointer();
- if (pointer_type) {
- return pointer_type->pointee_type()->kind() == analysis::Type::kArray ||
- pointer_type->pointee_type()->kind() == analysis::Type::kImage;
- }
- return false;
- }
- bool CopyPropagateArrays::IsInterpolationInstruction(Instruction* inst) {
- if (inst->opcode() == spv::Op::OpExtInst &&
- inst->GetSingleWordInOperand(kExtInstSetInIdx) ==
- context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450()) {
- uint32_t ext_inst = inst->GetSingleWordInOperand(kExtInstOpInIdx);
- switch (ext_inst) {
- case GLSLstd450InterpolateAtCentroid:
- case GLSLstd450InterpolateAtOffset:
- case GLSLstd450InterpolateAtSample:
- return true;
- }
- }
- return false;
- }
- bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst,
- uint32_t type_id) {
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
- analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- analysis::Type* type = type_mgr->GetType(type_id);
- if (type->AsRuntimeArray()) {
- return false;
- }
- if (!type->AsStruct() && !type->AsArray() && !type->AsPointer()) {
- // If the type is not an aggregate, then the desired type must be the
- // same as the current type. No work to do, and we can do that.
- return true;
- }
- return def_use_mgr->WhileEachUse(original_ptr_inst, [this, type_mgr,
- const_mgr,
- type](Instruction* use,
- uint32_t) {
- if (IsDebugDeclareOrValue(use)) return true;
- switch (use->opcode()) {
- case spv::Op::OpLoad: {
- analysis::Pointer* pointer_type = type->AsPointer();
- uint32_t new_type_id = type_mgr->GetId(pointer_type->pointee_type());
- if (new_type_id != use->type_id()) {
- return CanUpdateUses(use, new_type_id);
- }
- return true;
- }
- case spv::Op::OpExtInst:
- if (IsInterpolationInstruction(use)) {
- return true;
- }
- return false;
- case spv::Op::OpAccessChain: {
- analysis::Pointer* pointer_type = type->AsPointer();
- const analysis::Type* pointee_type = pointer_type->pointee_type();
- std::vector<uint32_t> access_chain;
- for (uint32_t i = 1; i < use->NumInOperands(); ++i) {
- const analysis::Constant* index_const =
- const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i));
- if (index_const) {
- access_chain.push_back(index_const->GetU32());
- } else {
- // Variable index means the type is a type where every element
- // is the same type. Use element 0 to get the type.
- access_chain.push_back(0);
- // We are trying to access a struct with variable indices.
- // This cannot happen.
- if (pointee_type->kind() == analysis::Type::kStruct) {
- return false;
- }
- }
- }
- const analysis::Type* new_pointee_type =
- type_mgr->GetMemberType(pointee_type, access_chain);
- analysis::Pointer pointerTy(new_pointee_type,
- pointer_type->storage_class());
- uint32_t new_pointer_type_id =
- context()->get_type_mgr()->GetTypeInstruction(&pointerTy);
- if (new_pointer_type_id == 0) {
- return false;
- }
- if (new_pointer_type_id != use->type_id()) {
- return CanUpdateUses(use, new_pointer_type_id);
- }
- return true;
- }
- case spv::Op::OpCompositeExtract: {
- std::vector<uint32_t> access_chain;
- for (uint32_t i = 1; i < use->NumInOperands(); ++i) {
- access_chain.push_back(use->GetSingleWordInOperand(i));
- }
- const analysis::Type* new_type =
- type_mgr->GetMemberType(type, access_chain);
- uint32_t new_type_id = type_mgr->GetTypeInstruction(new_type);
- if (new_type_id == 0) {
- return false;
- }
- if (new_type_id != use->type_id()) {
- return CanUpdateUses(use, new_type_id);
- }
- return true;
- }
- case spv::Op::OpStore:
- // If needed, we can create an element-by-element copy to change the
- // type of the value being stored. This way we can always handled
- // stores.
- return true;
- case spv::Op::OpImageTexelPointer:
- case spv::Op::OpName:
- return true;
- default:
- return use->IsDecoration();
- }
- });
- }
- void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
- Instruction* new_ptr_inst) {
- analysis::TypeManager* type_mgr = context()->get_type_mgr();
- analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
- analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- std::vector<std::pair<Instruction*, uint32_t> > uses;
- def_use_mgr->ForEachUse(original_ptr_inst,
- [&uses](Instruction* use, uint32_t index) {
- uses.push_back({use, index});
- });
- for (auto pair : uses) {
- Instruction* use = pair.first;
- uint32_t index = pair.second;
- if (use->IsCommonDebugInstr()) {
- switch (use->GetCommonDebugOpcode()) {
- case CommonDebugInfoDebugDeclare: {
- if (new_ptr_inst->opcode() == spv::Op::OpVariable ||
- new_ptr_inst->opcode() == spv::Op::OpFunctionParameter) {
- context()->ForgetUses(use);
- use->SetOperand(index, {new_ptr_inst->result_id()});
- context()->AnalyzeUses(use);
- } else {
- // Based on the spec, we cannot use a pointer other than OpVariable
- // or OpFunctionParameter for DebugDeclare. We have to use
- // DebugValue with Deref.
- context()->ForgetUses(use);
- // Change DebugDeclare to DebugValue.
- use->SetOperand(index - 2,
- {static_cast<uint32_t>(CommonDebugInfoDebugValue)});
- use->SetOperand(index, {new_ptr_inst->result_id()});
- // Add Deref operation.
- Instruction* dbg_expr =
- def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1));
- auto* deref_expr_instr =
- context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
- use->SetOperand(index + 1, {deref_expr_instr->result_id()});
- context()->AnalyzeUses(deref_expr_instr);
- context()->AnalyzeUses(use);
- }
- break;
- }
- case CommonDebugInfoDebugValue:
- context()->ForgetUses(use);
- use->SetOperand(index, {new_ptr_inst->result_id()});
- context()->AnalyzeUses(use);
- break;
- default:
- assert(false && "Don't know how to rewrite instruction");
- break;
- }
- continue;
- }
- switch (use->opcode()) {
- case spv::Op::OpLoad: {
- // Replace the actual use.
- context()->ForgetUses(use);
- use->SetOperand(index, {new_ptr_inst->result_id()});
- // Update the type.
- Instruction* pointer_type_inst =
- def_use_mgr->GetDef(new_ptr_inst->type_id());
- uint32_t new_type_id =
- pointer_type_inst->GetSingleWordInOperand(kTypePointerPointeeInIdx);
- if (new_type_id != use->type_id()) {
- use->SetResultType(new_type_id);
- context()->AnalyzeUses(use);
- UpdateUses(use, use);
- } else {
- context()->AnalyzeUses(use);
- }
- AddUsesToWorklist(use);
- } break;
- case spv::Op::OpExtInst: {
- if (IsInterpolationInstruction(use)) {
- // Replace the actual use.
- context()->ForgetUses(use);
- use->SetOperand(index, {new_ptr_inst->result_id()});
- context()->AnalyzeUses(use);
- } else {
- assert(false && "Don't know how to rewrite instruction");
- }
- } break;
- case spv::Op::OpAccessChain: {
- // Update the actual use.
- context()->ForgetUses(use);
- use->SetOperand(index, {new_ptr_inst->result_id()});
- // Convert the ids on the OpAccessChain to indices that can be used to
- // get the specific member.
- std::vector<uint32_t> access_chain;
- for (uint32_t i = 1; i < use->NumInOperands(); ++i) {
- const analysis::Constant* index_const =
- const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i));
- if (index_const) {
- access_chain.push_back(index_const->GetU32());
- } else {
- // Variable index means the type is an type where every element
- // is the same type. Use element 0 to get the type.
- access_chain.push_back(0);
- }
- }
- Instruction* pointer_type_inst =
- get_def_use_mgr()->GetDef(new_ptr_inst->type_id());
- uint32_t new_pointee_type_id = GetMemberTypeId(
- pointer_type_inst->GetSingleWordInOperand(kTypePointerPointeeInIdx),
- access_chain);
- spv::StorageClass storage_class = static_cast<spv::StorageClass>(
- pointer_type_inst->GetSingleWordInOperand(
- kTypePointerStorageClassInIdx));
- uint32_t new_pointer_type_id =
- type_mgr->FindPointerToType(new_pointee_type_id, storage_class);
- if (new_pointer_type_id != use->type_id()) {
- use->SetResultType(new_pointer_type_id);
- context()->AnalyzeUses(use);
- UpdateUses(use, use);
- } else {
- context()->AnalyzeUses(use);
- }
- } break;
- case spv::Op::OpCompositeExtract: {
- // Update the actual use.
- context()->ForgetUses(use);
- use->SetOperand(index, {new_ptr_inst->result_id()});
- uint32_t new_type_id = new_ptr_inst->type_id();
- std::vector<uint32_t> access_chain;
- for (uint32_t i = 1; i < use->NumInOperands(); ++i) {
- access_chain.push_back(use->GetSingleWordInOperand(i));
- }
- new_type_id = GetMemberTypeId(new_type_id, access_chain);
- if (new_type_id != use->type_id()) {
- use->SetResultType(new_type_id);
- context()->AnalyzeUses(use);
- UpdateUses(use, use);
- } else {
- context()->AnalyzeUses(use);
- }
- } break;
- case spv::Op::OpStore:
- // If the use is the pointer, then it is the single store to that
- // variable. We do not want to replace it. Instead, it will become
- // dead after all of the loads are removed, and ADCE will get rid of it.
- //
- // If the use is the object being stored, we will create a copy of the
- // object turning it into the correct type. The copy is done by
- // decomposing the object into the base type, which must be the same,
- // and then rebuilding them.
- if (index == 1) {
- Instruction* target_pointer = def_use_mgr->GetDef(
- use->GetSingleWordInOperand(kStorePointerInOperand));
- Instruction* pointer_type =
- def_use_mgr->GetDef(target_pointer->type_id());
- uint32_t pointee_type_id =
- pointer_type->GetSingleWordInOperand(kTypePointerPointeeInIdx);
- uint32_t copy = GenerateCopy(original_ptr_inst, pointee_type_id, use);
- assert(copy != 0 &&
- "Should not be updating uses unless we know it can be done.");
- context()->ForgetUses(use);
- use->SetInOperand(index, {copy});
- context()->AnalyzeUses(use);
- }
- break;
- case spv::Op::OpDecorate:
- // We treat an OpImageTexelPointer as a load. The result type should
- // always have the Image storage class, and should not need to be
- // updated.
- case spv::Op::OpImageTexelPointer:
- // Replace the actual use.
- context()->ForgetUses(use);
- use->SetOperand(index, {new_ptr_inst->result_id()});
- context()->AnalyzeUses(use);
- break;
- default:
- assert(false && "Don't know how to rewrite instruction");
- break;
- }
- }
- }
- uint32_t CopyPropagateArrays::GetMemberTypeId(
- uint32_t id, const std::vector<uint32_t>& access_chain) const {
- for (uint32_t element_index : access_chain) {
- Instruction* type_inst = get_def_use_mgr()->GetDef(id);
- switch (type_inst->opcode()) {
- case spv::Op::OpTypeArray:
- case spv::Op::OpTypeRuntimeArray:
- case spv::Op::OpTypeMatrix:
- case spv::Op::OpTypeVector:
- id = type_inst->GetSingleWordInOperand(0);
- break;
- case spv::Op::OpTypeStruct:
- id = type_inst->GetSingleWordInOperand(element_index);
- break;
- default:
- break;
- }
- assert(id != 0 &&
- "Tried to extract from an object where it cannot be done.");
- }
- return id;
- }
- void CopyPropagateArrays::AddUsesToWorklist(Instruction* inst) {
- analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
- def_use_mgr->ForEachUse(inst, [this](Instruction* use, uint32_t) {
- if (use->opcode() == spv::Op::OpStore) {
- uint32_t var_id;
- Instruction* target_pointer = GetPtr(use, &var_id);
- if (target_pointer->opcode() != spv::Op::OpVariable) {
- return;
- }
- worklist_.push(target_pointer);
- }
- });
- }
- void CopyPropagateArrays::MemoryObject::PushIndirection(
- const std::vector<AccessChainEntry>& access_chain) {
- access_chain_.insert(access_chain_.end(), access_chain.begin(),
- access_chain.end());
- }
- uint32_t CopyPropagateArrays::MemoryObject::GetNumberOfMembers() {
- IRContext* context = variable_inst_->context();
- analysis::TypeManager* type_mgr = context->get_type_mgr();
- const analysis::Type* type = type_mgr->GetType(variable_inst_->type_id());
- type = type->AsPointer()->pointee_type();
- std::vector<uint32_t> access_indices = GetAccessIds();
- type = type_mgr->GetMemberType(type, access_indices);
- return opt::GetNumberOfMembers(type, context);
- }
- template <class iterator>
- CopyPropagateArrays::MemoryObject::MemoryObject(Instruction* var_inst,
- iterator begin, iterator end)
- : variable_inst_(var_inst) {
- std::transform(begin, end, std::back_inserter(access_chain_),
- [](uint32_t id) {
- return AccessChainEntry{true, {id}};
- });
- }
- std::vector<uint32_t> CopyPropagateArrays::MemoryObject::GetAccessIds() const {
- analysis::ConstantManager* const_mgr =
- variable_inst_->context()->get_constant_mgr();
- std::vector<uint32_t> indices(AccessChain().size());
- std::transform(AccessChain().cbegin(), AccessChain().cend(), indices.begin(),
- [&const_mgr](const AccessChainEntry& entry) {
- if (entry.is_result_id) {
- const analysis::Constant* constant =
- const_mgr->FindDeclaredConstant(entry.result_id);
- return constant == nullptr ? 0 : constant->GetU32();
- }
- return entry.immediate;
- });
- return indices;
- }
- bool CopyPropagateArrays::MemoryObject::Contains(
- CopyPropagateArrays::MemoryObject* other) {
- if (this->GetVariable() != other->GetVariable()) {
- return false;
- }
- if (AccessChain().size() > other->AccessChain().size()) {
- return false;
- }
- for (uint32_t i = 0; i < AccessChain().size(); i++) {
- if (AccessChain()[i] != other->AccessChain()[i]) {
- return false;
- }
- }
- return true;
- }
- void CopyPropagateArrays::MemoryObject::BuildConstants() {
- for (auto& entry : access_chain_) {
- if (entry.is_result_id) {
- continue;
- }
- auto context = variable_inst_->context();
- analysis::Integer int_type(32, false);
- const analysis::Type* uint32_type =
- context->get_type_mgr()->GetRegisteredType(&int_type);
- analysis::ConstantManager* const_mgr = context->get_constant_mgr();
- const analysis::Constant* index_const =
- const_mgr->GetConstant(uint32_type, {entry.immediate});
- entry.result_id =
- const_mgr->GetDefiningInstruction(index_const)->result_id();
- entry.is_result_id = true;
- }
- }
- } // namespace opt
- } // namespace spvtools
|