| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780 |
- // 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 "upgrade_memory_model.h"
- #include <utility>
- #include "source/opt/ir_builder.h"
- #include "source/opt/ir_context.h"
- #include "source/spirv_constant.h"
- #include "source/util/make_unique.h"
- #include "source/util/string_utils.h"
- namespace spvtools {
- namespace opt {
- Pass::Status UpgradeMemoryModel::Process() {
- // TODO: This pass needs changes to support cooperative matrices.
- if (context()->get_feature_mgr()->HasCapability(
- spv::Capability::CooperativeMatrixNV)) {
- return Pass::Status::SuccessWithoutChange;
- }
- // Only update Logical GLSL450 to Logical VulkanKHR.
- Instruction* memory_model = get_module()->GetMemoryModel();
- if (memory_model->GetSingleWordInOperand(0u) !=
- uint32_t(spv::AddressingModel::Logical) ||
- memory_model->GetSingleWordInOperand(1u) !=
- uint32_t(spv::MemoryModel::GLSL450)) {
- return Pass::Status::SuccessWithoutChange;
- }
- UpgradeMemoryModelInstruction();
- UpgradeInstructions();
- CleanupDecorations();
- UpgradeBarriers();
- UpgradeMemoryScope();
- return Pass::Status::SuccessWithChange;
- }
- void UpgradeMemoryModel::UpgradeMemoryModelInstruction() {
- // Overall changes necessary:
- // 1. Add the OpExtension.
- // 2. Add the OpCapability.
- // 3. Modify the memory model.
- Instruction* memory_model = get_module()->GetMemoryModel();
- context()->AddCapability(MakeUnique<Instruction>(
- context(), spv::Op::OpCapability, 0, 0,
- std::initializer_list<Operand>{
- {SPV_OPERAND_TYPE_CAPABILITY,
- {uint32_t(spv::Capability::VulkanMemoryModelKHR)}}}));
- const std::string extension = "SPV_KHR_vulkan_memory_model";
- std::vector<uint32_t> words = spvtools::utils::MakeVector(extension);
- context()->AddExtension(
- MakeUnique<Instruction>(context(), spv::Op::OpExtension, 0, 0,
- std::initializer_list<Operand>{
- {SPV_OPERAND_TYPE_LITERAL_STRING, words}}));
- memory_model->SetInOperand(1u, {uint32_t(spv::MemoryModel::VulkanKHR)});
- }
- void UpgradeMemoryModel::UpgradeInstructions() {
- // Coherent and Volatile decorations are deprecated. Remove them and replace
- // with flags on the memory/image operations. The decorations can occur on
- // OpVariable, OpFunctionParameter (of pointer type) and OpStructType (member
- // decoration). Trace from the decoration target(s) to the final memory/image
- // instructions. Additionally, Workgroup storage class variables and function
- // parameters are implicitly coherent in GLSL450.
- // Upgrade modf and frexp first since they generate new stores.
- // In SPIR-V 1.4 or later, normalize OpCopyMemory* access operands.
- for (auto& func : *get_module()) {
- func.ForEachInst([this](Instruction* inst) {
- if (inst->opcode() == spv::Op::OpExtInst) {
- auto ext_inst = inst->GetSingleWordInOperand(1u);
- if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) {
- auto import =
- get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u));
- if (import->GetInOperand(0u).AsString() == "GLSL.std.450") {
- UpgradeExtInst(inst);
- }
- }
- } else if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
- if (inst->opcode() == spv::Op::OpCopyMemory ||
- inst->opcode() == spv::Op::OpCopyMemorySized) {
- uint32_t start_operand =
- inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u;
- if (inst->NumInOperands() > start_operand) {
- auto num_access_words = MemoryAccessNumWords(
- inst->GetSingleWordInOperand(start_operand));
- if ((num_access_words + start_operand) == inst->NumInOperands()) {
- // There is a single memory access operand. Duplicate it to have a
- // separate operand for both source and target.
- for (uint32_t i = 0; i < num_access_words; ++i) {
- auto operand = inst->GetInOperand(start_operand + i);
- inst->AddOperand(std::move(operand));
- }
- }
- } else {
- // Add two memory access operands.
- inst->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
- {uint32_t(spv::MemoryAccessMask::MaskNone)}});
- inst->AddOperand({SPV_OPERAND_TYPE_MEMORY_ACCESS,
- {uint32_t(spv::MemoryAccessMask::MaskNone)}});
- }
- }
- }
- });
- }
- UpgradeMemoryAndImages();
- UpgradeAtomics();
- }
- void UpgradeMemoryModel::UpgradeMemoryAndImages() {
- for (auto& func : *get_module()) {
- func.ForEachInst([this](Instruction* inst) {
- bool is_coherent = false;
- bool is_volatile = false;
- bool src_coherent = false;
- bool src_volatile = false;
- bool dst_coherent = false;
- bool dst_volatile = false;
- uint32_t start_operand = 0u;
- spv::Scope scope = spv::Scope::QueueFamilyKHR;
- spv::Scope src_scope = spv::Scope::QueueFamilyKHR;
- spv::Scope dst_scope = spv::Scope::QueueFamilyKHR;
- switch (inst->opcode()) {
- case spv::Op::OpLoad:
- case spv::Op::OpStore:
- std::tie(is_coherent, is_volatile, scope) =
- GetInstructionAttributes(inst->GetSingleWordInOperand(0u));
- break;
- case spv::Op::OpImageRead:
- case spv::Op::OpImageSparseRead:
- case spv::Op::OpImageWrite:
- std::tie(is_coherent, is_volatile, scope) =
- GetInstructionAttributes(inst->GetSingleWordInOperand(0u));
- break;
- case spv::Op::OpCopyMemory:
- case spv::Op::OpCopyMemorySized:
- std::tie(dst_coherent, dst_volatile, dst_scope) =
- GetInstructionAttributes(inst->GetSingleWordInOperand(0u));
- std::tie(src_coherent, src_volatile, src_scope) =
- GetInstructionAttributes(inst->GetSingleWordInOperand(1u));
- break;
- default:
- break;
- }
- switch (inst->opcode()) {
- case spv::Op::OpLoad:
- UpgradeFlags(inst, 1u, is_coherent, is_volatile, kVisibility,
- kMemory);
- break;
- case spv::Op::OpStore:
- UpgradeFlags(inst, 2u, is_coherent, is_volatile, kAvailability,
- kMemory);
- break;
- case spv::Op::OpCopyMemory:
- case spv::Op::OpCopyMemorySized:
- start_operand = inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u;
- if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
- // There are guaranteed to be two memory access operands at this
- // point so treat source and target separately.
- uint32_t num_access_words = MemoryAccessNumWords(
- inst->GetSingleWordInOperand(start_operand));
- UpgradeFlags(inst, start_operand, dst_coherent, dst_volatile,
- kAvailability, kMemory);
- UpgradeFlags(inst, start_operand + num_access_words, src_coherent,
- src_volatile, kVisibility, kMemory);
- } else {
- UpgradeFlags(inst, start_operand, dst_coherent, dst_volatile,
- kAvailability, kMemory);
- UpgradeFlags(inst, start_operand, src_coherent, src_volatile,
- kVisibility, kMemory);
- }
- break;
- case spv::Op::OpImageRead:
- case spv::Op::OpImageSparseRead:
- UpgradeFlags(inst, 2u, is_coherent, is_volatile, kVisibility, kImage);
- break;
- case spv::Op::OpImageWrite:
- UpgradeFlags(inst, 3u, is_coherent, is_volatile, kAvailability,
- kImage);
- break;
- default:
- break;
- }
- // |is_coherent| is never used for the same instructions as
- // |src_coherent| and |dst_coherent|.
- if (is_coherent) {
- inst->AddOperand(
- {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(scope)}});
- }
- if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
- // There are two memory access operands. The first is for the target and
- // the second is for the source.
- if (dst_coherent || src_coherent) {
- start_operand = inst->opcode() == spv::Op::OpCopyMemory ? 2u : 3u;
- std::vector<Operand> new_operands;
- uint32_t num_access_words =
- MemoryAccessNumWords(inst->GetSingleWordInOperand(start_operand));
- // The flags were already updated so subtract if we're adding a
- // scope.
- if (dst_coherent) --num_access_words;
- for (uint32_t i = 0; i < start_operand + num_access_words; ++i) {
- new_operands.push_back(inst->GetInOperand(i));
- }
- // Add the target scope if necessary.
- if (dst_coherent) {
- new_operands.push_back(
- {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}});
- }
- // Copy the remaining current operands.
- for (uint32_t i = start_operand + num_access_words;
- i < inst->NumInOperands(); ++i) {
- new_operands.push_back(inst->GetInOperand(i));
- }
- // Add the source scope if necessary.
- if (src_coherent) {
- new_operands.push_back(
- {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}});
- }
- inst->SetInOperands(std::move(new_operands));
- }
- } else {
- // According to SPV_KHR_vulkan_memory_model, if both available and
- // visible flags are used the first scope operand is for availability
- // (writes) and the second is for visibility (reads).
- if (dst_coherent) {
- inst->AddOperand(
- {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}});
- }
- if (src_coherent) {
- inst->AddOperand(
- {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}});
- }
- }
- });
- }
- }
- void UpgradeMemoryModel::UpgradeAtomics() {
- for (auto& func : *get_module()) {
- func.ForEachInst([this](Instruction* inst) {
- if (spvOpcodeIsAtomicOp(inst->opcode())) {
- bool unused_coherent = false;
- bool is_volatile = false;
- spv::Scope unused_scope = spv::Scope::QueueFamilyKHR;
- std::tie(unused_coherent, is_volatile, unused_scope) =
- GetInstructionAttributes(inst->GetSingleWordInOperand(0));
- UpgradeSemantics(inst, 2u, is_volatile);
- if (inst->opcode() == spv::Op::OpAtomicCompareExchange ||
- inst->opcode() == spv::Op::OpAtomicCompareExchangeWeak) {
- UpgradeSemantics(inst, 3u, is_volatile);
- }
- }
- });
- }
- }
- void UpgradeMemoryModel::UpgradeSemantics(Instruction* inst,
- uint32_t in_operand,
- bool is_volatile) {
- if (!is_volatile) return;
- uint32_t semantics_id = inst->GetSingleWordInOperand(in_operand);
- const analysis::Constant* constant =
- context()->get_constant_mgr()->FindDeclaredConstant(semantics_id);
- const analysis::Integer* type = constant->type()->AsInteger();
- assert(type && type->width() == 32);
- uint32_t value = 0;
- if (type->IsSigned()) {
- value = static_cast<uint32_t>(constant->GetS32());
- } else {
- value = constant->GetU32();
- }
- value |= uint32_t(spv::MemorySemanticsMask::Volatile);
- auto new_constant = context()->get_constant_mgr()->GetConstant(type, {value});
- auto new_semantics =
- context()->get_constant_mgr()->GetDefiningInstruction(new_constant);
- inst->SetInOperand(in_operand, {new_semantics->result_id()});
- }
- std::tuple<bool, bool, spv::Scope> UpgradeMemoryModel::GetInstructionAttributes(
- uint32_t id) {
- // |id| is a pointer used in a memory/image instruction. Need to determine if
- // that pointer points to volatile or coherent memory. Workgroup storage
- // class is implicitly coherent and cannot be decorated with volatile, so
- // short circuit that case.
- Instruction* inst = context()->get_def_use_mgr()->GetDef(id);
- analysis::Type* type = context()->get_type_mgr()->GetType(inst->type_id());
- if (type->AsPointer() &&
- type->AsPointer()->storage_class() == spv::StorageClass::Workgroup) {
- return std::make_tuple(true, false, spv::Scope::Workgroup);
- }
- bool is_coherent = false;
- bool is_volatile = false;
- std::unordered_set<uint32_t> visited;
- std::tie(is_coherent, is_volatile) =
- TraceInstruction(context()->get_def_use_mgr()->GetDef(id),
- std::vector<uint32_t>(), &visited);
- return std::make_tuple(is_coherent, is_volatile, spv::Scope::QueueFamilyKHR);
- }
- std::pair<bool, bool> UpgradeMemoryModel::TraceInstruction(
- Instruction* inst, std::vector<uint32_t> indices,
- std::unordered_set<uint32_t>* visited) {
- auto iter = cache_.find(std::make_pair(inst->result_id(), indices));
- if (iter != cache_.end()) {
- return iter->second;
- }
- if (!visited->insert(inst->result_id()).second) {
- return std::make_pair(false, false);
- }
- // Initialize the cache before |indices| is (potentially) modified.
- auto& cached_result = cache_[std::make_pair(inst->result_id(), indices)];
- cached_result.first = false;
- cached_result.second = false;
- bool is_coherent = false;
- bool is_volatile = false;
- switch (inst->opcode()) {
- case spv::Op::OpVariable:
- case spv::Op::OpFunctionParameter:
- is_coherent |= HasDecoration(inst, 0, spv::Decoration::Coherent);
- is_volatile |= HasDecoration(inst, 0, spv::Decoration::Volatile);
- if (!is_coherent || !is_volatile) {
- bool type_coherent = false;
- bool type_volatile = false;
- std::tie(type_coherent, type_volatile) =
- CheckType(inst->type_id(), indices);
- is_coherent |= type_coherent;
- is_volatile |= type_volatile;
- }
- break;
- case spv::Op::OpAccessChain:
- case spv::Op::OpInBoundsAccessChain:
- // Store indices in reverse order.
- for (uint32_t i = inst->NumInOperands() - 1; i > 0; --i) {
- indices.push_back(inst->GetSingleWordInOperand(i));
- }
- break;
- case spv::Op::OpPtrAccessChain:
- // Store indices in reverse order. Skip the |Element| operand.
- for (uint32_t i = inst->NumInOperands() - 1; i > 1; --i) {
- indices.push_back(inst->GetSingleWordInOperand(i));
- }
- break;
- default:
- break;
- }
- // No point searching further.
- if (is_coherent && is_volatile) {
- cached_result.first = true;
- cached_result.second = true;
- return std::make_pair(true, true);
- }
- // Variables and function parameters are sources. Continue searching until we
- // reach them.
- if (inst->opcode() != spv::Op::OpVariable &&
- inst->opcode() != spv::Op::OpFunctionParameter) {
- inst->ForEachInId([this, &is_coherent, &is_volatile, &indices,
- &visited](const uint32_t* id_ptr) {
- Instruction* op_inst = context()->get_def_use_mgr()->GetDef(*id_ptr);
- const analysis::Type* type =
- context()->get_type_mgr()->GetType(op_inst->type_id());
- if (type &&
- (type->AsPointer() || type->AsImage() || type->AsSampledImage())) {
- bool operand_coherent = false;
- bool operand_volatile = false;
- std::tie(operand_coherent, operand_volatile) =
- TraceInstruction(op_inst, indices, visited);
- is_coherent |= operand_coherent;
- is_volatile |= operand_volatile;
- }
- });
- }
- cached_result.first = is_coherent;
- cached_result.second = is_volatile;
- return std::make_pair(is_coherent, is_volatile);
- }
- std::pair<bool, bool> UpgradeMemoryModel::CheckType(
- uint32_t type_id, const std::vector<uint32_t>& indices) {
- bool is_coherent = false;
- bool is_volatile = false;
- Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id);
- assert(type_inst->opcode() == spv::Op::OpTypePointer);
- Instruction* element_inst = context()->get_def_use_mgr()->GetDef(
- type_inst->GetSingleWordInOperand(1u));
- for (int i = (int)indices.size() - 1; i >= 0; --i) {
- if (is_coherent && is_volatile) break;
- if (element_inst->opcode() == spv::Op::OpTypePointer) {
- element_inst = context()->get_def_use_mgr()->GetDef(
- element_inst->GetSingleWordInOperand(1u));
- } else if (element_inst->opcode() == spv::Op::OpTypeStruct) {
- uint32_t index = indices.at(i);
- Instruction* index_inst = context()->get_def_use_mgr()->GetDef(index);
- assert(index_inst->opcode() == spv::Op::OpConstant);
- uint64_t value = GetIndexValue(index_inst);
- is_coherent |= HasDecoration(element_inst, static_cast<uint32_t>(value),
- spv::Decoration::Coherent);
- is_volatile |= HasDecoration(element_inst, static_cast<uint32_t>(value),
- spv::Decoration::Volatile);
- element_inst = context()->get_def_use_mgr()->GetDef(
- element_inst->GetSingleWordInOperand(static_cast<uint32_t>(value)));
- } else {
- assert(spvOpcodeIsComposite(element_inst->opcode()));
- element_inst = context()->get_def_use_mgr()->GetDef(
- element_inst->GetSingleWordInOperand(0u));
- }
- }
- if (!is_coherent || !is_volatile) {
- bool remaining_coherent = false;
- bool remaining_volatile = false;
- std::tie(remaining_coherent, remaining_volatile) =
- CheckAllTypes(element_inst);
- is_coherent |= remaining_coherent;
- is_volatile |= remaining_volatile;
- }
- return std::make_pair(is_coherent, is_volatile);
- }
- std::pair<bool, bool> UpgradeMemoryModel::CheckAllTypes(
- const Instruction* inst) {
- std::unordered_set<const Instruction*> visited;
- std::vector<const Instruction*> stack;
- stack.push_back(inst);
- bool is_coherent = false;
- bool is_volatile = false;
- while (!stack.empty()) {
- const Instruction* def = stack.back();
- stack.pop_back();
- if (!visited.insert(def).second) continue;
- if (def->opcode() == spv::Op::OpTypeStruct) {
- // Any member decorated with coherent and/or volatile is enough to have
- // the related operation be flagged as coherent and/or volatile.
- is_coherent |= HasDecoration(def, std::numeric_limits<uint32_t>::max(),
- spv::Decoration::Coherent);
- is_volatile |= HasDecoration(def, std::numeric_limits<uint32_t>::max(),
- spv::Decoration::Volatile);
- if (is_coherent && is_volatile)
- return std::make_pair(is_coherent, is_volatile);
- // Check the subtypes.
- for (uint32_t i = 0; i < def->NumInOperands(); ++i) {
- stack.push_back(context()->get_def_use_mgr()->GetDef(
- def->GetSingleWordInOperand(i)));
- }
- } else if (spvOpcodeIsComposite(def->opcode())) {
- stack.push_back(context()->get_def_use_mgr()->GetDef(
- def->GetSingleWordInOperand(0u)));
- } else if (def->opcode() == spv::Op::OpTypePointer) {
- stack.push_back(context()->get_def_use_mgr()->GetDef(
- def->GetSingleWordInOperand(1u)));
- }
- }
- return std::make_pair(is_coherent, is_volatile);
- }
- uint64_t UpgradeMemoryModel::GetIndexValue(Instruction* index_inst) {
- const analysis::Constant* index_constant =
- context()->get_constant_mgr()->GetConstantFromInst(index_inst);
- assert(index_constant->AsIntConstant());
- if (index_constant->type()->AsInteger()->IsSigned()) {
- if (index_constant->type()->AsInteger()->width() == 32) {
- return index_constant->GetS32();
- } else {
- return index_constant->GetS64();
- }
- } else {
- if (index_constant->type()->AsInteger()->width() == 32) {
- return index_constant->GetU32();
- } else {
- return index_constant->GetU64();
- }
- }
- }
- bool UpgradeMemoryModel::HasDecoration(const Instruction* inst, uint32_t value,
- spv::Decoration decoration) {
- // If the iteration was terminated early then an appropriate decoration was
- // found.
- return !context()->get_decoration_mgr()->WhileEachDecoration(
- inst->result_id(), (uint32_t)decoration, [value](const Instruction& i) {
- if (i.opcode() == spv::Op::OpDecorate ||
- i.opcode() == spv::Op::OpDecorateId) {
- return false;
- } else if (i.opcode() == spv::Op::OpMemberDecorate) {
- if (value == i.GetSingleWordInOperand(1u) ||
- value == std::numeric_limits<uint32_t>::max())
- return false;
- }
- return true;
- });
- }
- void UpgradeMemoryModel::UpgradeFlags(Instruction* inst, uint32_t in_operand,
- bool is_coherent, bool is_volatile,
- OperationType operation_type,
- InstructionType inst_type) {
- if (!is_coherent && !is_volatile) return;
- uint32_t flags = 0;
- if (inst->NumInOperands() > in_operand) {
- flags |= inst->GetSingleWordInOperand(in_operand);
- }
- if (is_coherent) {
- if (inst_type == kMemory) {
- flags |= uint32_t(spv::MemoryAccessMask::NonPrivatePointerKHR);
- if (operation_type == kVisibility) {
- flags |= uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR);
- } else {
- flags |= uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR);
- }
- } else {
- flags |= uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR);
- if (operation_type == kVisibility) {
- flags |= uint32_t(spv::ImageOperandsMask::MakeTexelVisibleKHR);
- } else {
- flags |= uint32_t(spv::ImageOperandsMask::MakeTexelAvailableKHR);
- }
- }
- }
- if (is_volatile) {
- if (inst_type == kMemory) {
- flags |= uint32_t(spv::MemoryAccessMask::Volatile);
- } else {
- flags |= uint32_t(spv::ImageOperandsMask::VolatileTexelKHR);
- }
- }
- if (inst->NumInOperands() > in_operand) {
- inst->SetInOperand(in_operand, {flags});
- } else if (inst_type == kMemory) {
- inst->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, {flags}});
- } else {
- inst->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_IMAGE, {flags}});
- }
- }
- uint32_t UpgradeMemoryModel::GetScopeConstant(spv::Scope scope) {
- analysis::Integer int_ty(32, false);
- uint32_t int_id = context()->get_type_mgr()->GetTypeInstruction(&int_ty);
- const analysis::Constant* constant =
- context()->get_constant_mgr()->GetConstant(
- context()->get_type_mgr()->GetType(int_id),
- {static_cast<uint32_t>(scope)});
- return context()
- ->get_constant_mgr()
- ->GetDefiningInstruction(constant)
- ->result_id();
- }
- void UpgradeMemoryModel::CleanupDecorations() {
- // All of the volatile and coherent decorations have been dealt with, so now
- // we can just remove them.
- get_module()->ForEachInst([this](Instruction* inst) {
- if (inst->result_id() != 0) {
- context()->get_decoration_mgr()->RemoveDecorationsFrom(
- inst->result_id(), [](const Instruction& dec) {
- switch (dec.opcode()) {
- case spv::Op::OpDecorate:
- case spv::Op::OpDecorateId:
- if (spv::Decoration(dec.GetSingleWordInOperand(1u)) ==
- spv::Decoration::Coherent ||
- spv::Decoration(dec.GetSingleWordInOperand(1u)) ==
- spv::Decoration::Volatile)
- return true;
- break;
- case spv::Op::OpMemberDecorate:
- if (spv::Decoration(dec.GetSingleWordInOperand(2u)) ==
- spv::Decoration::Coherent ||
- spv::Decoration(dec.GetSingleWordInOperand(2u)) ==
- spv::Decoration::Volatile)
- return true;
- break;
- default:
- break;
- }
- return false;
- });
- }
- });
- }
- void UpgradeMemoryModel::UpgradeBarriers() {
- std::vector<Instruction*> barriers;
- // Collects all the control barriers in |function|. Returns true if the
- // function operates on the Output storage class.
- ProcessFunction CollectBarriers = [this, &barriers](Function* function) {
- bool operates_on_output = false;
- for (auto& block : *function) {
- block.ForEachInst([this, &barriers,
- &operates_on_output](Instruction* inst) {
- if (inst->opcode() == spv::Op::OpControlBarrier) {
- barriers.push_back(inst);
- } else if (!operates_on_output) {
- // This instruction operates on output storage class if it is a
- // pointer to output type or any input operand is a pointer to output
- // type.
- analysis::Type* type =
- context()->get_type_mgr()->GetType(inst->type_id());
- if (type && type->AsPointer() &&
- type->AsPointer()->storage_class() == spv::StorageClass::Output) {
- operates_on_output = true;
- return;
- }
- inst->ForEachInId([this, &operates_on_output](uint32_t* id_ptr) {
- Instruction* op_inst =
- context()->get_def_use_mgr()->GetDef(*id_ptr);
- analysis::Type* op_type =
- context()->get_type_mgr()->GetType(op_inst->type_id());
- if (op_type && op_type->AsPointer() &&
- op_type->AsPointer()->storage_class() ==
- spv::StorageClass::Output)
- operates_on_output = true;
- });
- }
- });
- }
- return operates_on_output;
- };
- std::queue<uint32_t> roots;
- for (auto& e : get_module()->entry_points())
- if (spv::ExecutionModel(e.GetSingleWordInOperand(0u)) ==
- spv::ExecutionModel::TessellationControl) {
- roots.push(e.GetSingleWordInOperand(1u));
- if (context()->ProcessCallTreeFromRoots(CollectBarriers, &roots)) {
- for (auto barrier : barriers) {
- // Add OutputMemoryKHR to the semantics of the barriers.
- uint32_t semantics_id = barrier->GetSingleWordInOperand(2u);
- Instruction* semantics_inst =
- context()->get_def_use_mgr()->GetDef(semantics_id);
- analysis::Type* semantics_type =
- context()->get_type_mgr()->GetType(semantics_inst->type_id());
- uint64_t semantics_value = GetIndexValue(semantics_inst);
- const analysis::Constant* constant =
- context()->get_constant_mgr()->GetConstant(
- semantics_type,
- {static_cast<uint32_t>(semantics_value) |
- uint32_t(spv::MemorySemanticsMask::OutputMemoryKHR)});
- barrier->SetInOperand(2u, {context()
- ->get_constant_mgr()
- ->GetDefiningInstruction(constant)
- ->result_id()});
- }
- }
- barriers.clear();
- }
- }
- void UpgradeMemoryModel::UpgradeMemoryScope() {
- get_module()->ForEachInst([this](Instruction* inst) {
- // Don't need to handle all the operations that take a scope.
- // * Group operations can only be subgroup
- // * Non-uniform can only be workgroup or subgroup
- // * Named barriers are not supported by Vulkan
- // * Workgroup ops (e.g. async_copy) have at most workgroup scope.
- if (spvOpcodeIsAtomicOp(inst->opcode())) {
- if (IsDeviceScope(inst->GetSingleWordInOperand(1))) {
- inst->SetInOperand(1, {GetScopeConstant(spv::Scope::QueueFamilyKHR)});
- }
- } else if (inst->opcode() == spv::Op::OpControlBarrier) {
- if (IsDeviceScope(inst->GetSingleWordInOperand(1))) {
- inst->SetInOperand(1, {GetScopeConstant(spv::Scope::QueueFamilyKHR)});
- }
- } else if (inst->opcode() == spv::Op::OpMemoryBarrier) {
- if (IsDeviceScope(inst->GetSingleWordInOperand(0))) {
- inst->SetInOperand(0, {GetScopeConstant(spv::Scope::QueueFamilyKHR)});
- }
- }
- });
- }
- bool UpgradeMemoryModel::IsDeviceScope(uint32_t scope_id) {
- const analysis::Constant* constant =
- context()->get_constant_mgr()->FindDeclaredConstant(scope_id);
- assert(constant && "Memory scope must be a constant");
- const analysis::Integer* type = constant->type()->AsInteger();
- assert(type);
- assert(type->width() == 32 || type->width() == 64);
- if (type->width() == 32) {
- if (type->IsSigned())
- return static_cast<spv::Scope>(constant->GetS32()) == spv::Scope::Device;
- else
- return static_cast<spv::Scope>(constant->GetU32()) == spv::Scope::Device;
- } else {
- if (type->IsSigned())
- return static_cast<spv::Scope>(constant->GetS64()) == spv::Scope::Device;
- else
- return static_cast<spv::Scope>(constant->GetU64()) == spv::Scope::Device;
- }
- assert(false);
- return false;
- }
- void UpgradeMemoryModel::UpgradeExtInst(Instruction* ext_inst) {
- const bool is_modf = ext_inst->GetSingleWordInOperand(1u) == GLSLstd450Modf;
- auto ptr_id = ext_inst->GetSingleWordInOperand(3u);
- auto ptr_type_id = get_def_use_mgr()->GetDef(ptr_id)->type_id();
- auto pointee_type_id =
- get_def_use_mgr()->GetDef(ptr_type_id)->GetSingleWordInOperand(1u);
- auto element_type_id = ext_inst->type_id();
- std::vector<const analysis::Type*> element_types(2);
- element_types[0] = context()->get_type_mgr()->GetType(element_type_id);
- element_types[1] = context()->get_type_mgr()->GetType(pointee_type_id);
- analysis::Struct struct_type(element_types);
- uint32_t struct_id =
- context()->get_type_mgr()->GetTypeInstruction(&struct_type);
- // Change the operation
- GLSLstd450 new_op = is_modf ? GLSLstd450ModfStruct : GLSLstd450FrexpStruct;
- ext_inst->SetOperand(3u, {static_cast<uint32_t>(new_op)});
- // Remove the pointer argument
- ext_inst->RemoveOperand(5u);
- // Set the type id to the new struct.
- ext_inst->SetResultType(struct_id);
- // The result is now a struct of the original result. The zero'th element is
- // old result and should replace the old result. The one'th element needs to
- // be stored via a new instruction.
- auto where = ext_inst->NextNode();
- InstructionBuilder builder(
- context(), where,
- IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
- auto extract_0 =
- builder.AddCompositeExtract(element_type_id, ext_inst->result_id(), {0});
- context()->ReplaceAllUsesWith(ext_inst->result_id(), extract_0->result_id());
- // The extract's input was just changed to itself, so fix that.
- extract_0->SetInOperand(0u, {ext_inst->result_id()});
- auto extract_1 =
- builder.AddCompositeExtract(pointee_type_id, ext_inst->result_id(), {1});
- builder.AddStore(ptr_id, extract_1->result_id());
- }
- uint32_t UpgradeMemoryModel::MemoryAccessNumWords(uint32_t mask) {
- uint32_t result = 1;
- if (mask & uint32_t(spv::MemoryAccessMask::Aligned)) ++result;
- if (mask & uint32_t(spv::MemoryAccessMask::MakePointerAvailableKHR)) ++result;
- if (mask & uint32_t(spv::MemoryAccessMask::MakePointerVisibleKHR)) ++result;
- return result;
- }
- } // namespace opt
- } // namespace spvtools
|