1// Copyright (c) 2015-2016 The Khronos Group Inc. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// Assembler tests for instructions in the "Mode-Setting" section of the 16// SPIR-V spec. 17 18#include <string> 19#include <tuple> 20#include <vector> 21 22#include "gmock/gmock.h" 23#include "source/util/string_utils.h" 24#include "test/test_fixture.h" 25#include "test/unit_spirv.h" 26 27namespace spvtools { 28namespace { 29 30using spvtest::EnumCase; 31using spvtest::MakeInstruction; 32using utils::MakeVector; 33using ::testing::Combine; 34using ::testing::Eq; 35using ::testing::TestWithParam; 36using ::testing::Values; 37using ::testing::ValuesIn; 38 39// Test OpMemoryModel 40 41// An example case for OpMemoryModel 42struct MemoryModelCase { 43 uint32_t get_addressing_value() const { 44 return static_cast<uint32_t>(addressing_value); 45 } 46 uint32_t get_memory_value() const { 47 return static_cast<uint32_t>(memory_value); 48 } 49 spv::AddressingModel addressing_value; 50 std::string addressing_name; 51 spv::MemoryModel memory_value; 52 std::string memory_name; 53}; 54 55using OpMemoryModelTest = 56 spvtest::TextToBinaryTestBase<TestWithParam<MemoryModelCase>>; 57 58TEST_P(OpMemoryModelTest, AnyMemoryModelCase) { 59 const std::string input = "OpMemoryModel " + GetParam().addressing_name + 60 " " + GetParam().memory_name; 61 EXPECT_THAT(CompiledInstructions(input), 62 Eq(MakeInstruction(spv::Op::OpMemoryModel, 63 {GetParam().get_addressing_value(), 64 GetParam().get_memory_value()}))); 65} 66 67#define CASE(ADDRESSING, MEMORY) \ 68 { \ 69 spv::AddressingModel::ADDRESSING, #ADDRESSING, spv::MemoryModel::MEMORY, \ 70 #MEMORY \ 71 } 72// clang-format off 73INSTANTIATE_TEST_SUITE_P(TextToBinaryMemoryModel, OpMemoryModelTest, 74 ValuesIn(std::vector<MemoryModelCase>{ 75 // These cases exercise each addressing model, and 76 // each memory model, but not necessarily in 77 // combination. 78 CASE(Logical,Simple), 79 CASE(Logical,GLSL450), 80 CASE(Physical32,OpenCL), 81 CASE(Physical64,OpenCL), 82 })); 83#undef CASE 84// clang-format on 85 86TEST_F(OpMemoryModelTest, WrongModel) { 87 EXPECT_THAT(CompileFailure("OpMemoryModel xxyyzz Simple"), 88 Eq("Invalid addressing model 'xxyyzz'.")); 89 EXPECT_THAT(CompileFailure("OpMemoryModel Logical xxyyzz"), 90 Eq("Invalid memory model 'xxyyzz'.")); 91} 92 93// Test OpEntryPoint 94 95// An example case for OpEntryPoint 96struct EntryPointCase { 97 uint32_t get_execution_value() const { 98 return static_cast<uint32_t>(execution_value); 99 } 100 spv::ExecutionModel execution_value; 101 std::string execution_name; 102 std::string entry_point_name; 103}; 104 105using OpEntryPointTest = 106 spvtest::TextToBinaryTestBase<TestWithParam<EntryPointCase>>; 107 108TEST_P(OpEntryPointTest, AnyEntryPointCase) { 109 // TODO(dneto): utf-8, escaping, quoting cases for entry point name. 110 const std::string input = "OpEntryPoint " + GetParam().execution_name + 111 " %1 \"" + GetParam().entry_point_name + "\""; 112 EXPECT_THAT(CompiledInstructions(input), 113 Eq(MakeInstruction(spv::Op::OpEntryPoint, 114 {GetParam().get_execution_value(), 1}, 115 MakeVector(GetParam().entry_point_name)))); 116} 117 118// clang-format off 119#define CASE(NAME) spv::ExecutionModel::NAME, #NAME 120INSTANTIATE_TEST_SUITE_P(TextToBinaryEntryPoint, OpEntryPointTest, 121 ValuesIn(std::vector<EntryPointCase>{ 122 { CASE(Vertex), "" }, 123 { CASE(TessellationControl), "my tess" }, 124 { CASE(TessellationEvaluation), "really fancy" }, 125 { CASE(Geometry), "Euclid" }, 126 { CASE(Fragment), "FAT32" }, 127 { CASE(GLCompute), "cubic" }, 128 { CASE(Kernel), "Sanders" }, 129 })); 130#undef CASE 131// clang-format on 132 133TEST_F(OpEntryPointTest, WrongModel) { 134 EXPECT_THAT(CompileFailure("OpEntryPoint xxyyzz %1 \"fun\""), 135 Eq("Invalid execution model 'xxyyzz'.")); 136} 137 138// Test OpExecutionMode 139using OpExecutionModeTest = spvtest::TextToBinaryTestBase< 140 TestWithParam<std::tuple<spv_target_env, EnumCase<spv::ExecutionMode>>>>; 141 142TEST_P(OpExecutionModeTest, AnyExecutionMode) { 143 // This string should assemble, but should not validate. 144 std::stringstream input; 145 input << "OpExecutionMode %1 " << std::get<1>(GetParam()).name(); 146 for (auto operand : std::get<1>(GetParam()).operands()) 147 input << " " << operand; 148 EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())), 149 Eq(MakeInstruction(spv::Op::OpExecutionMode, 150 {1, std::get<1>(GetParam()).value()}, 151 std::get<1>(GetParam()).operands()))); 152} 153 154#define CASE(NAME) spv::ExecutionMode::NAME, #NAME 155INSTANTIATE_TEST_SUITE_P( 156 TextToBinaryExecutionMode, OpExecutionModeTest, 157 Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), 158 ValuesIn(std::vector<EnumCase<spv::ExecutionMode>>{ 159 // The operand literal values are arbitrarily chosen, 160 // but there are the right number of them. 161 {CASE(Invocations), {101}}, 162 {CASE(SpacingEqual), {}}, 163 {CASE(SpacingFractionalEven), {}}, 164 {CASE(SpacingFractionalOdd), {}}, 165 {CASE(VertexOrderCw), {}}, 166 {CASE(VertexOrderCcw), {}}, 167 {CASE(PixelCenterInteger), {}}, 168 {CASE(OriginUpperLeft), {}}, 169 {CASE(OriginLowerLeft), {}}, 170 {CASE(EarlyFragmentTests), {}}, 171 {CASE(PointMode), {}}, 172 {CASE(Xfb), {}}, 173 {CASE(DepthReplacing), {}}, 174 {CASE(DepthGreater), {}}, 175 {CASE(DepthLess), {}}, 176 {CASE(DepthUnchanged), {}}, 177 {CASE(LocalSize), {64, 1, 2}}, 178 {CASE(LocalSizeHint), {8, 2, 4}}, 179 {CASE(InputPoints), {}}, 180 {CASE(InputLines), {}}, 181 {CASE(InputLinesAdjacency), {}}, 182 {CASE(Triangles), {}}, 183 {CASE(InputTrianglesAdjacency), {}}, 184 {CASE(Quads), {}}, 185 {CASE(Isolines), {}}, 186 {CASE(OutputVertices), {21}}, 187 {CASE(OutputPoints), {}}, 188 {CASE(OutputLineStrip), {}}, 189 {CASE(OutputTriangleStrip), {}}, 190 {CASE(VecTypeHint), {96}}, 191 {CASE(ContractionOff), {}}, 192 {CASE(SubgroupUniformControlFlowKHR), {}}, 193 }))); 194 195INSTANTIATE_TEST_SUITE_P( 196 TextToBinaryExecutionModeV11, OpExecutionModeTest, 197 Combine(Values(SPV_ENV_UNIVERSAL_1_1), 198 ValuesIn(std::vector<EnumCase<spv::ExecutionMode>>{ 199 {CASE(Initializer)}, 200 {CASE(Finalizer)}, 201 {CASE(SubgroupSize), {12}}, 202 {CASE(SubgroupsPerWorkgroup), {64}}}))); 203#undef CASE 204 205TEST_F(OpExecutionModeTest, WrongMode) { 206 EXPECT_THAT(CompileFailure("OpExecutionMode %1 xxyyzz"), 207 Eq("Invalid execution mode 'xxyyzz'.")); 208} 209 210TEST_F(OpExecutionModeTest, TooManyModes) { 211 EXPECT_THAT(CompileFailure("OpExecutionMode %1 Xfb PointMode"), 212 Eq("Expected <opcode> or <result-id> at the beginning of an " 213 "instruction, found 'PointMode'.")); 214} 215 216// Test OpCapability 217 218using OpCapabilityTest = 219 spvtest::TextToBinaryTestBase<TestWithParam<EnumCase<spv::Capability>>>; 220 221TEST_P(OpCapabilityTest, AnyCapability) { 222 const std::string input = "OpCapability " + GetParam().name(); 223 EXPECT_THAT(CompiledInstructions(input), 224 Eq(MakeInstruction(spv::Op::OpCapability, {GetParam().value()}))); 225} 226 227// clang-format off 228#define CASE(NAME) { spv::Capability::NAME, #NAME } 229INSTANTIATE_TEST_SUITE_P(TextToBinaryCapability, OpCapabilityTest, 230 ValuesIn(std::vector<EnumCase<spv::Capability>>{ 231 CASE(Matrix), 232 CASE(Shader), 233 CASE(Geometry), 234 CASE(Tessellation), 235 CASE(Addresses), 236 CASE(Linkage), 237 CASE(Kernel), 238 CASE(Vector16), 239 CASE(Float16Buffer), 240 CASE(Float16), 241 CASE(Float64), 242 CASE(Int64), 243 CASE(Int64Atomics), 244 CASE(ImageBasic), 245 CASE(ImageReadWrite), 246 CASE(ImageMipmap), 247 // Value 16 intentionally missing 248 CASE(Pipes), 249 CASE(Groups), 250 CASE(DeviceEnqueue), 251 CASE(LiteralSampler), 252 CASE(AtomicStorage), 253 CASE(Int16), 254 CASE(TessellationPointSize), 255 CASE(GeometryPointSize), 256 CASE(ImageGatherExtended), 257 // Value 26 intentionally missing 258 CASE(StorageImageMultisample), 259 CASE(UniformBufferArrayDynamicIndexing), 260 CASE(SampledImageArrayDynamicIndexing), 261 CASE(StorageBufferArrayDynamicIndexing), 262 CASE(StorageImageArrayDynamicIndexing), 263 CASE(ClipDistance), 264 CASE(CullDistance), 265 CASE(ImageCubeArray), 266 CASE(SampleRateShading), 267 CASE(ImageRect), 268 CASE(SampledRect), 269 CASE(GenericPointer), 270 CASE(Int8), 271 CASE(InputAttachment), 272 CASE(SparseResidency), 273 CASE(MinLod), 274 CASE(Sampled1D), 275 CASE(Image1D), 276 CASE(SampledCubeArray), 277 CASE(SampledBuffer), 278 CASE(ImageBuffer), 279 CASE(ImageMSArray), 280 CASE(StorageImageExtendedFormats), 281 CASE(ImageQuery), 282 CASE(DerivativeControl), 283 CASE(InterpolationFunction), 284 CASE(TransformFeedback), 285 })); 286#undef CASE 287// clang-format on 288 289using TextToBinaryCapability = spvtest::TextToBinaryTest; 290 291TEST_F(TextToBinaryCapability, BadMissingCapability) { 292 EXPECT_THAT(CompileFailure("OpCapability"), 293 Eq("Expected operand for OpCapability instruction, but found the " 294 "end of the stream.")); 295} 296 297TEST_F(TextToBinaryCapability, BadInvalidCapability) { 298 EXPECT_THAT(CompileFailure("OpCapability 123"), 299 Eq("Invalid capability '123'.")); 300} 301 302// TODO(dneto): OpExecutionMode 303 304} // namespace 305} // namespace spvtools 306