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