Handle volatile memory semantics in upgrade (#2674)
* If an atomic is decorated with volatile add the volatile bit to its
memory semantics
diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp
index 31c0abd..ef9f620 100644
--- a/source/opt/upgrade_memory_model.cpp
+++ b/source/opt/upgrade_memory_model.cpp
@@ -25,6 +25,12 @@
namespace opt {
Pass::Status UpgradeMemoryModel::Process() {
+ // TODO: This pass needs changes to support cooperative matrices.
+ if (context()->get_feature_mgr()->HasCapability(
+ SpvCapabilityCooperativeMatrixNV)) {
+ return Pass::Status::SuccessWithoutChange;
+ }
+
// Only update Logical GLSL450 to Logical VulkanKHR.
Instruction* memory_model = get_module()->GetMemoryModel();
if (memory_model->GetSingleWordInOperand(0u) != SpvAddressingModelLogical ||
@@ -110,6 +116,12 @@
}
});
}
+
+ UpgradeMemoryAndImages();
+ UpgradeAtomics();
+}
+
+void UpgradeMemoryModel::UpgradeMemoryAndImages() {
for (auto& func : *get_module()) {
func.ForEachInst([this](Instruction* inst) {
bool is_coherent = false;
@@ -239,6 +251,50 @@
}
}
+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;
+ SpvScope unused_scope = SpvScopeQueueFamilyKHR;
+ std::tie(unused_coherent, is_volatile, unused_scope) =
+ GetInstructionAttributes(inst->GetSingleWordInOperand(0));
+
+ UpgradeSemantics(inst, 2u, is_volatile);
+ if (inst->opcode() == SpvOpAtomicCompareExchange ||
+ inst->opcode() == SpvOpAtomicCompareExchangeWeak) {
+ 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 |= SpvMemorySemanticsVolatileMask;
+ 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, SpvScope> UpgradeMemoryModel::GetInstructionAttributes(
uint32_t id) {
// |id| is a pointer used in a memory/image instruction. Need to determine if
diff --git a/source/opt/upgrade_memory_model.h b/source/opt/upgrade_memory_model.h
index 0dcd4fd..f75304e 100644
--- a/source/opt/upgrade_memory_model.h
+++ b/source/opt/upgrade_memory_model.h
@@ -57,12 +57,19 @@
// capability and extension.
void UpgradeMemoryModelInstruction();
- // Upgrades memory, image and barrier instructions.
+ // Upgrades memory, image and atomic instructions.
// Memory and image instructions convert coherent and volatile decorations
- // into flags on the instruction. Barriers in tessellation shaders get the
- // output storage semantic if appropriate.
+ // into flags on the instruction.
+ // Atomic memory semantics convert volatile decoration into flags on the
+ // instruction.
void UpgradeInstructions();
+ // Upgrades memory and image operands for instructions that have them.
+ void UpgradeMemoryAndImages();
+
+ // Adds the volatile memory semantic if necessary.
+ void UpgradeAtomics();
+
// Returns whether |id| is coherent and/or volatile.
std::tuple<bool, bool, SpvScope> GetInstructionAttributes(uint32_t id);
@@ -95,6 +102,11 @@
bool is_volatile, OperationType operation_type,
InstructionType inst_type);
+ // Modifies the semantics at |in_operand| of |inst| to include the volatile
+ // bit if |is_volatile| is true.
+ void UpgradeSemantics(Instruction* inst, uint32_t in_operand,
+ bool is_volatile);
+
// Returns the result id for a constant for |scope|.
uint32_t GetScopeConstant(SpvScope scope);
diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp
index 8de7e02..b012383 100644
--- a/test/opt/upgrade_memory_model_test.cpp
+++ b/test/opt/upgrade_memory_model_test.cpp
@@ -2037,4 +2037,203 @@
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoad) {
+ const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768
+; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%relaxed = OpConstant %int 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicLoad %int %ssbo_var %device %relaxed
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadPreviousFlags) {
+ const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834
+; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%acquire_ssbo = OpConstant %int 66
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicLoad %int %ssbo_var %device %acquire_ssbo
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicStore) {
+ const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32768
+; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%device = OpConstant %int 1
+%relaxed = OpConstant %int 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpAtomicStore %ssbo_var %device %relaxed %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicStorePreviousFlags) {
+ const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32836
+; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%device = OpConstant %int 1
+%release_ssbo = OpConstant %int 68
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpAtomicStore %ssbo_var %device %release_ssbo %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchange) {
+ const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768
+; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile]] [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%device = OpConstant %int 1
+%relaxed = OpConstant %int 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicCompareExchange %int %ssbo_var %device %relaxed %relaxed %int_0 %int_1
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchangePreviousFlags) {
+ const std::string text = R"(
+; CHECK-NOT: OpDecorate {{.*}} Volatile
+; CHECK: [[volatile_acq_rel:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32840
+; CHECK: [[volatile_acq:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834
+; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile_acq_rel]] [[volatile_acq]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpDecorate %ssbo_var Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%device = OpConstant %int 1
+%acq_ssbo = OpConstant %int 66
+%acq_rel_ssbo = OpConstant %int 72
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%ld = OpAtomicCompareExchange %int %ssbo_var %device %acq_rel_ssbo %acq_ssbo %int_0 %int_1
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadMemberDecoration) {
+ const std::string text = R"(
+; CHECK-NOT: OpMemberDecorate {{.*}} {{.*}} Volatile
+; CHECK: [[relaxed:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 0
+; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768
+; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[relaxed]]
+; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]]
+OpCapability Shader
+OpCapability Linkage
+OpMemoryModel Logical GLSL450
+OpMemberDecorate %struct 1 Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%device = OpConstant %int 1
+%relaxed = OpConstant %int 0
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%struct = OpTypeStruct %int %int
+%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
+%ssbo_var = OpVariable %ptr_ssbo_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep0 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_0
+%ld0 = OpAtomicLoad %int %gep0 %device %relaxed
+%gep1 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_1
+%ld1 = OpAtomicLoad %int %gep1 %device %relaxed
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
} // namespace