|  | // Copyright (c) 2015-2016 The Khronos Group Inc. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | // Assembler tests for instructions in the "Mode-Setting" section of the | 
|  | // SPIR-V spec. | 
|  |  | 
|  | #include <string> | 
|  | #include <tuple> | 
|  | #include <vector> | 
|  |  | 
|  | #include "gmock/gmock.h" | 
|  | #include "source/util/string_utils.h" | 
|  | #include "test/test_fixture.h" | 
|  | #include "test/unit_spirv.h" | 
|  |  | 
|  | namespace spvtools { | 
|  | namespace { | 
|  |  | 
|  | using spvtest::EnumCase; | 
|  | using spvtest::MakeInstruction; | 
|  | using utils::MakeVector; | 
|  | using ::testing::Combine; | 
|  | using ::testing::Eq; | 
|  | using ::testing::TestWithParam; | 
|  | using ::testing::Values; | 
|  | using ::testing::ValuesIn; | 
|  |  | 
|  | // Test OpMemoryModel | 
|  |  | 
|  | // An example case for OpMemoryModel | 
|  | struct MemoryModelCase { | 
|  | uint32_t get_addressing_value() const { | 
|  | return static_cast<uint32_t>(addressing_value); | 
|  | } | 
|  | uint32_t get_memory_value() const { | 
|  | return static_cast<uint32_t>(memory_value); | 
|  | } | 
|  | SpvAddressingModel addressing_value; | 
|  | std::string addressing_name; | 
|  | SpvMemoryModel memory_value; | 
|  | std::string memory_name; | 
|  | }; | 
|  |  | 
|  | using OpMemoryModelTest = | 
|  | spvtest::TextToBinaryTestBase<TestWithParam<MemoryModelCase>>; | 
|  |  | 
|  | TEST_P(OpMemoryModelTest, AnyMemoryModelCase) { | 
|  | const std::string input = "OpMemoryModel " + GetParam().addressing_name + | 
|  | " " + GetParam().memory_name; | 
|  | EXPECT_THAT( | 
|  | CompiledInstructions(input), | 
|  | Eq(MakeInstruction(SpvOpMemoryModel, {GetParam().get_addressing_value(), | 
|  | GetParam().get_memory_value()}))); | 
|  | } | 
|  |  | 
|  | #define CASE(ADDRESSING, MEMORY)                                         \ | 
|  | {                                                                      \ | 
|  | SpvAddressingModel##ADDRESSING, #ADDRESSING, SpvMemoryModel##MEMORY, \ | 
|  | #MEMORY                                                          \ | 
|  | } | 
|  | // clang-format off | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryMemoryModel, OpMemoryModelTest, | 
|  | ValuesIn(std::vector<MemoryModelCase>{ | 
|  | // These cases exercise each addressing model, and | 
|  | // each memory model, but not necessarily in | 
|  | // combination. | 
|  | CASE(Logical,Simple), | 
|  | CASE(Logical,GLSL450), | 
|  | CASE(Physical32,OpenCL), | 
|  | CASE(Physical64,OpenCL), | 
|  | })); | 
|  | #undef CASE | 
|  | // clang-format on | 
|  |  | 
|  | TEST_F(OpMemoryModelTest, WrongModel) { | 
|  | EXPECT_THAT(CompileFailure("OpMemoryModel xxyyzz Simple"), | 
|  | Eq("Invalid addressing model 'xxyyzz'.")); | 
|  | EXPECT_THAT(CompileFailure("OpMemoryModel Logical xxyyzz"), | 
|  | Eq("Invalid memory model 'xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | // Test OpEntryPoint | 
|  |  | 
|  | // An example case for OpEntryPoint | 
|  | struct EntryPointCase { | 
|  | uint32_t get_execution_value() const { | 
|  | return static_cast<uint32_t>(execution_value); | 
|  | } | 
|  | SpvExecutionModel execution_value; | 
|  | std::string execution_name; | 
|  | std::string entry_point_name; | 
|  | }; | 
|  |  | 
|  | using OpEntryPointTest = | 
|  | spvtest::TextToBinaryTestBase<TestWithParam<EntryPointCase>>; | 
|  |  | 
|  | TEST_P(OpEntryPointTest, AnyEntryPointCase) { | 
|  | // TODO(dneto): utf-8, escaping, quoting cases for entry point name. | 
|  | const std::string input = "OpEntryPoint " + GetParam().execution_name + | 
|  | " %1 \"" + GetParam().entry_point_name + "\""; | 
|  | EXPECT_THAT( | 
|  | CompiledInstructions(input), | 
|  | Eq(MakeInstruction(SpvOpEntryPoint, {GetParam().get_execution_value(), 1}, | 
|  | MakeVector(GetParam().entry_point_name)))); | 
|  | } | 
|  |  | 
|  | // clang-format off | 
|  | #define CASE(NAME) SpvExecutionModel##NAME, #NAME | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryEntryPoint, OpEntryPointTest, | 
|  | ValuesIn(std::vector<EntryPointCase>{ | 
|  | { CASE(Vertex), "" }, | 
|  | { CASE(TessellationControl), "my tess" }, | 
|  | { CASE(TessellationEvaluation), "really fancy" }, | 
|  | { CASE(Geometry), "Euclid" }, | 
|  | { CASE(Fragment), "FAT32" }, | 
|  | { CASE(GLCompute), "cubic" }, | 
|  | { CASE(Kernel), "Sanders" }, | 
|  | })); | 
|  | #undef CASE | 
|  | // clang-format on | 
|  |  | 
|  | TEST_F(OpEntryPointTest, WrongModel) { | 
|  | EXPECT_THAT(CompileFailure("OpEntryPoint xxyyzz %1 \"fun\""), | 
|  | Eq("Invalid execution model 'xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | // Test OpExecutionMode | 
|  | using OpExecutionModeTest = spvtest::TextToBinaryTestBase< | 
|  | TestWithParam<std::tuple<spv_target_env, EnumCase<SpvExecutionMode>>>>; | 
|  |  | 
|  | TEST_P(OpExecutionModeTest, AnyExecutionMode) { | 
|  | // This string should assemble, but should not validate. | 
|  | std::stringstream input; | 
|  | input << "OpExecutionMode %1 " << std::get<1>(GetParam()).name(); | 
|  | for (auto operand : std::get<1>(GetParam()).operands()) | 
|  | input << " " << operand; | 
|  | EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())), | 
|  | Eq(MakeInstruction(SpvOpExecutionMode, | 
|  | {1, std::get<1>(GetParam()).value()}, | 
|  | std::get<1>(GetParam()).operands()))); | 
|  | } | 
|  |  | 
|  | #define CASE(NAME) SpvExecutionMode##NAME, #NAME | 
|  | INSTANTIATE_TEST_SUITE_P( | 
|  | TextToBinaryExecutionMode, OpExecutionModeTest, | 
|  | Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), | 
|  | ValuesIn(std::vector<EnumCase<SpvExecutionMode>>{ | 
|  | // The operand literal values are arbitrarily chosen, | 
|  | // but there are the right number of them. | 
|  | {CASE(Invocations), {101}}, | 
|  | {CASE(SpacingEqual), {}}, | 
|  | {CASE(SpacingFractionalEven), {}}, | 
|  | {CASE(SpacingFractionalOdd), {}}, | 
|  | {CASE(VertexOrderCw), {}}, | 
|  | {CASE(VertexOrderCcw), {}}, | 
|  | {CASE(PixelCenterInteger), {}}, | 
|  | {CASE(OriginUpperLeft), {}}, | 
|  | {CASE(OriginLowerLeft), {}}, | 
|  | {CASE(EarlyFragmentTests), {}}, | 
|  | {CASE(PointMode), {}}, | 
|  | {CASE(Xfb), {}}, | 
|  | {CASE(DepthReplacing), {}}, | 
|  | {CASE(DepthGreater), {}}, | 
|  | {CASE(DepthLess), {}}, | 
|  | {CASE(DepthUnchanged), {}}, | 
|  | {CASE(LocalSize), {64, 1, 2}}, | 
|  | {CASE(LocalSizeHint), {8, 2, 4}}, | 
|  | {CASE(InputPoints), {}}, | 
|  | {CASE(InputLines), {}}, | 
|  | {CASE(InputLinesAdjacency), {}}, | 
|  | {CASE(Triangles), {}}, | 
|  | {CASE(InputTrianglesAdjacency), {}}, | 
|  | {CASE(Quads), {}}, | 
|  | {CASE(Isolines), {}}, | 
|  | {CASE(OutputVertices), {21}}, | 
|  | {CASE(OutputPoints), {}}, | 
|  | {CASE(OutputLineStrip), {}}, | 
|  | {CASE(OutputTriangleStrip), {}}, | 
|  | {CASE(VecTypeHint), {96}}, | 
|  | {CASE(ContractionOff), {}}, | 
|  | }))); | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P( | 
|  | TextToBinaryExecutionModeV11, OpExecutionModeTest, | 
|  | Combine(Values(SPV_ENV_UNIVERSAL_1_1), | 
|  | ValuesIn(std::vector<EnumCase<SpvExecutionMode>>{ | 
|  | {CASE(Initializer)}, | 
|  | {CASE(Finalizer)}, | 
|  | {CASE(SubgroupSize), {12}}, | 
|  | {CASE(SubgroupsPerWorkgroup), {64}}}))); | 
|  | #undef CASE | 
|  |  | 
|  | TEST_F(OpExecutionModeTest, WrongMode) { | 
|  | EXPECT_THAT(CompileFailure("OpExecutionMode %1 xxyyzz"), | 
|  | Eq("Invalid execution mode 'xxyyzz'.")); | 
|  | } | 
|  |  | 
|  | TEST_F(OpExecutionModeTest, TooManyModes) { | 
|  | EXPECT_THAT(CompileFailure("OpExecutionMode %1 Xfb PointMode"), | 
|  | Eq("Expected <opcode> or <result-id> at the beginning of an " | 
|  | "instruction, found 'PointMode'.")); | 
|  | } | 
|  |  | 
|  | // Test OpCapability | 
|  |  | 
|  | using OpCapabilityTest = | 
|  | spvtest::TextToBinaryTestBase<TestWithParam<EnumCase<SpvCapability>>>; | 
|  |  | 
|  | TEST_P(OpCapabilityTest, AnyCapability) { | 
|  | const std::string input = "OpCapability " + GetParam().name(); | 
|  | EXPECT_THAT(CompiledInstructions(input), | 
|  | Eq(MakeInstruction(SpvOpCapability, {GetParam().value()}))); | 
|  | } | 
|  |  | 
|  | // clang-format off | 
|  | #define CASE(NAME) { SpvCapability##NAME, #NAME } | 
|  | INSTANTIATE_TEST_SUITE_P(TextToBinaryCapability, OpCapabilityTest, | 
|  | ValuesIn(std::vector<EnumCase<SpvCapability>>{ | 
|  | CASE(Matrix), | 
|  | CASE(Shader), | 
|  | CASE(Geometry), | 
|  | CASE(Tessellation), | 
|  | CASE(Addresses), | 
|  | CASE(Linkage), | 
|  | CASE(Kernel), | 
|  | CASE(Vector16), | 
|  | CASE(Float16Buffer), | 
|  | CASE(Float16), | 
|  | CASE(Float64), | 
|  | CASE(Int64), | 
|  | CASE(Int64Atomics), | 
|  | CASE(ImageBasic), | 
|  | CASE(ImageReadWrite), | 
|  | CASE(ImageMipmap), | 
|  | // Value 16 intentionally missing | 
|  | CASE(Pipes), | 
|  | CASE(Groups), | 
|  | CASE(DeviceEnqueue), | 
|  | CASE(LiteralSampler), | 
|  | CASE(AtomicStorage), | 
|  | CASE(Int16), | 
|  | CASE(TessellationPointSize), | 
|  | CASE(GeometryPointSize), | 
|  | CASE(ImageGatherExtended), | 
|  | // Value 26 intentionally missing | 
|  | CASE(StorageImageMultisample), | 
|  | CASE(UniformBufferArrayDynamicIndexing), | 
|  | CASE(SampledImageArrayDynamicIndexing), | 
|  | CASE(StorageBufferArrayDynamicIndexing), | 
|  | CASE(StorageImageArrayDynamicIndexing), | 
|  | CASE(ClipDistance), | 
|  | CASE(CullDistance), | 
|  | CASE(ImageCubeArray), | 
|  | CASE(SampleRateShading), | 
|  | CASE(ImageRect), | 
|  | CASE(SampledRect), | 
|  | CASE(GenericPointer), | 
|  | CASE(Int8), | 
|  | CASE(InputAttachment), | 
|  | CASE(SparseResidency), | 
|  | CASE(MinLod), | 
|  | CASE(Sampled1D), | 
|  | CASE(Image1D), | 
|  | CASE(SampledCubeArray), | 
|  | CASE(SampledBuffer), | 
|  | CASE(ImageBuffer), | 
|  | CASE(ImageMSArray), | 
|  | CASE(StorageImageExtendedFormats), | 
|  | CASE(ImageQuery), | 
|  | CASE(DerivativeControl), | 
|  | CASE(InterpolationFunction), | 
|  | CASE(TransformFeedback), | 
|  | })); | 
|  | #undef CASE | 
|  | // clang-format on | 
|  |  | 
|  | using TextToBinaryCapability = spvtest::TextToBinaryTest; | 
|  |  | 
|  | TEST_F(TextToBinaryCapability, BadMissingCapability) { | 
|  | EXPECT_THAT(CompileFailure("OpCapability"), | 
|  | Eq("Expected operand, found end of stream.")); | 
|  | } | 
|  |  | 
|  | TEST_F(TextToBinaryCapability, BadInvalidCapability) { | 
|  | EXPECT_THAT(CompileFailure("OpCapability 123"), | 
|  | Eq("Invalid capability '123'.")); | 
|  | } | 
|  |  | 
|  | // TODO(dneto): OpExecutionMode | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace spvtools |