Tighten relaxed layout alignments (#6819)
Fixes #6817
* Relaxed block layout only applies to vectors directly in the struct
and not through matrices nor arrays
Tested against CTS, no unexpected failures.
diff --git a/source/val/validate_explicit_layout.cpp b/source/val/validate_explicit_layout.cpp
index e25c79e..57424e5 100644
--- a/source/val/validate_explicit_layout.cpp
+++ b/source/val/validate_explicit_layout.cpp
@@ -519,7 +519,8 @@
// Returns the alignment for type_id for the given layout rules.
uint32_t GetAlign(uint32_t type_id, LayoutMode mode,
- const MatrixConstraints& matrix_constraints) {
+ const MatrixConstraints& matrix_constraints,
+ bool allow_relaxed = true) {
const auto* type_inst = vstate.FindDef(type_id);
uint32_t align = 1;
switch (type_inst->opcode()) {
@@ -547,7 +548,10 @@
const auto ele_id = type_inst->GetOperandAs<uint32_t>(1u);
const auto num_eles = vstate.GetDimension(type_id);
align = GetAlign(ele_id, mode, {});
- if (mode == LayoutMode::kScalar || vstate.IsRelaxedBlockLayout()) {
+ // Relaxed layout only applies to vectors as direct members of structs.
+ // Arrays and matrices are not relaxed.
+ if (mode == LayoutMode::kScalar ||
+ (allow_relaxed && vstate.IsRelaxedBlockLayout())) {
return align;
}
return align * ((num_eles == 3 || num_eles > 4) ? 4 : num_eles);
@@ -561,7 +565,8 @@
}
if (matrix_constraints.col_major) {
- align = GetAlign(type_inst->GetOperandAs<uint32_t>(1u), mode, {});
+ align = GetAlign(type_inst->GetOperandAs<uint32_t>(1u), mode, {},
+ /* allow_relaxed = */ false);
} else {
// A row-major matrix of C columns has a base alignment equal to the
// base alignment of a vector of C matrix components.
@@ -572,7 +577,7 @@
align = GetAlign(ele_id, mode, {});
// The equivalent vector may not exist so we replicate the vector rule
// here.
- if (mode != LayoutMode::kScalar && !vstate.IsRelaxedBlockLayout()) {
+ if (mode != LayoutMode::kScalar) {
align = align * (num_cols == 3 ? 4 : num_cols);
}
}
@@ -583,7 +588,7 @@
case spv::Op::OpTypeArray:
case spv::Op::OpTypeRuntimeArray:
align = GetAlign(type_inst->GetOperandAs<uint32_t>(1u), mode,
- matrix_constraints);
+ matrix_constraints, /* allow_relaxed = */ false);
if (mode == LayoutMode::kExtended) {
align = AlignTo(align, 16u);
}
diff --git a/test/val/val_explicit_layout_test.cpp b/test/val/val_explicit_layout_test.cpp
index a38c061..d013cdf 100644
--- a/test/val/val_explicit_layout_test.cpp
+++ b/test/val/val_explicit_layout_test.cpp
@@ -5468,6 +5468,128 @@
"from the ArrayStride decoration"));
}
+TEST_F(ValidateExplicitLayout, MatrixStride_TooSmall) {
+ const std::string spirv = R"(
+ OpCapability Shader
+ OpCapability PhysicalStorageBufferAddresses
+ OpMemoryModel PhysicalStorageBuffer64 GLSL450
+ OpEntryPoint GLCompute %main "main" %_
+ OpExecutionMode %main LocalSize 1 1 1
+ OpDecorate %PushConstant Block
+ OpMemberDecorate %PushConstant 0 Offset 0
+ OpMemberDecorate %Transform_0 0 ColMajor
+ OpMemberDecorate %Transform_0 0 MatrixStride 12
+ OpMemberDecorate %Transform_0 0 Offset 0
+ OpMemberDecorate %Transform_0 1 Offset 36
+ OpMemberDecorate %Transform_0 2 Offset 48
+ OpMemberDecorate %Transform_0 3 Offset 60
+ OpDecorate %_runtimearr_Transform_0 ArrayStride 64
+ OpDecorate %Transforms Block
+ OpMemberDecorate %Transforms 0 NonWritable
+ OpMemberDecorate %Transforms 0 Offset 0
+ %void = OpTypeVoid
+ %3 = OpTypeFunction %void
+ %float = OpTypeFloat 32
+ %v3float = OpTypeVector %float 3
+%mat3v3float = OpTypeMatrix %v3float 3
+ %Transform = OpTypeStruct %mat3v3float %v3float %v3float %float
+%_ptr_Function_Transform = OpTypePointer Function %Transform
+ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_Transforms PhysicalStorageBuffer
+%PushConstant = OpTypeStruct %_ptr_PhysicalStorageBuffer_Transforms
+%Transform_0 = OpTypeStruct %mat3v3float %v3float %v3float %float
+%_runtimearr_Transform_0 = OpTypeRuntimeArray %Transform_0
+ %Transforms = OpTypeStruct %_runtimearr_Transform_0
+%_ptr_PhysicalStorageBuffer_Transforms = OpTypePointer PhysicalStorageBuffer %Transforms
+%_ptr_PushConstant_PushConstant = OpTypePointer PushConstant %PushConstant
+ %_ = OpVariable %_ptr_PushConstant_PushConstant PushConstant
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_PushConstant__ptr_PhysicalStorageBuffer_Transforms = OpTypePointer PushConstant %_ptr_PhysicalStorageBuffer_Transforms
+%_ptr_PhysicalStorageBuffer_Transform_0 = OpTypePointer PhysicalStorageBuffer %Transform_0
+ %main = OpFunction %void None %3
+ %5 = OpLabel
+ %transform = OpVariable %_ptr_Function_Transform Function
+ %22 = OpAccessChain %_ptr_PushConstant__ptr_PhysicalStorageBuffer_Transforms %_ %int_0
+ %23 = OpLoad %_ptr_PhysicalStorageBuffer_Transforms %22
+ %25 = OpAccessChain %_ptr_PhysicalStorageBuffer_Transform_0 %23 %int_0 %int_0
+ %26 = OpLoad %Transform_0 %25 Aligned 64
+ %27 = OpCopyLogical %Transform %26
+ OpStore %transform %27
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(
+ getDiagnosticString(),
+ HasSubstr("Matrix with a stride 12 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, Offset_Unaligned) {
+ const std::string spirv = R"(
+ OpCapability PhysicalStorageBufferAddresses
+ OpCapability Shader
+ OpExtension "SPV_KHR_physical_storage_buffer"
+ OpMemoryModel PhysicalStorageBuffer64 GLSL450
+ OpEntryPoint GLCompute %main "main" %uniforms
+ OpExecutionMode %main LocalSize 1 1 1
+ OpDecorate %_ptr_PhysicalStorageBuffer_BDA_natural ArrayStride 260
+ OpDecorate %Uniforms_std430 Block
+ OpMemberDecorate %Uniforms_std430 0 Offset 0
+ OpMemberDecorate %Uniforms_std430 1 Offset 8
+ OpDecorate %_ptr_PhysicalStorageBuffer_uint ArrayStride 4
+ OpDecorate %_ptr_PhysicalStorageBuffer__Array_natural_vector_uint_4_16 ArrayStride 256
+ OpDecorate %_arr_v4uint_int_16 ArrayStride 16
+ OpDecorate %_ptr_PhysicalStorageBuffer__arr_v4uint_int_16 ArrayStride 256
+ OpDecorate %_ptr_PhysicalStorageBuffer_v4uint ArrayStride 16
+ OpMemberDecorate %_Array_natural_vector_uint_4_16 0 Offset 0
+ OpMemberDecorate %BDA_natural 0 Offset 0
+ OpMemberDecorate %BDA_natural 1 Offset 4
+ %void = OpTypeVoid
+ %13 = OpTypeFunction %void
+ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_BDA_natural PhysicalStorageBuffer
+%Uniforms_std430 = OpTypeStruct %_ptr_PhysicalStorageBuffer_BDA_natural %_ptr_PhysicalStorageBuffer_BDA_natural
+%_ptr_PushConstant_Uniforms_std430 = OpTypePointer PushConstant %Uniforms_std430
+ %int = OpTypeInt 32 1
+ %int_0 = OpConstant %int 0
+%_ptr_PushConstant_6 = OpTypePointer PushConstant %_ptr_PhysicalStorageBuffer_BDA_natural
+ %uint = OpTypeInt 32 0
+%_ptr_PhysicalStorageBuffer_uint = OpTypePointer PhysicalStorageBuffer %uint
+ %int_1 = OpConstant %int 1
+ OpTypeForwardPointer %_ptr_PhysicalStorageBuffer__Array_natural_vector_uint_4_16 PhysicalStorageBuffer
+ %v4uint = OpTypeVector %uint 4
+ %int_16 = OpConstant %int 16
+%_arr_v4uint_int_16 = OpTypeArray %v4uint %int_16
+%_ptr_PhysicalStorageBuffer__arr_v4uint_int_16 = OpTypePointer PhysicalStorageBuffer %_arr_v4uint_int_16
+%_ptr_PhysicalStorageBuffer_v4uint = OpTypePointer PhysicalStorageBuffer %v4uint
+%_Array_natural_vector_uint_4_16 = OpTypeStruct %_arr_v4uint_int_16
+%BDA_natural = OpTypeStruct %uint %_Array_natural_vector_uint_4_16
+%_ptr_PhysicalStorageBuffer_BDA_natural = OpTypePointer PhysicalStorageBuffer %BDA_natural
+%_ptr_PhysicalStorageBuffer__Array_natural_vector_uint_4_16 = OpTypePointer PhysicalStorageBuffer %_Array_natural_vector_uint_4_16
+ %uniforms = OpVariable %_ptr_PushConstant_Uniforms_std430 PushConstant
+ %main = OpFunction %void None %13
+ %22 = OpLabel
+ %23 = OpAccessChain %_ptr_PushConstant_6 %uniforms %int_0
+ %24 = OpLoad %_ptr_PhysicalStorageBuffer_BDA_natural %23
+ %25 = OpAccessChain %_ptr_PhysicalStorageBuffer_uint %24 %int_0
+ %26 = OpLoad %_ptr_PhysicalStorageBuffer_BDA_natural %23
+ %27 = OpAccessChain %_ptr_PhysicalStorageBuffer__Array_natural_vector_uint_4_16 %26 %int_1
+ %28 = OpAccessChain %_ptr_PhysicalStorageBuffer__arr_v4uint_int_16 %27 %int_0
+ %29 = OpAccessChain %_ptr_PhysicalStorageBuffer_v4uint %28 %int_0
+ %30 = OpLoad %v4uint %29 Aligned 4
+ %31 = OpCompositeExtract %uint %30 0
+ OpStore %25 %31 Aligned 4
+ OpReturn
+ OpFunctionEnd
+)";
+
+ CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+ EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+ EXPECT_THAT(getDiagnosticString(),
+ HasSubstr("Structure member 1 at offset 4 is not aligned to 16"));
+}
+
using UntypedPointerLayout =
spvtest::ValidateBase<std::tuple<std::string, std::string>>;
@@ -5574,13 +5696,9 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
const bool read_only = sc == "Uniform" || sc == "PushConstant";
- if (sc == "Uniform") {
+ if (!read_only || op.find("OpStore") == std::string::npos) {
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array stride 4 must satisfy alignment 16"));
- } else if (!read_only || op.find("OpStore") == std::string::npos) {
- EXPECT_THAT(
- getDiagnosticString(),
- HasSubstr("Array stride 4 is smaller than element type size 16"));
}
}
@@ -5633,12 +5751,9 @@
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
const bool read_only = sc == "Uniform" || sc == "PushConstant";
- if (sc == "Uniform") {
+ if (!read_only || op.find("OpStore") == std::string::npos) {
EXPECT_THAT(getDiagnosticString(),
HasSubstr("Array stride 15 must satisfy alignment 16"));
- } else if (!read_only || op.find("OpStore") == std::string::npos) {
- EXPECT_THAT(getDiagnosticString(),
- HasSubstr("Array stride 15 must satisfy alignment 4"));
}
}