Skip to content

Commit

Permalink
Support SPV_KHR_untyped_pointers
Browse files Browse the repository at this point in the history
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
  • Loading branch information
dneto0 authored and alan-baker committed Jul 15, 2024
1 parent 9f2ccae commit c746442
Show file tree
Hide file tree
Showing 33 changed files with 3,632 additions and 328 deletions.
5 changes: 5 additions & 0 deletions source/name_mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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") +
Expand Down
21 changes: 21 additions & 0 deletions source/opcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,15 +287,19 @@ 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:
case spv::Op::OpSelect:
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:
Expand All @@ -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:
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
}
3 changes: 3 additions & 0 deletions source/opcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_
9 changes: 9 additions & 0 deletions source/val/validate_adjacency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 7 additions & 3 deletions source/val/validate_annotation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
39 changes: 38 additions & 1 deletion source/val/validate_atomics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions source/val/validate_builtins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
3 changes: 2 additions & 1 deletion source/val/validate_cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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> "
Expand Down
1 change: 1 addition & 0 deletions source/val/validate_constants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit c746442

Please sign in to comment.