mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-22 19:50:05 +00:00
Validate SPV_KHR_workgroup_memory_explicit_layout (#4128)
* Validate SPV_KHR_workgroup_memory_explicit_layout * Check if SPIR-V is at least 1.4 to use the extension. * Check if either only Workgroup Blocks or only Workgroup non-Blocks are used. * Check that if more than one Workgroup Block is used, variables are decorated with Aliased. * Check layout decorations for Workgroup Blocks. * Implicitly use main capability if the ...8BitAccess or ...16BitAccess are used. * Allow 8-bit and 16-bit types when ...8BitAccess and ...16BitAccess are used respectively. * Update SPIRV-Headers dependency Bump it to include SPV_KHR_workgroup_memory_explicit_layout. * Add option to validate Workgroup blocks with scalar layout Validate the equivalent of scalarBlockLayout for Workgroup storage class Block variables from SPV_KHR_workgroup_memory_explicit_layout. Add option to the API and command line tool.
This commit is contained in:
parent
cc81f53d3d
commit
b812fd634e
2
DEPS
2
DEPS
@ -6,7 +6,7 @@ vars = {
|
||||
'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659',
|
||||
'googletest_revision': '3af06fe1664d30f98de1e78c53a7087e842a2547',
|
||||
're2_revision': 'ca11026a032ce2a3de4b3c389ee53d2bdc8794d6',
|
||||
'spirv_headers_revision': 'f027d53ded7e230e008d37c8b47ede7cd308e19d',
|
||||
'spirv_headers_revision': 'faa570afbc91ac73d594d787486bcf8f2df1ace0',
|
||||
}
|
||||
|
||||
deps = {
|
||||
|
@ -625,6 +625,12 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout(
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetScalarBlockLayout(
|
||||
spv_validator_options options, bool val);
|
||||
|
||||
// Records whether the validator should use "scalar" block layout
|
||||
// rules (as defined above) for Workgroup blocks. See Vulkan
|
||||
// extension VK_KHR_workgroup_memory_explicit_layout.
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetWorkgroupScalarBlockLayout(
|
||||
spv_validator_options options, bool val);
|
||||
|
||||
// Records whether or not the validator should skip validating standard
|
||||
// uniform/storage block layout.
|
||||
SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout(
|
||||
|
@ -104,6 +104,12 @@ class ValidatorOptions {
|
||||
spvValidatorOptionsSetScalarBlockLayout(options_, val);
|
||||
}
|
||||
|
||||
// Enables scalar layout when validating Workgroup blocks. See
|
||||
// VK_KHR_workgroup_memory_explicit_layout.
|
||||
void SetWorkgroupScalarBlockLayout(bool val) {
|
||||
spvValidatorOptionsSetWorkgroupScalarBlockLayout(options_, val);
|
||||
}
|
||||
|
||||
// Skips validating standard uniform/storage buffer/push-constant layout.
|
||||
void SetSkipBlockLayout(bool val) {
|
||||
spvValidatorOptionsSetSkipBlockLayout(options_, val);
|
||||
|
@ -111,6 +111,11 @@ void spvValidatorOptionsSetScalarBlockLayout(spv_validator_options options,
|
||||
options->scalar_block_layout = val;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetWorkgroupScalarBlockLayout(spv_validator_options options,
|
||||
bool val) {
|
||||
options->workgroup_scalar_block_layout = val;
|
||||
}
|
||||
|
||||
void spvValidatorOptionsSetSkipBlockLayout(spv_validator_options options,
|
||||
bool val) {
|
||||
options->skip_block_layout = val;
|
||||
|
@ -45,6 +45,7 @@ struct spv_validator_options_t {
|
||||
relax_block_layout(false),
|
||||
uniform_buffer_standard_layout(false),
|
||||
scalar_block_layout(false),
|
||||
workgroup_scalar_block_layout(false),
|
||||
skip_block_layout(false),
|
||||
before_hlsl_legalization(false) {}
|
||||
|
||||
@ -54,6 +55,7 @@ struct spv_validator_options_t {
|
||||
bool relax_block_layout;
|
||||
bool uniform_buffer_standard_layout;
|
||||
bool scalar_block_layout;
|
||||
bool workgroup_scalar_block_layout;
|
||||
bool skip_block_layout;
|
||||
bool before_hlsl_legalization;
|
||||
};
|
||||
|
@ -379,6 +379,7 @@ bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
|
||||
// or row major-ness.
|
||||
spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
||||
const char* decoration_str, bool blockRules,
|
||||
bool scalar_block_layout,
|
||||
uint32_t incoming_offset,
|
||||
MemberConstraints& constraints,
|
||||
ValidationState_t& vstate) {
|
||||
@ -392,7 +393,6 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
||||
// For example, relaxed layout is implied by Vulkan 1.1. But scalar layout
|
||||
// is more permissive than relaxed layout.
|
||||
const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
|
||||
const bool scalar_block_layout = vstate.options()->scalar_block_layout;
|
||||
|
||||
auto fail = [&vstate, struct_id, storage_class_str, decoration_str,
|
||||
blockRules, relaxed_block_layout,
|
||||
@ -501,6 +501,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
||||
if (SpvOpTypeStruct == opcode &&
|
||||
SPV_SUCCESS != (recursive_status = checkLayout(
|
||||
id, storage_class_str, decoration_str, blockRules,
|
||||
scalar_block_layout,
|
||||
offset, constraints, vstate)))
|
||||
return recursive_status;
|
||||
// Check matrix stride.
|
||||
@ -553,7 +554,8 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
||||
if (SpvOpTypeStruct == element_inst->opcode() &&
|
||||
SPV_SUCCESS != (recursive_status = checkLayout(
|
||||
typeId, storage_class_str, decoration_str,
|
||||
blockRules, next_offset, constraints, vstate)))
|
||||
blockRules, scalar_block_layout,
|
||||
next_offset, constraints, vstate)))
|
||||
return recursive_status;
|
||||
// If offsets accumulate up to a 16-byte multiple stop checking since
|
||||
// it will just repeat.
|
||||
@ -698,6 +700,9 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
||||
const auto& descs = vstate.entry_point_descriptions(entry_point);
|
||||
int num_builtin_inputs = 0;
|
||||
int num_builtin_outputs = 0;
|
||||
int num_workgroup_variables = 0;
|
||||
int num_workgroup_variables_with_block = 0;
|
||||
int num_workgroup_variables_with_aliased = 0;
|
||||
for (const auto& desc : descs) {
|
||||
std::unordered_set<Instruction*> seen_vars;
|
||||
for (auto interface : desc.interfaces) {
|
||||
@ -754,6 +759,16 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
||||
if (auto error = CheckBuiltInVariable(interface, vstate))
|
||||
return error;
|
||||
}
|
||||
|
||||
if (storage_class == SpvStorageClassWorkgroup) {
|
||||
++num_workgroup_variables;
|
||||
if (type_instr && SpvOpTypeStruct == type_instr->opcode()) {
|
||||
if (hasDecoration(type_id, SpvDecorationBlock, vstate))
|
||||
++num_workgroup_variables_with_block;
|
||||
if (hasDecoration(var_instr->id(), SpvDecorationAliased, vstate))
|
||||
++num_workgroup_variables_with_aliased;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (num_builtin_inputs > 1 || num_builtin_outputs > 1) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_BINARY,
|
||||
@ -777,6 +792,30 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
||||
<< " because it is targeted by an OpEntryPoint instruction.";
|
||||
}
|
||||
}
|
||||
|
||||
if (vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR) &&
|
||||
num_workgroup_variables > 0 &&
|
||||
num_workgroup_variables_with_block > 0) {
|
||||
if (num_workgroup_variables != num_workgroup_variables_with_block) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point))
|
||||
<< "When declaring WorkgroupMemoryExplicitLayoutKHR, "
|
||||
"either all or none of the Workgroup Storage Class variables "
|
||||
"in the entry point interface must point to struct types "
|
||||
"decorated with Block. Entry point id "
|
||||
<< entry_point << " does not meet this requirement.";
|
||||
}
|
||||
if (num_workgroup_variables_with_block > 1 &&
|
||||
num_workgroup_variables_with_block !=
|
||||
num_workgroup_variables_with_aliased) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point))
|
||||
<< "When declaring WorkgroupMemoryExplicitLayoutKHR, "
|
||||
"if more than one Workgroup Storage Class variable in "
|
||||
"the entry point interface point to a type decorated "
|
||||
"with Block, all of them must be decorated with Aliased. "
|
||||
"Entry point id "
|
||||
<< entry_point << " does not meet this requirement.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
@ -942,14 +981,16 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
|
||||
const bool phys_storage_buffer =
|
||||
storageClass == SpvStorageClassPhysicalStorageBufferEXT;
|
||||
if (uniform || push_constant || storage_buffer || phys_storage_buffer) {
|
||||
const bool workgroup = storageClass == SpvStorageClassWorkgroup;
|
||||
if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
|
||||
workgroup) {
|
||||
const auto ptrInst = vstate.FindDef(words[1]);
|
||||
assert(SpvOpTypePointer == ptrInst->opcode());
|
||||
auto id = ptrInst->words()[3];
|
||||
auto id_inst = vstate.FindDef(id);
|
||||
// Jump through one level of arraying.
|
||||
if (id_inst->opcode() == SpvOpTypeArray ||
|
||||
id_inst->opcode() == SpvOpTypeRuntimeArray) {
|
||||
if (!workgroup && (id_inst->opcode() == SpvOpTypeArray ||
|
||||
id_inst->opcode() == SpvOpTypeRuntimeArray)) {
|
||||
id = id_inst->GetOperandAs<uint32_t>(1u);
|
||||
id_inst = vstate.FindDef(id);
|
||||
}
|
||||
@ -961,7 +1002,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
// Prepare for messages
|
||||
const char* sc_str =
|
||||
uniform ? "Uniform"
|
||||
: (push_constant ? "PushConstant" : "StorageBuffer");
|
||||
: (push_constant ? "PushConstant"
|
||||
: (workgroup ? "Workgroup"
|
||||
: "StorageBuffer"));
|
||||
|
||||
if (spvIsVulkanEnv(vstate.context()->target_env)) {
|
||||
const bool block = hasDecoration(id, SpvDecorationBlock, vstate);
|
||||
@ -1029,8 +1072,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type();
|
||||
const bool blockRules = uniform && blockDeco;
|
||||
const bool bufferRules =
|
||||
(uniform && bufferDeco) || (push_constant && blockDeco) ||
|
||||
((storage_buffer || phys_storage_buffer) && blockDeco);
|
||||
(uniform && bufferDeco) ||
|
||||
((push_constant || storage_buffer ||
|
||||
phys_storage_buffer || workgroup) && blockDeco);
|
||||
if (uniform && blockDeco) {
|
||||
vstate.RegisterPointerToUniformBlock(ptrInst->id());
|
||||
vstate.RegisterStructForUniformBlock(id);
|
||||
@ -1044,6 +1088,10 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
if (blockRules || bufferRules) {
|
||||
const char* deco_str = blockDeco ? "Block" : "BufferBlock";
|
||||
spv_result_t recursive_status = SPV_SUCCESS;
|
||||
const bool scalar_block_layout = workgroup ?
|
||||
vstate.options()->workgroup_scalar_block_layout :
|
||||
vstate.options()->scalar_block_layout;
|
||||
|
||||
if (isMissingOffsetInStruct(id, vstate)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
|
||||
<< "Structure id " << id << " decorated as " << deco_str
|
||||
@ -1072,12 +1120,14 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
"decorations.";
|
||||
} else if (blockRules &&
|
||||
(SPV_SUCCESS != (recursive_status = checkLayout(
|
||||
id, sc_str, deco_str, true, 0,
|
||||
id, sc_str, deco_str, true,
|
||||
scalar_block_layout, 0,
|
||||
constraints, vstate)))) {
|
||||
return recursive_status;
|
||||
} else if (bufferRules &&
|
||||
(SPV_SUCCESS != (recursive_status = checkLayout(
|
||||
id, sc_str, deco_str, false, 0,
|
||||
id, sc_str, deco_str, false,
|
||||
scalar_block_layout, 0,
|
||||
constraints, vstate)))) {
|
||||
return recursive_status;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "source/latest_version_glsl_std_450_header.h"
|
||||
#include "source/latest_version_opencl_std_header.h"
|
||||
#include "source/opcode.h"
|
||||
#include "source/spirv_constant.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "source/val/instruction.h"
|
||||
#include "source/val/validate.h"
|
||||
@ -685,6 +686,20 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _,
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) {
|
||||
if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
std::string extension = GetExtensionString(&(inst->c_inst()));
|
||||
if (extension ==
|
||||
ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout)) {
|
||||
return _.diag(SPV_ERROR_WRONG_VERSION, inst)
|
||||
<< "SPV_KHR_workgroup_memory_explicit_layout extension "
|
||||
"requires SPIR-V version 1.4 or later.";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateExtInstImport(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto name_id = 1;
|
||||
@ -3124,6 +3139,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst) {
|
||||
const SpvOp opcode = inst->opcode();
|
||||
if (opcode == SpvOpExtension) return ValidateExtension(_, inst);
|
||||
if (opcode == SpvOpExtInstImport) return ValidateExtInstImport(_, inst);
|
||||
if (opcode == SpvOpExtInst) return ValidateExtInst(_, inst);
|
||||
|
||||
|
@ -759,6 +759,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
storage_class_ok = false;
|
||||
}
|
||||
break;
|
||||
case SpvStorageClassWorkgroup:
|
||||
if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR)) {
|
||||
storage_class_ok = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Cannot allocate a variable containing a 16-bit type in "
|
||||
@ -810,6 +815,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
storage_class_ok = false;
|
||||
}
|
||||
break;
|
||||
case SpvStorageClassWorkgroup:
|
||||
if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR)) {
|
||||
storage_class_ok = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Cannot allocate a variable containing a 8-bit type in "
|
||||
|
@ -359,6 +359,7 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) {
|
||||
case SpvCapabilityStorageBuffer8BitAccess:
|
||||
case SpvCapabilityUniformAndStorageBuffer8BitAccess:
|
||||
case SpvCapabilityStoragePushConstant8:
|
||||
case SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR:
|
||||
features_.declare_int8_type = true;
|
||||
break;
|
||||
case SpvCapabilityInt16:
|
||||
@ -372,6 +373,7 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) {
|
||||
case SpvCapabilityStorageUniform16:
|
||||
case SpvCapabilityStoragePushConstant16:
|
||||
case SpvCapabilityStorageInputOutput16:
|
||||
case SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR:
|
||||
features_.declare_int16_type = true;
|
||||
features_.declare_float16_type = true;
|
||||
features_.free_fp_rounding_mode = true;
|
||||
|
@ -103,6 +103,10 @@ class ValidationState_t {
|
||||
// Members need not be listed in offset order
|
||||
bool scalar_block_layout = false;
|
||||
|
||||
// Use scalar block layout (as defined above) for Workgroup block
|
||||
// variables. See VK_KHR_workgroup_memory_explicit_layout.
|
||||
bool workgroup_scalar_block_layout = false;
|
||||
|
||||
// SPIR-V 1.4 allows us to select between any two composite values
|
||||
// of the same type.
|
||||
bool select_between_composites = false;
|
||||
|
@ -7183,6 +7183,469 @@ OpDecorate %float Location 0
|
||||
"or member of a structure type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupSingleBlockVariable) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
OpMemberDecorate %first 0 Offset 0
|
||||
OpDecorate %first Block
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%first = OpTypeStruct %int
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_2 = OpConstant %int 2
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
|
||||
OpStore %13 %int_2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupBlockVariableRequiresV14) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
OpMemberDecorate %first 0 Offset 0
|
||||
OpDecorate %first Block
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%first = OpTypeStruct %int
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_2 = OpConstant %int 2
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
|
||||
OpStore %13 %int_2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3);
|
||||
EXPECT_EQ(SPV_ERROR_WRONG_VERSION,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires SPIR-V version 1.4 or later"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupSingleNonBlockVariable) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %a
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%a = OpVariable %_ptr_Workgroup_int Workgroup
|
||||
%int_2 = OpConstant %int 2
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
OpStore %a %int_2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupMultiBlockVariable) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_ %__0
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
OpMemberDecorate %first 0 Offset 0
|
||||
OpDecorate %first Block
|
||||
OpMemberDecorate %second 0 Offset 0
|
||||
OpDecorate %second Block
|
||||
OpDecorate %_ Aliased
|
||||
OpDecorate %__0 Aliased
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%first = OpTypeStruct %int
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_2 = OpConstant %int 2
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%second = OpTypeStruct %int
|
||||
%_ptr_Workgroup_second = OpTypePointer Workgroup %second
|
||||
%__0 = OpVariable %_ptr_Workgroup_second Workgroup
|
||||
%int_3 = OpConstant %int 3
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
|
||||
OpStore %13 %int_2
|
||||
%18 = OpAccessChain %_ptr_Workgroup_int %__0 %int_0
|
||||
OpStore %18 %int_3
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupBlockVariableWith8BitType) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Int8
|
||||
OpCapability WorkgroupMemoryExplicitLayout8BitAccessKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_
|
||||
OpExecutionMode %main LocalSize 2 1 1
|
||||
OpMemberDecorate %first 0 Offset 0
|
||||
OpDecorate %first Block
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%char = OpTypeInt 8 1
|
||||
%first = OpTypeStruct %char
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%char_2 = OpConstant %char 2
|
||||
%_ptr_Workgroup_char = OpTypePointer Workgroup %char
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%14 = OpAccessChain %_ptr_Workgroup_char %_ %int_0
|
||||
OpStore %14 %char_2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupMultiNonBlockVariable) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %a %b
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%a = OpVariable %_ptr_Workgroup_int Workgroup
|
||||
%int_2 = OpConstant %int 2
|
||||
%b = OpVariable %_ptr_Workgroup_int Workgroup
|
||||
%int_3 = OpConstant %int 3
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
OpStore %a %int_2
|
||||
OpStore %b %int_3
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupBlockVariableWith16BitType) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Float16
|
||||
OpCapability Int16
|
||||
OpCapability WorkgroupMemoryExplicitLayout16BitAccessKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_
|
||||
OpExecutionMode %main LocalSize 2 1 1
|
||||
OpMemberDecorate %first 0 Offset 0
|
||||
OpMemberDecorate %first 1 Offset 2
|
||||
OpDecorate %first Block
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%short = OpTypeInt 16 1
|
||||
%half = OpTypeFloat 16
|
||||
%first = OpTypeStruct %short %half
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int = OpTypeInt 32 1
|
||||
%int_0 = OpConstant %int 0
|
||||
%short_3 = OpConstant %short 3
|
||||
%_ptr_Workgroup_short = OpTypePointer Workgroup %short
|
||||
%int_1 = OpConstant %int 1
|
||||
%half_0x1_898p_3 = OpConstant %half 0x1.898p+3
|
||||
%_ptr_Workgroup_half = OpTypePointer Workgroup %half
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%15 = OpAccessChain %_ptr_Workgroup_short %_ %int_0
|
||||
OpStore %15 %short_3
|
||||
%19 = OpAccessChain %_ptr_Workgroup_half %_ %int_1
|
||||
OpStore %19 %half_0x1_898p_3
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupBlockVariableScalarLayout) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Vertex %main "main" %B
|
||||
OpSource GLSL 450
|
||||
OpMemberDecorate %S 0 Offset 0
|
||||
OpMemberDecorate %S 1 Offset 4
|
||||
OpMemberDecorate %S 2 Offset 16
|
||||
OpMemberDecorate %S 3 Offset 28
|
||||
OpDecorate %S Block
|
||||
OpDecorate %B Aliased
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%float = OpTypeFloat 32
|
||||
%v3float = OpTypeVector %float 3
|
||||
%S = OpTypeStruct %float %v3float %v3float %v3float
|
||||
%_ptr_Workgroup_S = OpTypePointer Workgroup %S
|
||||
%B = OpVariable %_ptr_Workgroup_S Workgroup
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
spvValidatorOptionsSetWorkgroupScalarBlockLayout(getValidatorOptions(), true);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4))
|
||||
<< getDiagnosticString();
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupMixBlockAndNonBlockBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_ %b
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
OpMemberDecorate %first 0 Offset 0
|
||||
OpDecorate %first Block
|
||||
OpDecorate %_ Aliased
|
||||
OpDecorate %b Aliased
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%first = OpTypeStruct %int
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_2 = OpConstant %int 2
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%b = OpVariable %_ptr_Workgroup_int Workgroup
|
||||
%int_3 = OpConstant %int 3
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
|
||||
OpStore %13 %int_2
|
||||
OpStore %b %int_3
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("either all or none of the Workgroup Storage Class variables "
|
||||
"in the entry point interface must point to struct types "
|
||||
"decorated with Block"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupMultiBlockVariableMissingAliased) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_ %__0
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
OpMemberDecorate %first 0 Offset 0
|
||||
OpDecorate %first Block
|
||||
OpMemberDecorate %second 0 Offset 0
|
||||
OpDecorate %second Block
|
||||
OpDecorate %_ Aliased
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%first = OpTypeStruct %int
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_2 = OpConstant %int 2
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%second = OpTypeStruct %int
|
||||
%_ptr_Workgroup_second = OpTypePointer Workgroup %second
|
||||
%__0 = OpVariable %_ptr_Workgroup_second Workgroup
|
||||
%int_3 = OpConstant %int 3
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
|
||||
OpStore %13 %int_2
|
||||
%18 = OpAccessChain %_ptr_Workgroup_int %__0 %int_0
|
||||
OpStore %18 %int_3
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("more than one Workgroup Storage Class variable in the "
|
||||
"entry point interface point to a type decorated with Block, "
|
||||
"all of them must be decorated with Aliased"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableNotAStruct) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
OpDecorate %first Block
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%int_3 = OpConstant %int 3
|
||||
%first = OpTypeArray %int %int_3
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_2 = OpConstant %int 2
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
|
||||
OpStore %13 %int_2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Block decoration on a non-struct type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableMissingLayout) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
OpDecorate %first Block
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%first = OpTypeStruct %int
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_2 = OpConstant %int 2
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
|
||||
OpStore %13 %int_2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Block must be explicitly laid out with Offset decorations"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableBadLayout) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %_
|
||||
OpExecutionMode %main LocalSize 8 1 1
|
||||
OpMemberDecorate %first 0 Offset 1
|
||||
OpDecorate %first Block
|
||||
%void = OpTypeVoid
|
||||
%3 = OpTypeFunction %void
|
||||
%int = OpTypeInt 32 1
|
||||
%first = OpTypeStruct %int
|
||||
%_ptr_Workgroup_first = OpTypePointer Workgroup %first
|
||||
%_ = OpVariable %_ptr_Workgroup_first Workgroup
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_2 = OpConstant %int 2
|
||||
%_ptr_Workgroup_int = OpTypePointer Workgroup %int
|
||||
%main = OpFunction %void None %3
|
||||
%5 = OpLabel
|
||||
%13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
|
||||
OpStore %13 %int_2
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID,
|
||||
ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Block for variable in Workgroup storage class must follow "
|
||||
"standard storage buffer layout rules: "
|
||||
"member 0 at offset 1 is not aligned to 4"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
@ -22,6 +22,16 @@
|
||||
#include "test/val/val_code_generator.h"
|
||||
#include "test/val/val_fixtures.h"
|
||||
|
||||
// For pretty-printing tuples with spv_target_env.
|
||||
std::ostream& operator<<(std::ostream& stream, spv_target_env target)
|
||||
{
|
||||
switch (target) {
|
||||
case SPV_ENV_UNIVERSAL_1_3: return stream << "SPV_ENV_UNIVERSAL_1_3";
|
||||
case SPV_ENV_UNIVERSAL_1_4: return stream << "SPV_ENV_UNIVERSAL_1_4";
|
||||
default: return stream << (unsigned)target;
|
||||
}
|
||||
}
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
@ -3444,9 +3454,10 @@ OpFunctionEnd
|
||||
}
|
||||
|
||||
using ValidateSizedVariable =
|
||||
spvtest::ValidateBase<std::tuple<std::string, std::string, std::string>>;
|
||||
spvtest::ValidateBase<std::tuple<std::string, std::string,
|
||||
std::string, spv_target_env>>;
|
||||
|
||||
CodeGenerator GetSizedVariableCodeGenerator(bool is_8bit) {
|
||||
CodeGenerator GetSizedVariableCodeGenerator(bool is_8bit, bool buffer_block) {
|
||||
CodeGenerator generator;
|
||||
generator.capabilities_ = "OpCapability Shader\nOpCapability Linkage\n";
|
||||
generator.extensions_ =
|
||||
@ -3454,20 +3465,25 @@ CodeGenerator GetSizedVariableCodeGenerator(bool is_8bit) {
|
||||
"\"SPV_KHR_8bit_storage\"\n";
|
||||
generator.memory_model_ = "OpMemoryModel Logical GLSL450\n";
|
||||
if (is_8bit) {
|
||||
generator.before_types_ = R"(OpDecorate %char_buffer_block BufferBlock
|
||||
OpMemberDecorate %char_buffer_block 0 Offset 0
|
||||
)";
|
||||
generator.before_types_ = "OpMemberDecorate %char_buffer_block 0 Offset 0\n";
|
||||
if (buffer_block)
|
||||
generator.before_types_ += "OpDecorate %char_buffer_block BufferBlock\n";
|
||||
|
||||
generator.types_ = R"(%void = OpTypeVoid
|
||||
%char = OpTypeInt 8 0
|
||||
%char4 = OpTypeVector %char 4
|
||||
%char_buffer_block = OpTypeStruct %char
|
||||
)";
|
||||
} else {
|
||||
generator.before_types_ = R"(OpDecorate %half_buffer_block BufferBlock
|
||||
OpDecorate %short_buffer_block BufferBlock
|
||||
OpMemberDecorate %half_buffer_block 0 Offset 0
|
||||
OpMemberDecorate %short_buffer_block 0 Offset 0
|
||||
)";
|
||||
generator.before_types_ =
|
||||
"OpMemberDecorate %half_buffer_block 0 Offset 0\n"
|
||||
"OpMemberDecorate %short_buffer_block 0 Offset 0\n";
|
||||
if (buffer_block) {
|
||||
generator.before_types_ +=
|
||||
"OpDecorate %half_buffer_block BufferBlock\n"
|
||||
"OpDecorate %short_buffer_block BufferBlock\n";
|
||||
}
|
||||
|
||||
generator.types_ = R"(%void = OpTypeVoid
|
||||
%short = OpTypeInt 16 0
|
||||
%half = OpTypeFloat 16
|
||||
@ -3490,6 +3506,10 @@ TEST_P(ValidateSizedVariable, Capability) {
|
||||
const std::string storage_class = std::get<0>(GetParam());
|
||||
const std::string capability = std::get<1>(GetParam());
|
||||
const std::string var_type = std::get<2>(GetParam());
|
||||
const spv_target_env target = std::get<3>(GetParam());
|
||||
|
||||
ASSERT_TRUE(target == SPV_ENV_UNIVERSAL_1_3 ||
|
||||
target == SPV_ENV_UNIVERSAL_1_4);
|
||||
|
||||
bool type_8bit = false;
|
||||
if (var_type == "%char" || var_type == "%char4" ||
|
||||
@ -3497,7 +3517,16 @@ TEST_P(ValidateSizedVariable, Capability) {
|
||||
type_8bit = true;
|
||||
}
|
||||
|
||||
auto generator = GetSizedVariableCodeGenerator(type_8bit);
|
||||
const bool buffer_block = var_type.find("buffer_block") != std::string::npos;
|
||||
|
||||
auto generator = GetSizedVariableCodeGenerator(type_8bit, buffer_block);
|
||||
|
||||
if (capability == "WorkgroupMemoryExplicitLayout8BitAccessKHR" ||
|
||||
capability == "WorkgroupMemoryExplicitLayout16BitAccessKHR") {
|
||||
generator.extensions_ +=
|
||||
"OpExtension \"SPV_KHR_workgroup_memory_explicit_layout\"\n";
|
||||
}
|
||||
|
||||
generator.types_ += "%ptr_type = OpTypePointer " + storage_class + " " +
|
||||
var_type + "\n%var = OpVariable %ptr_type " +
|
||||
storage_class + "\n";
|
||||
@ -3527,7 +3556,6 @@ TEST_P(ValidateSizedVariable, Capability) {
|
||||
}
|
||||
storage_class_ok = true;
|
||||
} else if (storage_class == "Uniform") {
|
||||
bool buffer_block = var_type.find("buffer_block") != std::string::npos;
|
||||
if (type_8bit) {
|
||||
capability_ok = capability == "UniformAndStorageBuffer8BitAccess" ||
|
||||
(capability == "StorageBuffer8BitAccess" && buffer_block);
|
||||
@ -3537,11 +3565,30 @@ TEST_P(ValidateSizedVariable, Capability) {
|
||||
(capability == "StorageBuffer16BitAccess" && buffer_block);
|
||||
}
|
||||
storage_class_ok = true;
|
||||
} else if (storage_class == "Workgroup") {
|
||||
if (type_8bit) {
|
||||
capability_ok =
|
||||
capability == "WorkgroupMemoryExplicitLayout8BitAccessKHR";
|
||||
} else {
|
||||
capability_ok =
|
||||
capability == "WorkgroupMemoryExplicitLayout16BitAccessKHR";
|
||||
}
|
||||
storage_class_ok = true;
|
||||
}
|
||||
|
||||
CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3);
|
||||
spv_result_t result = ValidateInstructions(SPV_ENV_UNIVERSAL_1_3);
|
||||
if (capability_ok) {
|
||||
CompileSuccessfully(generator.Build(), target);
|
||||
spv_result_t result = ValidateInstructions(target);
|
||||
if (target < SPV_ENV_UNIVERSAL_1_4 &&
|
||||
(capability == "WorkgroupMemoryExplicitLayout8BitAccessKHR" ||
|
||||
capability == "WorkgroupMemoryExplicitLayout16BitAccessKHR")) {
|
||||
EXPECT_EQ(SPV_ERROR_WRONG_VERSION, result);
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires SPIR-V version 1.4 or later"));
|
||||
} else if (buffer_block && target > SPV_ENV_UNIVERSAL_1_3) {
|
||||
EXPECT_EQ(SPV_ERROR_WRONG_VERSION, result);
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("requires SPIR-V version 1.3 or earlier"));
|
||||
} else if (capability_ok) {
|
||||
EXPECT_EQ(SPV_SUCCESS, result);
|
||||
} else {
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, result);
|
||||
@ -3566,8 +3613,10 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
Combine(Values("UniformConstant", "Input", "Output", "Workgroup",
|
||||
"CrossWorkgroup", "Private", "StorageBuffer", "Uniform"),
|
||||
Values("StorageBuffer8BitAccess",
|
||||
"UniformAndStorageBuffer8BitAccess", "StoragePushConstant8"),
|
||||
Values("%char", "%char4", "%char_buffer_block")));
|
||||
"UniformAndStorageBuffer8BitAccess", "StoragePushConstant8",
|
||||
"WorkgroupMemoryExplicitLayout8BitAccessKHR"),
|
||||
Values("%char", "%char4", "%char_buffer_block"),
|
||||
Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4)));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
Storage16, ValidateSizedVariable,
|
||||
@ -3575,9 +3624,11 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
"CrossWorkgroup", "Private", "StorageBuffer", "Uniform"),
|
||||
Values("StorageBuffer16BitAccess",
|
||||
"UniformAndStorageBuffer16BitAccess",
|
||||
"StoragePushConstant16", "StorageInputOutput16"),
|
||||
"StoragePushConstant16", "StorageInputOutput16",
|
||||
"WorkgroupMemoryExplicitLayout16BitAccessKHR"),
|
||||
Values("%short", "%half", "%short4", "%half4", "%mat4x4",
|
||||
"%short_buffer_block", "%half_buffer_block")));
|
||||
"%short_buffer_block", "%half_buffer_block"),
|
||||
Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4)));
|
||||
|
||||
using ValidateSizedLoadStore =
|
||||
spvtest::ValidateBase<std::tuple<std::string, uint32_t, std::string>>;
|
||||
|
@ -490,6 +490,10 @@ Options (in lexicographical order):)",
|
||||
avoid triggering those bugs.
|
||||
Current workarounds: Avoid OpUnreachable in loops.)");
|
||||
printf(R"(
|
||||
--workgroup-scalar-block-layout
|
||||
Forwards this option to the validator. See the validator help
|
||||
for details.)");
|
||||
printf(R"(
|
||||
--wrap-opkill
|
||||
Replaces all OpKill instructions in functions that can be called
|
||||
from a continue construct with a function call to a function
|
||||
@ -741,6 +745,8 @@ OptStatus ParseFlags(int argc, const char** argv,
|
||||
validator_options->SetRelaxBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
|
||||
validator_options->SetScalarBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--workgroup-scalar-block-layout")) {
|
||||
validator_options->SetWorkgroupScalarBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
|
||||
validator_options->SetSkipBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
|
||||
|
@ -58,6 +58,7 @@ Options:
|
||||
uniform, storage buffer, and push constant layouts. Scalar layout
|
||||
rules are more permissive than relaxed block layout so in effect
|
||||
this will override the --relax-block-layout option.
|
||||
--workgroup-scalar-block-layout Enable scalar block layout when checking Workgroup block layouts.
|
||||
--skip-block-layout Skip checking standard uniform/storage buffer layout.
|
||||
Overrides any --relax-block-layout or --scalar-block-layout option.
|
||||
--relax-struct-store Allow store from one struct type to a
|
||||
@ -148,6 +149,8 @@ int main(int argc, char** argv) {
|
||||
options.SetUniformBufferStandardLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--scalar-block-layout")) {
|
||||
options.SetScalarBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--workgroup-scalar-block-layout")) {
|
||||
options.SetWorkgroupScalarBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--skip-block-layout")) {
|
||||
options.SetSkipBlockLayout(true);
|
||||
} else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
|
||||
|
Loading…
Reference in New Issue
Block a user