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