1// Copyright (c) 2017 Pierre Moreau
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#include <memory>
16#include <string>
17#include <vector>
18
19#include "source/opt/build_module.h"
20#include "source/opt/ir_context.h"
21#include "source/opt/pass_manager.h"
22#include "source/opt/remove_duplicates_pass.h"
23#include "source/spirv_constant.h"
24#include "test/unit_spirv.h"
25
26namespace spvtools {
27namespace opt {
28namespace {
29
30class RemoveDuplicatesTest : public ::testing::Test {
31 public:
32  RemoveDuplicatesTest()
33      : tools_(SPV_ENV_UNIVERSAL_1_2),
34        context_(),
35        consumer_([this](spv_message_level_t level, const char*,
36                         const spv_position_t& position, const char* message) {
37          if (!error_message_.empty()) error_message_ += "\n";
38          switch (level) {
39            case SPV_MSG_FATAL:
40            case SPV_MSG_INTERNAL_ERROR:
41            case SPV_MSG_ERROR:
42              error_message_ += "ERROR";
43              break;
44            case SPV_MSG_WARNING:
45              error_message_ += "WARNING";
46              break;
47            case SPV_MSG_INFO:
48              error_message_ += "INFO";
49              break;
50            case SPV_MSG_DEBUG:
51              error_message_ += "DEBUG";
52              break;
53          }
54          error_message_ +=
55              ": " + std::to_string(position.index) + ": " + message;
56        }),
57        disassemble_options_(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER),
58        error_message_() {
59    tools_.SetMessageConsumer(consumer_);
60  }
61
62  void TearDown() override { error_message_.clear(); }
63
64  std::string RunPass(const std::string& text) {
65    context_ = spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer_, text);
66    if (!context_.get()) return std::string();
67
68    PassManager manager;
69    manager.SetMessageConsumer(consumer_);
70    manager.AddPass<RemoveDuplicatesPass>();
71
72    Pass::Status pass_res = manager.Run(context_.get());
73    if (pass_res == Pass::Status::Failure) return std::string();
74
75    return ModuleToText();
76  }
77
78  // Disassembles |binary| and outputs the result in |text|. If |text| is a
79  // null pointer, SPV_ERROR_INVALID_POINTER is returned.
80  spv_result_t Disassemble(const std::vector<uint32_t>& binary,
81                           std::string* text) {
82    if (!text) return SPV_ERROR_INVALID_POINTER;
83    return tools_.Disassemble(binary, text, disassemble_options_)
84               ? SPV_SUCCESS
85               : SPV_ERROR_INVALID_BINARY;
86  }
87
88  // Returns the accumulated error messages for the test.
89  std::string GetErrorMessage() const { return error_message_; }
90
91  std::string ToText(const std::vector<Instruction*>& inst) {
92    std::vector<uint32_t> binary = {spv::MagicNumber, 0x10200, 0u, 2u, 0u};
93    for (const Instruction* i : inst)
94      i->ToBinaryWithoutAttachedDebugInsts(&binary);
95    std::string text;
96    Disassemble(binary, &text);
97    return text;
98  }
99
100  std::string ModuleToText() {
101    std::vector<uint32_t> binary;
102    context_->module()->ToBinary(&binary, false);
103    std::string text;
104    Disassemble(binary, &text);
105    return text;
106  }
107
108 private:
109  spvtools::SpirvTools
110      tools_;  // An instance for calling SPIRV-Tools functionalities.
111  std::unique_ptr<IRContext> context_;
112  spvtools::MessageConsumer consumer_;
113  uint32_t disassemble_options_;
114  std::string error_message_;
115};
116
117TEST_F(RemoveDuplicatesTest, DuplicateCapabilities) {
118  const std::string spirv = R"(
119OpCapability Shader
120OpCapability Linkage
121OpCapability Shader
122OpMemoryModel Logical GLSL450
123)";
124  const std::string after = R"(OpCapability Shader
125OpCapability Linkage
126OpMemoryModel Logical GLSL450
127)";
128
129  EXPECT_EQ(RunPass(spirv), after);
130  EXPECT_EQ(GetErrorMessage(), "");
131}
132
133TEST_F(RemoveDuplicatesTest, DuplicateExtInstImports) {
134  const std::string spirv = R"(
135OpCapability Shader
136OpCapability Linkage
137%1 = OpExtInstImport "OpenCL.std"
138%2 = OpExtInstImport "OpenCL.std"
139%3 = OpExtInstImport "GLSL.std.450"
140OpMemoryModel Logical GLSL450
141)";
142  const std::string after = R"(OpCapability Shader
143OpCapability Linkage
144%1 = OpExtInstImport "OpenCL.std"
145%3 = OpExtInstImport "GLSL.std.450"
146OpMemoryModel Logical GLSL450
147)";
148
149  EXPECT_EQ(RunPass(spirv), after);
150  EXPECT_EQ(GetErrorMessage(), "");
151}
152
153TEST_F(RemoveDuplicatesTest, DuplicateTypes) {
154  const std::string spirv = R"(
155OpCapability Shader
156OpCapability Linkage
157OpMemoryModel Logical GLSL450
158%1 = OpTypeInt 32 0
159%2 = OpTypeInt 32 0
160%3 = OpTypeStruct %1 %2
161)";
162  const std::string after = R"(OpCapability Shader
163OpCapability Linkage
164OpMemoryModel Logical GLSL450
165%1 = OpTypeInt 32 0
166%3 = OpTypeStruct %1 %1
167)";
168
169  EXPECT_EQ(RunPass(spirv), after);
170  EXPECT_EQ(GetErrorMessage(), "");
171}
172
173TEST_F(RemoveDuplicatesTest, SameTypeDifferentMemberDecoration) {
174  const std::string spirv = R"(
175OpCapability Shader
176OpCapability Linkage
177OpMemoryModel Logical GLSL450
178OpDecorate %1 GLSLPacked
179%2 = OpTypeInt 32 0
180%1 = OpTypeStruct %2 %2
181%3 = OpTypeStruct %2 %2
182)";
183  const std::string after = R"(OpCapability Shader
184OpCapability Linkage
185OpMemoryModel Logical GLSL450
186OpDecorate %1 GLSLPacked
187%2 = OpTypeInt 32 0
188%1 = OpTypeStruct %2 %2
189%3 = OpTypeStruct %2 %2
190)";
191
192  EXPECT_EQ(RunPass(spirv), after);
193  EXPECT_EQ(GetErrorMessage(), "");
194}
195
196TEST_F(RemoveDuplicatesTest, SameTypeAndMemberDecoration) {
197  const std::string spirv = R"(
198OpCapability Shader
199OpCapability Linkage
200OpMemoryModel Logical GLSL450
201OpDecorate %1 GLSLPacked
202OpDecorate %2 GLSLPacked
203%3 = OpTypeInt 32 0
204%1 = OpTypeStruct %3 %3
205%2 = OpTypeStruct %3 %3
206)";
207  const std::string after = R"(OpCapability Shader
208OpCapability Linkage
209OpMemoryModel Logical GLSL450
210OpDecorate %1 GLSLPacked
211%3 = OpTypeInt 32 0
212%1 = OpTypeStruct %3 %3
213)";
214
215  EXPECT_EQ(RunPass(spirv), after);
216  EXPECT_EQ(GetErrorMessage(), "");
217}
218
219TEST_F(RemoveDuplicatesTest, SameTypeAndDifferentName) {
220  const std::string spirv = R"(
221OpCapability Shader
222OpCapability Linkage
223OpMemoryModel Logical GLSL450
224OpName %1 "Type1"
225OpName %2 "Type2"
226%3 = OpTypeInt 32 0
227%1 = OpTypeStruct %3 %3
228%2 = OpTypeStruct %3 %3
229)";
230  const std::string after = R"(OpCapability Shader
231OpCapability Linkage
232OpMemoryModel Logical GLSL450
233OpName %1 "Type1"
234%3 = OpTypeInt 32 0
235%1 = OpTypeStruct %3 %3
236)";
237
238  EXPECT_EQ(RunPass(spirv), after);
239  EXPECT_EQ(GetErrorMessage(), "");
240}
241
242// Check that #1033 has been fixed.
243TEST_F(RemoveDuplicatesTest, DoNotRemoveDifferentOpDecorationGroup) {
244  const std::string spirv = R"(
245OpCapability Shader
246OpCapability Linkage
247OpMemoryModel Logical GLSL450
248OpDecorate %1 Constant
249%1 = OpDecorationGroup
250OpDecorate %2 Restrict
251%2 = OpDecorationGroup
252OpGroupDecorate %3 %1 %2
253%4 = OpTypeInt 32 0
254%3 = OpVariable %4 Uniform
255)";
256  const std::string after = R"(OpCapability Shader
257OpCapability Linkage
258OpMemoryModel Logical GLSL450
259OpDecorate %1 Constant
260%1 = OpDecorationGroup
261OpDecorate %2 Restrict
262%2 = OpDecorationGroup
263OpGroupDecorate %3 %1 %2
264%4 = OpTypeInt 32 0
265%3 = OpVariable %4 Uniform
266)";
267
268  EXPECT_EQ(RunPass(spirv), after);
269  EXPECT_EQ(GetErrorMessage(), "");
270}
271
272TEST_F(RemoveDuplicatesTest, DifferentDecorationGroup) {
273  const std::string spirv = R"(
274OpCapability Shader
275OpCapability Linkage
276OpMemoryModel Logical GLSL450
277OpDecorate %1 Constant
278OpDecorate %1 Restrict
279%1 = OpDecorationGroup
280OpDecorate %2 Constant
281%2 = OpDecorationGroup
282OpGroupDecorate %1 %3
283OpGroupDecorate %2 %4
284%5 = OpTypeInt 32 0
285%3 = OpVariable %5 Uniform
286%4 = OpVariable %5 Uniform
287)";
288  const std::string after = R"(OpCapability Shader
289OpCapability Linkage
290OpMemoryModel Logical GLSL450
291OpDecorate %1 Constant
292OpDecorate %1 Restrict
293%1 = OpDecorationGroup
294OpDecorate %2 Constant
295%2 = OpDecorationGroup
296OpGroupDecorate %1 %3
297OpGroupDecorate %2 %4
298%5 = OpTypeInt 32 0
299%3 = OpVariable %5 Uniform
300%4 = OpVariable %5 Uniform
301)";
302
303  EXPECT_EQ(RunPass(spirv), after);
304  EXPECT_EQ(GetErrorMessage(), "");
305}
306
307// Test what happens when a type is a resource type.  For now we are merging
308// them, but, if we want to merge types and make reflection work (issue #1372),
309// we will not be able to merge %2 and %3 below.
310TEST_F(RemoveDuplicatesTest, DontMergeNestedResourceTypes) {
311  const std::string spirv = R"(OpCapability Shader
312OpMemoryModel Logical GLSL450
313OpSource HLSL 600
314OpName %1 "PositionAdjust"
315OpMemberName %1 0 "XAdjust"
316OpName %2 "NormalAdjust"
317OpMemberName %2 0 "XDir"
318OpMemberName %3 0 "AdjustXYZ"
319OpMemberName %3 1 "AdjustDir"
320OpName %4 "Constants"
321OpMemberDecorate %1 0 Offset 0
322OpMemberDecorate %2 0 Offset 0
323OpMemberDecorate %3 0 Offset 0
324OpMemberDecorate %3 1 Offset 16
325OpDecorate %3 Block
326OpDecorate %4 DescriptorSet 0
327OpDecorate %4 Binding 0
328%5 = OpTypeFloat 32
329%6 = OpTypeVector %5 3
330%1 = OpTypeStruct %6
331%2 = OpTypeStruct %6
332%3 = OpTypeStruct %1 %2
333%7 = OpTypePointer Uniform %3
334%4 = OpVariable %7 Uniform
335)";
336
337  const std::string result = R"(OpCapability Shader
338OpMemoryModel Logical GLSL450
339OpSource HLSL 600
340OpName %1 "PositionAdjust"
341OpMemberName %1 0 "XAdjust"
342OpMemberName %3 0 "AdjustXYZ"
343OpMemberName %3 1 "AdjustDir"
344OpName %4 "Constants"
345OpMemberDecorate %1 0 Offset 0
346OpMemberDecorate %3 0 Offset 0
347OpMemberDecorate %3 1 Offset 16
348OpDecorate %3 Block
349OpDecorate %4 DescriptorSet 0
350OpDecorate %4 Binding 0
351%5 = OpTypeFloat 32
352%6 = OpTypeVector %5 3
353%1 = OpTypeStruct %6
354%3 = OpTypeStruct %1 %1
355%7 = OpTypePointer Uniform %3
356%4 = OpVariable %7 Uniform
357)";
358
359  EXPECT_EQ(RunPass(spirv), result);
360  EXPECT_EQ(GetErrorMessage(), "");
361}
362
363// See comment for DontMergeNestedResourceTypes.
364TEST_F(RemoveDuplicatesTest, DontMergeResourceTypes) {
365  const std::string spirv = R"(OpCapability Shader
366OpMemoryModel Logical GLSL450
367OpSource HLSL 600
368OpName %1 "PositionAdjust"
369OpMemberName %1 0 "XAdjust"
370OpName %2 "NormalAdjust"
371OpMemberName %2 0 "XDir"
372OpName %3 "Constants"
373OpMemberDecorate %1 0 Offset 0
374OpMemberDecorate %2 0 Offset 0
375OpDecorate %3 DescriptorSet 0
376OpDecorate %3 Binding 0
377OpDecorate %4 DescriptorSet 1
378OpDecorate %4 Binding 0
379%5 = OpTypeFloat 32
380%6 = OpTypeVector %5 3
381%1 = OpTypeStruct %6
382%2 = OpTypeStruct %6
383%7 = OpTypePointer Uniform %1
384%8 = OpTypePointer Uniform %2
385%3 = OpVariable %7 Uniform
386%4 = OpVariable %8 Uniform
387)";
388
389  const std::string result = R"(OpCapability Shader
390OpMemoryModel Logical GLSL450
391OpSource HLSL 600
392OpName %1 "PositionAdjust"
393OpMemberName %1 0 "XAdjust"
394OpName %3 "Constants"
395OpMemberDecorate %1 0 Offset 0
396OpDecorate %3 DescriptorSet 0
397OpDecorate %3 Binding 0
398OpDecorate %4 DescriptorSet 1
399OpDecorate %4 Binding 0
400%5 = OpTypeFloat 32
401%6 = OpTypeVector %5 3
402%1 = OpTypeStruct %6
403%7 = OpTypePointer Uniform %1
404%3 = OpVariable %7 Uniform
405%4 = OpVariable %7 Uniform
406)";
407
408  EXPECT_EQ(RunPass(spirv), result);
409  EXPECT_EQ(GetErrorMessage(), "");
410}
411
412// See comment for DontMergeNestedResourceTypes.
413TEST_F(RemoveDuplicatesTest, DontMergeResourceTypesContainingArray) {
414  const std::string spirv = R"(OpCapability Shader
415OpMemoryModel Logical GLSL450
416OpSource HLSL 600
417OpName %1 "PositionAdjust"
418OpMemberName %1 0 "XAdjust"
419OpName %2 "NormalAdjust"
420OpMemberName %2 0 "XDir"
421OpName %3 "Constants"
422OpMemberDecorate %1 0 Offset 0
423OpMemberDecorate %2 0 Offset 0
424OpDecorate %3 DescriptorSet 0
425OpDecorate %3 Binding 0
426OpDecorate %4 DescriptorSet 1
427OpDecorate %4 Binding 0
428%5 = OpTypeFloat 32
429%6 = OpTypeVector %5 3
430%1 = OpTypeStruct %6
431%2 = OpTypeStruct %6
432%7 = OpTypeInt 32 0
433%8 = OpConstant %7 4
434%9 = OpTypeArray %1 %8
435%10 = OpTypeArray %2 %8
436%11 = OpTypePointer Uniform %9
437%12 = OpTypePointer Uniform %10
438%3 = OpVariable %11 Uniform
439%4 = OpVariable %12 Uniform
440)";
441
442  const std::string result = R"(OpCapability Shader
443OpMemoryModel Logical GLSL450
444OpSource HLSL 600
445OpName %1 "PositionAdjust"
446OpMemberName %1 0 "XAdjust"
447OpName %3 "Constants"
448OpMemberDecorate %1 0 Offset 0
449OpDecorate %3 DescriptorSet 0
450OpDecorate %3 Binding 0
451OpDecorate %4 DescriptorSet 1
452OpDecorate %4 Binding 0
453%5 = OpTypeFloat 32
454%6 = OpTypeVector %5 3
455%1 = OpTypeStruct %6
456%7 = OpTypeInt 32 0
457%8 = OpConstant %7 4
458%9 = OpTypeArray %1 %8
459%11 = OpTypePointer Uniform %9
460%3 = OpVariable %11 Uniform
461%4 = OpVariable %11 Uniform
462)";
463
464  EXPECT_EQ(RunPass(spirv), result);
465  EXPECT_EQ(GetErrorMessage(), "");
466}
467
468// Test that we merge the type of a resource with a type that is not the type
469// a resource.  The resource type appears first in this case.  We must keep
470// the resource type.
471TEST_F(RemoveDuplicatesTest, MergeResourceTypeWithNonresourceType1) {
472  const std::string spirv = R"(OpCapability Shader
473OpMemoryModel Logical GLSL450
474OpSource HLSL 600
475OpName %1 "PositionAdjust"
476OpMemberName %1 0 "XAdjust"
477OpName %2 "NormalAdjust"
478OpMemberName %2 0 "XDir"
479OpName %3 "Constants"
480OpMemberDecorate %1 0 Offset 0
481OpMemberDecorate %2 0 Offset 0
482OpDecorate %3 DescriptorSet 0
483OpDecorate %3 Binding 0
484%4 = OpTypeFloat 32
485%5 = OpTypeVector %4 3
486%1 = OpTypeStruct %5
487%2 = OpTypeStruct %5
488%6 = OpTypePointer Uniform %1
489%7 = OpTypePointer Uniform %2
490%3 = OpVariable %6 Uniform
491%8 = OpVariable %7 Uniform
492)";
493
494  const std::string result = R"(OpCapability Shader
495OpMemoryModel Logical GLSL450
496OpSource HLSL 600
497OpName %1 "PositionAdjust"
498OpMemberName %1 0 "XAdjust"
499OpName %3 "Constants"
500OpMemberDecorate %1 0 Offset 0
501OpDecorate %3 DescriptorSet 0
502OpDecorate %3 Binding 0
503%4 = OpTypeFloat 32
504%5 = OpTypeVector %4 3
505%1 = OpTypeStruct %5
506%6 = OpTypePointer Uniform %1
507%3 = OpVariable %6 Uniform
508%8 = OpVariable %6 Uniform
509)";
510
511  EXPECT_EQ(RunPass(spirv), result);
512  EXPECT_EQ(GetErrorMessage(), "");
513}
514
515// Test that we merge the type of a resource with a type that is not the type
516// a resource.  The resource type appears second in this case.  We must keep
517// the resource type.
518//
519// See comment for DontMergeNestedResourceTypes.
520TEST_F(RemoveDuplicatesTest, MergeResourceTypeWithNonresourceType2) {
521  const std::string spirv = R"(OpCapability Shader
522OpMemoryModel Logical GLSL450
523OpSource HLSL 600
524OpName %1 "PositionAdjust"
525OpMemberName %1 0 "XAdjust"
526OpName %2 "NormalAdjust"
527OpMemberName %2 0 "XDir"
528OpName %3 "Constants"
529OpMemberDecorate %1 0 Offset 0
530OpMemberDecorate %2 0 Offset 0
531OpDecorate %3 DescriptorSet 0
532OpDecorate %3 Binding 0
533%4 = OpTypeFloat 32
534%5 = OpTypeVector %4 3
535%1 = OpTypeStruct %5
536%2 = OpTypeStruct %5
537%6 = OpTypePointer Uniform %1
538%7 = OpTypePointer Uniform %2
539%8 = OpVariable %6 Uniform
540%3 = OpVariable %7 Uniform
541)";
542
543  const std::string result = R"(OpCapability Shader
544OpMemoryModel Logical GLSL450
545OpSource HLSL 600
546OpName %1 "PositionAdjust"
547OpMemberName %1 0 "XAdjust"
548OpName %3 "Constants"
549OpMemberDecorate %1 0 Offset 0
550OpDecorate %3 DescriptorSet 0
551OpDecorate %3 Binding 0
552%4 = OpTypeFloat 32
553%5 = OpTypeVector %4 3
554%1 = OpTypeStruct %5
555%6 = OpTypePointer Uniform %1
556%8 = OpVariable %6 Uniform
557%3 = OpVariable %6 Uniform
558)";
559
560  EXPECT_EQ(RunPass(spirv), result);
561  EXPECT_EQ(GetErrorMessage(), "");
562}
563
564// In this test, %8 and %9 are the same and only %9 is used in a resource.
565// However, we cannot merge them unless we also merge %2 and %3, which cannot
566// happen because both are used in resources.
567//
568// If we try to avoid replaces resource types, then remove duplicates should
569// have not change in this case.  That is not currently implemented.
570TEST_F(RemoveDuplicatesTest, MergeResourceTypeWithNonresourceType3) {
571  const std::string spirv = R"(OpCapability Shader
572OpMemoryModel Logical GLSL450
573OpEntryPoint GLCompute %1 "main"
574OpSource HLSL 600
575OpName %2 "PositionAdjust"
576OpMemberName %2 0 "XAdjust"
577OpName %3 "NormalAdjust"
578OpMemberName %3 0 "XDir"
579OpName %4 "Constants"
580OpMemberDecorate %2 0 Offset 0
581OpMemberDecorate %3 0 Offset 0
582OpDecorate %4 DescriptorSet 0
583OpDecorate %4 Binding 0
584OpDecorate %5 DescriptorSet 1
585OpDecorate %5 Binding 0
586%6 = OpTypeFloat 32
587%7 = OpTypeVector %6 3
588%2 = OpTypeStruct %7
589%3 = OpTypeStruct %7
590%8 = OpTypePointer Uniform %3
591%9 = OpTypePointer Uniform %2
592%10 = OpTypeStruct %3
593%11 = OpTypePointer Uniform %10
594%5 = OpVariable %9 Uniform
595%4 = OpVariable %11 Uniform
596%12 = OpTypeVoid
597%13 = OpTypeFunction %12
598%14 = OpTypeInt 32 0
599%15 = OpConstant %14 0
600%1 = OpFunction %12 None %13
601%16 = OpLabel
602%17 = OpAccessChain %8 %4 %15
603OpReturn
604OpFunctionEnd
605)";
606
607  const std::string result = R"(OpCapability Shader
608OpMemoryModel Logical GLSL450
609OpEntryPoint GLCompute %1 "main"
610OpSource HLSL 600
611OpName %2 "PositionAdjust"
612OpMemberName %2 0 "XAdjust"
613OpName %4 "Constants"
614OpMemberDecorate %2 0 Offset 0
615OpDecorate %4 DescriptorSet 0
616OpDecorate %4 Binding 0
617OpDecorate %5 DescriptorSet 1
618OpDecorate %5 Binding 0
619%6 = OpTypeFloat 32
620%7 = OpTypeVector %6 3
621%2 = OpTypeStruct %7
622%8 = OpTypePointer Uniform %2
623%10 = OpTypeStruct %2
624%11 = OpTypePointer Uniform %10
625%5 = OpVariable %8 Uniform
626%4 = OpVariable %11 Uniform
627%12 = OpTypeVoid
628%13 = OpTypeFunction %12
629%14 = OpTypeInt 32 0
630%15 = OpConstant %14 0
631%1 = OpFunction %12 None %13
632%16 = OpLabel
633%17 = OpAccessChain %8 %4 %15
634OpReturn
635OpFunctionEnd
636)";
637
638  EXPECT_EQ(RunPass(spirv), result);
639  EXPECT_EQ(GetErrorMessage(), "");
640}
641
642}  // namespace
643}  // namespace opt
644}  // namespace spvtools
645