Mem model spv 1.4 (#2565)
* Update memory model support for SPIR-V 1.4
Fixes #2552
* Upgrade memory model now supports two memory access operands for
OpCopyMemory*
* in all cases the pass will first generate two operands by either
adding them or copying
* updates accounts for multiple operands
* tests
diff --git a/source/opt/upgrade_memory_model.cpp b/source/opt/upgrade_memory_model.cpp
index d8836a4..31c0abd 100644
--- a/source/opt/upgrade_memory_model.cpp
+++ b/source/opt/upgrade_memory_model.cpp
@@ -18,6 +18,7 @@
#include "source/opt/ir_builder.h"
#include "source/opt/ir_context.h"
+#include "source/spirv_constant.h"
#include "source/util/make_unique.h"
namespace spvtools {
@@ -70,6 +71,7 @@
// 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() == SpvOpExtInst) {
@@ -82,6 +84,29 @@
UpgradeExtInst(inst);
}
}
+ } else if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
+ if (inst->opcode() == SpvOpCopyMemory ||
+ inst->opcode() == SpvOpCopyMemorySized) {
+ uint32_t start_operand = inst->opcode() == SpvOpCopyMemory ? 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, {SpvMemoryAccessMaskNone}});
+ inst->AddOperand(
+ {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}});
+ }
+ }
}
});
}
@@ -93,6 +118,7 @@
bool src_volatile = false;
bool dst_coherent = false;
bool dst_volatile = false;
+ uint32_t start_operand = 0u;
SpvScope scope = SpvScopeQueueFamilyKHR;
SpvScope src_scope = SpvScopeQueueFamilyKHR;
SpvScope dst_scope = SpvScopeQueueFamilyKHR;
@@ -129,16 +155,23 @@
kMemory);
break;
case SpvOpCopyMemory:
- UpgradeFlags(inst, 2u, dst_coherent, dst_volatile, kAvailability,
- kMemory);
- UpgradeFlags(inst, 2u, src_coherent, src_volatile, kVisibility,
- kMemory);
- break;
case SpvOpCopyMemorySized:
- UpgradeFlags(inst, 3u, dst_coherent, dst_volatile, kAvailability,
- kMemory);
- UpgradeFlags(inst, 3u, src_coherent, src_volatile, kVisibility,
- kMemory);
+ start_operand = inst->opcode() == SpvOpCopyMemory ? 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 SpvOpImageRead:
case SpvOpImageSparseRead:
@@ -158,16 +191,49 @@
inst->AddOperand(
{SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(scope)}});
}
- // 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)}});
+ 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() == SpvOpCopyMemory ? 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)}});
+ }
}
});
}
@@ -636,5 +702,13 @@
builder.AddStore(ptr_id, extract_1->result_id());
}
+uint32_t UpgradeMemoryModel::MemoryAccessNumWords(uint32_t mask) {
+ uint32_t result = 1;
+ if (mask & SpvMemoryAccessAlignedMask) ++result;
+ if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++result;
+ if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) ++result;
+ return result;
+}
+
} // namespace opt
} // namespace spvtools
diff --git a/source/opt/upgrade_memory_model.h b/source/opt/upgrade_memory_model.h
index 9adc33b..0dcd4fd 100644
--- a/source/opt/upgrade_memory_model.h
+++ b/source/opt/upgrade_memory_model.h
@@ -15,11 +15,11 @@
#ifndef LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_
#define LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_
-#include "pass.h"
-
#include <functional>
#include <tuple>
+#include "pass.h"
+
namespace spvtools {
namespace opt {
@@ -123,6 +123,10 @@
// facilitate adding memory model flags.
void UpgradeExtInst(Instruction* modf);
+ // Returns the number of words taken up by a memory access argument and its
+ // implied operands.
+ uint32_t MemoryAccessNumWords(uint32_t mask);
+
// Caches the result of TraceInstruction. For a given result id and set of
// indices, stores whether that combination is coherent and/or volatile.
std::unordered_map<std::pair<uint32_t, std::vector<uint32_t>>,
diff --git a/test/opt/upgrade_memory_model_test.cpp b/test/opt/upgrade_memory_model_test.cpp
index 9d2d762..8de7e02 100644
--- a/test/opt/upgrade_memory_model_test.cpp
+++ b/test/opt/upgrade_memory_model_test.cpp
@@ -1713,4 +1713,328 @@
SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
}
+TEST_F(UpgradeMemoryModelTest, SPV14NormalizeCopyMemoryAddOperands) {
+ const std::string text = R"(
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} None None
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14NormalizeCopyMemoryDuplicateOperand) {
+ const std::string text = R"(
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Nontemporal Nontemporal
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src Nontemporal
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14NormalizeCopyMemoryDuplicateOperands) {
+ const std::string text = R"(
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned 4 Aligned 4
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src Aligned 4
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherent) {
+ const std::string text = R"(
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]] None
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %dst Coherent
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherentPreviousArgs) {
+ const std::string text = R"(
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 [[scope]] Aligned 4
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %dst Coherent
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src Aligned 4
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14CopyMemorySrcCoherent) {
+ const std::string text = R"(
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} None MakePointerVisibleKHR|NonPrivatePointerKHR [[scope]]
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %src Coherent
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14CopyMemorySrcCoherentPreviousArgs) {
+ const std::string text = R"(
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned 4 Aligned|MakePointerVisibleKHR|NonPrivatePointerKHR 4 [[scope]]
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %src Coherent
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src Aligned 4
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothCoherent) {
+ const std::string text = R"(
+; CHECK-DAG: [[queue:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK-DAG: [[wg:%\w+]] = OpConstant {{%\w+}} 2
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[wg]] MakePointerVisibleKHR|NonPrivatePointerKHR [[queue]]
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %src Coherent
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ptr_wg_int = OpTypePointer Workgroup %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_wg_int Workgroup
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothCoherentPreviousArgs) {
+ const std::string text = R"(
+; CHECK-DAG: [[queue:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK-DAG: [[wg:%\w+]] = OpConstant {{%\w+}} 2
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 [[queue]] Aligned|MakePointerVisibleKHR|NonPrivatePointerKHR 4 [[wg]]
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %dst Coherent
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%ptr_wg_int = OpTypePointer Workgroup %int
+%src = OpVariable %ptr_wg_int Workgroup
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src Aligned 4
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothVolatile) {
+ const std::string text = R"(
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Volatile Volatile
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %src Volatile
+OpDecorate %dst Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothVolatilePreviousArgs) {
+ const std::string text = R"(
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Volatile|Aligned 4 Volatile|Aligned 4
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %src Volatile
+OpDecorate %dst Volatile
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src Aligned 4
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherentTwoOperands) {
+ const std::string text = R"(
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailableKHR|NonPrivatePointerKHR [[scope]] None
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %dst Coherent
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src None None
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
+TEST_F(UpgradeMemoryModelTest,
+ SPV14CopyMemoryDstCoherentPreviousArgsTwoOperands) {
+ const std::string text = R"(
+; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5
+; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 [[scope]] Aligned 8
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %func "func" %src %dst
+OpDecorate %dst Coherent
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr_ssbo_int = OpTypePointer StorageBuffer %int
+%src = OpVariable %ptr_ssbo_int StorageBuffer
+%dst = OpVariable %ptr_ssbo_int StorageBuffer
+%void_fn = OpTypeFunction %void
+%func = OpFunction %void None %void_fn
+%entry = OpLabel
+OpCopyMemory %dst %src Aligned 4 Aligned 8
+OpReturn
+OpFunctionEnd
+)";
+
+ SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
+ SinglePassRunAndMatch<opt::UpgradeMemoryModel>(text, true);
+}
+
} // namespace