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 "Memory Instructions" section of
16// the SPIR-V spec.
17
18#include <sstream>
19#include <string>
20#include <vector>
21
22#include "gmock/gmock.h"
23#include "test/test_fixture.h"
24#include "test/unit_spirv.h"
25
26namespace spvtools {
27namespace {
28
29using spvtest::EnumCase;
30using spvtest::MakeInstruction;
31using spvtest::TextToBinaryTest;
32using ::testing::Eq;
33using ::testing::HasSubstr;
34
35// Test assembly of Memory Access masks
36
37using MemoryAccessTest = spvtest::TextToBinaryTestBase<
38    ::testing::TestWithParam<EnumCase<spv::MemoryAccessMask>>>;
39
40TEST_P(MemoryAccessTest, AnySingleMemoryAccessMask) {
41  std::stringstream input;
42  input << "OpStore %ptr %value " << GetParam().name();
43  for (auto operand : GetParam().operands()) input << " " << operand;
44  EXPECT_THAT(
45      CompiledInstructions(input.str()),
46      Eq(MakeInstruction(spv::Op::OpStore, {1, 2, (uint32_t)GetParam().value()},
47                         GetParam().operands())));
48}
49
50INSTANTIATE_TEST_SUITE_P(
51    TextToBinaryMemoryAccessTest, MemoryAccessTest,
52    ::testing::ValuesIn(std::vector<EnumCase<spv::MemoryAccessMask>>{
53        {spv::MemoryAccessMask::MaskNone, "None", {}},
54        {spv::MemoryAccessMask::Volatile, "Volatile", {}},
55        {spv::MemoryAccessMask::Aligned, "Aligned", {16}},
56        {spv::MemoryAccessMask::Nontemporal, "Nontemporal", {}},
57    }));
58
59TEST_F(TextToBinaryTest, CombinedMemoryAccessMask) {
60  const std::string input = "OpStore %ptr %value Volatile|Aligned 16";
61  const uint32_t expected_mask = uint32_t(spv::MemoryAccessMask::Volatile |
62                                          spv::MemoryAccessMask::Aligned);
63  EXPECT_THAT(expected_mask, Eq(3u));
64  EXPECT_THAT(CompiledInstructions(input),
65              Eq(MakeInstruction(spv::Op::OpStore, {1, 2, expected_mask, 16})));
66}
67
68// Test Storage Class enum values
69
70using StorageClassTest = spvtest::TextToBinaryTestBase<
71    ::testing::TestWithParam<EnumCase<spv::StorageClass>>>;
72
73TEST_P(StorageClassTest, AnyStorageClass) {
74  const std::string input = "%1 = OpVariable %2 " + GetParam().name();
75  EXPECT_THAT(CompiledInstructions(input),
76              Eq(MakeInstruction(spv::Op::OpVariable,
77                                 {1, 2, (uint32_t)GetParam().value()})));
78}
79
80// clang-format off
81#define CASE(NAME) { spv::StorageClass::NAME, #NAME, {} }
82INSTANTIATE_TEST_SUITE_P(
83    TextToBinaryStorageClassTest, StorageClassTest,
84    ::testing::ValuesIn(std::vector<EnumCase<spv::StorageClass>>{
85        CASE(UniformConstant),
86        CASE(Input),
87        CASE(Uniform),
88        CASE(Output),
89        CASE(Workgroup),
90        CASE(CrossWorkgroup),
91        CASE(Private),
92        CASE(Function),
93        CASE(Generic),
94        CASE(PushConstant),
95        CASE(AtomicCounter),
96        CASE(Image),
97    }));
98#undef CASE
99// clang-format on
100
101using MemoryRoundTripTest = RoundTripTest;
102
103// OpPtrEqual appeared in SPIR-V 1.4
104
105TEST_F(MemoryRoundTripTest, OpPtrEqualGood) {
106  std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n";
107  EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
108              Eq(MakeInstruction(spv::Op::OpPtrEqual, {1, 2, 3, 4})));
109  std::string disassembly = EncodeAndDecodeSuccessfully(
110      spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
111  EXPECT_THAT(disassembly, Eq(spirv));
112}
113
114TEST_F(MemoryRoundTripTest, OpPtrEqualV13Bad) {
115  std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n";
116  std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3);
117  EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrEqual'"));
118}
119
120// OpPtrNotEqual appeared in SPIR-V 1.4
121
122TEST_F(MemoryRoundTripTest, OpPtrNotEqualGood) {
123  std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n";
124  EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
125              Eq(MakeInstruction(spv::Op::OpPtrNotEqual, {1, 2, 3, 4})));
126  std::string disassembly = EncodeAndDecodeSuccessfully(
127      spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
128  EXPECT_THAT(disassembly, Eq(spirv));
129}
130
131TEST_F(MemoryRoundTripTest, OpPtrNotEqualV13Bad) {
132  std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n";
133  std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3);
134  EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrNotEqual'"));
135}
136
137// OpPtrDiff appeared in SPIR-V 1.4
138
139TEST_F(MemoryRoundTripTest, OpPtrDiffGood) {
140  std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n";
141  EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
142              Eq(MakeInstruction(spv::Op::OpPtrDiff, {1, 2, 3, 4})));
143  std::string disassembly = EncodeAndDecodeSuccessfully(
144      spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
145  EXPECT_THAT(disassembly, Eq(spirv));
146}
147
148TEST_F(MemoryRoundTripTest, OpPtrDiffV13Good) {
149  // OpPtrDiff is enabled by a capability as well, so we can assemble
150  // it even in older SPIR-V environments.  We do that so we can
151  // write tests.
152  std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n";
153  std::string disassembly = EncodeAndDecodeSuccessfully(
154      spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4);
155}
156
157// OpCopyMemory
158
159TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) {
160  std::string spirv = "OpCopyMemory %1 %2\n";
161  EXPECT_THAT(CompiledInstructions(spirv),
162              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2})));
163  std::string disassembly =
164      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
165  EXPECT_THAT(disassembly, Eq(spirv));
166}
167
168TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) {
169  std::string spirv = "OpCopyMemory %1\n";
170  std::string err = CompileFailure(spirv);
171  EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemory instruction, "
172                             "but found the end of the stream."));
173}
174
175TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) {
176  std::string spirv = "OpCopyMemory %1 %2 %3\n";
177  std::string err = CompileFailure(spirv);
178  EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%3'"));
179}
180
181TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNoneGood) {
182  std::string spirv = "OpCopyMemory %1 %2 None\n";
183  EXPECT_THAT(CompiledInstructions(spirv),
184              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 0})));
185  std::string disassembly =
186      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
187  EXPECT_THAT(disassembly, Eq(spirv));
188}
189
190TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVolatileGood) {
191  std::string spirv = "OpCopyMemory %1 %2 Volatile\n";
192  EXPECT_THAT(CompiledInstructions(spirv),
193              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1})));
194  std::string disassembly =
195      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
196  EXPECT_THAT(disassembly, Eq(spirv));
197}
198
199TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAligned8Good) {
200  std::string spirv = "OpCopyMemory %1 %2 Aligned 8\n";
201  EXPECT_THAT(CompiledInstructions(spirv),
202              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 2, 8})));
203  std::string disassembly =
204      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
205  EXPECT_THAT(disassembly, Eq(spirv));
206}
207
208TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNontemporalGood) {
209  std::string spirv = "OpCopyMemory %1 %2 Nontemporal\n";
210  EXPECT_THAT(CompiledInstructions(spirv),
211              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 4})));
212  std::string disassembly =
213      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
214  EXPECT_THAT(disassembly, Eq(spirv));
215}
216
217TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAvGood) {
218  std::string spirv = "OpCopyMemory %1 %2 MakePointerAvailable %3\n";
219  EXPECT_THAT(CompiledInstructions(spirv),
220              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 8, 3})));
221  std::string disassembly =
222      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
223  EXPECT_THAT(disassembly, Eq(spirv));
224}
225
226TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVisGood) {
227  std::string spirv = "OpCopyMemory %1 %2 MakePointerVisible %3\n";
228  EXPECT_THAT(CompiledInstructions(spirv),
229              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 16, 3})));
230  std::string disassembly =
231      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
232  EXPECT_THAT(disassembly, Eq(spirv));
233}
234
235TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNonPrivateGood) {
236  std::string spirv = "OpCopyMemory %1 %2 NonPrivatePointer\n";
237  EXPECT_THAT(CompiledInstructions(spirv),
238              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 32})));
239  std::string disassembly =
240      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
241  EXPECT_THAT(disassembly, Eq(spirv));
242}
243
244TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessMixedGood) {
245  std::string spirv =
246      "OpCopyMemory %1 %2 "
247      "Volatile|Aligned|Nontemporal|MakePointerAvailable|"
248      "MakePointerVisible|NonPrivatePointer 16 %3 %4\n";
249  EXPECT_THAT(CompiledInstructions(spirv),
250              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 63, 16, 3, 4})));
251  std::string disassembly =
252      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
253  EXPECT_THAT(disassembly, Eq(spirv));
254}
255
256TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV13Good) {
257  std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n";
258  // Note: This will assemble but should not validate for SPIR-V 1.3
259  EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3),
260              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1, 1})));
261  std::string disassembly =
262      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
263  EXPECT_THAT(disassembly, Eq(spirv));
264}
265
266TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV14Good) {
267  std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n";
268  EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
269              Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 1, 1})));
270  std::string disassembly =
271      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
272  EXPECT_THAT(disassembly, Eq(spirv));
273}
274
275TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessMixedV14Good) {
276  std::string spirv =
277      "OpCopyMemory %1 %2 Volatile|Nontemporal|"
278      "MakePointerVisible %3 "
279      "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4\n";
280  EXPECT_THAT(
281      CompiledInstructions(spirv),
282      Eq(MakeInstruction(spv::Op::OpCopyMemory, {1, 2, 21, 3, 42, 16, 4})));
283  std::string disassembly =
284      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
285  EXPECT_THAT(disassembly, Eq(spirv));
286}
287
288// OpCopyMemorySized
289
290TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) {
291  std::string spirv = "OpCopyMemorySized %1 %2 %3\n";
292  EXPECT_THAT(CompiledInstructions(spirv),
293              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3})));
294  std::string disassembly =
295      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
296  EXPECT_THAT(disassembly, Eq(spirv));
297}
298
299TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) {
300  std::string spirv = "OpCopyMemorySized %1 %2\n";
301  std::string err = CompileFailure(spirv);
302  EXPECT_THAT(err, HasSubstr("Expected operand for OpCopyMemorySized "
303                             "instruction, but found the end of the stream."));
304}
305
306TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) {
307  std::string spirv = "OpCopyMemorySized %1 %2 %3 %4\n";
308  std::string err = CompileFailure(spirv);
309  EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%4'"));
310}
311
312TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNoneGood) {
313  std::string spirv = "OpCopyMemorySized %1 %2 %3 None\n";
314  EXPECT_THAT(CompiledInstructions(spirv),
315              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 0})));
316  std::string disassembly =
317      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
318  EXPECT_THAT(disassembly, Eq(spirv));
319}
320
321TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVolatileGood) {
322  std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile\n";
323  EXPECT_THAT(CompiledInstructions(spirv),
324              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1})));
325  std::string disassembly =
326      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
327  EXPECT_THAT(disassembly, Eq(spirv));
328}
329
330TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAligned8Good) {
331  std::string spirv = "OpCopyMemorySized %1 %2 %3 Aligned 8\n";
332  EXPECT_THAT(CompiledInstructions(spirv),
333              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 2, 8})));
334  std::string disassembly =
335      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
336  EXPECT_THAT(disassembly, Eq(spirv));
337}
338
339TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNontemporalGood) {
340  std::string spirv = "OpCopyMemorySized %1 %2 %3 Nontemporal\n";
341  EXPECT_THAT(CompiledInstructions(spirv),
342              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 4})));
343  std::string disassembly =
344      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
345  EXPECT_THAT(disassembly, Eq(spirv));
346}
347
348TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAvGood) {
349  std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerAvailable %4\n";
350  EXPECT_THAT(CompiledInstructions(spirv),
351              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 8, 4})));
352  std::string disassembly =
353      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
354  EXPECT_THAT(disassembly, Eq(spirv));
355}
356
357TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVisGood) {
358  std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerVisible %4\n";
359  EXPECT_THAT(
360      CompiledInstructions(spirv),
361      Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 16, 4})));
362  std::string disassembly =
363      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
364  EXPECT_THAT(disassembly, Eq(spirv));
365}
366
367TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNonPrivateGood) {
368  std::string spirv = "OpCopyMemorySized %1 %2 %3 NonPrivatePointer\n";
369  EXPECT_THAT(CompiledInstructions(spirv),
370              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 32})));
371  std::string disassembly =
372      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
373  EXPECT_THAT(disassembly, Eq(spirv));
374}
375
376TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessMixedGood) {
377  std::string spirv =
378      "OpCopyMemorySized %1 %2 %3 "
379      "Volatile|Aligned|Nontemporal|MakePointerAvailable|"
380      "MakePointerVisible|NonPrivatePointer 16 %4 %5\n";
381  EXPECT_THAT(
382      CompiledInstructions(spirv),
383      Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 63, 16, 4, 5})));
384  std::string disassembly =
385      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
386  EXPECT_THAT(disassembly, Eq(spirv));
387}
388
389TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV13Good) {
390  std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n";
391  // Note: This will assemble but should not validate for SPIR-V 1.3
392  EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3),
393              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1, 1})));
394  std::string disassembly =
395      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
396  EXPECT_THAT(disassembly, Eq(spirv));
397}
398
399TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV14Good) {
400  std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n";
401  EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4),
402              Eq(MakeInstruction(spv::Op::OpCopyMemorySized, {1, 2, 3, 1, 1})));
403  std::string disassembly =
404      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
405  EXPECT_THAT(disassembly, Eq(spirv));
406}
407
408TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessMixedV14Good) {
409  std::string spirv =
410      "OpCopyMemorySized %1 %2 %3 Volatile|Nontemporal|"
411      "MakePointerVisible %4 "
412      "Aligned|MakePointerAvailable|NonPrivatePointer 16 %5\n";
413  EXPECT_THAT(CompiledInstructions(spirv),
414              Eq(MakeInstruction(spv::Op::OpCopyMemorySized,
415                                 {1, 2, 3, 21, 4, 42, 16, 5})));
416  std::string disassembly =
417      EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE);
418  EXPECT_THAT(disassembly, Eq(spirv));
419}
420
421// TODO(dneto): OpVariable with initializers
422// TODO(dneto): OpImageTexelPointer
423// TODO(dneto): OpLoad
424// TODO(dneto): OpStore
425// TODO(dneto): OpAccessChain
426// TODO(dneto): OpInBoundsAccessChain
427// TODO(dneto): OpPtrAccessChain
428// TODO(dneto): OpArrayLength
429// TODO(dneto): OpGenercPtrMemSemantics
430
431}  // namespace
432}  // namespace spvtools
433