Validate presence of Stride operand to OpCooperativeMatrix{Load,Store}KHR (#5777)
* Validate Stride operand to OpCooperativeMatrix{Load,Store}KHR
The specification requires the Stride operand for the RowMajorKHR and
ColumnMajorKHR layouts.
Signed-off-by: Kevin Petit <kevin.petit@arm.com>
Change-Id: I51084b9b8dedebf9cab7ae25334ee56b75ef0126
* Update source/val/validate_memory.cpp
Co-authored-by: alan-baker <alanbaker@google.com>
* add test to exercise memory layout from spec constant and fix validation
Change-Id: I06d7308c4a2b62d26d69e88e03bfa009a7f8fff3
* format fixes
Change-Id: I9cbabec0ed2172dcd228cc385551cb7a5b79df1a
---------
Signed-off-by: Kevin Petit <kevin.petit@arm.com>
Co-authored-by: alan-baker <alanbaker@google.com>diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index beaa79c..9bfa3c2 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -2115,16 +2115,23 @@
const auto layout_index =
(inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 3u : 2u;
- const auto colmajor_id = inst->GetOperandAs<uint32_t>(layout_index);
- const auto colmajor = _.FindDef(colmajor_id);
- if (!colmajor || !_.IsIntScalarType(colmajor->type_id()) ||
- !(spvOpcodeIsConstant(colmajor->opcode()) ||
- spvOpcodeIsSpecConstant(colmajor->opcode()))) {
+ const auto layout_id = inst->GetOperandAs<uint32_t>(layout_index);
+ const auto layout_inst = _.FindDef(layout_id);
+ if (!layout_inst || !_.IsIntScalarType(layout_inst->type_id()) ||
+ !spvOpcodeIsConstant(layout_inst->opcode())) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "MemoryLayout operand <id> " << _.getIdName(colmajor_id)
+ << "MemoryLayout operand <id> " << _.getIdName(layout_id)
<< " must be a 32-bit integer constant instruction.";
}
+ bool stride_required = false;
+ uint64_t layout;
+ if (_.EvalConstantValUint64(layout_id, &layout)) {
+ stride_required =
+ (layout == (uint64_t)spv::CooperativeMatrixLayout::RowMajorKHR) ||
+ (layout == (uint64_t)spv::CooperativeMatrixLayout::ColumnMajorKHR);
+ }
+
const auto stride_index =
(inst->opcode() == spv::Op::OpCooperativeMatrixLoadKHR) ? 4u : 3u;
if (inst->operands().size() > stride_index) {
@@ -2135,6 +2142,9 @@
<< "Stride operand <id> " << _.getIdName(stride_id)
<< " must be a scalar integer type.";
}
+ } else if (stride_required) {
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
+ << "MemoryLayout " << layout << " requires a Stride.";
}
const auto memory_access_index =
diff --git a/test/opt/aggressive_dead_code_elim_test.cpp b/test/opt/aggressive_dead_code_elim_test.cpp
index dcce4f5..d837099 100644
--- a/test/opt/aggressive_dead_code_elim_test.cpp
+++ b/test/opt/aggressive_dead_code_elim_test.cpp
@@ -8018,6 +8018,7 @@
%uint_3 = OpConstant %uint 3
%uint_16 = OpConstant %uint 16
%uint_4 = OpConstant %uint 4
+%coop_stride = OpConstant %int 42
%_runtimearr_int = OpTypeRuntimeArray %int
%_struct_4 = OpTypeStruct %_runtimearr_int
%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4
@@ -8047,7 +8048,7 @@
%26 = OpVariable %_ptr_Function__ptr_Function_int Function
%27 = OpVariable %_ptr_Function__struct_18 Function
%28 = OpAccessChain %_ptr_StorageBuffer_int %2 %int_0 %uint_0
- %29 = OpCooperativeMatrixLoadKHR %17 %28 %int_1
+ %29 = OpCooperativeMatrixLoadKHR %17 %28 %int_1 %coop_stride
%30 = OpCompositeConstruct %_struct_18 %29
OpStore %27 %30
%31 = OpAccessChain %_ptr_Function_17 %27 %int_0
@@ -8059,7 +8060,7 @@
OpStore %32 %34
%35 = OpAccessChain %_ptr_StorageBuffer_int %2 %int_0 %uint_64
%36 = OpLoad %17 %31
- OpCooperativeMatrixStoreKHR %35 %36 %int_0
+ OpCooperativeMatrixStoreKHR %35 %36 %int_0 %coop_stride
OpReturn
OpFunctionEnd
)";
diff --git a/test/val/val_memory_test.cpp b/test/val/val_memory_test.cpp
index b4689f2..df92fff 100644
--- a/test/val/val_memory_test.cpp
+++ b/test/val/val_memory_test.cpp
@@ -2351,7 +2351,11 @@
}
std::string GenCoopMatLoadStoreShaderKHR(const std::string& storeMemoryAccess,
- const std::string& loadMemoryAccess) {
+ const std::string& loadMemoryAccess,
+ unsigned layout = 0,
+ bool useSpecConstantLayout = false,
+ bool useStoreStride = true,
+ bool useLoadStride = true) {
std::string s = R"(
OpCapability Shader
OpCapability GroupNonUniform
@@ -2408,11 +2412,18 @@
%33 = OpConstant %6 1024
%34 = OpConstant %6 1
%38 = OpConstant %6 8
-%39 = OpConstant %6 0
+%uint_0 = OpConstant %6 0
+)";
+ if (useSpecConstantLayout) {
+ s += "%layout = OpSpecConstant %6 " + std::to_string(layout);
+ } else {
+ s += "%layout = OpConstant %6 " + std::to_string(layout);
+ }
+ s += R"(
%68 = OpTypeFloat 32
%69 = OpConstant %6 16
%70 = OpConstant %6 3
-%71 = OpTypeCooperativeMatrixKHR %68 %70 %69 %38 %39
+%71 = OpTypeCooperativeMatrixKHR %68 %70 %69 %38 %uint_0
%72 = OpTypePointer Function %71
%74 = OpTypeRuntimeArray %68
%75 = OpTypeStruct %74
@@ -2422,7 +2433,7 @@
%79 = OpConstant %78 0
%81 = OpConstant %6 5
%82 = OpTypePointer StorageBuffer %68
-%84 = OpConstant %6 64
+%stride = OpConstant %6 64
%88 = OpTypePointer Private %71
%89 = OpVariable %88 Private
%92 = OpTypeRuntimeArray %68
@@ -2478,7 +2489,7 @@
%35 = OpAccessChain %31 %18 %34
%36 = OpLoad %6 %35
%37 = OpIMul %6 %33 %36
-%40 = OpAccessChain %31 %18 %39
+%40 = OpAccessChain %31 %18 %uint_0
%41 = OpLoad %6 %40
%42 = OpIMul %6 %38 %41
%43 = OpIAdd %6 %37 %42
@@ -2486,7 +2497,7 @@
%45 = OpAccessChain %31 %18 %34
%46 = OpLoad %6 %45
%47 = OpIMul %6 %33 %46
-%48 = OpAccessChain %31 %18 %39
+%48 = OpAccessChain %31 %18 %uint_0
%49 = OpLoad %6 %48
%50 = OpIMul %6 %38 %49
%51 = OpIAdd %6 %47 %50
@@ -2494,7 +2505,7 @@
%53 = OpAccessChain %31 %18 %34
%54 = OpLoad %6 %53
%55 = OpIMul %6 %33 %54
-%56 = OpAccessChain %31 %18 %39
+%56 = OpAccessChain %31 %18 %uint_0
%57 = OpLoad %6 %56
%58 = OpIMul %6 %38 %57
%59 = OpIAdd %6 %55 %58
@@ -2502,27 +2513,33 @@
%61 = OpAccessChain %31 %18 %34
%62 = OpLoad %6 %61
%63 = OpIMul %6 %33 %62
-%64 = OpAccessChain %31 %18 %39
+%64 = OpAccessChain %31 %18 %uint_0
%65 = OpLoad %6 %64
%66 = OpIMul %6 %38 %65
%67 = OpIAdd %6 %63 %66
OpStore %60 %67
%80 = OpLoad %6 %32
%83 = OpAccessChain %82 %77 %79 %80
-%87 = OpCooperativeMatrixLoadKHR %71 %83 %39 %84 )" +
- loadMemoryAccess + R"( %81
+)";
+ if (useLoadStride) {
+ s += "%87 = OpCooperativeMatrixLoadKHR %71 %83 %layout %stride " +
+ loadMemoryAccess + " %81";
+ } else {
+ s += "%87 = OpCooperativeMatrixLoadKHR %71 %83 %layout";
+ }
+ s += R"(
OpStore %73 %87
%90 = OpLoad %71 %73
OpStore %89 %90
%96 = OpLoad %6 %44
%97 = OpAccessChain %82 %95 %79 %96
-%98 = OpCooperativeMatrixLoadKHR %71 %97 %39 %84 MakePointerVisibleKHR|NonPrivatePointerKHR %81
+%98 = OpCooperativeMatrixLoadKHR %71 %97 %layout %stride MakePointerVisibleKHR|NonPrivatePointerKHR %81
OpStore %91 %98
%100 = OpLoad %71 %91
OpStore %99 %100
%106 = OpLoad %6 %52
%107 = OpAccessChain %82 %105 %79 %106
-%108 = OpCooperativeMatrixLoadKHR %71 %107 %39 %84 MakePointerVisibleKHR|NonPrivatePointerKHR %81
+%108 = OpCooperativeMatrixLoadKHR %71 %107 %layout %stride MakePointerVisibleKHR|NonPrivatePointerKHR %81
OpStore %101 %108
%110 = OpLoad %71 %101
OpStore %109 %110
@@ -2532,7 +2549,14 @@
%116 = OpLoad %71 %111
%121 = OpLoad %6 %60
%122 = OpAccessChain %82 %120 %79 %121
-OpCooperativeMatrixStoreKHR %122 %116 %39 %84 )" + storeMemoryAccess + R"( %81
+)";
+ if (useStoreStride) {
+ s += "OpCooperativeMatrixStoreKHR %122 %116 %layout %stride " +
+ storeMemoryAccess + " %81";
+ } else {
+ s += "OpCooperativeMatrixStoreKHR %122 %116 %layout";
+ }
+ s += R"(
OpReturn
OpFunctionEnd
)";
@@ -2549,6 +2573,54 @@
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
}
+struct StrideMissingCase {
+ unsigned layout;
+ bool useLoadStride;
+ bool useStoreStride;
+};
+
+using ValidateCoopMatrixStrideMissing =
+ spvtest::ValidateBase<StrideMissingCase>;
+
+INSTANTIATE_TEST_SUITE_P(
+ CoopMatrixStrideMissing, ValidateCoopMatrixStrideMissing,
+ Values(
+ StrideMissingCase{(unsigned)spv::CooperativeMatrixLayout::RowMajorKHR,
+ false, true},
+ StrideMissingCase{(unsigned)spv::CooperativeMatrixLayout::RowMajorKHR,
+ true, false},
+ StrideMissingCase{
+ (unsigned)spv::CooperativeMatrixLayout::ColumnMajorKHR, false,
+ true},
+ StrideMissingCase{
+ (unsigned)spv::CooperativeMatrixLayout::ColumnMajorKHR, true,
+ false}));
+
+TEST_P(ValidateCoopMatrixStrideMissing, CoopMatKHRLoadStrideMissingFail) {
+ const StrideMissingCase& param = GetParam();
+ std::string spirv = GenCoopMatLoadStoreShaderKHR(
+ "MakePointerAvailableKHR|NonPrivatePointerKHR",
+ "MakePointerVisibleKHR|NonPrivatePointerKHR", param.layout,
+ false /*useSpecConstantLayout*/, param.useStoreStride,
+ param.useLoadStride);
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("MemoryLayout " + std::to_string(param.layout) +
+ " requires a Stride"));
+}
+
+TEST_F(ValidateMemory, CoopMatKHRMemoryLayoutFromSpecConstantSuccess) {
+ std::string spirv = GenCoopMatLoadStoreShaderKHR(
+ "MakePointerAvailableKHR|NonPrivatePointerKHR",
+ "MakePointerVisibleKHR|NonPrivatePointerKHR",
+ (unsigned)spv::CooperativeMatrixLayout::RowMajorKHR,
+ true /*useSpecConstantLayout*/);
+
+ CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1);
+ EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
TEST_F(ValidateMemory, CoopMatKHRStoreMemoryAccessFail) {
std::string spirv = GenCoopMatLoadStoreShaderKHR(
"MakePointerVisibleKHR|NonPrivatePointerKHR",
@@ -6791,11 +6863,12 @@
%rows = OpSpecConstant %int 1
%cols = OpSpecConstant %int 1
%matrix_a = OpConstant %int 1
+%stride = OpConstant %int 42
%matrix = OpTypeCooperativeMatrixKHR %float %subgroup %rows %cols %matrix_a
%var = OpUntypedVariableKHR %untyped StorageBuffer %block
%main = OpFunction %void None %void_fn
%entry = OpLabel
-%ld = OpCooperativeMatrixLoadKHR %matrix %var %int_0
+%ld = OpCooperativeMatrixLoadKHR %matrix %var %int_0 %stride
OpReturn
OpFunctionEnd
)";
@@ -6832,12 +6905,13 @@
%rows = OpSpecConstant %int 1
%cols = OpSpecConstant %int 1
%matrix_a = OpConstant %int 1
+%stride = OpConstant %int 42
%matrix = OpTypeCooperativeMatrixKHR %float %subgroup %rows %cols %matrix_a
%var = OpUntypedVariableKHR %untyped StorageBuffer %block
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpUntypedAccessChainKHR %untyped %block %var %int_0 %int_0
-%ld = OpCooperativeMatrixLoadKHR %matrix %gep %int_0
+%ld = OpCooperativeMatrixLoadKHR %matrix %gep %int_0 %stride
OpReturn
OpFunctionEnd
)";
@@ -6878,14 +6952,15 @@
%rows = OpSpecConstant %int 1
%cols = OpSpecConstant %int 1
%matrix_a = OpConstant %int 1
+%stride = OpConstant %int 42
%matrix = OpTypeCooperativeMatrixKHR %float %subgroup %rows %cols %matrix_a
%var1 = OpVariable %ptr StorageBuffer
%var2 = OpUntypedVariableKHR %untyped StorageBuffer %block
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpAccessChain %ptr_float %var1 %int_0 %int_0
-%ld = OpCooperativeMatrixLoadKHR %matrix %gep %int_0
-OpCooperativeMatrixStoreKHR %var2 %ld %int_0
+%ld = OpCooperativeMatrixLoadKHR %matrix %gep %int_0 %stride
+OpCooperativeMatrixStoreKHR %var2 %ld %int_0 %stride
OpReturn
OpFunctionEnd
)";
@@ -6926,15 +7001,16 @@
%rows = OpSpecConstant %int 1
%cols = OpSpecConstant %int 1
%matrix_a = OpConstant %int 1
+%stride = OpConstant %int 42
%matrix = OpTypeCooperativeMatrixKHR %float %subgroup %rows %cols %matrix_a
%var1 = OpVariable %ptr StorageBuffer
%var2 = OpUntypedVariableKHR %untyped StorageBuffer %block
%main = OpFunction %void None %void_fn
%entry = OpLabel
%gep = OpAccessChain %ptr_float %var1 %int_0 %int_0
-%ld = OpCooperativeMatrixLoadKHR %matrix %gep %int_0
+%ld = OpCooperativeMatrixLoadKHR %matrix %gep %int_0 %stride
%gep2 = OpUntypedAccessChainKHR %untyped %block %var2 %int_0 %int_0
-OpCooperativeMatrixStoreKHR %gep2 %ld %int_0
+OpCooperativeMatrixStoreKHR %gep2 %ld %int_0 %stride
OpReturn
OpFunctionEnd
)";