spirv-val: Add remaining OpSpecConstantOp (#6596)
My personal final PR for
https://github.com/KhronosGroup/SPIRV-Tools/issues/6564
There is still the access chains left, but those are going to be ugly,
and since its only a `Kernel` feature, I don't have the bandwidth to
look into right now. (But did left some test for the person who does in
the future)
diff --git a/source/val/validate_composites.cpp b/source/val/validate_composites.cpp
index c9aadca..98c1343 100644
--- a/source/val/validate_composites.cpp
+++ b/source/val/validate_composites.cpp
@@ -17,6 +17,7 @@
// Validates correctness of composite SPIR-V instructions.
#include <climits>
+#include <cstdint>
#include "source/opcode.h"
#include "source/spirv_target_env.h"
@@ -36,14 +37,11 @@
// deep).
spv_result_t GetExtractInsertValueType(ValidationState_t& _,
const Instruction* inst,
- uint32_t* member_type) {
- const spv::Op opcode = inst->opcode();
- assert(opcode == spv::Op::OpCompositeExtract ||
- opcode == spv::Op::OpCompositeInsert);
- uint32_t word_index = opcode == spv::Op::OpCompositeExtract ? 4 : 5;
- const uint32_t num_words = static_cast<uint32_t>(inst->words().size());
- const uint32_t composite_id_index = word_index - 1;
- const uint32_t num_indices = num_words - word_index;
+ uint32_t* member_type,
+ uint32_t composite_id_index) {
+ const uint32_t num_operands = static_cast<uint32_t>(inst->operands().size());
+ const uint32_t first_literal_index = composite_id_index + 1;
+ const uint32_t num_indices = num_operands - first_literal_index;
const uint32_t kCompositeExtractInsertMaxNumIndices = 255;
if (num_indices == 0) {
@@ -53,19 +51,21 @@
} else if (num_indices > kCompositeExtractInsertMaxNumIndices) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "The number of indexes in Op" << spvOpcodeString(opcode)
+ << "The number of indexes in Op" << spvOpcodeString(inst->opcode())
<< " may not exceed " << kCompositeExtractInsertMaxNumIndices
<< ". Found " << num_indices << " indexes.";
}
- *member_type = _.GetTypeId(inst->word(composite_id_index));
+ *member_type = _.GetOperandTypeId(inst, composite_id_index);
if (*member_type == 0) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
<< "Expected Composite to be an object of composite type";
}
- for (; word_index < num_words; ++word_index) {
- const uint32_t component_index = inst->word(word_index);
+ for (uint32_t operand_index = first_literal_index;
+ operand_index < num_operands; ++operand_index) {
+ const uint32_t component_index =
+ inst->GetOperandAs<uint32_t>(operand_index);
const Instruction* const type_inst = _.FindDef(*member_type);
assert(type_inst);
switch (type_inst->opcode()) {
@@ -471,9 +471,12 @@
}
spv_result_t ValidateCompositeExtract(ValidationState_t& _,
- const Instruction* inst) {
+ const Instruction* inst,
+ uint32_t operand_index = 2) {
uint32_t member_type = 0;
- if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) {
+
+ if (spv_result_t error =
+ GetExtractInsertValueType(_, inst, &member_type, operand_index)) {
return error;
}
@@ -496,9 +499,10 @@
}
spv_result_t ValidateCompositeInsert(ValidationState_t& _,
- const Instruction* inst) {
- const uint32_t object_type = _.GetOperandTypeId(inst, 2);
- const uint32_t composite_type = _.GetOperandTypeId(inst, 3);
+ const Instruction* inst,
+ uint32_t operand_index = 2) {
+ const uint32_t object_type = _.GetOperandTypeId(inst, operand_index);
+ const uint32_t composite_type = _.GetOperandTypeId(inst, operand_index + 1);
const uint32_t result_type = inst->type_id();
if (result_type != composite_type) {
return _.diag(SPV_ERROR_INVALID_DATA, inst)
@@ -508,7 +512,8 @@
}
uint32_t member_type = 0;
- if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) {
+ if (spv_result_t error =
+ GetExtractInsertValueType(_, inst, &member_type, operand_index + 1)) {
return error;
}
@@ -589,58 +594,58 @@
}
spv_result_t ValidateVectorShuffle(ValidationState_t& _,
- const Instruction* inst) {
- auto resultType = _.FindDef(inst->type_id());
- if (!_.IsVectorType(resultType->id())) {
+ const Instruction* inst,
+ uint32_t operand_index = 2) {
+ auto result_type = _.FindDef(inst->type_id());
+ if (!_.IsVectorType(result_type->id())) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The Result Type of OpVectorShuffle must be"
<< " a vector type. Found Op"
- << spvOpcodeString(resultType->opcode()) << ".";
+ << spvOpcodeString(result_type->opcode()) << ".";
}
// The number of components in Result Type must be the same as the number of
// Component operands.
- auto componentCount = inst->operands().size() - 4;
- auto resultVectorDimension = _.GetDimension(resultType->id());
- if (resultVectorDimension > 0 && componentCount != resultVectorDimension) {
+ uint32_t first_literal_index = operand_index + 2;
+ uint32_t component_count =
+ static_cast<uint32_t>(inst->operands().size()) - first_literal_index;
+ auto result_vec_dimension = _.GetDimension(result_type->id());
+ if (result_vec_dimension > 0 && component_count != result_vec_dimension) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "OpVectorShuffle component literals count does not match "
"Result Type <id> "
- << _.getIdName(resultType->id()) << "s vector component count.";
+ << _.getIdName(result_type->id()) << "s vector component count.";
}
// Vector 1 and Vector 2 must both have vector types, with the same Component
// Type as Result Type.
- auto vector1Object = _.FindDef(inst->GetOperandAs<uint32_t>(2));
- auto vector1Type = _.FindDef(vector1Object->type_id());
- auto vector2Object = _.FindDef(inst->GetOperandAs<uint32_t>(3));
- auto vector2Type = _.FindDef(vector2Object->type_id());
- if (!_.IsVectorType(vector1Type->id())) {
+ auto vec1_type = _.FindDef(_.GetOperandTypeId(inst, operand_index));
+ auto vec2_type = _.FindDef(_.GetOperandTypeId(inst, operand_index + 1));
+ if (!_.IsVectorType(vec1_type->id())) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The type of Vector 1 must be a vector type.";
}
- if (!_.IsVectorType(vector2Type->id())) {
+ if (!_.IsVectorType(vec2_type->id())) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The type of Vector 2 must be a vector type.";
}
- auto resultComponentType = resultType->GetOperandAs<uint32_t>(1);
- if (vector1Type->GetOperandAs<uint32_t>(1) != resultComponentType) {
+ uint32_t result_component_type = result_type->GetOperandAs<uint32_t>(1);
+ if (vec1_type->GetOperandAs<uint32_t>(1) != result_component_type) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The Component Type of Vector 1 must be the same as ResultType.";
}
- if (vector2Type->GetOperandAs<uint32_t>(1) != resultComponentType) {
+ if (vec2_type->GetOperandAs<uint32_t>(1) != result_component_type) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "The Component Type of Vector 2 must be the same as ResultType.";
}
// All Component literals must either be FFFFFFFF or in [0, N - 1].
- auto vector1ComponentCount = vector1Type->GetOperandAs<uint32_t>(2);
- auto vector2ComponentCount = vector2Type->GetOperandAs<uint32_t>(2);
- auto N = vector1ComponentCount + vector2ComponentCount;
- auto firstLiteralIndex = 4;
- for (size_t i = firstLiteralIndex; i < inst->operands().size(); ++i) {
- auto literal = inst->GetOperandAs<uint32_t>(i);
+ uint32_t vec1_component_count = vec1_type->GetOperandAs<uint32_t>(2);
+ uint32_t vec2_component_count = vec2_type->GetOperandAs<uint32_t>(2);
+ uint32_t N = vec1_component_count + vec2_component_count;
+ for (size_t i = first_literal_index; i < inst->operands().size(); ++i) {
+ uint32_t literal = inst->GetOperandAs<uint32_t>(i);
if (literal != 0xFFFFFFFF && literal >= N) {
return _.diag(SPV_ERROR_INVALID_ID, inst)
<< "Component index " << literal << " is out of bounds for "
@@ -1168,6 +1173,20 @@
return ValidateCompositeExtractCoopMatQCOM(_, inst);
case spv::Op::OpExtractSubArrayQCOM:
return ValidateExtractSubArrayQCOM(_, inst);
+
+ case spv::Op::OpSpecConstantOp: {
+ switch (inst->GetOperandAs<spv::Op>(2u)) {
+ case spv::Op::OpVectorShuffle:
+ return ValidateVectorShuffle(_, inst, 3);
+ case spv::Op::OpCompositeExtract:
+ return ValidateCompositeExtract(_, inst, 3);
+ case spv::Op::OpCompositeInsert:
+ return ValidateCompositeInsert(_, inst, 3);
+ default:
+ break;
+ }
+ }
+
default:
break;
}
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 4ade235..47d1a3c 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -3535,13 +3535,14 @@
return ValidatePtrComparison(_, inst);
case spv::Op::OpImageTexelPointer:
case spv::Op::OpGenericPtrMemSemantics:
-
+ break; // no validation currently
case spv::Op::OpSpecConstantOp: {
switch (inst->GetOperandAs<spv::Op>(2u)) {
case spv::Op::OpCooperativeMatrixLengthKHR:
return ValidateCooperativeMatrixLength(_, inst, true, 3);
case spv::Op::OpCooperativeMatrixLengthNV:
return ValidateCooperativeMatrixLength(_, inst, false, 3);
+ // TODO - Add AccesChains
default:
break;
}
diff --git a/test/val/val_constants_test.cpp b/test/val/val_constants_test.cpp
index 71d17ec..b50266d 100644
--- a/test/val/val_constants_test.cpp
+++ b/test/val/val_constants_test.cpp
@@ -772,12 +772,16 @@
HasSubstr("must be OpTypeCooperativeMatrixKHR"));
}
+// Some check use SPV_ERROR_INVALID_DATA vs SPV_ERROR_INVALID_ID
#define BAD_KERNEL_OPERANDS(STR, ERR) \
{ \
SPV_ENV_UNIVERSAL_1_0, kKernelPreamble kBasicTypes STR, false, ERR, \
SPV_ERROR_INVALID_DATA \
}
+#define BAD_KERNEL_OPERANDS_ID(STR, ERR) \
+ { SPV_ENV_UNIVERSAL_1_0, kKernelPreamble kBasicTypes STR, false, ERR, }
+
// 2 of each, first has bad return type, second has bad operand
INSTANTIATE_TEST_SUITE_P(
BadOperandsKernel, ValidateConstantOp,
@@ -1079,7 +1083,57 @@
"float vector or scalar type"),
BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint Bitcast %true",
"Expected input to be a pointer or int or float "
- "vector or scalar")}));
+ "vector or scalar"),
+
+ BAD_KERNEL_OPERANDS_ID(
+ "%v = OpSpecConstantOp %float VectorShuffle %uint2_0 %uint2_0 1 3",
+ "The Result Type of OpVectorShuffle must be a vector type"),
+ BAD_KERNEL_OPERANDS_ID(
+ "%v = OpSpecConstantOp %uint2 VectorShuffle %uint2_0 %uint_0 1 3",
+ "The type of Vector 2 must be a vector type"),
+ BAD_KERNEL_OPERANDS(
+ "%v = OpSpecConstantOp %float CompositeExtract %uint2_0 1",
+ "Result type (OpTypeFloat) does not match the type that results "
+ "from indexing into the composite (OpTypeInt)"),
+ BAD_KERNEL_OPERANDS(
+ "%v = OpSpecConstantOp %uint CompositeExtract %uint_0 1",
+ "Reached non-composite type while indexes still remain to be "
+ "traversed"),
+ BAD_KERNEL_OPERANDS(
+ "%v = OpSpecConstantOp %float CompositeInsert %uint_0 %uint2_0 1",
+ "The Result Type must be the same as Composite type in "
+ "OpSpecConstantOp yielding Result Id 5"),
+ BAD_KERNEL_OPERANDS(
+ "%v = OpSpecConstantOp %uint2 CompositeInsert %uint_0 %uint_0 1",
+ "The Result Type must be the same as Composite type in "
+ "OpSpecConstantOp yielding Result Id 4"),
+
+ // TODO - Still need to add access chains
+ //
+ // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint AccessChain %null",
+ // "AccessChain"),
+ // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint AccessChain
+ // %null %float_0",
+ // "AccessChain"),
+ // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint InBoundsAccessChain
+ // %null",
+ // "InBoundsAccessChain"),
+ // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint
+ // InBoundsAccessChain %null %float_0",
+ // "InBoundsAccessChain"),
+ // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint PtrAccessChain %null
+ // %uint_0",
+ // "PtrAccessChain"),
+ // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint PtrAccessChain
+ // %float_0 %float_0",
+ // "PtrAccessChain"),
+ // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %uint
+ // InBoundsPtrAccessChain %null %uint_0",
+ // "InBoundsPtrAccessChain"),
+ // BAD_KERNEL_OPERANDS("%v = OpSpecConstantOp %_ptr_uint
+ // InBoundsPtrAccessChain %float_0 %float_0",
+ // "InBoundsPtrAccessChain"),
+ }));
} // namespace
} // namespace val