From 61a627586b7f47d39b0e19caabc9bd880aa3832a Mon Sep 17 00:00:00 2001 From: Umar Arshad Date: Sat, 23 Jan 2016 14:14:32 -0500 Subject: [PATCH] Initial capability implementation and unit tests * Register capabilility and checks * Add validate storage class capabilities checks and tests * Instruction pass refactor - More generic form of capability checking by checking operands instead of instructions * Execution Model capabilities checks * Decorate BuiltIn capability checks * Addressing Model capability checks * Memory Model capability checks * Execution Mode capability checks * Dim capability checks * SamplerAddressingMode capability checks --- CMakeLists.txt | 1 + include/libspirv/libspirv.h | 1 + source/validate.h | 9 +- source/validate_instruction.cpp | 707 +++++++++++++++++++++++++++++++- source/validate_types.cpp | 99 ++++- test/Validate.Capability.cpp | 579 ++++++++++++++++++++++++++ test/ValidateFixtures.cpp | 2 + 7 files changed, 1385 insertions(+), 13 deletions(-) create mode 100644 test/Validate.Capability.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e749d70c..15d66ada4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,6 +234,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES}) ${CMAKE_CURRENT_SOURCE_DIR}/test/TextWordGet.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/UnitSPIRV.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/ValidateFixtures.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test/Validate.Capability.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/Validate.Layout.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/Validate.SSA.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test/ValidateID.cpp diff --git a/include/libspirv/libspirv.h b/include/libspirv/libspirv.h index 33957592f..1646e5681 100644 --- a/include/libspirv/libspirv.h +++ b/include/libspirv/libspirv.h @@ -76,6 +76,7 @@ typedef enum spv_result_t { SPV_ERROR_INVALID_ID = -10, SPV_ERROR_INVALID_CFG = -11, SPV_ERROR_INVALID_LAYOUT = -12, + SPV_ERROR_INVALID_CAPABILITY = -13, SPV_FORCE_32_BIT_ENUM(spv_result_t) } spv_result_t; diff --git a/source/validate.h b/source/validate.h index f2b5c64a6..d0c053e73 100644 --- a/source/validate.h +++ b/source/validate.h @@ -261,6 +261,12 @@ class ValidationState_t { std::vector& entry_points() { return entry_points_; } const std::vector& entry_points() const { return entry_points_; } + // Registers the capability and its dependent capabilities + void registerCapability(SpvCapability cap); + + // Returns true if the capabillity is enabled in the module + bool hasCapability(SpvCapability cap); + private: spv_diagnostic* diagnostic_; // Tracks the number of instructions evaluated by the validator @@ -279,13 +285,14 @@ class ValidationState_t { Functions module_functions_; - std::vector module_capabilities_; + std::vector module_capabilities_; // Definitions and uses of all the IDs in the module. UseDefTracker usedefs_; // IDs that are entry points, ie, arguments to OpEntryPoint. std::vector entry_points_; + }; } // namespace libspirv diff --git a/source/validate_instruction.cpp b/source/validate_instruction.cpp index cd35a4f2c..b262a9ae3 100644 --- a/source/validate_instruction.cpp +++ b/source/validate_instruction.cpp @@ -26,18 +26,473 @@ // Performs validation on instructions that appear inside of a SPIR-V block. +#include #include "validate_passes.h" +using libspirv::ValidationState_t; + +namespace { + +#define STORAGE_CLASS_CASE(CLASS, CAPABILITY) \ + case SpvStorageClass##CLASS: \ + if (_.hasCapability(SpvCapability##CAPABILITY) == false) { \ + return _.diag(SPV_ERROR_INVALID_CAPABILITY) \ + << #CLASS " storage class requires " #CAPABILITY " capability"; \ + } \ + break + +spv_result_t StorageClassCapabilityCheck(ValidationState_t& _, + SpvStorageClass storage_class) { + switch (storage_class) { + STORAGE_CLASS_CASE(Input, Shader); + STORAGE_CLASS_CASE(Uniform, Shader); + STORAGE_CLASS_CASE(Output, Shader); + STORAGE_CLASS_CASE(Private, Shader); + STORAGE_CLASS_CASE(Generic, Kernel); + STORAGE_CLASS_CASE(PushConstant, Shader); + STORAGE_CLASS_CASE(AtomicCounter, AtomicStorage); + default: + // No capabilities are required for UniformConstant, WorkgroupLocal, + // WorkgroupGlobal, Function, and Image + break; + } + return SPV_SUCCESS; +} +#undef VARIABLE_STORAGE_CASE + +#define DECORATION_CASE(DECORATION, CAPABILITY) \ + case SpvDecoration##DECORATION: \ + if (_.hasCapability(SpvCapability##CAPABILITY) == false) { \ + return _.diag(SPV_ERROR_INVALID_CAPABILITY) \ + << #DECORATION " decoration requires " #CAPABILITY " capability"; \ + } \ + break + +#define BUILTIN_CASE(BUILTIN, CAPABILITY) \ + case SpvBuiltIn##BUILTIN: \ + if (_.hasCapability(SpvCapability##CAPABILITY) == false) { \ + return _.diag(SPV_ERROR_INVALID_CAPABILITY) \ + << #BUILTIN " builtin requires " #CAPABILITY " capability"; \ + } \ + break + +#define BUILTIN_CASE2(BUILTIN, CAPABILITY1, CAPABILITY2) \ + case SpvBuiltIn##BUILTIN: \ + if (_.hasCapability(SpvCapability##CAPABILITY1) == false && \ + _.hasCapability(SpvCapability##CAPABILITY2) == false) { \ + return _.diag(SPV_ERROR_INVALID_CAPABILITY) \ + << #BUILTIN " builtin requires " #CAPABILITY1 " or " #CAPABILITY2 \ + " capabilities"; \ + } \ + break + +spv_result_t DecorationCapabilityCheck(ValidationState_t& _, + SpvDecoration decoration, + SpvBuiltIn optional_builtin) { + switch (decoration) { + DECORATION_CASE(RelaxedPrecision, Shader); + DECORATION_CASE(SpecId, Shader); + DECORATION_CASE(Block, Shader); + DECORATION_CASE(BufferBlock, Shader); + DECORATION_CASE(RowMajor, Matrix); + DECORATION_CASE(ColMajor, Matrix); + DECORATION_CASE(ArrayStride, Shader); + DECORATION_CASE(MatrixStride, Shader); + DECORATION_CASE(GLSLShared, Shader); + DECORATION_CASE(GLSLPacked, Shader); + DECORATION_CASE(CPacked, Kernel); + DECORATION_CASE(NoPerspective, Shader); + DECORATION_CASE(Flat, Shader); + DECORATION_CASE(Patch, Tessellation); + DECORATION_CASE(Centroid, Shader); + DECORATION_CASE(Sample, Shader); + DECORATION_CASE(Invariant, Shader); + DECORATION_CASE(Constant, Kernel); + DECORATION_CASE(Uniform, Shader); + DECORATION_CASE(SaturatedConversion, Kernel); + DECORATION_CASE(Stream, GeometryStreams); + DECORATION_CASE(Location, Shader); + DECORATION_CASE(Component, Shader); + DECORATION_CASE(Index, Shader); + DECORATION_CASE(Binding, Shader); + DECORATION_CASE(DescriptorSet, Shader); + DECORATION_CASE(XfbBuffer, TransformFeedback); + DECORATION_CASE(XfbStride, TransformFeedback); + DECORATION_CASE(FuncParamAttr, Kernel); + DECORATION_CASE(FPRoundingMode, Kernel); + DECORATION_CASE(FPFastMathMode, Kernel); + DECORATION_CASE(LinkageAttributes, Linkage); + DECORATION_CASE(NoContraction, Shader); + DECORATION_CASE(Alignment, Kernel); + DECORATION_CASE(InputAttachmentIndex, InputAttachment); + case SpvDecorationBuiltIn: + switch (optional_builtin) { + BUILTIN_CASE(Position, Shader); + BUILTIN_CASE(PointSize, Shader); + BUILTIN_CASE(ClipDistance, ClipDistance); + BUILTIN_CASE(CullDistance, CullDistance); + BUILTIN_CASE(VertexId, Shader); + BUILTIN_CASE(InstanceId, Shader); + BUILTIN_CASE2(PrimitiveId, Geometry, Tessellation); + BUILTIN_CASE2(InvocationId, Geometry, Tessellation); + BUILTIN_CASE(Layer, Geometry); + case SpvBuiltInViewportIndex: + assert( + false && + "UNHANDLED"); // TODO(umar): missing SpvCapabilityMultiViewport + // BUILTIN_CASE(ViewportIndex, MultiViewport); + BUILTIN_CASE(TessLevelOuter, Tessellation); + BUILTIN_CASE(TessLevelInner, Tessellation); + BUILTIN_CASE(TessCoord, Tessellation); + BUILTIN_CASE(PatchVertices, Tessellation); + BUILTIN_CASE(FragCoord, Shader); + BUILTIN_CASE(PointCoord, Shader); + BUILTIN_CASE(FrontFacing, Shader); + BUILTIN_CASE(SampleId, SampleRateShading); + BUILTIN_CASE(SamplePosition, SampleRateShading); + BUILTIN_CASE(SampleMask, SampleRateShading); + BUILTIN_CASE(FragDepth, Shader); + BUILTIN_CASE(HelperInvocation, Shader); + BUILTIN_CASE(WorkDim, Kernel); + BUILTIN_CASE(GlobalSize, Kernel); + BUILTIN_CASE(EnqueuedWorkgroupSize, Kernel); + BUILTIN_CASE(GlobalOffset, Kernel); + BUILTIN_CASE(GlobalLinearId, Kernel); + BUILTIN_CASE(SubgroupSize, Kernel); + BUILTIN_CASE(SubgroupMaxSize, Kernel); + BUILTIN_CASE(NumSubgroups, Kernel); + BUILTIN_CASE(NumEnqueuedSubgroups, Kernel); + BUILTIN_CASE(SubgroupId, Kernel); + BUILTIN_CASE(SubgroupLocalInvocationId, Kernel); + BUILTIN_CASE(VertexIndex, Shader); + BUILTIN_CASE(InstanceIndex, Shader); + case SpvBuiltInNumWorkgroups: + case SpvBuiltInWorkgroupSize: + case SpvBuiltInWorkgroupId: + case SpvBuiltInLocalInvocationId: + case SpvBuiltInGlobalInvocationId: + case SpvBuiltInLocalInvocationIndex: + break; + } + default: + // No capabilities are required for Restrict, Aliased, BuiltIn, Volatile, + // Coherent, NonWritable, NonReadable, and Offset + break; + } +#undef DECORATION_CASE +#undef BUILTIN_CASE +#undef BUILTIN_CASE2 + + return SPV_SUCCESS; +} + +#define EXECUTION_MODEL_CASE(MODEL, CAPABILITY) \ + case SpvExecutionModel##MODEL: \ + if (_.hasCapability(SpvCapability##CAPABILITY) == false) { \ + return _.diag(SPV_ERROR_INVALID_CAPABILITY) \ + << #MODEL " execution model requires " #CAPABILITY " capability"; \ + } \ + break + +spv_result_t ExecutionModelCapabilityCheck(ValidationState_t& _, + SpvExecutionModel execution_model) { + switch (execution_model) { + EXECUTION_MODEL_CASE(Vertex, Shader); + EXECUTION_MODEL_CASE(TessellationControl, Tessellation); + EXECUTION_MODEL_CASE(TessellationEvaluation, Tessellation); + EXECUTION_MODEL_CASE(Geometry, Geometry); + EXECUTION_MODEL_CASE(Fragment, Shader); + EXECUTION_MODEL_CASE(GLCompute, Shader); + EXECUTION_MODEL_CASE(Kernel, Kernel); + } +#undef EXECUTION_MODEL_CASE + return SPV_SUCCESS; +} + +spv_result_t AddressingAndMemoryModelCapabilityCheck( + ValidationState_t& _, SpvAddressingModel addressing_model, + SpvMemoryModel memory_model) { + switch (addressing_model) { + case SpvAddressingModelPhysical32: + case SpvAddressingModelPhysical64: + if (_.hasCapability(SpvCapabilityAddresses) == false) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY) + << "Physical32 and Physical64 addressing models require the " + "Addresses capability"; + } + break; + case SpvAddressingModelLogical: + break; + } + + switch (memory_model) { + case SpvMemoryModelSimple: + case SpvMemoryModelGLSL450: + if (_.hasCapability(SpvCapabilityShader) == false) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY) + << "Simple and GLSL450 memory models require the Shader " + "capability"; + } + break; + case SpvMemoryModelOpenCL: + if (_.hasCapability(SpvCapabilityKernel) == false) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY) + << "OpenCL memory model requires the Kernel capability"; + } + break; + } + return SPV_SUCCESS; +} + +#define EXECUTION_MODE_CASE(MODE, CAPABILITY) \ + case SpvExecutionMode##MODE: \ + if (_.hasCapability(SpvCapability##CAPABILITY) == false) { \ + return _.diag(SPV_ERROR_INVALID_CAPABILITY) \ + << #MODE " mode requires " #CAPABILITY " capability"; \ + } \ + break +#define EXECUTION_MODE_CASE2(MODE, CAPABILITY1, CAPABILITY2) \ + case SpvExecutionMode##MODE: \ + if (_.hasCapability(SpvCapability##CAPABILITY1) == false && \ + _.hasCapability(SpvCapability##CAPABILITY2) == false) { \ + return _.diag(SPV_ERROR_INVALID_CAPABILITY) << #MODE \ + " mode requires " #CAPABILITY1 " or " #CAPABILITY2 " capability"; \ + } \ + break +spv_result_t ExecutionModeCapabilityCheck(ValidationState_t& _, + SpvExecutionMode execution_mode) { + switch (execution_mode) { + EXECUTION_MODE_CASE(Invocations, Geometry); + EXECUTION_MODE_CASE(SpacingEqual, Tessellation); + EXECUTION_MODE_CASE(SpacingFractionalEven, Tessellation); + EXECUTION_MODE_CASE(SpacingFractionalOdd, Tessellation); + EXECUTION_MODE_CASE(VertexOrderCw, Tessellation); + EXECUTION_MODE_CASE(VertexOrderCcw, Tessellation); + EXECUTION_MODE_CASE(PixelCenterInteger, Shader); + EXECUTION_MODE_CASE(OriginUpperLeft, Shader); + EXECUTION_MODE_CASE(OriginLowerLeft, Shader); + EXECUTION_MODE_CASE(EarlyFragmentTests, Shader); + EXECUTION_MODE_CASE(PointMode, Tessellation); + EXECUTION_MODE_CASE(Xfb, TransformFeedback); + EXECUTION_MODE_CASE(DepthReplacing, Shader); + EXECUTION_MODE_CASE(DepthGreater, Shader); + EXECUTION_MODE_CASE(DepthLess, Shader); + EXECUTION_MODE_CASE(DepthUnchanged, Shader); + EXECUTION_MODE_CASE(LocalSizeHint, Kernel); + EXECUTION_MODE_CASE(InputPoints, Geometry); + EXECUTION_MODE_CASE(InputLines, Geometry); + EXECUTION_MODE_CASE(InputLinesAdjacency, Geometry); + EXECUTION_MODE_CASE2(Triangles, Geometry, Tessellation); + EXECUTION_MODE_CASE(InputTrianglesAdjacency, Geometry); + EXECUTION_MODE_CASE(Quads, Tessellation); + EXECUTION_MODE_CASE(Isolines, Tessellation); + EXECUTION_MODE_CASE2(OutputVertices, Geometry, Tessellation); + EXECUTION_MODE_CASE(OutputPoints, Geometry); + EXECUTION_MODE_CASE(OutputLineStrip, Geometry); + EXECUTION_MODE_CASE(OutputTriangleStrip, Geometry); + EXECUTION_MODE_CASE(VecTypeHint, Kernel); + EXECUTION_MODE_CASE(ContractionOff, Kernel); + case SpvExecutionModeLocalSize: + break; + } +#undef EXECUTION_MODE_CASE +#undef EXECUTION_MODE_CASE2 + return SPV_SUCCESS; +} + +#define DIM_CASE(DIM, CAPABILITY) \ + case SpvDim##DIM: \ + if (_.hasCapability(SpvCapability##CAPABILITY) == false) { \ + return _.diag(SPV_ERROR_INVALID_CAPABILITY) \ + << "Dim " #DIM " requires " #CAPABILITY " capability"; \ + } \ + break + +spv_result_t DimCapabilityCheck(ValidationState_t& _, SpvDim dim) { + switch (dim) { + DIM_CASE(1D, Sampled1D); + DIM_CASE(Cube, Shader); + DIM_CASE(Rect, SampledRect); + DIM_CASE(Buffer, SampledBuffer); + DIM_CASE(SubpassData, InputAttachment); + case SpvDim2D: + case SpvDim3D: + break; + } +#undef DIM_CASE + return SPV_SUCCESS; +} + +spv_result_t SamplerAddressingModeCapabilityCheck( + ValidationState_t& _, SpvSamplerAddressingMode sampler_addressing_mode) { + std::string mode; + switch (sampler_addressing_mode) { + case SpvSamplerAddressingModeNone: + mode = "None"; + break; + case SpvSamplerAddressingModeClampToEdge: + mode = "ClampToEdge"; + break; + case SpvSamplerAddressingModeClamp: + mode = "Clamp"; + break; + case SpvSamplerAddressingModeRepeat: + mode = "Repeat"; + break; + case SpvSamplerAddressingModeRepeatMirrored: + mode = "RepeatMirrored"; + break; + } + if (_.hasCapability(SpvCapabilityKernel) == false) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY) + << mode + " sample address mode requires Kernel capability"; + } + return SPV_SUCCESS; +} +} + namespace libspirv { +// clang-format off spv_result_t InstructionPass(ValidationState_t& _, const spv_parsed_instruction_t* inst) { if (_.is_enabled(SPV_VALIDATE_INSTRUCTION_BIT)) { SpvOp opcode = inst->opcode; switch (opcode) { + case SpvOpNop: break; + case SpvOpSourceContinued: break; + case SpvOpSource: break; + case SpvOpSourceExtension: break; + case SpvOpName: break; + case SpvOpMemberName: break; + case SpvOpString: break; + case SpvOpLine: break; + case SpvOpNoLine: break; + case SpvOpDecorate: { + SpvDecoration decoration = + static_cast(inst->words[inst->operands[1].offset]); + SpvBuiltIn builtin = static_cast(0); + if(decoration == SpvDecorationBuiltIn) { + builtin = static_cast(inst->words[inst->operands[2].offset]); + } + spvCheckReturn(DecorationCapabilityCheck(_, decoration, builtin)); + } break; + + case SpvOpMemberDecorate: { + SpvDecoration decoration = + static_cast(inst->words[inst->operands[2].offset]); + SpvBuiltIn builtin = static_cast(0); + if(decoration == SpvDecorationBuiltIn) { + builtin = static_cast(inst->words[inst->operands[3].offset]); + } + spvCheckReturn(DecorationCapabilityCheck(_, decoration, builtin)); + } break; + + case SpvOpDecorationGroup: break; + case SpvOpGroupDecorate: break; + case SpvOpGroupMemberDecorate: break; + case SpvOpExtension: break; + case SpvOpExtInstImport: break; + case SpvOpExtInst: break; + case SpvOpMemoryModel: { + SpvAddressingModel addressing_model = + static_cast(inst->words[inst->operands[0].offset]); + SpvMemoryModel memory_model = + static_cast(inst->words[inst->operands[1].offset]); + spvCheckReturn(AddressingAndMemoryModelCapabilityCheck(_, + addressing_model, memory_model)); + } break; + + case SpvOpEntryPoint: { + SpvExecutionModel execution_model = + static_cast(inst->words[inst->operands[0].offset]); + spvCheckReturn(ExecutionModelCapabilityCheck(_, execution_model)); + } break; + + case SpvOpExecutionMode: { + SpvExecutionMode execution_mode = + static_cast(inst->words[inst->operands[1].offset]); + spvCheckReturn(ExecutionModeCapabilityCheck(_, execution_mode)); + } break; + + case SpvOpCapability: + _.registerCapability( + static_cast(inst->words[inst->operands[0].offset])); + break; + + case SpvOpTypeVoid: break; + case SpvOpTypeBool: break; + case SpvOpTypeInt: break; + case SpvOpTypeFloat: break; + case SpvOpTypeVector: break; + case SpvOpTypeMatrix: + if (_.hasCapability(SpvCapabilityMatrix) == false) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY) + << "Matrix type requires Matrix capability"; + } + break; + case SpvOpTypeImage: { + if (_.hasCapability(SpvCapabilityImageBasic) == false) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY) + << "TypeImage requires the ImageBasic capability"; + } + SpvDim dim = + static_cast(inst->words[inst->operands[2].offset]); + spvCheckReturn(DimCapabilityCheck(_, dim)); + + } break; + + case SpvOpTypeSampler: break; + case SpvOpTypeSampledImage: break; + case SpvOpTypeArray: break; + case SpvOpTypeRuntimeArray: break; + case SpvOpTypeStruct: break; + case SpvOpTypeOpaque: break; + case SpvOpTypePointer: { + const SpvStorageClass storage_class = + static_cast(inst->words[inst->operands[1].offset]); + spvCheckReturn(StorageClassCapabilityCheck(_, storage_class)); + } break; + + case SpvOpTypeFunction: break; + case SpvOpTypeEvent: break; + case SpvOpTypeDeviceEvent: break; + case SpvOpTypeReserveId: break; + case SpvOpTypeQueue: break; + case SpvOpTypePipe: break; + case SpvOpTypeForwardPointer: { + const SpvStorageClass storage_class = + static_cast(inst->words[inst->operands[1].offset]); + spvCheckReturn(StorageClassCapabilityCheck(_, storage_class)); + } break; + + case SpvOpUndef: break; + case SpvOpConstantTrue: break; + case SpvOpConstantFalse: break; + case SpvOpConstant: break; + case SpvOpConstantComposite: break; + case SpvOpConstantSampler: { + if (_.hasCapability(SpvCapabilityLiteralSampler) == false) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY) + << "ConstantSampler requires the LiteralSampler capability"; + } + const SpvSamplerAddressingMode sampler_addressing_mode = + static_cast(inst->words[inst->operands[1].offset]); + spvCheckReturn(SamplerAddressingModeCapabilityCheck(_, sampler_addressing_mode)); + } break; + + case SpvOpConstantNull: break; + case SpvOpSpecConstantTrue: break; + case SpvOpSpecConstantFalse: break; + case SpvOpSpecConstant: break; + case SpvOpSpecConstantComposite: break; + case SpvOpSpecConstantOp: break; case SpvOpVariable: { - const uint32_t storage_class = inst->words[inst->operands[2].offset]; - if (_.getLayoutSection() > kLayoutFunctionDeclarations) { + const SpvStorageClass storage_class = + static_cast(inst->words[inst->operands[2].offset]); + spvCheckReturn(StorageClassCapabilityCheck(_, storage_class)); + + if (_.getLayoutSection() == kLayoutFunctionDeclarations) { if (storage_class != SpvStorageClassFunction) { return _.diag(SPV_ERROR_INVALID_LAYOUT) << "Variables must have a function[7] storage class inside" @@ -51,11 +506,253 @@ spv_result_t InstructionPass(ValidationState_t& _, } } } break; - default: - break; + + case SpvOpImageTexelPointer: break; + case SpvOpLoad: break; + case SpvOpStore: break; + case SpvOpCopyMemory: break; + case SpvOpCopyMemorySized: break; + case SpvOpAccessChain: break; + case SpvOpInBoundsAccessChain: break; + case SpvOpPtrAccessChain: break; + case SpvOpArrayLength: break; + case SpvOpGenericPtrMemSemantics: break; + case SpvOpInBoundsPtrAccessChain: break; + case SpvOpFunction: break; + case SpvOpFunctionParameter: break; + case SpvOpFunctionEnd: break; + case SpvOpFunctionCall: break; + case SpvOpSampledImage: break; + case SpvOpImageSampleImplicitLod: break; + case SpvOpImageSampleExplicitLod: break; + case SpvOpImageSampleDrefImplicitLod: break; + case SpvOpImageSampleDrefExplicitLod: break; + case SpvOpImageSampleProjImplicitLod: break; + case SpvOpImageSampleProjExplicitLod: break; + case SpvOpImageSampleProjDrefImplicitLod: break; + case SpvOpImageSampleProjDrefExplicitLod: break; + case SpvOpImageFetch: break; + case SpvOpImageGather: break; + case SpvOpImageDrefGather: break; + case SpvOpImageRead: break; + case SpvOpImageWrite: break; + case SpvOpImage: break; + case SpvOpImageQueryFormat: break; + case SpvOpImageQueryOrder: break; + case SpvOpImageQuerySizeLod: break; + case SpvOpImageQuerySize: break; + case SpvOpImageQueryLod: break; + case SpvOpImageQueryLevels: break; + case SpvOpImageQuerySamples: break; + case SpvOpImageSparseSampleImplicitLod: break; + case SpvOpImageSparseSampleExplicitLod: break; + case SpvOpImageSparseSampleDrefImplicitLod: break; + case SpvOpImageSparseSampleDrefExplicitLod: break; + case SpvOpImageSparseSampleProjImplicitLod: break; + case SpvOpImageSparseSampleProjExplicitLod: break; + case SpvOpImageSparseSampleProjDrefImplicitLod: break; + case SpvOpImageSparseSampleProjDrefExplicitLod: break; + case SpvOpImageSparseFetch: break; + case SpvOpImageSparseGather: break; + case SpvOpImageSparseDrefGather: break; + case SpvOpImageSparseTexelsResident: break; + case SpvOpConvertFToU: break; + case SpvOpConvertFToS: break; + case SpvOpConvertSToF: break; + case SpvOpConvertUToF: break; + case SpvOpUConvert: break; + case SpvOpSConvert: break; + case SpvOpFConvert: break; + case SpvOpQuantizeToF16: break; + case SpvOpConvertPtrToU: break; + case SpvOpSatConvertSToU: break; + case SpvOpSatConvertUToS: break; + case SpvOpConvertUToPtr: break; + case SpvOpPtrCastToGeneric: break; + case SpvOpGenericCastToPtr: break; + case SpvOpGenericCastToPtrExplicit: { + const SpvStorageClass storage_class = + static_cast(inst->words[inst->operands[3].offset]); + spvCheckReturn(StorageClassCapabilityCheck(_, storage_class)); + } break; + + case SpvOpBitcast: break; + case SpvOpVectorExtractDynamic: break; + case SpvOpVectorInsertDynamic: break; + case SpvOpVectorShuffle: break; + case SpvOpCompositeConstruct: break; + case SpvOpCompositeExtract: break; + case SpvOpCompositeInsert: break; + case SpvOpCopyObject: break; + case SpvOpTranspose: break; + case SpvOpSNegate: break; + case SpvOpFNegate: break; + case SpvOpIAdd: break; + case SpvOpFAdd: break; + case SpvOpISub: break; + case SpvOpFSub: break; + case SpvOpIMul: break; + case SpvOpFMul: break; + case SpvOpUDiv: break; + case SpvOpSDiv: break; + case SpvOpFDiv: break; + case SpvOpUMod: break; + case SpvOpSRem: break; + case SpvOpSMod: break; + case SpvOpFRem: break; + case SpvOpFMod: break; + case SpvOpVectorTimesScalar: break; + case SpvOpMatrixTimesScalar: break; + case SpvOpVectorTimesMatrix: break; + case SpvOpMatrixTimesVector: break; + case SpvOpMatrixTimesMatrix: break; + case SpvOpOuterProduct: break; + case SpvOpDot: break; + case SpvOpIAddCarry: break; + case SpvOpISubBorrow: break; + case SpvOpUMulExtended: break; + case SpvOpSMulExtended: break; break; + case SpvOpShiftRightLogical: break; + case SpvOpShiftRightArithmetic: break; + case SpvOpShiftLeftLogical: break; + case SpvOpBitwiseOr: break; + case SpvOpBitwiseXor: break; + case SpvOpBitwiseAnd: break; + case SpvOpNot: break; + case SpvOpBitFieldInsert: break; + case SpvOpBitFieldSExtract: break; + case SpvOpBitFieldUExtract: break; + case SpvOpBitReverse: break; + case SpvOpBitCount: break; + case SpvOpAny: break; + case SpvOpAll: break; + case SpvOpIsNan: break; + case SpvOpIsInf: break; + case SpvOpIsFinite: break; + case SpvOpIsNormal: break; + case SpvOpSignBitSet: break; + case SpvOpLessOrGreater: break; + case SpvOpOrdered: break; + case SpvOpUnordered: break; + case SpvOpLogicalEqual: break; + case SpvOpLogicalNotEqual: break; + case SpvOpLogicalOr: break; + case SpvOpLogicalAnd: break; + case SpvOpLogicalNot: break; + case SpvOpSelect: break; + case SpvOpIEqual: break; + case SpvOpINotEqual: break; + case SpvOpUGreaterThan: break; + case SpvOpSGreaterThan: break; + case SpvOpUGreaterThanEqual: break; + case SpvOpSGreaterThanEqual: break; + case SpvOpULessThan: break; + case SpvOpSLessThan: break; + case SpvOpULessThanEqual: break; + case SpvOpSLessThanEqual: break; + case SpvOpFOrdEqual: break; + case SpvOpFUnordEqual: break; + case SpvOpFOrdNotEqual: break; + case SpvOpFUnordNotEqual: break; + case SpvOpFOrdLessThan: break; + case SpvOpFUnordLessThan: break; + case SpvOpFOrdGreaterThan: break; + case SpvOpFUnordGreaterThan: break; + case SpvOpFOrdLessThanEqual: break; + case SpvOpFUnordLessThanEqual: break; + case SpvOpFOrdGreaterThanEqual: break; + case SpvOpFUnordGreaterThanEqual: break; + case SpvOpDPdx: break; + case SpvOpDPdy: break; + case SpvOpFwidth: break; + case SpvOpDPdxFine: break; + case SpvOpDPdyFine: break; + case SpvOpFwidthFine: break; + case SpvOpDPdxCoarse: break; + case SpvOpDPdyCoarse: break; + case SpvOpFwidthCoarse: break; + case SpvOpPhi: break; + case SpvOpLoopMerge: break; + case SpvOpSelectionMerge: break; + case SpvOpLabel: break; + case SpvOpBranch: break; + case SpvOpBranchConditional: break; + case SpvOpSwitch: break; + case SpvOpKill: break; + case SpvOpReturn: break; + case SpvOpReturnValue: break; + case SpvOpUnreachable: break; + case SpvOpLifetimeStart: break; + case SpvOpLifetimeStop: break; + case SpvOpAtomicLoad: break; + case SpvOpAtomicStore: break; + case SpvOpAtomicExchange: break; + case SpvOpAtomicCompareExchange: break; + case SpvOpAtomicCompareExchangeWeak: break; + case SpvOpAtomicIIncrement: break; + case SpvOpAtomicIDecrement: break; + case SpvOpAtomicIAdd: break; + case SpvOpAtomicISub: break; + case SpvOpAtomicSMin: break; + case SpvOpAtomicUMin: break; + case SpvOpAtomicSMax: break; + case SpvOpAtomicUMax: break; + case SpvOpAtomicAnd: break; + case SpvOpAtomicOr: break; + case SpvOpAtomicXor: break; + case SpvOpAtomicFlagTestAndSet: break; + case SpvOpAtomicFlagClear: break; + case SpvOpEmitVertex: break; + case SpvOpEndPrimitive: break; + case SpvOpEmitStreamVertex: break; + case SpvOpEndStreamPrimitive: break; + case SpvOpControlBarrier: break; + case SpvOpMemoryBarrier: break; + case SpvOpGroupAsyncCopy: break; + case SpvOpGroupWaitEvents: break; + case SpvOpGroupAll: break; + case SpvOpGroupAny: break; + case SpvOpGroupBroadcast: break; + case SpvOpGroupIAdd: break; + case SpvOpGroupFAdd: break; + case SpvOpGroupFMin: break; + case SpvOpGroupUMin: break; + case SpvOpGroupSMin: break; + case SpvOpGroupFMax: break; + case SpvOpGroupUMax: break; + case SpvOpGroupSMax: break; + case SpvOpEnqueueMarker: break; + case SpvOpEnqueueKernel: break; + case SpvOpGetKernelNDrangeSubGroupCount: break; + case SpvOpGetKernelNDrangeMaxSubGroupSize: break; + case SpvOpGetKernelWorkGroupSize: break; + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: break; + case SpvOpRetainEvent: break; + case SpvOpReleaseEvent: break; + case SpvOpCreateUserEvent: break; + case SpvOpIsValidEvent: break; + case SpvOpSetUserEventStatus: break; + case SpvOpCaptureEventProfilingInfo: break; + case SpvOpGetDefaultQueue: break; + case SpvOpBuildNDRange: break; + case SpvOpReadPipe: break; + case SpvOpWritePipe: break; + case SpvOpReservedReadPipe: break; + case SpvOpReservedWritePipe: break; + case SpvOpReserveReadPipePackets: break; + case SpvOpReserveWritePipePackets: break; + case SpvOpCommitReadPipe: break; + case SpvOpCommitWritePipe: break; + case SpvOpIsValidReserveId: break; + case SpvOpGetNumPipePackets: break; + case SpvOpGetMaxPipePackets: break; + case SpvOpGroupReserveReadPipePackets: break; + case SpvOpGroupReserveWritePipePackets: break; + case SpvOpGroupCommitReadPipe: break; + case SpvOpGroupCommitWritePipe: break; } } return SPV_SUCCESS; } - +// clang-format on } diff --git a/source/validate_types.cpp b/source/validate_types.cpp index 4130450c1..363121416 100644 --- a/source/validate_types.cpp +++ b/source/validate_types.cpp @@ -203,7 +203,15 @@ bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) { // clang-format on return out; } -} + +// NOTE: We are using vector to map the ID of the capability to its +// availability. this variable needs to be the maximum id plus one to cover the +// entire range of the capability. Currently capability 26 is missing so the +// count is actually 54 +// The maximum ID of capabilities plus one +static const size_t kCapabilitiesCount = 56; + +} // anonymous namespace namespace libspirv { @@ -215,7 +223,8 @@ ValidationState_t::ValidationState_t(spv_diagnostic* diagnostic, validation_flags_(options), operand_names_{}, current_layout_section_(kLayoutCapabilities), - module_functions_(*this) {} + module_functions_(*this), + module_capabilities_(kCapabilitiesCount, false) {} spv_result_t ValidationState_t::forwardDeclareId(uint32_t id) { unresolved_forward_ids_.insert(id); @@ -295,6 +304,85 @@ bool ValidationState_t::in_block() const { return module_functions_.in_block(); } +// Returns the dependant capability of the input capibility. If the capability +// does not have a capability then the same capability is returned. +// clang-format off +SpvCapability +getDependantCapability(SpvCapability cap) { + SpvCapability out; + switch(cap) { + case SpvCapabilityShader: out = SpvCapabilityMatrix; break; + case SpvCapabilityGeometry: out = SpvCapabilityShader; break; + case SpvCapabilityTessellation: out = SpvCapabilityShader; break; + case SpvCapabilityVector16: out = SpvCapabilityKernel; break; + case SpvCapabilityFloat16Buffer: out = SpvCapabilityKernel; break; + case SpvCapabilityFloat16: out = SpvCapabilityFloat16Buffer; break; + case SpvCapabilityInt64Atomics: out = SpvCapabilityInt64; break; + case SpvCapabilityImageBasic: out = SpvCapabilityKernel; break; + case SpvCapabilityImageReadWrite: out = SpvCapabilityImageBasic; break; + case SpvCapabilityImageMipmap: out = SpvCapabilityImageBasic; break; + //case SpvCapabilityImageSRGBWrite: break; + case SpvCapabilityPipes: out = SpvCapabilityKernel; break; + case SpvCapabilityDeviceEnqueue: out = SpvCapabilityKernel; break; + case SpvCapabilityLiteralSampler: out = SpvCapabilityKernel; break; + case SpvCapabilityAtomicStorage: out = SpvCapabilityShader; break; + case SpvCapabilityTessellationPointSize: out = SpvCapabilityTessellation; break; + case SpvCapabilityGeometryPointSize: out = SpvCapabilityGeometry; break; + case SpvCapabilityImageGatherExtended: out = SpvCapabilityShader; break; + case SpvCapabilityStorageImageMultisample: out = SpvCapabilityShader; break; + case SpvCapabilityUniformBufferArrayDynamicIndexing: out = SpvCapabilityShader; break; + case SpvCapabilitySampledImageArrayDynamicIndexing: out = SpvCapabilityShader; break; + case SpvCapabilityStorageBufferArrayDynamicIndexing: out = SpvCapabilityShader; break; + case SpvCapabilityStorageImageArrayDynamicIndexing: out = SpvCapabilityShader; break; + case SpvCapabilityClipDistance: out = SpvCapabilityShader; break; + case SpvCapabilityCullDistance: out = SpvCapabilityShader; break; + case SpvCapabilityImageCubeArray: out = SpvCapabilitySampledCubeArray; break; + case SpvCapabilitySampleRateShading: out = SpvCapabilityShader; break; + case SpvCapabilityImageRect: out = SpvCapabilitySampledRect; break; + case SpvCapabilitySampledRect: out = SpvCapabilityShader; break; + case SpvCapabilityGenericPointer: out = SpvCapabilityAddresses; break; + case SpvCapabilityInt8: out = SpvCapabilityKernel; break; + case SpvCapabilityInputAttachment: out = SpvCapabilityShader; break; + case SpvCapabilitySparseResidency: out = SpvCapabilityShader; break; + case SpvCapabilityMinLod: out = SpvCapabilityShader; break; + case SpvCapabilitySampled1D: out = SpvCapabilityShader; break; + case SpvCapabilityImage1D: out = SpvCapabilitySampled1D; break; + case SpvCapabilitySampledCubeArray: out = SpvCapabilityShader; break; + case SpvCapabilitySampledBuffer: out = SpvCapabilityShader; break; + case SpvCapabilityImageBuffer: out = SpvCapabilitySampledBuffer; break; + case SpvCapabilityImageMSArray: out = SpvCapabilityShader; break; + case SpvCapabilityStorageImageExtendedFormats: out = SpvCapabilityShader; break; + case SpvCapabilityImageQuery: out = SpvCapabilityShader; break; + case SpvCapabilityDerivativeControl: out = SpvCapabilityShader; break; + case SpvCapabilityInterpolationFunction: out = SpvCapabilityShader; break; + case SpvCapabilityTransformFeedback: out = SpvCapabilityShader; break; + case SpvCapabilityGeometryStreams: out = SpvCapabilityGeometry; break; + case SpvCapabilityStorageImageReadWithoutFormat: out = SpvCapabilityShader; break; + case SpvCapabilityStorageImageWriteWithoutFormat: out = SpvCapabilityShader; break; + default: + out = cap; + break; + } + return out; +} +// clang-format on + +void ValidationState_t::registerCapability(SpvCapability cap) { + SpvCapability capability = cap; + // Set dependant capabilities + do { + module_capabilities_[capability] = true; + capability = getDependantCapability(capability); + } while (getDependantCapability(capability) != capability); + + // Set Base capability + module_capabilities_[capability] = true; +} + +bool ValidationState_t::hasCapability(SpvCapability cap) { + return module_capabilities_[cap]; +} + Functions::Functions(ValidationState_t& module) : module_(module), in_function_(false), in_block_(false) {} @@ -359,8 +447,7 @@ spv_result_t Functions::RegisterBlock(uint32_t id) { spv_result_t Functions::RegisterFunctionEnd() { assert(in_function_ == true && "Function end can only be called in functions"); - assert(in_block_ == false && - "Function end cannot be called inside a block"); + assert(in_block_ == false && "Function end cannot be called inside a block"); in_function_ = false; return SPV_SUCCESS; } @@ -374,7 +461,5 @@ spv_result_t Functions::RegisterBlockEnd() { return SPV_SUCCESS; } -size_t Functions::get_block_count() const { - return block_ids_.back().size(); -} +size_t Functions::get_block_count() const { return block_ids_.back().size(); } } diff --git a/test/Validate.Capability.cpp b/test/Validate.Capability.cpp new file mode 100644 index 000000000..964a8eea5 --- /dev/null +++ b/test/Validate.Capability.cpp @@ -0,0 +1,579 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +// Validation tests for Logical Layout + +#include +#include "UnitSPIRV.h" +#include "ValidateFixtures.h" + +#include +#include +#include +#include + +using std::function; +using std::ostream; +using std::ostream_iterator; +using std::pair; +using std::make_pair; +using std::stringstream; +using std::string; +using std::tie; +using std::tuple; +using std::vector; + +using ::testing::HasSubstr; + +using ValidateCapability = + spvtest::ValidateBase>>, + SPV_VALIDATE_INSTRUCTION_BIT>; + +TEST_F(ValidateCapability, Default) { + const char str[] = R"( + OpCapability Kernel + OpCapability Matrix + OpMemoryModel Logical OpenCL +%intt = OpTypeInt 32 1 +%vec3 = OpTypeVector %intt 3 +%mat33 = OpTypeMatrix %vec3 3 +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// clang-format off +const vector kAllCapabilities = + { + "", + "Matrix", + "Shader", + "Geometry", + "Tessellation", + "Addresses", + "Linkage", + "Kernel", + "Vector16", + "Float16Buffer", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "ImageBasic", + "ImageReadWrite", + "ImageMipmap", + "Pipes", + "Groups", + "DeviceEnqueue", + "LiteralSampler", + "AtomicStorage", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "ImageRect", + "SampledRect", + "GenericPointer", + "Int8", + "InputAttachment", + "SparseResidency", + "MinLod", + "Sampled1D", + "Image1D", + "SampledCubeArray", + "SampledBuffer", + "ImageBuffer", + "ImageMSArray", + "StorageImageExtendedFormats", + "ImageQuery", + "DerivativeControl", + "InterpolationFunction", + "TransformFeedback", + "GeometryStreams", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat"}; + +const vector kMatrixDependencies = { + "Matrix", + "Shader", + "Geometry", + "Tessellation", + "AtomicStorage", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "ImageRect", + "SampledRect", + "InputAttachment", + "SparseResidency", + "MinLod", + "Sampled1D", + "Image1D", + "SampledCubeArray", + "SampledBuffer", + "ImageMSArray", + "ImageBuffer", + "StorageImageExtendedFormats", + "ImageQuery", + "DerivativeControl", + "InterpolationFunction", + "TransformFeedback", + "GeometryStreams", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", +}; + +const vector kShaderDependencies = { + "Shader", + "Geometry", + "Tessellation", + "AtomicStorage", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "ImageRect", + "SampledRect", + "InputAttachment", + "SparseResidency", + "MinLod", + "Sampled1D", + "Image1D", + "SampledCubeArray", + "SampledBuffer", + "ImageMSArray", + "ImageBuffer", + "StorageImageExtendedFormats", + "ImageQuery", + "DerivativeControl", + "InterpolationFunction", + "TransformFeedback", + "GeometryStreams", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", +}; + +const vector kTessellationDependencies = { + "Tessellation", + "TessellationPointSize", +}; + +const vector kGeometryDependencies = { + "Geometry", + "GeometryPointSize", + "GeometryStreams" +}; + +const vector kGeometryTessellationDependencies = { + "Tessellation", + "TessellationPointSize", + "Geometry", + "GeometryPointSize", + "GeometryStreams" +}; + +const vector kKernelDependencies = { + "Kernel", + "Vector16", + "Float16", + "Float16Buffer", + "ImageBasic", + "ImageReadWrite", + "ImageMipmap", + "Pipes", + "DeviceEnqueue", + "LiteralSampler", + "Int8" +}; + +const vector kAddressesDependencies = { + "Addresses", + "GenericPointer" +}; + +const vector kSampled1DDependencies = { + "Sampled1D", + "Image1D" +}; + +const vector kSampledRectDependencies = { + "SampledRect", + "ImageRect", +}; + +const vector kSampledBufferDependencies = { + "SampledBuffer", + "ImageBuffer", +}; + +INSTANTIATE_TEST_CASE_P(ExecutionModel, + ValidateCapability, + ::testing::Combine( + testing::ValuesIn(kAllCapabilities), + testing::Values( +make_pair("OpEntryPoint Vertex %func \"shader\" %var1 %var2\n", kShaderDependencies), +make_pair("OpEntryPoint TessellationControl %func \"shader\" %var1 %var2\n", kTessellationDependencies), +make_pair("OpEntryPoint TessellationEvaluation %func \"shader\" %var1 %var2\n", kTessellationDependencies), +make_pair("OpEntryPoint Geometry %func \"shader\" %var1 %var2\n", kGeometryDependencies), +make_pair("OpEntryPoint Fragment %func \"shader\" %var1 %var2\n", kShaderDependencies), +make_pair("OpEntryPoint GLCompute %func \"shader\" %var1 %var2\n", kShaderDependencies), +make_pair("OpEntryPoint Kernel %func \"shader\" %var1 %var2\n", kKernelDependencies) + ))); + +INSTANTIATE_TEST_CASE_P(AddressingAndMemoryModel, + ValidateCapability, + ::testing::Combine( + testing::ValuesIn(kAllCapabilities), + testing::Values( +make_pair(" OpCapability Shader" + " OpMemoryModel Logical Simple", kAllCapabilities), +make_pair(" OpCapability Shader" + " OpMemoryModel Logical GLSL450", kAllCapabilities), +make_pair(" OpCapability Kernel" + " OpMemoryModel Logical OpenCL", kAllCapabilities), +make_pair(" OpCapability Shader" + " OpMemoryModel Physical32 Simple", kAddressesDependencies), +make_pair(" OpCapability Shader" + " OpMemoryModel Physical32 GLSL450", kAddressesDependencies), +make_pair(" OpCapability Kernel" + " OpMemoryModel Physical32 OpenCL", kAddressesDependencies), +make_pair(" OpCapability Shader" + " OpMemoryModel Physical64 Simple", kAddressesDependencies), +make_pair(" OpCapability Shader" + " OpMemoryModel Physical64 GLSL450", kAddressesDependencies), +make_pair(" OpCapability Kernel" + " OpMemoryModel Physical64 OpenCL", kAddressesDependencies) + ))); + +INSTANTIATE_TEST_CASE_P(ExecutionMode, + ValidateCapability, + ::testing::Combine( + testing::ValuesIn(kAllCapabilities), + testing::Values( +make_pair("OpExecutionMode %func Invocations 42", kGeometryDependencies), +make_pair("OpExecutionMode %func SpacingEqual", kTessellationDependencies), +make_pair("OpExecutionMode %func SpacingFractionalEven", kTessellationDependencies), +make_pair("OpExecutionMode %func SpacingFractionalOdd", kTessellationDependencies), +make_pair("OpExecutionMode %func VertexOrderCw", kTessellationDependencies), +make_pair("OpExecutionMode %func VertexOrderCcw", kTessellationDependencies), +make_pair("OpExecutionMode %func PixelCenterInteger", kShaderDependencies), +make_pair("OpExecutionMode %func OriginUpperLeft", kShaderDependencies), +make_pair("OpExecutionMode %func OriginLowerLeft", kShaderDependencies), +make_pair("OpExecutionMode %func EarlyFragmentTests", kShaderDependencies), +make_pair("OpExecutionMode %func PointMode", kTessellationDependencies), +make_pair("OpExecutionMode %func Xfb", vector{"TransformFeedback"}), +make_pair("OpExecutionMode %func DepthReplacing", kShaderDependencies), +make_pair("OpExecutionMode %func DepthGreater", kShaderDependencies), +make_pair("OpExecutionMode %func DepthLess", kShaderDependencies), +make_pair("OpExecutionMode %func DepthUnchanged", kShaderDependencies), +make_pair("OpExecutionMode %func LocalSize 42 42 42", kAllCapabilities), +make_pair("OpExecutionMode %func LocalSizeHint 42 42 42", kKernelDependencies), +make_pair("OpExecutionMode %func InputPoints", kGeometryDependencies), +make_pair("OpExecutionMode %func InputLines", kGeometryDependencies), +make_pair("OpExecutionMode %func InputLinesAdjacency", kGeometryDependencies), +make_pair("OpExecutionMode %func Triangles", kGeometryTessellationDependencies), +make_pair("OpExecutionMode %func InputTrianglesAdjacency", kGeometryDependencies), +make_pair("OpExecutionMode %func Quads", kTessellationDependencies), +make_pair("OpExecutionMode %func Isolines", kTessellationDependencies), +make_pair("OpExecutionMode %func OutputVertices 42", kGeometryTessellationDependencies), +make_pair("OpExecutionMode %func OutputPoints", kGeometryDependencies), +make_pair("OpExecutionMode %func OutputLineStrip", kGeometryDependencies), +make_pair("OpExecutionMode %func OutputTriangleStrip", kGeometryDependencies), +make_pair("OpExecutionMode %func VecTypeHint 2", kKernelDependencies), +make_pair("OpExecutionMode %func ContractionOff", kKernelDependencies) +))); + +INSTANTIATE_TEST_CASE_P(StorageClass, + ValidateCapability, + ::testing::Combine( +testing::ValuesIn(kAllCapabilities), +testing::Values( +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer UniformConstant %intt\n" + " %var = OpVariable %ptrt UniformConstant\n", kAllCapabilities), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Input %intt" + " %var = OpVariable %ptrt Input\n", kShaderDependencies), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Uniform %intt\n" + " %var = OpVariable %ptrt Uniform\n", kShaderDependencies), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Output %intt\n" + " %var = OpVariable %ptrt Output\n", kShaderDependencies), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Workgroup %intt\n" + " %var = OpVariable %ptrt Workgroup\n", kAllCapabilities), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer CrossWorkgroup %intt\n" + " %var = OpVariable %ptrt CrossWorkgroup\n", kAllCapabilities), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Private %intt\n" + " %var = OpVariable %ptrt Private\n", kShaderDependencies), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Generic %intt\n" + " %var = OpVariable %ptrt Generic\n", kKernelDependencies), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer PushConstant %intt\n" + " %var = OpVariable %ptrt PushConstant\n", kShaderDependencies), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer AtomicCounter %intt\n" + " %var = OpVariable %ptrt AtomicCounter\n", vector{"AtomicStorage"}), +make_pair(" %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Image %intt\n" + " %var = OpVariable %ptrt Image\n", kAllCapabilities) + ))); + +INSTANTIATE_TEST_CASE_P(Dim, + ValidateCapability, + ::testing::Combine( + testing::ValuesIn(kAllCapabilities), + testing::Values( +make_pair(" OpCapability ImageBasic" + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt 1D 0 0 0 0 Unknown", kSampled1DDependencies), +make_pair(" OpCapability ImageBasic" + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt 2D 0 0 0 0 Unknown", kAllCapabilities), +make_pair(" OpCapability ImageBasic" + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt 3D 0 0 0 0 Unknown", kAllCapabilities), +make_pair(" OpCapability ImageBasic" + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt Cube 0 0 0 0 Unknown", kShaderDependencies), +make_pair(" OpCapability ImageBasic" + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt Rect 0 0 0 0 Unknown", kSampledRectDependencies), +make_pair(" OpCapability ImageBasic" + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt Buffer 0 0 0 0 Unknown", kSampledBufferDependencies), +make_pair(" OpCapability ImageBasic" + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt SubpassData 0 0 0 2 Unknown", vector{"InputAttachment"}) + ))); + +// NOTE: All Sampler Address Modes require kernel capabilities but the +// OpConstantSampler requires LiteralSampler which depends on Kernel +INSTANTIATE_TEST_CASE_P(SamplerAddressingMode, + ValidateCapability, + ::testing::Combine( + testing::ValuesIn(kAllCapabilities), + testing::Values( +make_pair(" %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert None 1 Nearest", vector{"LiteralSampler"}), +make_pair(" %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert ClampToEdge 1 Nearest", vector{"LiteralSampler"}), +make_pair(" %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert Clamp 1 Nearest", vector{"LiteralSampler"}), +make_pair(" %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert Repeat 1 Nearest", vector{"LiteralSampler"}), +make_pair(" %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert RepeatMirrored 1 Nearest", vector{"LiteralSampler"}) + ))); + +//TODO(umar): Sampler Filter Mode +//TODO(umar): Image Format +//TODO(umar): Image Channel Order +//TODO(umar): Image Channel Data Type +//TODO(umar): Image Operands +//TODO(umar): FP Fast Math Mode +//TODO(umar): FP Rounding Mode +//TODO(umar): Linkage Type +//TODO(umar): Access Qualifier +//TODO(umar): Function Parameter Attribute + +INSTANTIATE_TEST_CASE_P(Decoration, + ValidateCapability, + ::testing::Combine( + testing::ValuesIn(kAllCapabilities), + testing::Values( +make_pair("OpDecorate %intt RelaxedPrecision\n", kShaderDependencies), +make_pair("OpDecorate %intt SpecId 1\n", kShaderDependencies), +make_pair("OpDecorate %intt Block\n", kShaderDependencies), +make_pair("OpDecorate %intt BufferBlock\n", kShaderDependencies), +make_pair("OpDecorate %intt RowMajor\n", kMatrixDependencies), +make_pair("OpDecorate %intt ColMajor\n", kMatrixDependencies), +make_pair("OpDecorate %intt ArrayStride 1\n", kShaderDependencies), +make_pair("OpDecorate %intt MatrixStride 1\n", kShaderDependencies), +make_pair("OpDecorate %intt GLSLShared\n", kShaderDependencies), +make_pair("OpDecorate %intt GLSLPacked\n", kShaderDependencies), +make_pair("OpDecorate %intt CPacked\n", kKernelDependencies), +make_pair("OpDecorate %intt NoPerspective\n", kShaderDependencies), +make_pair("OpDecorate %intt Flat\n", kShaderDependencies), +make_pair("OpDecorate %intt Patch\n", kTessellationDependencies), +make_pair("OpDecorate %intt Centroid\n", kShaderDependencies), +make_pair("OpDecorate %intt Sample\n", kShaderDependencies), +make_pair("OpDecorate %intt Invariant\n", kShaderDependencies), +make_pair("OpDecorate %intt Restrict\n", kAllCapabilities), +make_pair("OpDecorate %intt Aliased\n", kAllCapabilities), +make_pair("OpDecorate %intt Volatile\n", kAllCapabilities), +make_pair("OpDecorate %intt Constant\n", kKernelDependencies), +make_pair("OpDecorate %intt Coherent\n", kAllCapabilities), +make_pair("OpDecorate %intt NonWritable\n", kAllCapabilities), +make_pair("OpDecorate %intt NonReadable\n", kAllCapabilities), +make_pair("OpDecorate %intt Uniform\n", kShaderDependencies), +make_pair("OpDecorate %intt SaturatedConversion\n", kKernelDependencies), +make_pair("OpDecorate %intt Stream 0\n", vector{"GeometryStreams"}), +make_pair("OpDecorate %intt Location 0\n", kShaderDependencies), +make_pair("OpDecorate %intt Component 0\n", kShaderDependencies), +make_pair("OpDecorate %intt Index 0\n", kShaderDependencies), +make_pair("OpDecorate %intt Binding 0\n", kShaderDependencies), +make_pair("OpDecorate %intt DescriptorSet 0\n", kShaderDependencies), +make_pair("OpDecorate %intt Offset 0\n", kAllCapabilities), +make_pair("OpDecorate %intt XfbBuffer 0\n", vector{"TransformFeedback"}), +make_pair("OpDecorate %intt XfbStride 0\n", vector{"TransformFeedback"}), +make_pair("OpDecorate %intt FuncParamAttr Zext\n", kKernelDependencies), +make_pair("OpDecorate %intt FPRoundingMode RTE\n", kKernelDependencies), +make_pair("OpDecorate %intt FPFastMathMode Fast\n", kKernelDependencies), +make_pair("OpDecorate %intt LinkageAttributes \"other\" Import\n", vector{"Linkage"}), +make_pair("OpDecorate %intt NoContraction\n", kShaderDependencies), +make_pair("OpDecorate %intt InputAttachmentIndex 0\n", vector{"InputAttachment"}), +make_pair("OpDecorate %intt Alignment 4\n", kKernelDependencies) + ))); + + +INSTANTIATE_TEST_CASE_P(BuiltIn, + ValidateCapability, + ::testing::Combine( + testing::ValuesIn(kAllCapabilities), + testing::Values( +make_pair("OpDecorate %intt BuiltIn Position\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn PointSize\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn ClipDistance\n", vector{"ClipDistance"}), +make_pair("OpDecorate %intt BuiltIn CullDistance\n", vector{"CullDistance"}), +make_pair("OpDecorate %intt BuiltIn VertexId\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn InstanceId\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn PrimitiveId\n", kGeometryTessellationDependencies), +make_pair("OpDecorate %intt BuiltIn InvocationId\n", kGeometryTessellationDependencies), +make_pair("OpDecorate %intt BuiltIn Layer\n", kGeometryDependencies), +//make_pair("OpDecorate %intt BuiltIn ViewPortIndex\n", vector{"MultiViewport"}), +make_pair("OpDecorate %intt BuiltIn TessLevelOuter\n", kTessellationDependencies), +make_pair("OpDecorate %intt BuiltIn TessLevelInner\n", kTessellationDependencies), +make_pair("OpDecorate %intt BuiltIn TessCoord\n", kTessellationDependencies), +make_pair("OpDecorate %intt BuiltIn PatchVertices\n", kTessellationDependencies), +make_pair("OpDecorate %intt BuiltIn FragCoord\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn PointCoord\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn FrontFacing\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn SampleId\n", vector{"SampleRateShading"}), +make_pair("OpDecorate %intt BuiltIn SamplePosition\n", vector{"SampleRateShading"}), +make_pair("OpDecorate %intt BuiltIn SampleMask\n", vector{"SampleRateShading"}), +make_pair("OpDecorate %intt BuiltIn FragDepth\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn HelperInvocation\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn NumWorkgroups\n", kAllCapabilities), +make_pair("OpDecorate %intt BuiltIn WorkgroupSize\n", kAllCapabilities), +make_pair("OpDecorate %intt BuiltIn WorkgroupId\n", kAllCapabilities), +make_pair("OpDecorate %intt BuiltIn LocalInvocationId\n", kAllCapabilities), +make_pair("OpDecorate %intt BuiltIn GlobalInvocationId\n", kAllCapabilities), +make_pair("OpDecorate %intt BuiltIn LocalInvocationIndex", kAllCapabilities), +make_pair("OpDecorate %intt BuiltIn WorkDim\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn GlobalSize\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn EnqueuedWorkgroupSize\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn GlobalOffset\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn GlobalLinearId\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn SubgroupSize\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn SubgroupMaxSize\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn NumSubgroups\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn NumEnqueuedSubgroups\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn SubgroupId\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn SubgroupLocalInvocationId\n", kKernelDependencies), +make_pair("OpDecorate %intt BuiltIn VertexIndex\n", kShaderDependencies), +make_pair("OpDecorate %intt BuiltIn InstanceIndex\n", kShaderDependencies) + ))); + +// TODO(umar): Selection Control +// TODO(umar): Loop Control +// TODO(umar): Function Control +// TODO(umar): Memory Semantics +// TODO(umar): Memory Access +// TODO(umar): Scope +// TODO(umar): Group Operation +// TODO(umar): Kernel Enqueue Flags +// TODO(umar): Kernel Profiling Flags + +INSTANTIATE_TEST_CASE_P(MatrixOp, + ValidateCapability, + ::testing::Combine( + testing::ValuesIn(kAllCapabilities), + testing::Values( +make_pair( + "%intt = OpTypeInt 32 1\n" + "%vec3 = OpTypeVector %intt 3\n" + "%mat33 = OpTypeMatrix %vec3 3\n", kMatrixDependencies)))); +// clang-format on + +// TODO(umar): Instruction capability checks + +TEST_P(ValidateCapability, Capability) { + string capability; + pair> operation; + std::tie(capability, operation) = GetParam(); + stringstream ss; + if (capability.empty() == false) { + ss << "OpCapability " + capability + " "; + } + + ss << operation.first; + + spv_result_t res = SPV_ERROR_INTERNAL; + auto& valid_capabilities = operation.second; + + auto it = + find(begin(valid_capabilities), end(valid_capabilities), capability); + if (it != end(valid_capabilities)) { + res = SPV_SUCCESS; + } else { + res = SPV_ERROR_INVALID_CAPABILITY; + } + + CompileSuccessfully(ss.str()); + ASSERT_EQ(res, ValidateInstructions()); +} diff --git a/test/ValidateFixtures.cpp b/test/ValidateFixtures.cpp index 45735a21d..187994fe9 100644 --- a/test/ValidateFixtures.cpp +++ b/test/ValidateFixtures.cpp @@ -91,4 +91,6 @@ template class spvtest::ValidateBase< std::tuple, std::function>>, SPV_VALIDATE_LAYOUT_BIT>; + + template class spvtest::ValidateBase > >, SPV_VALIDATE_INSTRUCTION_BIT>; }