mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-21 19:20:07 +00:00
Support SPV_KHR_untyped_pointers (#5736)
* Support SPV_KHR_untyped_pointers Covers: - assembler - disassembler - validator fix copyright Validate OpTypeUntypedPointerKHR * Disallow an untyped pointer in a typed pointer * Validate capability requirements for untyped pointer * Allow duplicate untyped pointer declarations Add round trip tests Validate OpUntypedVariableKHR Validate untyped access chains * Add a test for opcodes that generate untyped pointers * simplify some checks for operands needing types * validate OpUnypedAccessChainKHR, OpUntypedInBoundsAccessChainKHR, OpUntypedPtrAccessChainKHR, OpUntypedInBoundsPtrAccessChainKHR Unify variable validation Validate OpCopyMemorySized * Fix some opcode tests to accound for untyped pointers * Add validation for OpCopyMemorySized for shaders and untyped pointers * fix up tests Validate pointer comparisons and bitcast * Update more helpers * Fix entry validation to allow OpUntypedVariableKHR * Validate OpPtrEqual, OpPtrNotEqual and OpPtrDiff * Validate OpBitcast Validate atomics and untyped pointers Make interface variable validation aware of untyped pointers * Check OpUntypedVariableKHR in interface validation More untyped pointer validation * Validate interfaces more thoroughly * Validate layouts for untyped pointer uses * Improve capability checks for vulkan with OpTypeUntypedPointerKHR * workgroup member explicit layout validation updates More validation * validate function arguments and parameters * handle untyped pointer and variable in more places Add a friendly assembly name for untyped pointers Update OpCopyMemory validation and tests Fix test for token update Fixes for validation * Allow typed pointers to contain untyped pointers * Fix decoration validation * add untyped pointer as a case for size and alignments Fix interface validation * Grabbed the wrong storage class operand for untyped variables * Add ability to specify assembler options in validation tests Add passthrough validation for OpUntypedArrayLengthKHR More validation of untyped pointers * Validate OpUntypedArrayLengthKHR * Validate layout for OpLoad, OpStore, and OpUntypedArrayLengthKHR Validation support for cooperative matrix and untyped pointers * Allow untyped pointers for cooperative matrix KHR load and store Updates to match spec * Remove extra capability references * Swap untyped variable data type and storage class operands * update validation of variables * update deps --------- Co-authored-by: David Neto <dneto@google.com>
This commit is contained in:
parent
6248fda376
commit
3ab0d22608
2
DEPS
2
DEPS
@ -14,7 +14,7 @@ vars = {
|
||||
|
||||
're2_revision': '6dcd83d60f7944926bfd308cc13979fc53dd69ca',
|
||||
|
||||
'spirv_headers_revision': '41a8eb27f1a7554dadfcdd45819954eaa94935e6',
|
||||
'spirv_headers_revision': 'db5a00f8cebe81146cafabf89019674a3c4bf03d',
|
||||
}
|
||||
|
||||
deps = {
|
||||
|
@ -256,6 +256,11 @@ spv_result_t FriendlyNameMapper::ParseInstruction(
|
||||
inst.words[2]) +
|
||||
"_" + NameForId(inst.words[3]));
|
||||
break;
|
||||
case spv::Op::OpTypeUntypedPointerKHR:
|
||||
SaveName(result_id, std::string("_ptr_") +
|
||||
NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS,
|
||||
inst.words[2]));
|
||||
break;
|
||||
case spv::Op::OpTypePipe:
|
||||
SaveName(result_id,
|
||||
std::string("Pipe") +
|
||||
|
@ -287,8 +287,11 @@ int32_t spvOpcodeIsComposite(const spv::Op opcode) {
|
||||
bool spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode) {
|
||||
switch (opcode) {
|
||||
case spv::Op::OpVariable:
|
||||
case spv::Op::OpUntypedVariableKHR:
|
||||
case spv::Op::OpAccessChain:
|
||||
case spv::Op::OpInBoundsAccessChain:
|
||||
case spv::Op::OpUntypedAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsAccessChainKHR:
|
||||
case spv::Op::OpFunctionParameter:
|
||||
case spv::Op::OpImageTexelPointer:
|
||||
case spv::Op::OpCopyObject:
|
||||
@ -296,6 +299,7 @@ bool spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode) {
|
||||
case spv::Op::OpPhi:
|
||||
case spv::Op::OpFunctionCall:
|
||||
case spv::Op::OpPtrAccessChain:
|
||||
case spv::Op::OpUntypedPtrAccessChainKHR:
|
||||
case spv::Op::OpLoad:
|
||||
case spv::Op::OpConstantNull:
|
||||
case spv::Op::OpRawAccessChainNV:
|
||||
@ -308,8 +312,11 @@ bool spvOpcodeReturnsLogicalVariablePointer(const spv::Op opcode) {
|
||||
int32_t spvOpcodeReturnsLogicalPointer(const spv::Op opcode) {
|
||||
switch (opcode) {
|
||||
case spv::Op::OpVariable:
|
||||
case spv::Op::OpUntypedVariableKHR:
|
||||
case spv::Op::OpAccessChain:
|
||||
case spv::Op::OpInBoundsAccessChain:
|
||||
case spv::Op::OpUntypedAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsAccessChainKHR:
|
||||
case spv::Op::OpFunctionParameter:
|
||||
case spv::Op::OpImageTexelPointer:
|
||||
case spv::Op::OpCopyObject:
|
||||
@ -351,6 +358,7 @@ int32_t spvOpcodeGeneratesType(spv::Op op) {
|
||||
// spv::Op::OpTypeAccelerationStructureNV
|
||||
case spv::Op::OpTypeRayQueryKHR:
|
||||
case spv::Op::OpTypeHitObjectNV:
|
||||
case spv::Op::OpTypeUntypedPointerKHR:
|
||||
return true;
|
||||
default:
|
||||
// In particular, OpTypeForwardPointer does not generate a type,
|
||||
@ -792,3 +800,16 @@ bool spvOpcodeIsBit(spv::Op opcode) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool spvOpcodeGeneratesUntypedPointer(spv::Op opcode) {
|
||||
switch (opcode) {
|
||||
case spv::Op::OpUntypedVariableKHR:
|
||||
case spv::Op::OpUntypedAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsAccessChainKHR:
|
||||
case spv::Op::OpUntypedPtrAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -162,4 +162,7 @@ bool spvOpcodeIsBit(spv::Op opcode);
|
||||
// Gets the name of an instruction, without the "Op" prefix.
|
||||
const char* spvOpcodeString(const spv::Op opcode);
|
||||
|
||||
// Returns true for opcodes that generate an untyped pointer result.
|
||||
bool spvOpcodeGeneratesUntypedPointer(spv::Op opcode);
|
||||
|
||||
#endif // SOURCE_OPCODE_H_
|
||||
|
@ -117,6 +117,15 @@ spv_result_t ValidateAdjacency(ValidationState_t& _) {
|
||||
"first instructions in the first block.";
|
||||
}
|
||||
break;
|
||||
case spv::Op::OpUntypedVariableKHR:
|
||||
if (inst.GetOperandAs<spv::StorageClass>(2) ==
|
||||
spv::StorageClass::Function &&
|
||||
adjacency_status != IN_ENTRY_BLOCK) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, &inst)
|
||||
<< "All OpUntypedVariableKHR instructions in a function must "
|
||||
"be the first instructions in the first block.";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
adjacency_status = PHI_AND_VAR_INVALID;
|
||||
break;
|
||||
|
@ -129,6 +129,7 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
|
||||
break;
|
||||
case spv::Decoration::BuiltIn:
|
||||
if (target->opcode() != spv::Op::OpVariable &&
|
||||
target->opcode() != spv::Op::OpUntypedVariableKHR &&
|
||||
!spvOpcodeIsConstant(target->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "BuiltIns can only target variables, structure members or "
|
||||
@ -139,7 +140,8 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
|
||||
if (!spvOpcodeIsConstant(target->opcode())) {
|
||||
return fail(0) << "must be a constant for WorkgroupSize";
|
||||
}
|
||||
} else if (target->opcode() != spv::Op::OpVariable) {
|
||||
} else if (target->opcode() != spv::Op::OpVariable &&
|
||||
target->opcode() != spv::Op::OpUntypedVariableKHR) {
|
||||
return fail(0) << "must be a variable";
|
||||
}
|
||||
break;
|
||||
@ -161,11 +163,12 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
|
||||
case spv::Decoration::RestrictPointer:
|
||||
case spv::Decoration::AliasedPointer:
|
||||
if (target->opcode() != spv::Op::OpVariable &&
|
||||
target->opcode() != spv::Op::OpUntypedVariableKHR &&
|
||||
target->opcode() != spv::Op::OpFunctionParameter &&
|
||||
target->opcode() != spv::Op::OpRawAccessChainNV) {
|
||||
return fail(0) << "must be a memory object declaration";
|
||||
}
|
||||
if (_.GetIdOpcode(target->type_id()) != spv::Op::OpTypePointer) {
|
||||
if (!_.IsPointerType(target->type_id())) {
|
||||
return fail(0) << "must be a pointer type";
|
||||
}
|
||||
break;
|
||||
@ -176,7 +179,8 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
|
||||
case spv::Decoration::Binding:
|
||||
case spv::Decoration::DescriptorSet:
|
||||
case spv::Decoration::InputAttachmentIndex:
|
||||
if (target->opcode() != spv::Op::OpVariable) {
|
||||
if (target->opcode() != spv::Op::OpVariable &&
|
||||
target->opcode() != spv::Op::OpUntypedVariableKHR) {
|
||||
return fail(0) << "must be a variable";
|
||||
}
|
||||
break;
|
||||
|
@ -183,7 +183,44 @@ spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< spvOpcodeString(opcode)
|
||||
<< ": expected Pointer to be of type OpTypePointer";
|
||||
<< ": expected Pointer to be a pointer type";
|
||||
}
|
||||
|
||||
// If the pointer is an untyped pointer, get the data type elsewhere.
|
||||
if (data_type == 0) {
|
||||
switch (opcode) {
|
||||
case spv::Op::OpAtomicLoad:
|
||||
case spv::Op::OpAtomicExchange:
|
||||
case spv::Op::OpAtomicFAddEXT:
|
||||
case spv::Op::OpAtomicCompareExchange:
|
||||
case spv::Op::OpAtomicCompareExchangeWeak:
|
||||
case spv::Op::OpAtomicIIncrement:
|
||||
case spv::Op::OpAtomicIDecrement:
|
||||
case spv::Op::OpAtomicIAdd:
|
||||
case spv::Op::OpAtomicISub:
|
||||
case spv::Op::OpAtomicSMin:
|
||||
case spv::Op::OpAtomicUMin:
|
||||
case spv::Op::OpAtomicFMinEXT:
|
||||
case spv::Op::OpAtomicSMax:
|
||||
case spv::Op::OpAtomicUMax:
|
||||
case spv::Op::OpAtomicFMaxEXT:
|
||||
case spv::Op::OpAtomicAnd:
|
||||
case spv::Op::OpAtomicOr:
|
||||
case spv::Op::OpAtomicXor:
|
||||
data_type = inst->type_id();
|
||||
break;
|
||||
case spv::Op::OpAtomicFlagTestAndSet:
|
||||
case spv::Op::OpAtomicFlagClear:
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Untyped pointers are not supported by atomic flag "
|
||||
"instructions";
|
||||
break;
|
||||
case spv::Op::OpAtomicStore:
|
||||
data_type = _.FindDef(inst->GetOperandAs<uint32_t>(3))->type_id();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Can't use result_type because OpAtomicStore doesn't have a result
|
||||
|
@ -97,12 +97,16 @@ spv_result_t GetUnderlyingType(ValidationState_t& _,
|
||||
spv::StorageClass GetStorageClass(const Instruction& inst) {
|
||||
switch (inst.opcode()) {
|
||||
case spv::Op::OpTypePointer:
|
||||
case spv::Op::OpTypeUntypedPointerKHR:
|
||||
case spv::Op::OpTypeForwardPointer: {
|
||||
return spv::StorageClass(inst.word(2));
|
||||
}
|
||||
case spv::Op::OpVariable: {
|
||||
return spv::StorageClass(inst.word(3));
|
||||
}
|
||||
case spv::Op::OpUntypedVariableKHR: {
|
||||
return spv::StorageClass(inst.word(4));
|
||||
}
|
||||
case spv::Op::OpGenericCastToPtrExplicit: {
|
||||
return spv::StorageClass(inst.word(4));
|
||||
}
|
||||
|
@ -250,7 +250,8 @@ spv_result_t ValidateReturnValue(ValidationState_t& _,
|
||||
}
|
||||
|
||||
if (_.addressing_model() == spv::AddressingModel::Logical &&
|
||||
spv::Op::OpTypePointer == value_type->opcode() &&
|
||||
(spv::Op::OpTypePointer == value_type->opcode() ||
|
||||
spv::Op::OpTypeUntypedPointerKHR == value_type->opcode()) &&
|
||||
!_.features().variable_pointers && !_.options()->relax_logical_pointer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpReturnValue value's type <id> "
|
||||
|
@ -324,6 +324,7 @@ bool IsTypeNullable(const std::vector<uint32_t>& instruction,
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case spv::Op::OpTypeUntypedPointerKHR:
|
||||
case spv::Op::OpTypePointer:
|
||||
if (spv::StorageClass(instruction[2]) ==
|
||||
spv::StorageClass::PhysicalStorageBuffer) {
|
||||
|
@ -224,6 +224,7 @@ uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
|
||||
break;
|
||||
}
|
||||
case spv::Op::OpTypePointer:
|
||||
case spv::Op::OpTypeUntypedPointerKHR:
|
||||
baseAlignment = vstate.pointer_size_and_alignment();
|
||||
break;
|
||||
default:
|
||||
@ -270,6 +271,7 @@ uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) {
|
||||
return max_member_alignment;
|
||||
} break;
|
||||
case spv::Op::OpTypePointer:
|
||||
case spv::Op::OpTypeUntypedPointerKHR:
|
||||
return vstate.pointer_size_and_alignment();
|
||||
default:
|
||||
assert(0);
|
||||
@ -359,6 +361,7 @@ uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
|
||||
return offset + getSize(lastMember, constraint, constraints, vstate);
|
||||
}
|
||||
case spv::Op::OpTypePointer:
|
||||
case spv::Op::OpTypeUntypedPointerKHR:
|
||||
return vstate.pointer_size_and_alignment();
|
||||
default:
|
||||
assert(0);
|
||||
@ -432,9 +435,9 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
||||
return ds;
|
||||
};
|
||||
|
||||
// If we are checking physical storage buffer pointers, we may not actually
|
||||
// have a struct here. Instead, pretend we have a struct with a single member
|
||||
// at offset 0.
|
||||
// If we are checking the layout of untyped pointers or physical storage
|
||||
// buffer pointers, we may not actually have a struct here. Instead, pretend
|
||||
// we have a struct with a single member at offset 0.
|
||||
const auto& struct_type = vstate.FindDef(struct_id);
|
||||
std::vector<uint32_t> members;
|
||||
if (struct_type->opcode() == spv::Op::OpTypeStruct) {
|
||||
@ -451,8 +454,8 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
|
||||
};
|
||||
std::vector<MemberOffsetPair> member_offsets;
|
||||
|
||||
// With physical storage buffers, we might be checking layouts that do not
|
||||
// originate from a structure.
|
||||
// With untyped pointers or physical storage buffers, we might be checking
|
||||
// layouts that do not originate from a structure.
|
||||
if (struct_type->opcode() == spv::Op::OpTypeStruct) {
|
||||
member_offsets.reserve(members.size());
|
||||
for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
|
||||
@ -770,14 +773,19 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
||||
std::unordered_set<spv::BuiltIn> output_var_builtin;
|
||||
for (auto interface : desc.interfaces) {
|
||||
Instruction* var_instr = vstate.FindDef(interface);
|
||||
if (!var_instr || spv::Op::OpVariable != var_instr->opcode()) {
|
||||
if (!var_instr ||
|
||||
(spv::Op::OpVariable != var_instr->opcode() &&
|
||||
spv::Op::OpUntypedVariableKHR != var_instr->opcode())) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "Interfaces passed to OpEntryPoint must be of type "
|
||||
"OpTypeVariable. Found Op"
|
||||
<< "Interfaces passed to OpEntryPoint must be variables. "
|
||||
"Found Op"
|
||||
<< spvOpcodeString(var_instr->opcode()) << ".";
|
||||
}
|
||||
const bool untyped_pointers =
|
||||
var_instr->opcode() == spv::Op::OpUntypedVariableKHR;
|
||||
const auto sc_index = 2u;
|
||||
const spv::StorageClass storage_class =
|
||||
var_instr->GetOperandAs<spv::StorageClass>(2);
|
||||
var_instr->GetOperandAs<spv::StorageClass>(sc_index);
|
||||
if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
|
||||
// Starting in 1.4, OpEntryPoint must list all global variables
|
||||
// it statically uses and those interfaces must be unique.
|
||||
@ -804,12 +812,13 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t ptr_id = var_instr->word(1);
|
||||
Instruction* ptr_instr = vstate.FindDef(ptr_id);
|
||||
// It is guaranteed (by validator ID checks) that ptr_instr is
|
||||
// OpTypePointer. Word 3 of this instruction is the type being pointed
|
||||
// to.
|
||||
const uint32_t type_id = ptr_instr->word(3);
|
||||
// to. For untyped variables, the pointee type comes from the data type
|
||||
// operand.
|
||||
const uint32_t type_id =
|
||||
untyped_pointers ? var_instr->word(4)
|
||||
: vstate.FindDef(var_instr->word(1))->word(3);
|
||||
Instruction* type_instr = vstate.FindDef(type_id);
|
||||
const bool is_struct =
|
||||
type_instr && spv::Op::OpTypeStruct == type_instr->opcode();
|
||||
@ -874,12 +883,25 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
||||
|
||||
if (storage_class == spv::StorageClass::Workgroup) {
|
||||
++num_workgroup_variables;
|
||||
if (is_struct) {
|
||||
if (hasDecoration(type_id, spv::Decoration::Block, vstate))
|
||||
++num_workgroup_variables_with_block;
|
||||
if (hasDecoration(var_instr->id(), spv::Decoration::Aliased,
|
||||
vstate))
|
||||
++num_workgroup_variables_with_aliased;
|
||||
if (type_instr) {
|
||||
if (spv::Op::OpTypeStruct == type_instr->opcode()) {
|
||||
if (hasDecoration(type_id, spv::Decoration::Block, vstate)) {
|
||||
++num_workgroup_variables_with_block;
|
||||
} else if (untyped_pointers &&
|
||||
vstate.HasCapability(spv::Capability::Shader)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "Untyped workgroup variables in shaders must be "
|
||||
"block decorated";
|
||||
}
|
||||
if (hasDecoration(var_instr->id(), spv::Decoration::Aliased,
|
||||
vstate))
|
||||
++num_workgroup_variables_with_aliased;
|
||||
} else if (untyped_pointers &&
|
||||
vstate.HasCapability(spv::Capability::Shader)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, var_instr)
|
||||
<< "Untyped workgroup variables in shaders must be block "
|
||||
"decorated structs";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -960,25 +982,33 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
|
||||
|
||||
const bool workgroup_blocks_allowed = vstate.HasCapability(
|
||||
spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
|
||||
if (workgroup_blocks_allowed && num_workgroup_variables > 0 &&
|
||||
if (workgroup_blocks_allowed &&
|
||||
!vstate.HasCapability(spv::Capability::UntypedPointersKHR) &&
|
||||
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))
|
||||
return vstate.diag(SPV_ERROR_INVALID_BINARY,
|
||||
vstate.FindDef(entry_point))
|
||||
<< "When declaring WorkgroupMemoryExplicitLayoutKHR, "
|
||||
"either all or none of the Workgroup Storage Class variables "
|
||||
"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 "
|
||||
"decorated with Block (unless the "
|
||||
"UntypedPointersKHR capability is declared). "
|
||||
"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))
|
||||
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 "
|
||||
"with Block, all of them must be decorated with Aliased "
|
||||
"(unless the UntypedPointerWorkgroupKHR capability is "
|
||||
"declared). Entry point id "
|
||||
<< entry_point << " does not meet this requirement.";
|
||||
}
|
||||
} else if (!workgroup_blocks_allowed &&
|
||||
@ -1084,11 +1114,17 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
const auto& words = inst.words();
|
||||
auto type_id = inst.type_id();
|
||||
const Instruction* type_inst = vstate.FindDef(type_id);
|
||||
if (spv::Op::OpVariable == inst.opcode()) {
|
||||
bool scalar_block_layout = false;
|
||||
MemberConstraints constraints;
|
||||
if (spv::Op::OpVariable == inst.opcode() ||
|
||||
spv::Op::OpUntypedVariableKHR == inst.opcode()) {
|
||||
const bool untyped_pointer =
|
||||
inst.opcode() == spv::Op::OpUntypedVariableKHR;
|
||||
const auto var_id = inst.id();
|
||||
// For storage class / decoration combinations, see Vulkan 14.5.4 "Offset
|
||||
// and Stride Assignment".
|
||||
const auto storageClass = inst.GetOperandAs<spv::StorageClass>(2);
|
||||
const auto storageClassVal = words[3];
|
||||
const auto storageClass = spv::StorageClass(storageClassVal);
|
||||
const bool uniform = storageClass == spv::StorageClass::Uniform;
|
||||
const bool uniform_constant =
|
||||
storageClass == spv::StorageClass::UniformConstant;
|
||||
@ -1167,20 +1203,24 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
if (uniform || push_constant || storage_buffer || phys_storage_buffer ||
|
||||
workgroup) {
|
||||
const auto ptrInst = vstate.FindDef(words[1]);
|
||||
assert(spv::Op::OpTypePointer == ptrInst->opcode());
|
||||
auto id = ptrInst->words()[3];
|
||||
auto id_inst = vstate.FindDef(id);
|
||||
// Jump through one level of arraying.
|
||||
if (!workgroup && (id_inst->opcode() == spv::Op::OpTypeArray ||
|
||||
id_inst->opcode() == spv::Op::OpTypeRuntimeArray)) {
|
||||
id = id_inst->GetOperandAs<uint32_t>(1u);
|
||||
id_inst = vstate.FindDef(id);
|
||||
assert(spv::Op::OpTypePointer == ptrInst->opcode() ||
|
||||
spv::Op::OpTypeUntypedPointerKHR == ptrInst->opcode());
|
||||
auto id = untyped_pointer ? (words.size() > 4 ? words[4] : 0)
|
||||
: ptrInst->words()[3];
|
||||
if (id != 0) {
|
||||
auto id_inst = vstate.FindDef(id);
|
||||
// Jump through one level of arraying.
|
||||
if (!workgroup &&
|
||||
(id_inst->opcode() == spv::Op::OpTypeArray ||
|
||||
id_inst->opcode() == spv::Op::OpTypeRuntimeArray)) {
|
||||
id = id_inst->GetOperandAs<uint32_t>(1u);
|
||||
id_inst = vstate.FindDef(id);
|
||||
}
|
||||
// Struct requirement is checked on variables so just move on here.
|
||||
if (spv::Op::OpTypeStruct != id_inst->opcode()) continue;
|
||||
ComputeMemberConstraintsForStruct(&constraints, id,
|
||||
LayoutConstraints(), vstate);
|
||||
}
|
||||
// Struct requirement is checked on variables so just move on here.
|
||||
if (spv::Op::OpTypeStruct != id_inst->opcode()) continue;
|
||||
MemberConstraints constraints;
|
||||
ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(),
|
||||
vstate);
|
||||
// Prepare for messages
|
||||
const char* sc_str =
|
||||
uniform ? "Uniform"
|
||||
@ -1250,88 +1290,91 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& dec : vstate.id_decorations(id)) {
|
||||
const bool blockDeco = spv::Decoration::Block == dec.dec_type();
|
||||
const bool bufferDeco =
|
||||
spv::Decoration::BufferBlock == dec.dec_type();
|
||||
const bool blockRules = uniform && blockDeco;
|
||||
const bool bufferRules =
|
||||
(uniform && bufferDeco) ||
|
||||
((push_constant || storage_buffer ||
|
||||
phys_storage_buffer || workgroup) && blockDeco);
|
||||
if (uniform && blockDeco) {
|
||||
vstate.RegisterPointerToUniformBlock(ptrInst->id());
|
||||
vstate.RegisterStructForUniformBlock(id);
|
||||
}
|
||||
if ((uniform && bufferDeco) ||
|
||||
((storage_buffer || phys_storage_buffer) && blockDeco)) {
|
||||
vstate.RegisterPointerToStorageBuffer(ptrInst->id());
|
||||
vstate.RegisterStructForStorageBuffer(id);
|
||||
}
|
||||
|
||||
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
|
||||
<< " must be explicitly laid out with Offset "
|
||||
"decorations.";
|
||||
if (id != 0) {
|
||||
for (const auto& dec : vstate.id_decorations(id)) {
|
||||
const bool blockDeco = spv::Decoration::Block == dec.dec_type();
|
||||
const bool bufferDeco =
|
||||
spv::Decoration::BufferBlock == dec.dec_type();
|
||||
const bool blockRules = uniform && blockDeco;
|
||||
const bool bufferRules = (uniform && bufferDeco) ||
|
||||
((push_constant || storage_buffer ||
|
||||
phys_storage_buffer || workgroup) &&
|
||||
blockDeco);
|
||||
if (uniform && blockDeco) {
|
||||
vstate.RegisterPointerToUniformBlock(ptrInst->id());
|
||||
vstate.RegisterStructForUniformBlock(id);
|
||||
}
|
||||
if ((uniform && bufferDeco) ||
|
||||
((storage_buffer || phys_storage_buffer) && blockDeco)) {
|
||||
vstate.RegisterPointerToStorageBuffer(ptrInst->id());
|
||||
vstate.RegisterStructForStorageBuffer(id);
|
||||
}
|
||||
|
||||
if (!checkForRequiredDecoration(
|
||||
id,
|
||||
[](spv::Decoration d) {
|
||||
return d == spv::Decoration::ArrayStride;
|
||||
},
|
||||
spv::Op::OpTypeArray, vstate)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
|
||||
<< "Structure id " << id << " decorated as " << deco_str
|
||||
<< " must be explicitly laid out with ArrayStride "
|
||||
"decorations.";
|
||||
}
|
||||
if (blockRules || bufferRules) {
|
||||
const char* deco_str = blockDeco ? "Block" : "BufferBlock";
|
||||
spv_result_t recursive_status = SPV_SUCCESS;
|
||||
scalar_block_layout =
|
||||
workgroup ? vstate.options()->workgroup_scalar_block_layout
|
||||
: vstate.options()->scalar_block_layout;
|
||||
|
||||
if (!checkForRequiredDecoration(
|
||||
id,
|
||||
[](spv::Decoration d) {
|
||||
return d == spv::Decoration::MatrixStride;
|
||||
},
|
||||
spv::Op::OpTypeMatrix, vstate)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
|
||||
<< "Structure id " << id << " decorated as " << deco_str
|
||||
<< " must be explicitly laid out with MatrixStride "
|
||||
"decorations.";
|
||||
}
|
||||
if (isMissingOffsetInStruct(id, vstate)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
|
||||
<< "Structure id " << id << " decorated as " << deco_str
|
||||
<< " must be explicitly laid out with Offset "
|
||||
"decorations.";
|
||||
}
|
||||
|
||||
if (!checkForRequiredDecoration(
|
||||
id,
|
||||
[](spv::Decoration d) {
|
||||
return d == spv::Decoration::RowMajor ||
|
||||
d == spv::Decoration::ColMajor;
|
||||
},
|
||||
spv::Op::OpTypeMatrix, vstate)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
|
||||
<< "Structure id " << id << " decorated as " << deco_str
|
||||
<< " must be explicitly laid out with RowMajor or "
|
||||
"ColMajor decorations.";
|
||||
}
|
||||
if (!checkForRequiredDecoration(
|
||||
id,
|
||||
[](spv::Decoration d) {
|
||||
return d == spv::Decoration::ArrayStride;
|
||||
},
|
||||
spv::Op::OpTypeArray, vstate)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
|
||||
<< "Structure id " << id << " decorated as " << deco_str
|
||||
<< " must be explicitly laid out with ArrayStride "
|
||||
"decorations.";
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(vstate.context()->target_env)) {
|
||||
if (blockRules && (SPV_SUCCESS != (recursive_status = checkLayout(
|
||||
id, sc_str, deco_str, true,
|
||||
if (!checkForRequiredDecoration(
|
||||
id,
|
||||
[](spv::Decoration d) {
|
||||
return d == spv::Decoration::MatrixStride;
|
||||
},
|
||||
spv::Op::OpTypeMatrix, vstate)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
|
||||
<< "Structure id " << id << " decorated as " << deco_str
|
||||
<< " must be explicitly laid out with MatrixStride "
|
||||
"decorations.";
|
||||
}
|
||||
|
||||
if (!checkForRequiredDecoration(
|
||||
id,
|
||||
[](spv::Decoration d) {
|
||||
return d == spv::Decoration::RowMajor ||
|
||||
d == spv::Decoration::ColMajor;
|
||||
},
|
||||
spv::Op::OpTypeMatrix, vstate)) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
|
||||
<< "Structure id " << id << " decorated as " << deco_str
|
||||
<< " must be explicitly laid out with RowMajor or "
|
||||
"ColMajor decorations.";
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(vstate.context()->target_env)) {
|
||||
if (blockRules &&
|
||||
(SPV_SUCCESS !=
|
||||
(recursive_status = checkLayout(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, scalar_block_layout,
|
||||
0, constraints, vstate)))) {
|
||||
return recursive_status;
|
||||
return recursive_status;
|
||||
} else if (bufferRules &&
|
||||
(SPV_SUCCESS != (recursive_status = checkLayout(
|
||||
id, sc_str, deco_str, false,
|
||||
scalar_block_layout, 0,
|
||||
constraints, vstate)))) {
|
||||
return recursive_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1340,19 +1383,97 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
|
||||
} else if (type_inst && type_inst->opcode() == spv::Op::OpTypePointer &&
|
||||
type_inst->GetOperandAs<spv::StorageClass>(1u) ==
|
||||
spv::StorageClass::PhysicalStorageBuffer) {
|
||||
const bool scalar_block_layout = vstate.options()->scalar_block_layout;
|
||||
MemberConstraints constraints;
|
||||
const bool buffer = true;
|
||||
const auto data_type_id = type_inst->GetOperandAs<uint32_t>(2u);
|
||||
const auto* data_type_inst = vstate.FindDef(data_type_id);
|
||||
const auto pointee_type_id = type_inst->GetOperandAs<uint32_t>(2u);
|
||||
const auto* data_type_inst = vstate.FindDef(pointee_type_id);
|
||||
scalar_block_layout = vstate.options()->scalar_block_layout;
|
||||
if (data_type_inst->opcode() == spv::Op::OpTypeStruct) {
|
||||
ComputeMemberConstraintsForStruct(&constraints, pointee_type_id,
|
||||
LayoutConstraints(), vstate);
|
||||
}
|
||||
if (auto res = checkLayout(pointee_type_id, "PhysicalStorageBuffer",
|
||||
"Block", !buffer, scalar_block_layout, 0,
|
||||
constraints, vstate)) {
|
||||
return res;
|
||||
}
|
||||
} else if (vstate.HasCapability(spv::Capability::UntypedPointersKHR) &&
|
||||
spvIsVulkanEnv(vstate.context()->target_env)) {
|
||||
// Untyped variables are checked above. Here we check that instructions
|
||||
// using an untyped pointer have a valid layout.
|
||||
uint32_t ptr_ty_id = 0;
|
||||
uint32_t data_type_id = 0;
|
||||
switch (inst.opcode()) {
|
||||
case spv::Op::OpUntypedAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsAccessChainKHR:
|
||||
case spv::Op::OpUntypedPtrAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
|
||||
ptr_ty_id = inst.type_id();
|
||||
data_type_id = inst.GetOperandAs<uint32_t>(2);
|
||||
break;
|
||||
case spv::Op::OpLoad:
|
||||
if (vstate.GetIdOpcode(vstate.GetOperandTypeId(&inst, 2)) ==
|
||||
spv::Op::OpTypeUntypedPointerKHR) {
|
||||
const auto ptr_id = inst.GetOperandAs<uint32_t>(2);
|
||||
ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
|
||||
data_type_id = inst.type_id();
|
||||
}
|
||||
break;
|
||||
case spv::Op::OpStore:
|
||||
if (vstate.GetIdOpcode(vstate.GetOperandTypeId(&inst, 0)) ==
|
||||
spv::Op::OpTypeUntypedPointerKHR) {
|
||||
const auto ptr_id = inst.GetOperandAs<uint32_t>(0);
|
||||
ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
|
||||
data_type_id = vstate.GetOperandTypeId(&inst, 1);
|
||||
}
|
||||
break;
|
||||
case spv::Op::OpUntypedArrayLengthKHR:
|
||||
ptr_ty_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->type_id();
|
||||
data_type_id = inst.GetOperandAs<uint32_t>(2);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ptr_ty_id == 0 || data_type_id == 0) {
|
||||
// Not an untyped pointer.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto sc =
|
||||
vstate.FindDef(ptr_ty_id)->GetOperandAs<spv::StorageClass>(1);
|
||||
|
||||
const char* sc_str =
|
||||
sc == spv::StorageClass::Uniform
|
||||
? "Uniform"
|
||||
: (sc == spv::StorageClass::PushConstant
|
||||
? "PushConstant"
|
||||
: (sc == spv::StorageClass::Workgroup ? "Workgroup"
|
||||
: "StorageBuffer"));
|
||||
|
||||
const auto data_type = vstate.FindDef(data_type_id);
|
||||
scalar_block_layout =
|
||||
sc == spv::StorageClass::Workgroup
|
||||
? vstate.options()->workgroup_scalar_block_layout
|
||||
: vstate.options()->scalar_block_layout;
|
||||
// Assume uniform storage class uses block rules unless we see a
|
||||
// BufferBlock decorated struct in the data type.
|
||||
bool bufferRules = sc == spv::StorageClass::Uniform ? false : true;
|
||||
if (data_type->opcode() == spv::Op::OpTypeStruct) {
|
||||
if (sc == spv::StorageClass::Uniform) {
|
||||
bufferRules =
|
||||
vstate.HasDecoration(data_type_id, spv::Decoration::BufferBlock);
|
||||
}
|
||||
ComputeMemberConstraintsForStruct(&constraints, data_type_id,
|
||||
LayoutConstraints(), vstate);
|
||||
}
|
||||
if (auto res = checkLayout(data_type_id, "PhysicalStorageBuffer", "Block",
|
||||
!buffer, scalar_block_layout, 0, constraints,
|
||||
vstate)) {
|
||||
return res;
|
||||
const char* deco_str =
|
||||
bufferRules
|
||||
? (sc == spv::StorageClass::Uniform ? "BufferBlock" : "Block")
|
||||
: "Block";
|
||||
if (auto result =
|
||||
checkLayout(data_type_id, sc_str, deco_str, !bufferRules,
|
||||
scalar_block_layout, 0, constraints, vstate)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1585,15 +1706,19 @@ spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate,
|
||||
const auto opcode = inst.opcode();
|
||||
const auto type_id = inst.type_id();
|
||||
if (opcode != spv::Op::OpVariable &&
|
||||
opcode != spv::Op::OpUntypedVariableKHR &&
|
||||
opcode != spv::Op::OpFunctionParameter &&
|
||||
opcode != spv::Op::OpRawAccessChainNV) {
|
||||
return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
|
||||
<< "Target of NonWritable decoration must be a memory object "
|
||||
"declaration (a variable or a function parameter)";
|
||||
}
|
||||
const auto var_storage_class = opcode == spv::Op::OpVariable
|
||||
? inst.GetOperandAs<spv::StorageClass>(2)
|
||||
: spv::StorageClass::Max;
|
||||
const auto var_storage_class =
|
||||
opcode == spv::Op::OpVariable
|
||||
? inst.GetOperandAs<spv::StorageClass>(2)
|
||||
: opcode == spv::Op::OpUntypedVariableKHR
|
||||
? inst.GetOperandAs<spv::StorageClass>(3)
|
||||
: spv::StorageClass::Max;
|
||||
if ((var_storage_class == spv::StorageClass::Function ||
|
||||
var_storage_class == spv::StorageClass::Private) &&
|
||||
vstate.features().nonwritable_var_in_function_or_private) {
|
||||
|
@ -156,7 +156,9 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _,
|
||||
param_nonarray_type_id =
|
||||
_.FindDef(param_nonarray_type_id)->GetOperandAs<uint32_t>(1u);
|
||||
}
|
||||
if (_.GetIdOpcode(param_nonarray_type_id) == spv::Op::OpTypePointer) {
|
||||
if (_.GetIdOpcode(param_nonarray_type_id) == spv::Op::OpTypePointer ||
|
||||
_.GetIdOpcode(param_nonarray_type_id) ==
|
||||
spv::Op::OpTypeUntypedPointerKHR) {
|
||||
auto param_nonarray_type = _.FindDef(param_nonarray_type_id);
|
||||
if (param_nonarray_type->GetOperandAs<spv::StorageClass>(1u) ==
|
||||
spv::StorageClass::PhysicalStorageBuffer) {
|
||||
@ -185,7 +187,7 @@ spv_result_t ValidateFunctionParameter(ValidationState_t& _,
|
||||
<< ": can't specify both Aliased and Restrict for "
|
||||
"PhysicalStorageBuffer pointer.";
|
||||
}
|
||||
} else {
|
||||
} else if (param_nonarray_type->opcode() == spv::Op::OpTypePointer) {
|
||||
const auto pointee_type_id =
|
||||
param_nonarray_type->GetOperandAs<uint32_t>(2);
|
||||
const auto pointee_type = _.FindDef(pointee_type_id);
|
||||
@ -288,7 +290,8 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _,
|
||||
}
|
||||
|
||||
if (_.addressing_model() == spv::AddressingModel::Logical) {
|
||||
if (parameter_type->opcode() == spv::Op::OpTypePointer &&
|
||||
if ((parameter_type->opcode() == spv::Op::OpTypePointer ||
|
||||
parameter_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) &&
|
||||
!_.options()->relax_logical_pointer) {
|
||||
spv::StorageClass sc =
|
||||
parameter_type->GetOperandAs<spv::StorageClass>(1u);
|
||||
@ -317,9 +320,11 @@ spv_result_t ValidateFunctionCall(ValidationState_t& _,
|
||||
|
||||
// Validate memory object declaration requirements.
|
||||
if (argument->opcode() != spv::Op::OpVariable &&
|
||||
argument->opcode() != spv::Op::OpUntypedVariableKHR &&
|
||||
argument->opcode() != spv::Op::OpFunctionParameter) {
|
||||
const bool ssbo_vptr = _.features().variable_pointers &&
|
||||
sc == spv::StorageClass::StorageBuffer;
|
||||
const bool ssbo_vptr =
|
||||
_.HasCapability(spv::Capability::VariablePointersStorageBuffer) &&
|
||||
sc == spv::StorageClass::StorageBuffer;
|
||||
const bool wg_vptr =
|
||||
_.HasCapability(spv::Capability::VariablePointers) &&
|
||||
sc == spv::StorageClass::Workgroup;
|
||||
|
@ -165,6 +165,8 @@ spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
|
||||
!spvOpcodeIsDecoration(opcode) && opcode != spv::Op::OpFunction &&
|
||||
opcode != spv::Op::OpCooperativeMatrixLengthNV &&
|
||||
opcode != spv::Op::OpCooperativeMatrixLengthKHR &&
|
||||
!spvOpcodeGeneratesUntypedPointer(opcode) &&
|
||||
opcode != spv::Op::OpUntypedArrayLengthKHR &&
|
||||
!(opcode == spv::Op::OpSpecConstantOp &&
|
||||
(spv::Op(inst->word(3)) ==
|
||||
spv::Op::OpCooperativeMatrixLengthNV ||
|
||||
@ -185,6 +187,8 @@ spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
|
||||
opcode != spv::Op::OpFunction &&
|
||||
opcode != spv::Op::OpCooperativeMatrixLengthNV &&
|
||||
opcode != spv::Op::OpCooperativeMatrixLengthKHR &&
|
||||
!spvOpcodeGeneratesUntypedPointer(opcode) &&
|
||||
opcode != spv::Op::OpUntypedArrayLengthKHR &&
|
||||
!(opcode == spv::Op::OpSpecConstantOp &&
|
||||
(spv::Op(inst->word(3)) ==
|
||||
spv::Op::OpCooperativeMatrixLengthNV ||
|
||||
|
@ -1121,7 +1121,8 @@ spv_result_t ValidateSampledImage(ValidationState_t& _,
|
||||
spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
const auto result_type = _.FindDef(inst->type_id());
|
||||
if (result_type->opcode() != spv::Op::OpTypePointer) {
|
||||
if (result_type->opcode() != spv::Op::OpTypePointer &&
|
||||
result_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Result Type to be OpTypePointer";
|
||||
}
|
||||
@ -1133,16 +1134,20 @@ spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
|
||||
"operand is Image";
|
||||
}
|
||||
|
||||
const auto ptr_type = result_type->GetOperandAs<uint32_t>(2);
|
||||
const auto ptr_opcode = _.GetIdOpcode(ptr_type);
|
||||
if (ptr_opcode != spv::Op::OpTypeInt && ptr_opcode != spv::Op::OpTypeFloat &&
|
||||
ptr_opcode != spv::Op::OpTypeVoid &&
|
||||
!(ptr_opcode == spv::Op::OpTypeVector &&
|
||||
_.HasCapability(spv::Capability::AtomicFloat16VectorNV) &&
|
||||
_.IsFloat16Vector2Or4Type(ptr_type))) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Result Type to be OpTypePointer whose Type operand "
|
||||
"must be a scalar numerical type or OpTypeVoid";
|
||||
uint32_t ptr_type = 0;
|
||||
if (result_type->opcode() == spv::Op::OpTypePointer) {
|
||||
ptr_type = result_type->GetOperandAs<uint32_t>(2);
|
||||
const auto ptr_opcode = _.GetIdOpcode(ptr_type);
|
||||
if (ptr_opcode != spv::Op::OpTypeInt &&
|
||||
ptr_opcode != spv::Op::OpTypeFloat &&
|
||||
ptr_opcode != spv::Op::OpTypeVoid &&
|
||||
!(ptr_opcode == spv::Op::OpTypeVector &&
|
||||
_.HasCapability(spv::Capability::AtomicFloat16VectorNV) &&
|
||||
_.IsFloat16Vector2Or4Type(ptr_type))) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Expected Result Type to be OpTypePointer whose Type operand "
|
||||
"must be a scalar numerical type or OpTypeVoid";
|
||||
}
|
||||
}
|
||||
|
||||
const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2));
|
||||
@ -1163,7 +1168,8 @@ spv_result_t ValidateImageTexelPointer(ValidationState_t& _,
|
||||
<< "Corrupt image type definition";
|
||||
}
|
||||
|
||||
if (info.sampled_type != ptr_type &&
|
||||
if (result_type->opcode() == spv::Op::OpTypePointer &&
|
||||
info.sampled_type != ptr_type &&
|
||||
!(_.HasCapability(spv::Capability::AtomicFloat16VectorNV) &&
|
||||
_.IsFloat16Vector2Or4Type(ptr_type) &&
|
||||
_.GetIdOpcode(info.sampled_type) == spv::Op::OpTypeFloat &&
|
||||
|
@ -34,11 +34,13 @@ const uint32_t kMaxLocations = 4096 * 4;
|
||||
bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) {
|
||||
if (is_spv_1_4) {
|
||||
// Starting in SPIR-V 1.4, all global variables are interface variables.
|
||||
return inst->opcode() == spv::Op::OpVariable &&
|
||||
return (inst->opcode() == spv::Op::OpVariable ||
|
||||
inst->opcode() == spv::Op::OpUntypedVariableKHR) &&
|
||||
inst->GetOperandAs<spv::StorageClass>(2u) !=
|
||||
spv::StorageClass::Function;
|
||||
} else {
|
||||
return inst->opcode() == spv::Op::OpVariable &&
|
||||
return (inst->opcode() == spv::Op::OpVariable ||
|
||||
inst->opcode() == spv::Op::OpUntypedVariableKHR) &&
|
||||
(inst->GetOperandAs<spv::StorageClass>(2u) ==
|
||||
spv::StorageClass::Input ||
|
||||
inst->GetOperandAs<spv::StorageClass>(2u) ==
|
||||
@ -242,8 +244,9 @@ spv_result_t GetLocationsForVariable(
|
||||
std::unordered_set<uint32_t>* output_index1_locations) {
|
||||
const bool is_fragment = entry_point->GetOperandAs<spv::ExecutionModel>(0) ==
|
||||
spv::ExecutionModel::Fragment;
|
||||
const bool is_output =
|
||||
variable->GetOperandAs<spv::StorageClass>(2) == spv::StorageClass::Output;
|
||||
const auto sc_index = 2u;
|
||||
const bool is_output = variable->GetOperandAs<spv::StorageClass>(sc_index) ==
|
||||
spv::StorageClass::Output;
|
||||
auto ptr_type_id = variable->GetOperandAs<uint32_t>(0);
|
||||
auto ptr_type = _.FindDef(ptr_type_id);
|
||||
auto type_id = ptr_type->GetOperandAs<uint32_t>(2);
|
||||
@ -525,7 +528,9 @@ spv_result_t ValidateLocations(ValidationState_t& _,
|
||||
for (uint32_t i = 3; i < entry_point->operands().size(); ++i) {
|
||||
auto interface_id = entry_point->GetOperandAs<uint32_t>(i);
|
||||
auto interface_var = _.FindDef(interface_id);
|
||||
auto storage_class = interface_var->GetOperandAs<spv::StorageClass>(2);
|
||||
const auto sc_index = 2u;
|
||||
auto storage_class =
|
||||
interface_var->GetOperandAs<spv::StorageClass>(sc_index);
|
||||
if (storage_class != spv::StorageClass::Input &&
|
||||
storage_class != spv::StorageClass::Output) {
|
||||
continue;
|
||||
|
@ -159,9 +159,11 @@ spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
const spv::Op type_opcode = type_inst->opcode();
|
||||
switch (type_opcode) {
|
||||
case spv::Op::OpTypeUntypedPointerKHR:
|
||||
case spv::Op::OpTypePointer: {
|
||||
if (_.addressing_model() == spv::AddressingModel::Logical &&
|
||||
!_.features().variable_pointers)
|
||||
!_.HasCapability(
|
||||
spv::Capability::VariablePointersStorageBuffer))
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Using pointers with OpSelect requires capability "
|
||||
<< "VariablePointers or VariablePointersStorageBuffer";
|
||||
|
@ -407,19 +407,58 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
}
|
||||
|
||||
spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
const bool untyped_pointer = inst->opcode() == spv::Op::OpUntypedVariableKHR;
|
||||
|
||||
auto result_type = _.FindDef(inst->type_id());
|
||||
if (!result_type || result_type->opcode() != spv::Op::OpTypePointer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable Result Type <id> " << _.getIdName(inst->type_id())
|
||||
<< " is not a pointer type.";
|
||||
if (untyped_pointer) {
|
||||
if (!result_type ||
|
||||
result_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Result type must be an untyped pointer";
|
||||
} else {
|
||||
if (!result_type || result_type->opcode() != spv::Op::OpTypePointer) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable Result Type <id> " << _.getIdName(inst->type_id())
|
||||
<< " is not a pointer type.";
|
||||
}
|
||||
}
|
||||
|
||||
const auto type_index = 2;
|
||||
const auto value_id = result_type->GetOperandAs<uint32_t>(type_index);
|
||||
auto value_type = _.FindDef(value_id);
|
||||
const auto storage_class_index = 2u;
|
||||
auto storage_class =
|
||||
inst->GetOperandAs<spv::StorageClass>(storage_class_index);
|
||||
uint32_t value_id = 0;
|
||||
if (untyped_pointer) {
|
||||
const auto has_data_type = 3u < inst->operands().size();
|
||||
if (has_data_type) {
|
||||
value_id = inst->GetOperandAs<uint32_t>(3u);
|
||||
auto data_type = _.FindDef(value_id);
|
||||
if (!data_type || !spvOpcodeGeneratesType(data_type->opcode())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Data type must be a type instruction";
|
||||
}
|
||||
} else {
|
||||
if (storage_class == spv::StorageClass::Function ||
|
||||
storage_class == spv::StorageClass::Private ||
|
||||
storage_class == spv::StorageClass::Workgroup) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Data type must be specified for Function, Private, and "
|
||||
"Workgroup storage classes";
|
||||
}
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Vulkan requires that data type be specified";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto initializer_index = 3;
|
||||
const auto storage_class_index = 2;
|
||||
// For OpVariable the data type comes from pointee type of the result type,
|
||||
// while for OpUntypedVariableKHR the data type comes from the operand.
|
||||
if (!untyped_pointer) {
|
||||
value_id = result_type->GetOperandAs<uint32_t>(2);
|
||||
}
|
||||
auto value_type = value_id == 0 ? nullptr : _.FindDef(value_id);
|
||||
|
||||
const auto initializer_index = untyped_pointer ? 4u : 3u;
|
||||
if (initializer_index < inst->operands().size()) {
|
||||
const auto initializer_id = inst->GetOperandAs<uint32_t>(initializer_index);
|
||||
const auto initializer = _.FindDef(initializer_id);
|
||||
@ -431,18 +470,15 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
initializer && spvOpcodeIsConstant(initializer->opcode());
|
||||
if (!initializer || !(is_constant || is_module_scope_var)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable Initializer <id> " << _.getIdName(initializer_id)
|
||||
<< "Variable Initializer <id> " << _.getIdName(initializer_id)
|
||||
<< " is not a constant or module-scope variable.";
|
||||
}
|
||||
if (initializer->type_id() != value_id) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Initializer type must match the type pointed to by the Result "
|
||||
"Type";
|
||||
<< "Initializer type must match the data type";
|
||||
}
|
||||
}
|
||||
|
||||
auto storage_class =
|
||||
inst->GetOperandAs<spv::StorageClass>(storage_class_index);
|
||||
if (storage_class != spv::StorageClass::Workgroup &&
|
||||
storage_class != spv::StorageClass::CrossWorkgroup &&
|
||||
storage_class != spv::StorageClass::Private &&
|
||||
@ -466,7 +502,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!builtin &&
|
||||
if (!builtin && value_type &&
|
||||
ContainsInvalidBool(_, value_type, storage_input_or_output)) {
|
||||
if (storage_input_or_output) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
@ -495,7 +531,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
if (storage_class == spv::StorageClass::Generic) {
|
||||
return _.diag(SPV_ERROR_INVALID_BINARY, inst)
|
||||
<< "OpVariable storage class cannot be Generic";
|
||||
<< "Variable storage class cannot be Generic";
|
||||
}
|
||||
|
||||
if (inst->function() && storage_class != spv::StorageClass::Function) {
|
||||
@ -517,17 +553,17 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
result_type->GetOperandAs<spv::StorageClass>(result_storage_class_index);
|
||||
if (storage_class != result_storage_class) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "From SPIR-V spec, section 3.32.8 on OpVariable:\n"
|
||||
<< "Its Storage Class operand must be the same as the Storage Class "
|
||||
<< "operand of the result type.";
|
||||
<< "Storage class must match result type storage class";
|
||||
}
|
||||
|
||||
// Variable pointer related restrictions.
|
||||
const auto pointee = _.FindDef(result_type->word(3));
|
||||
const auto pointee = untyped_pointer
|
||||
? value_id == 0 ? nullptr : _.FindDef(value_id)
|
||||
: _.FindDef(result_type->word(3));
|
||||
if (_.addressing_model() == spv::AddressingModel::Logical &&
|
||||
!_.options()->relax_logical_pointer) {
|
||||
// VariablePointersStorageBuffer is implied by VariablePointers.
|
||||
if (pointee->opcode() == spv::Op::OpTypePointer) {
|
||||
if (pointee && pointee->opcode() == spv::Op::OpTypePointer) {
|
||||
if (!_.HasCapability(spv::Capability::VariablePointersStorageBuffer)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "In Logical addressing, variables may not allocate a pointer "
|
||||
@ -546,7 +582,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
// Vulkan Push Constant Interface section: Check type of PushConstant
|
||||
// variables.
|
||||
if (storage_class == spv::StorageClass::PushConstant) {
|
||||
if (pointee->opcode() != spv::Op::OpTypeStruct) {
|
||||
if (pointee && pointee->opcode() != spv::Op::OpTypeStruct) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< _.VkErrorID(6808) << "PushConstant OpVariable <id> "
|
||||
<< _.getIdName(inst->id()) << " has illegal type.\n"
|
||||
@ -558,11 +594,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
// Vulkan Descriptor Set Interface: Check type of UniformConstant and
|
||||
// Uniform variables.
|
||||
if (storage_class == spv::StorageClass::UniformConstant) {
|
||||
if (!IsAllowedTypeOrArrayOfSame(
|
||||
_, pointee,
|
||||
{spv::Op::OpTypeImage, spv::Op::OpTypeSampler,
|
||||
spv::Op::OpTypeSampledImage,
|
||||
spv::Op::OpTypeAccelerationStructureKHR})) {
|
||||
if (pointee && !IsAllowedTypeOrArrayOfSame(
|
||||
_, pointee,
|
||||
{spv::Op::OpTypeImage, spv::Op::OpTypeSampler,
|
||||
spv::Op::OpTypeSampledImage,
|
||||
spv::Op::OpTypeAccelerationStructureKHR})) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< _.VkErrorID(4655) << "UniformConstant OpVariable <id> "
|
||||
<< _.getIdName(inst->id()) << " has illegal type.\n"
|
||||
@ -575,7 +611,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
|
||||
if (storage_class == spv::StorageClass::Uniform) {
|
||||
if (!IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) {
|
||||
if (pointee &&
|
||||
!IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< _.VkErrorID(6807) << "Uniform OpVariable <id> "
|
||||
<< _.getIdName(inst->id()) << " has illegal type.\n"
|
||||
@ -588,7 +625,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
|
||||
if (storage_class == spv::StorageClass::StorageBuffer) {
|
||||
if (!IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) {
|
||||
if (pointee &&
|
||||
!IsAllowedTypeOrArrayOfSame(_, pointee, {spv::Op::OpTypeStruct})) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< _.VkErrorID(6807) << "StorageBuffer OpVariable <id> "
|
||||
<< _.getIdName(inst->id()) << " has illegal type.\n"
|
||||
@ -621,11 +659,17 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initializers in Vulkan are only allowed in some storage clases
|
||||
if (inst->operands().size() > 3) {
|
||||
// Vulkan Appendix A: Check that if contains initializer, then
|
||||
// storage class is Output, Private, or Function.
|
||||
if (inst->operands().size() > initializer_index &&
|
||||
storage_class != spv::StorageClass::Output &&
|
||||
storage_class != spv::StorageClass::Private &&
|
||||
storage_class != spv::StorageClass::Function) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
if (storage_class == spv::StorageClass::Workgroup) {
|
||||
auto init_id = inst->GetOperandAs<uint32_t>(3);
|
||||
auto init_id = inst->GetOperandAs<uint32_t>(initializer_index);
|
||||
auto init = _.FindDef(init_id);
|
||||
if (init->opcode() != spv::Op::OpConstantNull) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
@ -652,7 +696,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->operands().size() > 3) {
|
||||
if (initializer_index < inst->operands().size()) {
|
||||
if (storage_class == spv::StorageClass::TaskPayloadWorkgroupEXT) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable, <id> " << _.getIdName(inst->id())
|
||||
@ -676,10 +720,10 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
|
||||
auto pointee_base = pointee;
|
||||
while (pointee_base->opcode() == spv::Op::OpTypeArray) {
|
||||
while (pointee_base && pointee_base->opcode() == spv::Op::OpTypeArray) {
|
||||
pointee_base = _.FindDef(pointee_base->GetOperandAs<uint32_t>(1u));
|
||||
}
|
||||
if (pointee_base->opcode() == spv::Op::OpTypePointer) {
|
||||
if (pointee_base && pointee_base->opcode() == spv::Op::OpTypePointer) {
|
||||
if (pointee_base->GetOperandAs<spv::StorageClass>(1u) ==
|
||||
spv::StorageClass::PhysicalStorageBuffer) {
|
||||
// check for AliasedPointer/RestrictPointer
|
||||
@ -769,7 +813,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
// Cooperative matrix types can only be allocated in Function or Private
|
||||
if ((storage_class != spv::StorageClass::Function &&
|
||||
storage_class != spv::StorageClass::Private) &&
|
||||
ContainsCooperativeMatrix(_, pointee)) {
|
||||
pointee && ContainsCooperativeMatrix(_, pointee)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Cooperative matrix types (or types containing them) can only be "
|
||||
"allocated "
|
||||
@ -785,7 +829,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
(!_.HasCapability(spv::Capability::Float16) &&
|
||||
_.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeFloat, 16))) {
|
||||
auto underlying_type = value_type;
|
||||
while (underlying_type->opcode() == spv::Op::OpTypePointer) {
|
||||
while (underlying_type &&
|
||||
underlying_type->opcode() == spv::Op::OpTypePointer) {
|
||||
storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u);
|
||||
underlying_type =
|
||||
_.FindDef(underlying_type->GetOperandAs<uint32_t>(2u));
|
||||
@ -801,7 +846,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
break;
|
||||
case spv::StorageClass::Uniform:
|
||||
if (!_.HasCapability(
|
||||
if (underlying_type &&
|
||||
!_.HasCapability(
|
||||
spv::Capability::UniformAndStorageBuffer16BitAccess)) {
|
||||
if (underlying_type->opcode() == spv::Op::OpTypeArray ||
|
||||
underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) {
|
||||
@ -849,7 +895,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
if (!_.HasCapability(spv::Capability::Int8) &&
|
||||
_.ContainsSizedIntOrFloatType(value_id, spv::Op::OpTypeInt, 8)) {
|
||||
auto underlying_type = value_type;
|
||||
while (underlying_type->opcode() == spv::Op::OpTypePointer) {
|
||||
while (underlying_type &&
|
||||
underlying_type->opcode() == spv::Op::OpTypePointer) {
|
||||
storage_class = underlying_type->GetOperandAs<spv::StorageClass>(1u);
|
||||
underlying_type =
|
||||
_.FindDef(underlying_type->GetOperandAs<uint32_t>(2u));
|
||||
@ -865,7 +912,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
break;
|
||||
case spv::StorageClass::Uniform:
|
||||
if (!_.HasCapability(
|
||||
if (underlying_type &&
|
||||
!_.HasCapability(
|
||||
spv::Capability::UniformAndStorageBuffer8BitAccess)) {
|
||||
if (underlying_type->opcode() == spv::Op::OpTypeArray ||
|
||||
underlying_type->opcode() == spv::Op::OpTypeRuntimeArray) {
|
||||
@ -930,21 +978,23 @@ spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
|
||||
const auto pointer_type = _.FindDef(pointer->type_id());
|
||||
if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) {
|
||||
if (!pointer_type ||
|
||||
(pointer_type->opcode() != spv::Op::OpTypePointer &&
|
||||
pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpLoad type for pointer <id> " << _.getIdName(pointer_id)
|
||||
<< " is not a pointer type.";
|
||||
}
|
||||
|
||||
uint32_t pointee_data_type;
|
||||
spv::StorageClass storage_class;
|
||||
if (!_.GetPointerTypeInfo(pointer_type->id(), &pointee_data_type,
|
||||
&storage_class) ||
|
||||
result_type->id() != pointee_data_type) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpLoad Result Type <id> " << _.getIdName(inst->type_id())
|
||||
<< " does not match Pointer <id> " << _.getIdName(pointer->id())
|
||||
<< "s type.";
|
||||
if (pointer_type->opcode() == spv::Op::OpTypePointer) {
|
||||
const auto pointee_type =
|
||||
_.FindDef(pointer_type->GetOperandAs<uint32_t>(2));
|
||||
if (!pointee_type || result_type->id() != pointee_type->id()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpLoad Result Type <id> " << _.getIdName(inst->type_id())
|
||||
<< " does not match Pointer <id> " << _.getIdName(pointer->id())
|
||||
<< "s type.";
|
||||
}
|
||||
}
|
||||
|
||||
if (!_.options()->before_hlsl_legalization &&
|
||||
@ -987,17 +1037,23 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
|
||||
<< " is not a logical pointer.";
|
||||
}
|
||||
const auto pointer_type = _.FindDef(pointer->type_id());
|
||||
if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) {
|
||||
if (!pointer_type ||
|
||||
(pointer_type->opcode() != spv::Op::OpTypePointer &&
|
||||
pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpStore type for pointer <id> " << _.getIdName(pointer_id)
|
||||
<< " is not a pointer type.";
|
||||
}
|
||||
const auto type_id = pointer_type->GetOperandAs<uint32_t>(2);
|
||||
const auto type = _.FindDef(type_id);
|
||||
if (!type || spv::Op::OpTypeVoid == type->opcode()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpStore Pointer <id> " << _.getIdName(pointer_id)
|
||||
<< "s type is void.";
|
||||
|
||||
Instruction* type = nullptr;
|
||||
if (pointer_type->opcode() == spv::Op::OpTypePointer) {
|
||||
const auto type_id = pointer_type->GetOperandAs<uint32_t>(2);
|
||||
type = _.FindDef(type_id);
|
||||
if (!type || spv::Op::OpTypeVoid == type->opcode()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpStore Pointer <id> " << _.getIdName(pointer_id)
|
||||
<< "s type is void.";
|
||||
}
|
||||
}
|
||||
|
||||
// validate storage class
|
||||
@ -1074,7 +1130,7 @@ spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) {
|
||||
<< "s type is void.";
|
||||
}
|
||||
|
||||
if (type->id() != object_type->id()) {
|
||||
if (type && (type->id() != object_type->id())) {
|
||||
if (!_.options()->relax_struct_store ||
|
||||
type->opcode() != spv::Op::OpTypeStruct ||
|
||||
object_type->opcode() != spv::Op::OpTypeStruct) {
|
||||
@ -1179,7 +1235,8 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
const auto target_pointer_type = _.FindDef(target->type_id());
|
||||
if (!target_pointer_type ||
|
||||
target_pointer_type->opcode() != spv::Op::OpTypePointer) {
|
||||
(target_pointer_type->opcode() != spv::Op::OpTypePointer &&
|
||||
target_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Target operand <id> " << _.getIdName(target_id)
|
||||
<< " is not a pointer.";
|
||||
@ -1187,35 +1244,52 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
|
||||
|
||||
const auto source_pointer_type = _.FindDef(source->type_id());
|
||||
if (!source_pointer_type ||
|
||||
source_pointer_type->opcode() != spv::Op::OpTypePointer) {
|
||||
(source_pointer_type->opcode() != spv::Op::OpTypePointer &&
|
||||
source_pointer_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Source operand <id> " << _.getIdName(source_id)
|
||||
<< " is not a pointer.";
|
||||
}
|
||||
|
||||
if (inst->opcode() == spv::Op::OpCopyMemory) {
|
||||
const auto target_type =
|
||||
_.FindDef(target_pointer_type->GetOperandAs<uint32_t>(2));
|
||||
if (!target_type || target_type->opcode() == spv::Op::OpTypeVoid) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Target operand <id> " << _.getIdName(target_id)
|
||||
<< " cannot be a void pointer.";
|
||||
const bool target_typed =
|
||||
target_pointer_type->opcode() == spv::Op::OpTypePointer;
|
||||
const bool source_typed =
|
||||
source_pointer_type->opcode() == spv::Op::OpTypePointer;
|
||||
Instruction* target_type = nullptr;
|
||||
Instruction* source_type = nullptr;
|
||||
if (target_typed) {
|
||||
target_type = _.FindDef(target_pointer_type->GetOperandAs<uint32_t>(2));
|
||||
|
||||
if (!target_type || target_type->opcode() == spv::Op::OpTypeVoid) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Target operand <id> " << _.getIdName(target_id)
|
||||
<< " cannot be a void pointer.";
|
||||
}
|
||||
}
|
||||
|
||||
const auto source_type =
|
||||
_.FindDef(source_pointer_type->GetOperandAs<uint32_t>(2));
|
||||
if (!source_type || source_type->opcode() == spv::Op::OpTypeVoid) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Source operand <id> " << _.getIdName(source_id)
|
||||
<< " cannot be a void pointer.";
|
||||
if (source_typed) {
|
||||
source_type = _.FindDef(source_pointer_type->GetOperandAs<uint32_t>(2));
|
||||
if (!source_type || source_type->opcode() == spv::Op::OpTypeVoid) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Source operand <id> " << _.getIdName(source_id)
|
||||
<< " cannot be a void pointer.";
|
||||
}
|
||||
}
|
||||
|
||||
if (target_type->id() != source_type->id()) {
|
||||
if (target_type && source_type && target_type->id() != source_type->id()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Target <id> " << _.getIdName(source_id)
|
||||
<< "s type does not match Source <id> "
|
||||
<< _.getIdName(source_type->id()) << "s type.";
|
||||
}
|
||||
|
||||
if (!target_type && !source_type) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "One of Source or Target must be a typed pointer";
|
||||
}
|
||||
|
||||
if (auto error = CheckMemoryAccess(_, inst, 2)) return error;
|
||||
} else {
|
||||
const auto size_id = inst->GetOperandAs<uint32_t>(2);
|
||||
const auto size = _.FindDef(size_id);
|
||||
@ -1231,7 +1305,6 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
|
||||
<< "Size operand <id> " << _.getIdName(size_id)
|
||||
<< " must be a scalar integer type.";
|
||||
}
|
||||
|
||||
bool is_zero = true;
|
||||
switch (size->opcode()) {
|
||||
case spv::Op::OpConstantNull:
|
||||
@ -1258,18 +1331,125 @@ spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) {
|
||||
// Cannot infer any other opcodes.
|
||||
break;
|
||||
}
|
||||
|
||||
if (_.HasCapability(spv::Capability::Shader)) {
|
||||
bool is_int = false;
|
||||
bool is_const = false;
|
||||
uint32_t value = 0;
|
||||
std::tie(is_int, is_const, value) = _.EvalInt32IfConst(size_id);
|
||||
if (is_const) {
|
||||
if (value % 4 != 0) {
|
||||
const auto source_sc =
|
||||
source_pointer_type->GetOperandAs<spv::StorageClass>(1);
|
||||
const auto target_sc =
|
||||
target_pointer_type->GetOperandAs<spv::StorageClass>(1);
|
||||
const bool int8 = _.HasCapability(spv::Capability::Int8);
|
||||
const bool ubo_int8 = _.HasCapability(
|
||||
spv::Capability::UniformAndStorageBuffer8BitAccess);
|
||||
const bool ssbo_int8 =
|
||||
_.HasCapability(spv::Capability::StorageBuffer8BitAccess) ||
|
||||
ubo_int8;
|
||||
const bool pc_int8 =
|
||||
_.HasCapability(spv::Capability::StoragePushConstant8);
|
||||
const bool wg_int8 = _.HasCapability(
|
||||
spv::Capability::WorkgroupMemoryExplicitLayout8BitAccessKHR);
|
||||
const bool int16 = _.HasCapability(spv::Capability::Int16) || int8;
|
||||
const bool ubo_int16 =
|
||||
_.HasCapability(
|
||||
spv::Capability::UniformAndStorageBuffer16BitAccess) ||
|
||||
ubo_int8;
|
||||
const bool ssbo_int16 =
|
||||
_.HasCapability(spv::Capability::StorageBuffer16BitAccess) ||
|
||||
ubo_int16 || ssbo_int8;
|
||||
const bool pc_int16 =
|
||||
_.HasCapability(spv::Capability::StoragePushConstant16) ||
|
||||
pc_int8;
|
||||
const bool io_int16 =
|
||||
_.HasCapability(spv::Capability::StorageInputOutput16);
|
||||
const bool wg_int16 = _.HasCapability(
|
||||
spv::Capability::WorkgroupMemoryExplicitLayout16BitAccessKHR);
|
||||
|
||||
bool source_int16_match = false;
|
||||
bool target_int16_match = false;
|
||||
bool source_int8_match = false;
|
||||
bool target_int8_match = false;
|
||||
switch (source_sc) {
|
||||
case spv::StorageClass::StorageBuffer:
|
||||
source_int16_match = ssbo_int16;
|
||||
source_int8_match = ssbo_int8;
|
||||
break;
|
||||
case spv::StorageClass::Uniform:
|
||||
source_int16_match = ubo_int16;
|
||||
source_int8_match = ubo_int8;
|
||||
break;
|
||||
case spv::StorageClass::PushConstant:
|
||||
source_int16_match = pc_int16;
|
||||
source_int8_match = pc_int8;
|
||||
break;
|
||||
case spv::StorageClass::Input:
|
||||
case spv::StorageClass::Output:
|
||||
source_int16_match = io_int16;
|
||||
break;
|
||||
case spv::StorageClass::Workgroup:
|
||||
source_int16_match = wg_int16;
|
||||
source_int8_match = wg_int8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (target_sc) {
|
||||
case spv::StorageClass::StorageBuffer:
|
||||
target_int16_match = ssbo_int16;
|
||||
target_int8_match = ssbo_int8;
|
||||
break;
|
||||
case spv::StorageClass::Uniform:
|
||||
target_int16_match = ubo_int16;
|
||||
target_int8_match = ubo_int8;
|
||||
break;
|
||||
case spv::StorageClass::PushConstant:
|
||||
target_int16_match = pc_int16;
|
||||
target_int8_match = pc_int8;
|
||||
break;
|
||||
// Input is read-only so it cannot be the target pointer.
|
||||
case spv::StorageClass::Output:
|
||||
target_int16_match = io_int16;
|
||||
break;
|
||||
case spv::StorageClass::Workgroup:
|
||||
target_int16_match = wg_int16;
|
||||
target_int8_match = wg_int8;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!int8 && !int16 && !(source_int16_match && target_int16_match)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Size must be a multiple of 4";
|
||||
}
|
||||
if (value % 2 != 0) {
|
||||
if (!int8 && !(source_int8_match && target_int8_match)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Size must be a multiple of 2";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto error = CheckMemoryAccess(_, inst, 3)) return error;
|
||||
}
|
||||
if (auto error = ValidateCopyMemoryMemoryAccess(_, inst)) return error;
|
||||
|
||||
// Get past the pointers to avoid checking a pointer copy.
|
||||
auto sub_type = _.FindDef(target_pointer_type->GetOperandAs<uint32_t>(2));
|
||||
while (sub_type->opcode() == spv::Op::OpTypePointer) {
|
||||
sub_type = _.FindDef(sub_type->GetOperandAs<uint32_t>(2));
|
||||
}
|
||||
if (_.HasCapability(spv::Capability::Shader) &&
|
||||
_.ContainsLimitedUseIntOrFloatType(sub_type->id())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Cannot copy memory of objects containing 8- or 16-bit types";
|
||||
if (target_pointer_type->opcode() == spv::Op::OpTypePointer) {
|
||||
auto sub_type = _.FindDef(target_pointer_type->GetOperandAs<uint32_t>(2));
|
||||
while (sub_type->opcode() == spv::Op::OpTypePointer) {
|
||||
sub_type = _.FindDef(sub_type->GetOperandAs<uint32_t>(2));
|
||||
}
|
||||
if (_.HasCapability(spv::Capability::Shader) &&
|
||||
_.ContainsLimitedUseIntOrFloatType(sub_type->id())) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Cannot copy memory of objects containing 8- or 16-bit types";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
@ -1280,27 +1460,50 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
|
||||
std::string instr_name =
|
||||
"Op" + std::string(spvOpcodeString(static_cast<spv::Op>(inst->opcode())));
|
||||
|
||||
// The result type must be OpTypePointer.
|
||||
const bool untyped_pointer = spvOpcodeGeneratesUntypedPointer(inst->opcode());
|
||||
|
||||
// The result type must be OpTypePointer for regular access chains and an
|
||||
// OpTypeUntypedPointerKHR for untyped access chains.
|
||||
auto result_type = _.FindDef(inst->type_id());
|
||||
if (spv::Op::OpTypePointer != result_type->opcode()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The Result Type of " << instr_name << " <id> "
|
||||
<< _.getIdName(inst->id()) << " must be OpTypePointer. Found Op"
|
||||
<< spvOpcodeString(static_cast<spv::Op>(result_type->opcode()))
|
||||
<< ".";
|
||||
if (untyped_pointer) {
|
||||
if (!result_type ||
|
||||
spv::Op::OpTypeUntypedPointerKHR != result_type->opcode()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The Result Type of " << instr_name << " <id> "
|
||||
<< _.getIdName(inst->id())
|
||||
<< " must be OpTypeUntypedPointerKHR. Found Op"
|
||||
<< spvOpcodeString(static_cast<spv::Op>(result_type->opcode()))
|
||||
<< ".";
|
||||
}
|
||||
} else {
|
||||
if (!result_type || spv::Op::OpTypePointer != result_type->opcode()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The Result Type of " << instr_name << " <id> "
|
||||
<< _.getIdName(inst->id()) << " must be OpTypePointer. Found Op"
|
||||
<< spvOpcodeString(static_cast<spv::Op>(result_type->opcode()))
|
||||
<< ".";
|
||||
}
|
||||
}
|
||||
|
||||
// Result type is a pointer. Find out what it's pointing to.
|
||||
// This will be used to make sure the indexing results in the same type.
|
||||
// OpTypePointer word 3 is the type being pointed to.
|
||||
const auto result_type_pointee = _.FindDef(result_type->word(3));
|
||||
if (untyped_pointer) {
|
||||
// Base type must be a non-pointer type.
|
||||
const auto base_type = _.FindDef(inst->GetOperandAs<uint32_t>(2));
|
||||
if (!base_type || !spvOpcodeGeneratesType(base_type->opcode()) ||
|
||||
base_type->opcode() == spv::Op::OpTypePointer ||
|
||||
base_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Base type must be a non-pointer type";
|
||||
}
|
||||
}
|
||||
|
||||
// Base must be a pointer, pointing to the base of a composite object.
|
||||
const auto base_index = 2;
|
||||
const auto base_index = untyped_pointer ? 3 : 2;
|
||||
const auto base_id = inst->GetOperandAs<uint32_t>(base_index);
|
||||
const auto base = _.FindDef(base_id);
|
||||
const auto base_type = _.FindDef(base->type_id());
|
||||
if (!base_type || spv::Op::OpTypePointer != base_type->opcode()) {
|
||||
if (!base_type || !(spv::Op::OpTypePointer == base_type->opcode() ||
|
||||
(untyped_pointer && spv::Op::OpTypeUntypedPointerKHR ==
|
||||
base_type->opcode()))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The Base <id> " << _.getIdName(base_id) << " in " << instr_name
|
||||
<< " instruction must be a pointer.";
|
||||
@ -1318,14 +1521,18 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
|
||||
}
|
||||
|
||||
// The type pointed to by OpTypePointer (word 3) must be a composite type.
|
||||
auto type_pointee = _.FindDef(base_type->word(3));
|
||||
auto type_pointee = untyped_pointer
|
||||
? _.FindDef(inst->GetOperandAs<uint32_t>(2))
|
||||
: _.FindDef(base_type->word(3));
|
||||
|
||||
// Check Universal Limit (SPIR-V Spec. Section 2.17).
|
||||
// The number of indexes passed to OpAccessChain may not exceed 255
|
||||
// The instruction includes 4 words + N words (for N indexes)
|
||||
size_t num_indexes = inst->words().size() - 4;
|
||||
if (inst->opcode() == spv::Op::OpPtrAccessChain ||
|
||||
inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) {
|
||||
inst->opcode() == spv::Op::OpInBoundsPtrAccessChain ||
|
||||
inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR ||
|
||||
inst->opcode() == spv::Op::OpUntypedInBoundsPtrAccessChainKHR) {
|
||||
// In pointer access chains, the element operand is required, but not
|
||||
// counted as an index.
|
||||
--num_indexes;
|
||||
@ -1344,9 +1551,11 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
|
||||
// instruction. The second index will apply similarly to that result, and so
|
||||
// on. Once any non-composite type is reached, there must be no remaining
|
||||
// (unused) indexes.
|
||||
auto starting_index = 4;
|
||||
auto starting_index = untyped_pointer ? 5 : 4;
|
||||
if (inst->opcode() == spv::Op::OpPtrAccessChain ||
|
||||
inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) {
|
||||
inst->opcode() == spv::Op::OpInBoundsPtrAccessChain ||
|
||||
inst->opcode() == spv::Op::OpUntypedPtrAccessChainKHR ||
|
||||
inst->opcode() == spv::Op::OpUntypedInBoundsPtrAccessChainKHR) {
|
||||
++starting_index;
|
||||
}
|
||||
for (size_t i = starting_index; i < inst->words().size(); ++i) {
|
||||
@ -1411,18 +1620,25 @@ spv_result_t ValidateAccessChain(ValidationState_t& _,
|
||||
}
|
||||
}
|
||||
}
|
||||
// At this point, we have fully walked down from the base using the indices.
|
||||
// The type being pointed to should be the same as the result type.
|
||||
if (type_pointee->id() != result_type_pointee->id()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< instr_name << " result type (Op"
|
||||
<< spvOpcodeString(
|
||||
static_cast<spv::Op>(result_type_pointee->opcode()))
|
||||
<< ") does not match the type that results from indexing into the "
|
||||
"base "
|
||||
"<id> (Op"
|
||||
<< spvOpcodeString(static_cast<spv::Op>(type_pointee->opcode()))
|
||||
<< ").";
|
||||
|
||||
if (!untyped_pointer) {
|
||||
// Result type is a pointer. Find out what it's pointing to.
|
||||
// This will be used to make sure the indexing results in the same type.
|
||||
// OpTypePointer word 3 is the type being pointed to.
|
||||
const auto result_type_pointee = _.FindDef(result_type->word(3));
|
||||
// At this point, we have fully walked down from the base using the indeces.
|
||||
// The type being pointed to should be the same as the result type.
|
||||
if (type_pointee->id() != result_type_pointee->id()) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< instr_name << " result type (Op"
|
||||
<< spvOpcodeString(
|
||||
static_cast<spv::Op>(result_type_pointee->opcode()))
|
||||
<< ") does not match the type that results from indexing into the "
|
||||
"base "
|
||||
"<id> (Op"
|
||||
<< spvOpcodeString(static_cast<spv::Op>(type_pointee->opcode()))
|
||||
<< ").";
|
||||
}
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
@ -1550,7 +1766,8 @@ spv_result_t ValidateRawAccessChain(ValidationState_t& _,
|
||||
|
||||
spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (_.addressing_model() == spv::AddressingModel::Logical) {
|
||||
if (_.addressing_model() == spv::AddressingModel::Logical &&
|
||||
inst->opcode() == spv::Op::OpPtrAccessChain) {
|
||||
if (!_.features().variable_pointers) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Generating variable pointers requires capability "
|
||||
@ -1561,9 +1778,13 @@ spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
|
||||
// Need to call first, will make sure Base is a valid ID
|
||||
if (auto error = ValidateAccessChain(_, inst)) return error;
|
||||
|
||||
const bool untyped_pointer = spvOpcodeGeneratesUntypedPointer(inst->opcode());
|
||||
|
||||
const auto base_id = inst->GetOperandAs<uint32_t>(2);
|
||||
const auto base = _.FindDef(base_id);
|
||||
const auto base_type = _.FindDef(base->type_id());
|
||||
const auto base_type = untyped_pointer
|
||||
? _.FindDef(inst->GetOperandAs<uint32_t>(2))
|
||||
: _.FindDef(base->type_id());
|
||||
const auto base_type_storage_class =
|
||||
base_type->GetOperandAs<spv::StorageClass>(1);
|
||||
|
||||
@ -1581,15 +1802,17 @@ spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
const auto untyped_cap =
|
||||
untyped_pointer && _.HasCapability(spv::Capability::UntypedPointersKHR);
|
||||
if (base_type_storage_class == spv::StorageClass::Workgroup) {
|
||||
if (!_.HasCapability(spv::Capability::VariablePointers)) {
|
||||
if (!_.HasCapability(spv::Capability::VariablePointers) && !untyped_cap) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(7651)
|
||||
<< "OpPtrAccessChain Base operand pointing to Workgroup "
|
||||
"storage class must use VariablePointers capability";
|
||||
}
|
||||
} else if (base_type_storage_class == spv::StorageClass::StorageBuffer) {
|
||||
if (!_.features().variable_pointers) {
|
||||
if (!_.features().variable_pointers && !untyped_cap) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(7652)
|
||||
<< "OpPtrAccessChain Base operand pointing to StorageBuffer "
|
||||
@ -1597,7 +1820,8 @@ spv_result_t ValidatePtrAccessChain(ValidationState_t& _,
|
||||
"VariablePointersStorageBuffer capability";
|
||||
}
|
||||
} else if (base_type_storage_class !=
|
||||
spv::StorageClass::PhysicalStorageBuffer) {
|
||||
spv::StorageClass::PhysicalStorageBuffer &&
|
||||
!untyped_cap) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< _.VkErrorID(7650)
|
||||
<< "OpPtrAccessChain Base operand must point to Workgroup, "
|
||||
@ -1624,18 +1848,28 @@ spv_result_t ValidateArrayLength(ValidationState_t& state,
|
||||
<< " must be OpTypeInt with width 32 and signedness 0.";
|
||||
}
|
||||
|
||||
// The structure that is passed in must be an pointer to a structure, whose
|
||||
// last element is a runtime array.
|
||||
auto pointer = state.FindDef(inst->GetOperandAs<uint32_t>(2));
|
||||
auto pointer_type = state.FindDef(pointer->type_id());
|
||||
if (pointer_type->opcode() != spv::Op::OpTypePointer) {
|
||||
const bool untyped = inst->opcode() == spv::Op::OpUntypedArrayLengthKHR;
|
||||
auto pointer_ty_id = state.GetOperandTypeId(inst, (untyped ? 3 : 2));
|
||||
auto pointer_ty = state.FindDef(pointer_ty_id);
|
||||
if (untyped) {
|
||||
if (pointer_ty->opcode() != spv::Op::OpTypeUntypedPointerKHR) {
|
||||
return state.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Pointer must be an untyped pointer";
|
||||
}
|
||||
} else if (pointer_ty->opcode() != spv::Op::OpTypePointer) {
|
||||
return state.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The Structure's type in " << instr_name << " <id> "
|
||||
<< state.getIdName(inst->id())
|
||||
<< " must be a pointer to an OpTypeStruct.";
|
||||
}
|
||||
|
||||
auto structure_type = state.FindDef(pointer_type->GetOperandAs<uint32_t>(2));
|
||||
Instruction* structure_type = nullptr;
|
||||
if (untyped) {
|
||||
structure_type = state.FindDef(inst->GetOperandAs<uint32_t>(2));
|
||||
} else {
|
||||
structure_type = state.FindDef(pointer_ty->GetOperandAs<uint32_t>(2));
|
||||
}
|
||||
|
||||
if (structure_type->opcode() != spv::Op::OpTypeStruct) {
|
||||
return state.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The Structure's type in " << instr_name << " <id> "
|
||||
@ -1654,11 +1888,12 @@ spv_result_t ValidateArrayLength(ValidationState_t& state,
|
||||
|
||||
// The array member must the index of the last element (the run time
|
||||
// array).
|
||||
if (inst->GetOperandAs<uint32_t>(3) != num_of_members - 1) {
|
||||
const auto index = untyped ? 4 : 3;
|
||||
if (inst->GetOperandAs<uint32_t>(index) != num_of_members - 1) {
|
||||
return state.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "The array member in " << instr_name << " <id> "
|
||||
<< state.getIdName(inst->id())
|
||||
<< " must be an the last member of the struct.";
|
||||
<< " must be the last member of the struct.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
@ -1843,12 +2078,16 @@ spv_result_t ValidateCooperativeMatrixLoadStoreKHR(ValidationState_t& _,
|
||||
|
||||
const auto pointer_type_id = pointer->type_id();
|
||||
const auto pointer_type = _.FindDef(pointer_type_id);
|
||||
if (!pointer_type || pointer_type->opcode() != spv::Op::OpTypePointer) {
|
||||
if (!pointer_type ||
|
||||
!(pointer_type->opcode() == spv::Op::OpTypePointer ||
|
||||
pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< opname << " type for pointer <id> " << _.getIdName(pointer_id)
|
||||
<< " is not a pointer type.";
|
||||
}
|
||||
|
||||
const bool untyped =
|
||||
pointer_type->opcode() == spv::Op::OpTypeUntypedPointerKHR;
|
||||
const auto storage_class_index = 1u;
|
||||
const auto storage_class =
|
||||
pointer_type->GetOperandAs<spv::StorageClass>(storage_class_index);
|
||||
@ -1863,13 +2102,15 @@ spv_result_t ValidateCooperativeMatrixLoadStoreKHR(ValidationState_t& _,
|
||||
<< " is not Workgroup, StorageBuffer, or PhysicalStorageBuffer.";
|
||||
}
|
||||
|
||||
const auto pointee_id = pointer_type->GetOperandAs<uint32_t>(2);
|
||||
const auto pointee_type = _.FindDef(pointee_id);
|
||||
if (!pointee_type || !(_.IsIntScalarOrVectorType(pointee_id) ||
|
||||
_.IsFloatScalarOrVectorType(pointee_id))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< opname << " Pointer <id> " << _.getIdName(pointer->id())
|
||||
<< "s Type must be a scalar or vector type.";
|
||||
if (!untyped) {
|
||||
const auto pointee_id = pointer_type->GetOperandAs<uint32_t>(2);
|
||||
const auto pointee_type = _.FindDef(pointee_id);
|
||||
if (!pointee_type || !(_.IsIntScalarOrVectorType(pointee_id) ||
|
||||
_.IsFloatScalarOrVectorType(pointee_id))) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< opname << " Pointer <id> " << _.getIdName(pointer->id())
|
||||
<< "s Type must be a scalar or vector type.";
|
||||
}
|
||||
}
|
||||
|
||||
const auto layout_index =
|
||||
@ -1935,7 +2176,8 @@ spv_result_t ValidatePtrComparison(ValidationState_t& _,
|
||||
<< "The types of Operand 1 and Operand 2 must match";
|
||||
}
|
||||
const auto op1_type = _.FindDef(op1->type_id());
|
||||
if (!op1_type || op1_type->opcode() != spv::Op::OpTypePointer) {
|
||||
if (!op1_type || (op1_type->opcode() != spv::Op::OpTypePointer &&
|
||||
op1_type->opcode() != spv::Op::OpTypeUntypedPointerKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Operand type must be a pointer";
|
||||
}
|
||||
@ -1967,6 +2209,7 @@ spv_result_t ValidatePtrComparison(ValidationState_t& _,
|
||||
spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
|
||||
switch (inst->opcode()) {
|
||||
case spv::Op::OpVariable:
|
||||
case spv::Op::OpUntypedVariableKHR:
|
||||
if (auto error = ValidateVariable(_, inst)) return error;
|
||||
break;
|
||||
case spv::Op::OpLoad:
|
||||
@ -1980,17 +2223,22 @@ spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) {
|
||||
if (auto error = ValidateCopyMemory(_, inst)) return error;
|
||||
break;
|
||||
case spv::Op::OpPtrAccessChain:
|
||||
case spv::Op::OpUntypedPtrAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
|
||||
if (auto error = ValidatePtrAccessChain(_, inst)) return error;
|
||||
break;
|
||||
case spv::Op::OpAccessChain:
|
||||
case spv::Op::OpInBoundsAccessChain:
|
||||
case spv::Op::OpInBoundsPtrAccessChain:
|
||||
case spv::Op::OpUntypedAccessChainKHR:
|
||||
case spv::Op::OpUntypedInBoundsAccessChainKHR:
|
||||
if (auto error = ValidateAccessChain(_, inst)) return error;
|
||||
break;
|
||||
case spv::Op::OpRawAccessChainNV:
|
||||
if (auto error = ValidateRawAccessChain(_, inst)) return error;
|
||||
break;
|
||||
case spv::Op::OpArrayLength:
|
||||
case spv::Op::OpUntypedArrayLengthKHR:
|
||||
if (auto error = ValidateArrayLength(_, inst)) return error;
|
||||
break;
|
||||
case spv::Op::OpCooperativeMatrixLoadNV:
|
||||
|
@ -36,6 +36,7 @@ spv_result_t ValidateUniqueness(ValidationState_t& _, const Instruction* inst) {
|
||||
const auto opcode = inst->opcode();
|
||||
if (opcode != spv::Op::OpTypeArray && opcode != spv::Op::OpTypeRuntimeArray &&
|
||||
opcode != spv::Op::OpTypeStruct && opcode != spv::Op::OpTypePointer &&
|
||||
opcode != spv::Op::OpTypeUntypedPointerKHR &&
|
||||
!_.RegisterUniqueTypeDeclaration(inst)) {
|
||||
return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
||||
<< "Duplicate non-aggregate type declarations are not allowed. "
|
||||
@ -583,6 +584,33 @@ spv_result_t ValidateTypeCooperativeMatrix(ValidationState_t& _,
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
|
||||
spv_result_t ValidateTypeUntypedPointerKHR(ValidationState_t& _,
|
||||
const Instruction* inst) {
|
||||
if (spvIsVulkanEnv(_.context()->target_env)) {
|
||||
const auto sc = inst->GetOperandAs<spv::StorageClass>(1);
|
||||
switch (sc) {
|
||||
case spv::StorageClass::Workgroup:
|
||||
if (!_.HasCapability(
|
||||
spv::Capability::WorkgroupMemoryExplicitLayoutKHR)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Workgroup storage class untyped pointers in Vulkan "
|
||||
"require WorkgroupMemoryExplicitLayoutKHR be declared";
|
||||
}
|
||||
break;
|
||||
case spv::StorageClass::StorageBuffer:
|
||||
case spv::StorageClass::PhysicalStorageBuffer:
|
||||
case spv::StorageClass::Uniform:
|
||||
case spv::StorageClass::PushConstant:
|
||||
break;
|
||||
default:
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "In Vulkan, untyped pointers can only be used in an "
|
||||
"explicitly laid out storage class";
|
||||
}
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
@ -628,6 +656,9 @@ spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) {
|
||||
case spv::Op::OpTypeCooperativeMatrixKHR:
|
||||
if (auto error = ValidateTypeCooperativeMatrix(_, inst)) return error;
|
||||
break;
|
||||
case spv::Op::OpTypeUntypedPointerKHR:
|
||||
if (auto error = ValidateTypeUntypedPointerKHR(_, inst)) return error;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ ModuleLayoutSection InstructionLayoutSection(
|
||||
case spv::Op::OpTypeForwardPointer:
|
||||
return kLayoutTypes;
|
||||
case spv::Op::OpVariable:
|
||||
case spv::Op::OpUntypedVariableKHR:
|
||||
if (current_section == kLayoutTypes) return kLayoutTypes;
|
||||
return kLayoutFunctionDefinitions;
|
||||
case spv::Op::OpExtInst:
|
||||
@ -1185,7 +1186,9 @@ bool ValidationState_t::GetStructMemberTypes(
|
||||
|
||||
bool ValidationState_t::IsPointerType(uint32_t id) const {
|
||||
const Instruction* inst = FindDef(id);
|
||||
return inst && inst->opcode() == spv::Op::OpTypePointer;
|
||||
assert(inst);
|
||||
return inst->opcode() == spv::Op::OpTypePointer ||
|
||||
inst->opcode() == spv::Op::OpTypeUntypedPointerKHR;
|
||||
}
|
||||
|
||||
bool ValidationState_t::GetPointerTypeInfo(
|
||||
@ -1195,6 +1198,12 @@ bool ValidationState_t::GetPointerTypeInfo(
|
||||
|
||||
const Instruction* inst = FindDef(id);
|
||||
assert(inst);
|
||||
if (inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
|
||||
*storage_class = spv::StorageClass(inst->word(2));
|
||||
*data_type = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (inst->opcode() != spv::Op::OpTypePointer) return false;
|
||||
|
||||
*storage_class = spv::StorageClass(inst->word(2));
|
||||
@ -1705,6 +1714,39 @@ bool ValidationState_t::ContainsRuntimeArray(uint32_t id) const {
|
||||
return ContainsType(id, f, /* traverse_all_types = */ false);
|
||||
}
|
||||
|
||||
bool ValidationState_t::ContainsUntypedPointer(uint32_t id) const {
|
||||
const auto inst = FindDef(id);
|
||||
if (!inst) return false;
|
||||
if (!spvOpcodeGeneratesType(inst->opcode())) return false;
|
||||
if (inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) return true;
|
||||
|
||||
switch (inst->opcode()) {
|
||||
case spv::Op::OpTypeArray:
|
||||
case spv::Op::OpTypeRuntimeArray:
|
||||
case spv::Op::OpTypeVector:
|
||||
case spv::Op::OpTypeMatrix:
|
||||
case spv::Op::OpTypeImage:
|
||||
case spv::Op::OpTypeSampledImage:
|
||||
case spv::Op::OpTypeCooperativeMatrixNV:
|
||||
return ContainsUntypedPointer(inst->GetOperandAs<uint32_t>(1u));
|
||||
case spv::Op::OpTypePointer:
|
||||
if (IsForwardPointer(id)) return false;
|
||||
return ContainsUntypedPointer(inst->GetOperandAs<uint32_t>(2u));
|
||||
case spv::Op::OpTypeFunction:
|
||||
case spv::Op::OpTypeStruct: {
|
||||
for (uint32_t i = 1; i < inst->operands().size(); ++i) {
|
||||
if (ContainsUntypedPointer(inst->GetOperandAs<uint32_t>(i)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ValidationState_t::IsValidStorageClass(
|
||||
spv::StorageClass storage_class) const {
|
||||
if (spvIsVulkanEnv(context()->target_env)) {
|
||||
|
@ -649,6 +649,9 @@ class ValidationState_t {
|
||||
const std::function<bool(const Instruction*)>& f,
|
||||
bool traverse_all_types = true) const;
|
||||
|
||||
// Returns true if |id| is type id that contains an untyped pointer.
|
||||
bool ContainsUntypedPointer(uint32_t id) const;
|
||||
|
||||
// Returns type_id if id has type or zero otherwise.
|
||||
uint32_t GetTypeId(uint32_t id) const;
|
||||
|
||||
|
@ -61,8 +61,33 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
ExpectedOpCodeCapabilities{
|
||||
spv::Op::OpImageSparseSampleImplicitLod,
|
||||
CapabilitySet{spv::Capability::SparseResidency}},
|
||||
ExpectedOpCodeCapabilities{spv::Op::OpCopyMemorySized,
|
||||
CapabilitySet{spv::Capability::Addresses}},
|
||||
ExpectedOpCodeCapabilities{
|
||||
spv::Op::OpCopyMemorySized,
|
||||
CapabilitySet{spv::Capability::Addresses,
|
||||
spv::Capability::UntypedPointersKHR}},
|
||||
ExpectedOpCodeCapabilities{spv::Op::OpArrayLength,
|
||||
CapabilitySet{spv::Capability::Shader}},
|
||||
ExpectedOpCodeCapabilities{spv::Op::OpFunction, CapabilitySet()},
|
||||
ExpectedOpCodeCapabilities{spv::Op::OpConvertFToS, CapabilitySet()},
|
||||
ExpectedOpCodeCapabilities{
|
||||
spv::Op::OpEmitStreamVertex,
|
||||
CapabilitySet{spv::Capability::GeometryStreams}},
|
||||
ExpectedOpCodeCapabilities{
|
||||
spv::Op::OpTypeNamedBarrier,
|
||||
CapabilitySet{spv::Capability::NamedBarrier}},
|
||||
ExpectedOpCodeCapabilities{
|
||||
spv::Op::OpGetKernelMaxNumSubgroups,
|
||||
CapabilitySet{spv::Capability::SubgroupDispatch}},
|
||||
ExpectedOpCodeCapabilities{spv::Op::OpImageQuerySamples,
|
||||
CapabilitySet{spv::Capability::Kernel,
|
||||
spv::Capability::ImageQuery}},
|
||||
ExpectedOpCodeCapabilities{
|
||||
spv::Op::OpImageSparseSampleImplicitLod,
|
||||
CapabilitySet{spv::Capability::SparseResidency}},
|
||||
ExpectedOpCodeCapabilities{
|
||||
spv::Op::OpCopyMemorySized,
|
||||
CapabilitySet{spv::Capability::Addresses,
|
||||
spv::Capability::UntypedPointersKHR}},
|
||||
ExpectedOpCodeCapabilities{spv::Op::OpArrayLength,
|
||||
CapabilitySet{spv::Capability::Shader}},
|
||||
ExpectedOpCodeCapabilities{spv::Op::OpFunction, CapabilitySet()},
|
||||
|
@ -1327,5 +1327,54 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
{1, 2, 3})},
|
||||
})));
|
||||
|
||||
// SPV_KHR_untyped_pointers
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SPV_KHR_untyped_pointers, ExtensionRoundTripTest,
|
||||
Combine(
|
||||
Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0,
|
||||
SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_2),
|
||||
ValuesIn(std::vector<AssemblyCase>{
|
||||
{"OpExtension \"SPV_KHR_untyped_pointers\"\n",
|
||||
MakeInstruction(spv::Op::OpExtension,
|
||||
MakeVector("SPV_KHR_untyped_pointers"))},
|
||||
{"OpCapability UntypedPointersKHR\n",
|
||||
MakeInstruction(spv::Op::OpCapability,
|
||||
{(int)spv::Capability::UntypedPointersKHR})},
|
||||
{"OpCapability UntypedPointersKHR\n",
|
||||
MakeInstruction(spv::Op::OpCapability, {4473})},
|
||||
{"%1 = OpTypeUntypedPointerKHR Workgroup\n",
|
||||
MakeInstruction(spv::Op::OpTypeUntypedPointerKHR,
|
||||
{1, int(spv::StorageClass::Workgroup)})},
|
||||
{"%2 = OpUntypedVariableKHR %1 Workgroup %3\n",
|
||||
MakeInstruction(spv::Op::OpUntypedVariableKHR,
|
||||
{1, 2, int(spv::StorageClass::Workgroup), 3})},
|
||||
{"%2 = OpUntypedVariableKHR %1 Workgroup %3 %4\n",
|
||||
MakeInstruction(spv::Op::OpUntypedVariableKHR,
|
||||
{1, 2, int(spv::StorageClass::Workgroup), 3, 4})},
|
||||
{"%2 = OpUntypedAccessChainKHR %1 %3 %4\n",
|
||||
MakeInstruction(spv::Op::OpUntypedAccessChainKHR, {1, 2, 3, 4})},
|
||||
{"%2 = OpUntypedAccessChainKHR %1 %3 %4 %5 %6 %7\n",
|
||||
MakeInstruction(spv::Op::OpUntypedAccessChainKHR,
|
||||
{1, 2, 3, 4, 5, 6, 7})},
|
||||
{"%2 = OpUntypedInBoundsAccessChainKHR %1 %3 %4\n",
|
||||
MakeInstruction(spv::Op::OpUntypedInBoundsAccessChainKHR,
|
||||
{1, 2, 3, 4})},
|
||||
{"%2 = OpUntypedInBoundsAccessChainKHR %1 %3 %4 %5 %6 %7\n",
|
||||
MakeInstruction(spv::Op::OpUntypedInBoundsAccessChainKHR,
|
||||
{1, 2, 3, 4, 5, 6, 7})},
|
||||
{"%2 = OpUntypedPtrAccessChainKHR %1 %3 %4 %5\n",
|
||||
MakeInstruction(spv::Op::OpUntypedPtrAccessChainKHR,
|
||||
{1, 2, 3, 4, 5})},
|
||||
{"%2 = OpUntypedPtrAccessChainKHR %1 %3 %4 %5 %6 %7\n",
|
||||
MakeInstruction(spv::Op::OpUntypedPtrAccessChainKHR,
|
||||
{1, 2, 3, 4, 5, 6, 7})},
|
||||
{"%2 = OpUntypedInBoundsPtrAccessChainKHR %1 %3 %4 %5\n",
|
||||
MakeInstruction(spv::Op::OpUntypedInBoundsPtrAccessChainKHR,
|
||||
{1, 2, 3, 4, 5})},
|
||||
{"%2 = OpUntypedInBoundsPtrAccessChainKHR %1 %3 %4 %5 %6 %7\n",
|
||||
MakeInstruction(spv::Op::OpUntypedInBoundsPtrAccessChainKHR,
|
||||
{1, 2, 3, 4, 5, 6, 7})},
|
||||
})));
|
||||
|
||||
} // namespace
|
||||
} // namespace spvtools
|
||||
|
@ -230,6 +230,33 @@ OpFunctionEnd
|
||||
"FPFastMathMode and NoContraction cannot decorate the same target"));
|
||||
}
|
||||
|
||||
TEST_F(DecorationTest, RestrictOnUntypedPointer) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability SampleRateShading
|
||||
OpCapability TransformFeedback
|
||||
OpCapability GeometryStreams
|
||||
OpCapability Tessellation
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpDecorate %param Restrict
|
||||
%ptr = OpTypeUntypedPointerKHR StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%f_ty = OpTypeFunction %void %ptr
|
||||
%f = OpFunction %void None %f_ty
|
||||
%param = OpFunctionParameter %ptr
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
using MemberOnlyDecorations = spvtest::ValidateBase<std::string>;
|
||||
|
||||
TEST_P(MemberOnlyDecorations, MemberDecoration) {
|
||||
|
@ -1142,9 +1142,8 @@ OpAtomicStore %f32_1 %device %relaxed %f32_1
|
||||
|
||||
CompileSuccessfully(GenerateKernelCode(body));
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("AtomicStore: expected Pointer to be of type OpTypePointer"));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("AtomicStore: expected Pointer to be a pointer type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicStoreWrongPointerDataType) {
|
||||
@ -1607,7 +1606,7 @@ TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotPointer) {
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("AtomicFlagTestAndSet: "
|
||||
"expected Pointer to be of type OpTypePointer"));
|
||||
"expected Pointer to be a pointer type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotIntPointer) {
|
||||
@ -1681,7 +1680,7 @@ OpAtomicFlagClear %u32_1 %device %relaxed
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("AtomicFlagClear: "
|
||||
"expected Pointer to be of type OpTypePointer"));
|
||||
"expected Pointer to be a pointer type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicFlagClearNotIntPointer) {
|
||||
@ -2847,6 +2846,125 @@ TEST_F(ValidateAtomics, AtomicFloat16Vector3ExchangeFail) {
|
||||
"float scalar type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicLoadUntypedPointer) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%load = OpAtomicLoad %int %var %int_1 %int_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicStoreUntypedPointer) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpAtomicStore %var %int_1 %int_0 %int_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicExchangeUntypedPointer) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%ex = OpAtomicExchange %int %var %int_1 %int_0 %int_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateAtomics, AtomicFlagClearUntypedPointer) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Kernel
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical OpenCL
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_1 = OpConstant %int 1
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %int
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpAtomicFlagClear %var %int_1 %int_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Untyped pointers are not supported by atomic flag instructions"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
@ -1940,6 +1940,64 @@ OpExtension "SPV_KHR_ray_query"
|
||||
"uint vector as input"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateConversion, BitcastUntypedPointerInput) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VariablePointers
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_variable_pointers"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%cast = OpBitcast %int %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_F(ValidateConversion, BitcastUntypedPointerOutput) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VariablePointers
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_variable_pointers"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %int
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%cast = OpBitcast %ptr %int_0
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
using ValidateSmallConversions = spvtest::ValidateBase<std::string>;
|
||||
|
||||
CodeGenerator GetSmallConversionsCodeGenerator() {
|
||||
|
@ -9362,6 +9362,37 @@ OpFunctionEnd
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UntypedVariableDuplicateInterface) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var %var
|
||||
OpName %var "var"
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Non-unique OpEntryPoint interface '2[%var]' is disallowed"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, PhysicalStorageBufferMissingOffset) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@ -9967,6 +9998,372 @@ TEST_F(ValidateDecorations, MultipleBuiltinsBlockMixed) {
|
||||
AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-09659"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UntypedVariableWorkgroupRequiresStruct) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %int
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Untyped workgroup variables in shaders must be block "
|
||||
"decorated structs"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UntypedVariableWorkgroupRequiresBlockStruct) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Untyped workgroup variables in shaders must be block "
|
||||
"decorated"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UntypedVariableStorageBufferMissingBlock) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %struct "struct"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR StorageBuffer
|
||||
%var = OpUntypedVariableKHR %ptr StorageBuffer %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("StorageBuffer id '2' is missing Block decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UntypedVariableUniformMissingBlock) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %struct "struct"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR Uniform
|
||||
%var = OpUntypedVariableKHR %ptr Uniform %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Uniform id '2' is missing Block or BufferBlock decoration"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateDecorations, UntypedVariablePushConstantMissingBlock) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %struct "struct"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR PushConstant
|
||||
%var = OpUntypedVariableKHR %ptr PushConstant %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("PushConstant id '2' is missing Block decoration"));
|
||||
}
|
||||
|
||||
using UntypedVariableSetAndBinding = spvtest::ValidateBase<std::string>;
|
||||
|
||||
TEST_P(UntypedVariableSetAndBinding, MissingSet) {
|
||||
const auto sc = GetParam();
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
OpDecorate %var Binding 0
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR )" +
|
||||
sc + R"(
|
||||
%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%load = OpLoad %struct %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr(sc + " id '2' is missing DescriptorSet decoration"));
|
||||
}
|
||||
|
||||
TEST_P(UntypedVariableSetAndBinding, MissingBinding) {
|
||||
const auto sc = GetParam();
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
OpDecorate %var DescriptorSet 0
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR )" +
|
||||
sc + R"(
|
||||
%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%load = OpLoad %struct %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr(sc + " id '2' is missing Binding decoration"));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ValidateUntypedVariableSetAndBinding,
|
||||
UntypedVariableSetAndBinding,
|
||||
Values("StorageBuffer", "Uniform"));
|
||||
|
||||
using UntypedPointerLayout =
|
||||
spvtest::ValidateBase<std::tuple<std::string, std::string>>;
|
||||
|
||||
TEST_P(UntypedPointerLayout, BadOffset) {
|
||||
const auto sc = std::get<0>(GetParam());
|
||||
const auto op = std::get<1>(GetParam());
|
||||
const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
|
||||
? R"(OpDecorate %var DescriptorSet 0
|
||||
OpDecorate %var Binding 0
|
||||
)"
|
||||
: R"()");
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VariablePointers
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_variable_pointers"
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
OpMemberDecorate %struct 1 Offset 4
|
||||
)" + set + R"(OpMemberDecorate %test_type 0 Offset 0
|
||||
OpMemberDecorate %test_type 1 Offset 1
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%struct = OpTypeStruct %int %int
|
||||
%test_type = OpTypeStruct %int %int
|
||||
%test_val = OpConstantNull %test_type
|
||||
%ptr = OpTypeUntypedPointerKHR )" +
|
||||
sc + R"(
|
||||
%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
)" + op + R"(
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
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 (!read_only || op.find("OpStore") == std::string::npos) {
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("member 1 at offset 1 is not aligned to"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(UntypedPointerLayout, BadStride) {
|
||||
const auto sc = std::get<0>(GetParam());
|
||||
const auto op = std::get<1>(GetParam());
|
||||
const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
|
||||
? R"(OpDecorate %var DescriptorSet 0
|
||||
OpDecorate %var Binding 0
|
||||
)"
|
||||
: R"()");
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VariablePointers
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_variable_pointers"
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main" %var
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
OpDecorate %struct Block
|
||||
OpMemberDecorate %struct 0 Offset 0
|
||||
OpMemberDecorate %struct 1 Offset 4
|
||||
)" + set + R"(OpDecorate %test_type ArrayStride 4
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%int_4 = OpConstant %int 4
|
||||
%int4 = OpTypeVector %int 4
|
||||
%test_type = OpTypeArray %int4 %int_4
|
||||
%test_val = OpConstantNull %test_type
|
||||
%struct = OpTypeStruct %int %int
|
||||
%ptr = OpTypeUntypedPointerKHR )" +
|
||||
sc + R"(
|
||||
%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
)" + op + R"(
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
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 (!read_only || op.find("OpStore") == std::string::npos) {
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("array with stride 4 not satisfying alignment to 16"));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ValidateUntypedPointerLayout, UntypedPointerLayout,
|
||||
Combine(Values("StorageBuffer", "Uniform", "PushConstant", "Workgroup"),
|
||||
Values("%gep = OpUntypedAccessChainKHR %ptr %test_type %var %int_0",
|
||||
"%gep = OpUntypedInBoundsAccessChainKHR %ptr %test_type "
|
||||
"%var %int_0",
|
||||
"%gep = OpUntypedPtrAccessChainKHR %ptr %test_type %var "
|
||||
"%int_0 %int_0",
|
||||
"%ld = OpLoad %test_type %var", "OpStore %var %test_val")));
|
||||
|
||||
TEST_F(ValidateDecorations, UntypedArrayLengthMissingOffset) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpDecorate %struct Block
|
||||
OpDecorate %block Block
|
||||
OpMemberDecorate %block 0 Offset 0
|
||||
OpDecorate %array ArrayStride 4
|
||||
OpDecorate %var DescriptorSet 0
|
||||
OpDecorate %var Binding 0
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%array = OpTypeRuntimeArray %int
|
||||
%struct = OpTypeStruct %array
|
||||
%block = OpTypeStruct %array
|
||||
%ptr = OpTypeUntypedPointerKHR StorageBuffer
|
||||
%var = OpUntypedVariableKHR %ptr StorageBuffer %block
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%len = OpUntypedArrayLengthKHR %int %struct %var 0
|
||||
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("member 0 is missing an Offset decoration"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
110
test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp
Normal file
110
test/val/val_extension_spv_khr_subgroup_uniform_control_flow.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2021 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Tests for OpExtension validator rules.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "source/enum_string_mapping.h"
|
||||
#include "source/extensions.h"
|
||||
#include "source/spirv_target_env.h"
|
||||
#include "test/test_fixture.h"
|
||||
#include "test/unit_spirv.h"
|
||||
#include "test/val/val_fixtures.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace val {
|
||||
namespace {
|
||||
|
||||
using ::testing::HasSubstr;
|
||||
using ::testing::Values;
|
||||
using ::testing::ValuesIn;
|
||||
|
||||
using ValidateSpvKHRSubgroupUniformControlFlow = spvtest::ValidateBase<bool>;
|
||||
|
||||
TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, Valid) {
|
||||
const std::string str = R"(
|
||||
OpCapability Shader
|
||||
OpExtension "SPV_KHR_subgroup_uniform_control_flow"
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpExecutionMode %main SubgroupUniformControlFlowKHR
|
||||
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(str.c_str());
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, RequiresExtension) {
|
||||
const std::string str = R"(
|
||||
OpCapability Shader
|
||||
OpMemoryModel Logical Simple
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpExecutionMode %main SubgroupUniformControlFlowKHR
|
||||
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(str.c_str());
|
||||
EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("2nd operand of ExecutionMode: operand "
|
||||
"SubgroupUniformControlFlowKHR(4421) "
|
||||
"requires one of these extensions: "
|
||||
"SPV_KHR_subgroup_uniform_control_flow"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateSpvKHRSubgroupUniformControlFlow, RequiresShaderCapability) {
|
||||
const std::string str = R"(
|
||||
OpCapability Kernel
|
||||
OpCapability Addresses
|
||||
OpExtension "SPV_KHR_subgroup_uniform_control_flow"
|
||||
OpMemoryModel Physical32 OpenCL
|
||||
OpEntryPoint Kernel %main "main"
|
||||
OpExecutionMode %main SubgroupUniformControlFlowKHR
|
||||
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(str.c_str());
|
||||
EXPECT_NE(SPV_SUCCESS, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Operand 2 of ExecutionMode requires one of these "
|
||||
"capabilities: Shader"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
@ -76,6 +76,8 @@ class ValidateBase : public ::testing::Test,
|
||||
diagnostic_ = nullptr;
|
||||
}
|
||||
|
||||
void SetAssembleOptions(uint32_t options) { assemble_options_ = options; }
|
||||
|
||||
std::string getDiagnosticString();
|
||||
spv_position_t getErrorPosition();
|
||||
spv_validator_options getValidatorOptions();
|
||||
@ -84,6 +86,7 @@ class ValidateBase : public ::testing::Test,
|
||||
spv_diagnostic diagnostic_;
|
||||
spv_validator_options options_;
|
||||
std::unique_ptr<spvtools::val::ValidationState_t> vstate_;
|
||||
uint32_t assemble_options_ = SPV_TEXT_TO_BINARY_OPTION_NONE;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -132,8 +135,9 @@ void ValidateBase<T>::CompileSuccessfully(std::string code,
|
||||
DestroyBinary();
|
||||
spv_diagnostic diagnostic = nullptr;
|
||||
ScopedContext context(env);
|
||||
auto status = spvTextToBinary(context.context, code.c_str(), code.size(),
|
||||
&binary_, &diagnostic);
|
||||
auto status =
|
||||
spvTextToBinaryWithOptions(context.context, code.c_str(), code.size(),
|
||||
assemble_options_, &binary_, &diagnostic);
|
||||
EXPECT_EQ(SPV_SUCCESS, status)
|
||||
<< "ERROR: " << diagnostic->error
|
||||
<< "\nSPIR-V could not be compiled into binary:\n"
|
||||
|
@ -836,6 +836,113 @@ TEST_F(ValidateFunctionCall, LogicallyMismatchedPointersArraySize) {
|
||||
HasSubstr("type does not match Function <id>"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall, UntypedPointerParameterMismatch) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpName %var "var"
|
||||
OpName %ptr2 "ptr2"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr = OpTypeUntypedPointerKHR Private
|
||||
%ptr2 = OpTypeUntypedPointerKHR Private
|
||||
%var = OpUntypedVariableKHR %ptr Private %int
|
||||
%void_fn = OpTypeFunction %void
|
||||
%ptr_fn = OpTypeFunction %void %ptr2
|
||||
%foo = OpFunction %void None %ptr_fn
|
||||
%param = OpFunctionParameter %ptr2
|
||||
%first = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%call = OpFunctionCall %void %foo %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpFunctionCall Argument <id> '2[%var]'s type does not "
|
||||
"match Function <id> '3[%ptr2]'s parameter type"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall, UntypedPointerParameterGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr = OpTypeUntypedPointerKHR Private
|
||||
%var = OpUntypedVariableKHR %ptr Private %int
|
||||
%void_fn = OpTypeFunction %void
|
||||
%ptr_fn = OpTypeFunction %void %ptr
|
||||
%foo = OpFunction %void None %ptr_fn
|
||||
%param = OpFunctionParameter %ptr
|
||||
%first = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%call = OpFunctionCall %void %foo %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_F(ValidateFunctionCall,
|
||||
UntypedPointerParameterNotMemoryObjectDeclaration) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpName %var "var"
|
||||
OpName %gep "gep"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int_0 = OpConstant %int 0
|
||||
%struct = OpTypeStruct %int
|
||||
%ptr = OpTypeUntypedPointerKHR Private
|
||||
%var = OpUntypedVariableKHR %ptr Private %int
|
||||
%void_fn = OpTypeFunction %void
|
||||
%ptr_fn = OpTypeFunction %void %ptr
|
||||
%foo = OpFunction %void None %ptr_fn
|
||||
%param = OpFunctionParameter %ptr
|
||||
%first = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%gep = OpUntypedAccessChainKHR %ptr %struct %var %int_0
|
||||
%call = OpFunctionCall %void %foo %gep
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"Pointer operand '3[%gep]' must be a memory object declaration"));
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(StorageClass, ValidateFunctionCall,
|
||||
Values("UniformConstant", "Input", "Uniform", "Output",
|
||||
"Workgroup", "Private", "Function",
|
||||
|
@ -578,9 +578,8 @@ TEST_P(ValidateIdWithMessage, OpEntryPointInterfaceIsNotVariableTypeBad) {
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr(make_message(
|
||||
"Interfaces passed to OpEntryPoint must be of type "
|
||||
"OpTypeVariable. Found OpTypePointer.")));
|
||||
HasSubstr("Interfaces passed to OpEntryPoint must be variables. "
|
||||
"Found OpTypePointer."));
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpEntryPointInterfaceStorageClassBad) {
|
||||
@ -1167,6 +1166,160 @@ TEST_P(ValidateIdWithMessage, OpTypePointerBad) {
|
||||
"type.")));
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpTypePointerCanHaveUntypedPointer) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Kernel
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical OpenCL
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%ptr2 = OpTypePointer Private %ptr
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpTypeUntypedPointerWorkgroupGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage,
|
||||
OpTypeUntypedPointerWorkgroupMissingExplicitLayout) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("Workgroup storage class untyped pointers in Vulkan require "
|
||||
"WorkgroupMemoryExplicitLayoutKHR be declared"));
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpTypeUntypedPointerWorkgroupGoodAll) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpTypeUntypedPointerStorageBufferGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_storage_buffer_storage_class"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
%ptr = OpTypeUntypedPointerKHR StorageBuffer
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpTypeUntypedPointerUniformGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
%ptr = OpTypeUntypedPointerKHR Uniform
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpTypeUntypedPointerPushConstantGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
%ptr = OpTypeUntypedPointerKHR PushConstant
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpTypeUntypedPointerCrossWorkgroupGood) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Kernel
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical OpenCL
|
||||
%ptr = OpTypeUntypedPointerKHR CrossWorkgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions());
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpTypeUntypedPointerVulkanInvalidStorageClass) {
|
||||
const std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
%void = OpTypeVoid
|
||||
%void_fn = OpTypeFunction %void
|
||||
%ptr = OpTypeUntypedPointerKHR Private
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("In Vulkan, untyped pointers can only be used in an "
|
||||
"explicitly laid out storage class"));
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpTypeFunctionGood) {
|
||||
std::string spirv = kGLSL450MemoryModel + R"(
|
||||
%1 = OpTypeVoid
|
||||
@ -2270,9 +2423,8 @@ OpFunctionEnd
|
||||
CompileSuccessfully(spirv.c_str());
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr(make_message(
|
||||
"OpVariable Initializer <id> '8[%8]' is not a constant "
|
||||
"or module-scope variable")));
|
||||
HasSubstr("Variable Initializer <id> '8[%8]' is not a constant "
|
||||
"or module-scope variable"));
|
||||
}
|
||||
|
||||
TEST_P(ValidateIdWithMessage, OpVariableInitializerIsModuleVarGood) {
|
||||
@ -6404,9 +6556,10 @@ OpMemoryModel Logical VulkanKHR
|
||||
%7 = OpConstant %2 2
|
||||
%8 = OpConstant %2 5
|
||||
%9 = OpTypeFunction %1
|
||||
%12 = OpConstant %2 4
|
||||
%10 = OpFunction %1 None %9
|
||||
%11 = OpLabel
|
||||
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR %7
|
||||
OpCopyMemorySized %4 %6 %12 NonPrivatePointerKHR|MakePointerAvailableKHR %7
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
@ -6431,10 +6584,11 @@ OpMemoryModel Logical VulkanKHR
|
||||
%6 = OpVariable %5 Uniform
|
||||
%7 = OpConstant %2 2
|
||||
%8 = OpConstant %2 5
|
||||
%12 = OpConstant %2 4
|
||||
%9 = OpTypeFunction %1
|
||||
%10 = OpFunction %1 None %9
|
||||
%11 = OpLabel
|
||||
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerVisibleKHR %8
|
||||
OpCopyMemorySized %4 %6 %12 NonPrivatePointerKHR|MakePointerVisibleKHR %8
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
@ -6460,10 +6614,11 @@ OpMemoryModel Logical VulkanKHR
|
||||
%6 = OpVariable %5 Uniform
|
||||
%7 = OpConstant %2 2
|
||||
%8 = OpConstant %2 5
|
||||
%12 = OpConstant %2 4
|
||||
%9 = OpTypeFunction %1
|
||||
%10 = OpFunction %1 None %9
|
||||
%11 = OpLabel
|
||||
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8
|
||||
OpCopyMemorySized %4 %6 %12 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
@ -6489,10 +6644,11 @@ OpMemoryModel Logical VulkanKHR
|
||||
%6 = OpVariable %5 Uniform
|
||||
%7 = OpConstant %2 2
|
||||
%8 = OpConstant %2 5
|
||||
%12 = OpConstant %2 4
|
||||
%9 = OpTypeFunction %1
|
||||
%10 = OpFunction %1 None %9
|
||||
%11 = OpLabel
|
||||
OpCopyMemorySized %4 %6 %7 MakePointerAvailableKHR %7
|
||||
OpCopyMemorySized %4 %6 %12 MakePointerAvailableKHR %7
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
@ -6522,10 +6678,11 @@ OpMemoryModel Logical VulkanKHR
|
||||
%6 = OpVariable %5 Uniform
|
||||
%7 = OpConstant %2 2
|
||||
%8 = OpConstant %2 5
|
||||
%12 = OpConstant %2 4
|
||||
%9 = OpTypeFunction %1
|
||||
%10 = OpFunction %1 None %9
|
||||
%11 = OpLabel
|
||||
OpCopyMemorySized %4 %6 %7 MakePointerVisibleKHR %8
|
||||
OpCopyMemorySized %4 %6 %12 MakePointerVisibleKHR %8
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
@ -6555,10 +6712,11 @@ OpMemoryModel Logical VulkanKHR
|
||||
%6 = OpVariable %5 Uniform
|
||||
%7 = OpConstant %2 2
|
||||
%8 = OpConstant %2 5
|
||||
%12 = OpConstant %2 4
|
||||
%9 = OpTypeFunction %1
|
||||
%10 = OpFunction %1 None %9
|
||||
%11 = OpLabel
|
||||
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR
|
||||
OpCopyMemorySized %4 %6 %12 NonPrivatePointerKHR
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
@ -6589,10 +6747,11 @@ OpMemoryModel Logical VulkanKHR
|
||||
%6 = OpVariable %5 Input
|
||||
%7 = OpConstant %2 2
|
||||
%8 = OpConstant %2 5
|
||||
%12 = OpConstant %2 4
|
||||
%9 = OpTypeFunction %1
|
||||
%10 = OpFunction %1 None %9
|
||||
%11 = OpLabel
|
||||
OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR
|
||||
OpCopyMemorySized %4 %6 %12 NonPrivatePointerKHR
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
@ -1691,6 +1691,122 @@ OpFunctionEnd
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, UntypedVariableInputMissing) {
|
||||
const std::string text = R"(
|
||||
OpCapability Kernel
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical OpenCL
|
||||
OpEntryPoint Kernel %main "main"
|
||||
OpExecutionMode %main LocalSize 1 1 1
|
||||
OpName %var "var"
|
||||
OpDecorate %var BuiltIn LocalInvocationId
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%int3 = OpTypeVector %int 3
|
||||
%ptr = OpTypeUntypedPointerKHR Input
|
||||
%var = OpUntypedVariableKHR %ptr Input %int3
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%load = OpLoad %int3 %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
CompileSuccessfully(text);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Interface variable id <2> is used by entry point "
|
||||
"'main' id <1>, but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, UntypedVariableWorkgroupMissingSpv1p4) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint GLCompute %main "main"
|
||||
OpName %var "var"
|
||||
%void = OpTypeVoid
|
||||
%int = OpTypeInt 32 0
|
||||
%ptr = OpTypeUntypedPointerKHR Workgroup
|
||||
%var = OpUntypedVariableKHR %ptr Workgroup %int
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
%load = OpLoad %int %var
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("Interface variable id <2> is used by entry point "
|
||||
"'main' id <1>, but is not listed as an interface"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, UntypedIdMatchesInputVulkan1p3) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %var
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpDecorate %var DescriptorSet 0
|
||||
OpDecorate %var Binding 0
|
||||
OpDecorate %1 Block
|
||||
OpMemberDecorate %1 0 Offset 0
|
||||
%void = OpTypeVoid
|
||||
%float = OpTypeFloat 32
|
||||
%1 = OpTypeStruct %float ; this id matches Input storage class
|
||||
%ptr = OpTypeUntypedPointerKHR Uniform
|
||||
%var = OpUntypedVariableKHR %ptr Uniform %1
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
|
||||
}
|
||||
|
||||
TEST_F(ValidateInterfacesTest, UntypedIdMatchesPushConstantVulkan1p3) {
|
||||
const std::string text = R"(
|
||||
OpCapability Shader
|
||||
OpCapability UntypedPointersKHR
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
OpEntryPoint Fragment %main "main" %var
|
||||
OpExecutionMode %main OriginUpperLeft
|
||||
OpDecorate %var DescriptorSet 0
|
||||
OpDecorate %var Binding 0
|
||||
OpDecorate %9 Block
|
||||
OpMemberDecorate %9 0 Offset 0
|
||||
%void = OpTypeVoid
|
||||
%float = OpTypeFloat 32
|
||||
%9 = OpTypeStruct %float ; this id matches PushConstant storage class
|
||||
%ptr = OpTypeUntypedPointerKHR Uniform
|
||||
%var = OpUntypedVariableKHR %ptr Uniform %9
|
||||
%void_fn = OpTypeFunction %void
|
||||
%main = OpFunction %void None %void_fn
|
||||
%entry = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
|
||||
CompileSuccessfully(text, SPV_ENV_VULKAN_1_3);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -165,7 +165,7 @@ TEST_F(ValidateStorage, GenericVariableOutsideFunction) {
|
||||
CompileSuccessfully(str);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpVariable storage class cannot be Generic"));
|
||||
HasSubstr("Variable storage class cannot be Generic"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateStorage, GenericVariableInsideFunction) {
|
||||
@ -187,7 +187,7 @@ TEST_F(ValidateStorage, GenericVariableInsideFunction) {
|
||||
CompileSuccessfully(str);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions());
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("OpVariable storage class cannot be Generic"));
|
||||
HasSubstr("Variable storage class cannot be Generic"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParam) {
|
||||
|
@ -270,6 +270,24 @@ OpMemoryModel Logical GLSL450
|
||||
Not(HasSubstr(GetErrorString(spv::Op::OpTypePointer))));
|
||||
}
|
||||
|
||||
TEST_F(ValidateTypeUnique, DuplicateUntypedPointer) {
|
||||
std::string str = R"(
|
||||
OpCapability Shader
|
||||
OpCapability Linkage
|
||||
OpCapability UntypedPointersKHR
|
||||
OpCapability WorkgroupMemoryExplicitLayoutKHR
|
||||
OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
|
||||
OpExtension "SPV_KHR_untyped_pointers"
|
||||
OpMemoryModel Logical GLSL450
|
||||
%u32 = OpTypeInt 32 0
|
||||
%ptr1 = OpTypeUntypedPointerKHR Workgroup
|
||||
%ptr2 = OpTypeUntypedPointerKHR Workgroup
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_4);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
Loading…
Reference in New Issue
Block a user