|
|
@@ -138,14 +138,14 @@ std::string LogStringForDecoration(uint32_t decoration) {
|
|
|
return "PerTaskNV";
|
|
|
case SpvDecorationPerVertexNV:
|
|
|
return "PerVertexNV";
|
|
|
- case SpvDecorationNonUniformEXT:
|
|
|
- return "NonUniformEXT";
|
|
|
- case SpvDecorationRestrictPointerEXT:
|
|
|
- return "RestrictPointerEXT";
|
|
|
- case SpvDecorationAliasedPointerEXT:
|
|
|
- return "AliasedPointerEXT";
|
|
|
- case SpvDecorationHlslCounterBufferGOOGLE:
|
|
|
- return "HlslCounterBufferGOOGLE";
|
|
|
+ case SpvDecorationNonUniform:
|
|
|
+ return "NonUniform";
|
|
|
+ case SpvDecorationRestrictPointer:
|
|
|
+ return "RestrictPointer";
|
|
|
+ case SpvDecorationAliasedPointer:
|
|
|
+ return "AliasedPointer";
|
|
|
+ case SpvDecorationCounterBuffer:
|
|
|
+ return "CounterBuffer";
|
|
|
case SpvDecorationHlslSemanticGOOGLE:
|
|
|
return "HlslSemanticGOOGLE";
|
|
|
default:
|
|
|
@@ -156,8 +156,8 @@ std::string LogStringForDecoration(uint32_t decoration) {
|
|
|
|
|
|
// Returns true if the decoration takes ID parameters.
|
|
|
// TODO(dneto): This can be generated from the grammar.
|
|
|
-bool DecorationTakesIdParameters(uint32_t type) {
|
|
|
- switch (static_cast<SpvDecoration>(type)) {
|
|
|
+bool DecorationTakesIdParameters(SpvDecoration type) {
|
|
|
+ switch (type) {
|
|
|
case SpvDecorationUniformId:
|
|
|
case SpvDecorationAlignmentId:
|
|
|
case SpvDecorationMaxByteOffsetId:
|
|
|
@@ -169,17 +169,212 @@ bool DecorationTakesIdParameters(uint32_t type) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
|
|
|
- const auto decoration = inst->GetOperandAs<uint32_t>(1);
|
|
|
- if (decoration == SpvDecorationSpecId) {
|
|
|
- const auto target_id = inst->GetOperandAs<uint32_t>(0);
|
|
|
- const auto target = _.FindDef(target_id);
|
|
|
- if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) {
|
|
|
- return _.diag(SPV_ERROR_INVALID_ID, inst)
|
|
|
- << "OpDecorate SpecId decoration target <id> '"
|
|
|
- << _.getIdName(target_id)
|
|
|
- << "' is not a scalar specialization constant.";
|
|
|
+bool IsMemberDecorationOnly(SpvDecoration dec) {
|
|
|
+ switch (dec) {
|
|
|
+ case SpvDecorationRowMajor:
|
|
|
+ case SpvDecorationColMajor:
|
|
|
+ case SpvDecorationMatrixStride:
|
|
|
+ // SPIR-V spec bug? Offset is generated on variables when dealing with
|
|
|
+ // transform feedback.
|
|
|
+ // case SpvDecorationOffset:
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool IsNotMemberDecoration(SpvDecoration dec) {
|
|
|
+ switch (dec) {
|
|
|
+ case SpvDecorationSpecId:
|
|
|
+ case SpvDecorationBlock:
|
|
|
+ case SpvDecorationBufferBlock:
|
|
|
+ case SpvDecorationArrayStride:
|
|
|
+ case SpvDecorationGLSLShared:
|
|
|
+ case SpvDecorationGLSLPacked:
|
|
|
+ case SpvDecorationCPacked:
|
|
|
+ // TODO: https://github.com/KhronosGroup/glslang/issues/703:
|
|
|
+ // glslang applies Restrict to structure members.
|
|
|
+ // case SpvDecorationRestrict:
|
|
|
+ case SpvDecorationAliased:
|
|
|
+ case SpvDecorationConstant:
|
|
|
+ case SpvDecorationUniform:
|
|
|
+ case SpvDecorationUniformId:
|
|
|
+ case SpvDecorationSaturatedConversion:
|
|
|
+ case SpvDecorationIndex:
|
|
|
+ case SpvDecorationBinding:
|
|
|
+ case SpvDecorationDescriptorSet:
|
|
|
+ case SpvDecorationFuncParamAttr:
|
|
|
+ case SpvDecorationFPRoundingMode:
|
|
|
+ case SpvDecorationFPFastMathMode:
|
|
|
+ case SpvDecorationLinkageAttributes:
|
|
|
+ case SpvDecorationNoContraction:
|
|
|
+ case SpvDecorationInputAttachmentIndex:
|
|
|
+ case SpvDecorationAlignment:
|
|
|
+ case SpvDecorationMaxByteOffset:
|
|
|
+ case SpvDecorationAlignmentId:
|
|
|
+ case SpvDecorationMaxByteOffsetId:
|
|
|
+ case SpvDecorationNoSignedWrap:
|
|
|
+ case SpvDecorationNoUnsignedWrap:
|
|
|
+ case SpvDecorationNonUniform:
|
|
|
+ case SpvDecorationRestrictPointer:
|
|
|
+ case SpvDecorationAliasedPointer:
|
|
|
+ case SpvDecorationCounterBuffer:
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+spv_result_t ValidateDecorationTarget(ValidationState_t& _, SpvDecoration dec,
|
|
|
+ const Instruction* inst,
|
|
|
+ const Instruction* target) {
|
|
|
+ auto fail = [&_, dec, inst, target](uint32_t vuid = 0) -> DiagnosticStream {
|
|
|
+ DiagnosticStream ds = std::move(
|
|
|
+ _.diag(SPV_ERROR_INVALID_ID, inst)
|
|
|
+ << _.VkErrorID(vuid) << LogStringForDecoration(dec)
|
|
|
+ << " decoration on target <id> '" << _.getIdName(target->id()) << "' ");
|
|
|
+ return ds;
|
|
|
+ };
|
|
|
+ switch (dec) {
|
|
|
+ case SpvDecorationSpecId:
|
|
|
+ if (!spvOpcodeIsScalarSpecConstant(target->opcode())) {
|
|
|
+ return fail() << "must be a scalar specialization constant";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SpvDecorationBlock:
|
|
|
+ case SpvDecorationBufferBlock:
|
|
|
+ case SpvDecorationGLSLShared:
|
|
|
+ case SpvDecorationGLSLPacked:
|
|
|
+ case SpvDecorationCPacked:
|
|
|
+ if (target->opcode() != SpvOpTypeStruct) {
|
|
|
+ return fail() << "must be a structure type";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SpvDecorationArrayStride:
|
|
|
+ if (target->opcode() != SpvOpTypeArray &&
|
|
|
+ target->opcode() != SpvOpTypeRuntimeArray &&
|
|
|
+ target->opcode() != SpvOpTypePointer) {
|
|
|
+ return fail() << "must be an array or pointer type";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SpvDecorationBuiltIn:
|
|
|
+ if (target->opcode() != SpvOpVariable &&
|
|
|
+ !spvOpcodeIsConstant(target->opcode())) {
|
|
|
+ return _.diag(SPV_ERROR_INVALID_DATA, inst)
|
|
|
+ << "BuiltIns can only target variables, structure members or "
|
|
|
+ "constants";
|
|
|
+ }
|
|
|
+ if (inst->GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) {
|
|
|
+ if (!spvOpcodeIsConstant(target->opcode())) {
|
|
|
+ return fail() << "must be a constant for WorkgroupSize";
|
|
|
+ }
|
|
|
+ } else if (target->opcode() != SpvOpVariable) {
|
|
|
+ return fail() << "must be a variable";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SpvDecorationNoPerspective:
|
|
|
+ case SpvDecorationFlat:
|
|
|
+ case SpvDecorationPatch:
|
|
|
+ case SpvDecorationCentroid:
|
|
|
+ case SpvDecorationSample:
|
|
|
+ case SpvDecorationRestrict:
|
|
|
+ case SpvDecorationAliased:
|
|
|
+ case SpvDecorationVolatile:
|
|
|
+ case SpvDecorationCoherent:
|
|
|
+ case SpvDecorationNonWritable:
|
|
|
+ case SpvDecorationNonReadable:
|
|
|
+ case SpvDecorationXfbBuffer:
|
|
|
+ case SpvDecorationXfbStride:
|
|
|
+ case SpvDecorationComponent:
|
|
|
+ case SpvDecorationStream:
|
|
|
+ case SpvDecorationRestrictPointer:
|
|
|
+ case SpvDecorationAliasedPointer:
|
|
|
+ if (target->opcode() != SpvOpVariable &&
|
|
|
+ target->opcode() != SpvOpFunctionParameter) {
|
|
|
+ return fail() << "must be a memory object declaration";
|
|
|
+ }
|
|
|
+ if (_.GetIdOpcode(target->type_id()) != SpvOpTypePointer) {
|
|
|
+ return fail() << "must be a pointer type";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SpvDecorationInvariant:
|
|
|
+ case SpvDecorationConstant:
|
|
|
+ case SpvDecorationLocation:
|
|
|
+ case SpvDecorationIndex:
|
|
|
+ case SpvDecorationBinding:
|
|
|
+ case SpvDecorationDescriptorSet:
|
|
|
+ case SpvDecorationInputAttachmentIndex:
|
|
|
+ if (target->opcode() != SpvOpVariable) {
|
|
|
+ return fail() << "must be a variable";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (spvIsVulkanEnv(_.context()->target_env)) {
|
|
|
+ // The following were all checked as pointer types above.
|
|
|
+ SpvStorageClass sc = SpvStorageClassUniform;
|
|
|
+ const auto type = _.FindDef(target->type_id());
|
|
|
+ if (type && type->operands().size() > 2) {
|
|
|
+ sc = type->GetOperandAs<SpvStorageClass>(1);
|
|
|
}
|
|
|
+ switch (dec) {
|
|
|
+ case SpvDecorationLocation:
|
|
|
+ case SpvDecorationComponent:
|
|
|
+ // Location is used for input, output and ray tracing stages.
|
|
|
+ if (sc == SpvStorageClassStorageBuffer ||
|
|
|
+ sc == SpvStorageClassUniform ||
|
|
|
+ sc == SpvStorageClassUniformConstant ||
|
|
|
+ sc == SpvStorageClassWorkgroup || sc == SpvStorageClassPrivate ||
|
|
|
+ sc == SpvStorageClassFunction) {
|
|
|
+ return _.diag(SPV_ERROR_INVALID_ID, target)
|
|
|
+ << LogStringForDecoration(dec)
|
|
|
+ << " decoration must not be applied to this storage class";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SpvDecorationIndex:
|
|
|
+ if (sc != SpvStorageClassOutput) {
|
|
|
+ return fail() << "must be in the Output storage class";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SpvDecorationBinding:
|
|
|
+ case SpvDecorationDescriptorSet:
|
|
|
+ if (sc != SpvStorageClassStorageBuffer &&
|
|
|
+ sc != SpvStorageClassUniform &&
|
|
|
+ sc != SpvStorageClassUniformConstant) {
|
|
|
+ return fail() << "must be in the StorageBuffer, Uniform, or "
|
|
|
+ "UniformConstant storage class";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SpvDecorationInputAttachmentIndex:
|
|
|
+ if (sc != SpvStorageClassUniformConstant) {
|
|
|
+ return fail() << "must be in the UniformConstant storage class";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SpvDecorationFlat:
|
|
|
+ case SpvDecorationNoPerspective:
|
|
|
+ case SpvDecorationCentroid:
|
|
|
+ case SpvDecorationSample:
|
|
|
+ if (sc != SpvStorageClassInput && sc != SpvStorageClassOutput) {
|
|
|
+ return fail(4670) << "storage class must be Input or Output";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return SPV_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
+spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
|
|
|
+ const auto decoration = inst->GetOperandAs<SpvDecoration>(1);
|
|
|
+ const auto target_id = inst->GetOperandAs<uint32_t>(0);
|
|
|
+ const auto target = _.FindDef(target_id);
|
|
|
+ if (!target) {
|
|
|
+ return _.diag(SPV_ERROR_INVALID_ID, inst) << "target is not defined";
|
|
|
}
|
|
|
|
|
|
if (spvIsVulkanEnv(_.context()->target_env)) {
|
|
|
@@ -197,17 +392,34 @@ spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
|
|
|
<< "Decorations taking ID parameters may not be used with "
|
|
|
"OpDecorateId";
|
|
|
}
|
|
|
+
|
|
|
+ if (target->opcode() != SpvOpDecorationGroup) {
|
|
|
+ if (IsMemberDecorationOnly(decoration)) {
|
|
|
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
|
|
|
+ << LogStringForDecoration(decoration)
|
|
|
+ << " can only be applied to structure members";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (auto error = ValidateDecorationTarget(_, decoration, inst, target)) {
|
|
|
+ return error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// TODO: Add validations for all decorations.
|
|
|
return SPV_SUCCESS;
|
|
|
}
|
|
|
|
|
|
spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
|
|
|
- const auto decoration = inst->GetOperandAs<uint32_t>(1);
|
|
|
+ const auto decoration = inst->GetOperandAs<SpvDecoration>(1);
|
|
|
if (!DecorationTakesIdParameters(decoration)) {
|
|
|
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
|
|
<< "Decorations that don't take ID parameters may not be used with "
|
|
|
"OpDecorateId";
|
|
|
}
|
|
|
+
|
|
|
+ // No member decorations take id parameters, so we don't bother checking if
|
|
|
+ // we are using a member only decoration here.
|
|
|
+
|
|
|
// TODO: Add validations for these decorations.
|
|
|
// UniformId is covered elsewhere.
|
|
|
return SPV_SUCCESS;
|
|
|
@@ -234,6 +446,13 @@ spv_result_t ValidateMemberDecorate(ValidationState_t& _,
|
|
|
<< " members. Largest valid index is " << member_count - 1 << ".";
|
|
|
}
|
|
|
|
|
|
+ const auto decoration = inst->GetOperandAs<SpvDecoration>(2);
|
|
|
+ if (IsNotMemberDecoration(decoration)) {
|
|
|
+ return _.diag(SPV_ERROR_INVALID_ID, inst)
|
|
|
+ << LogStringForDecoration(decoration)
|
|
|
+ << " cannot be applied to structure members";
|
|
|
+ }
|
|
|
+
|
|
|
return SPV_SUCCESS;
|
|
|
}
|
|
|
|