• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 Google 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 #include "source/opt/ir_context.h"
16 
17 #include <algorithm>
18 #include <memory>
19 #include <string>
20 #include <utility>
21 
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 #include "source/opt/pass.h"
25 #include "test/opt/pass_fixture.h"
26 #include "test/opt/pass_utils.h"
27 
28 namespace spvtools {
29 namespace opt {
30 namespace {
31 
32 using Analysis = IRContext::Analysis;
33 using ::testing::Each;
34 using ::testing::UnorderedElementsAre;
35 
36 class DummyPassPreservesNothing : public Pass {
37  public:
DummyPassPreservesNothing(Status s)38   DummyPassPreservesNothing(Status s) : Pass(), status_to_return_(s) {}
39 
name() const40   const char* name() const override { return "dummy-pass"; }
Process()41   Status Process() override { return status_to_return_; }
42 
43  private:
44   Status status_to_return_;
45 };
46 
47 class DummyPassPreservesAll : public Pass {
48  public:
DummyPassPreservesAll(Status s)49   DummyPassPreservesAll(Status s) : Pass(), status_to_return_(s) {}
50 
name() const51   const char* name() const override { return "dummy-pass"; }
Process()52   Status Process() override { return status_to_return_; }
53 
GetPreservedAnalyses()54   Analysis GetPreservedAnalyses() override {
55     return Analysis(IRContext::kAnalysisEnd - 1);
56   }
57 
58  private:
59   Status status_to_return_;
60 };
61 
62 class DummyPassPreservesFirst : public Pass {
63  public:
DummyPassPreservesFirst(Status s)64   DummyPassPreservesFirst(Status s) : Pass(), status_to_return_(s) {}
65 
name() const66   const char* name() const override { return "dummy-pass"; }
Process()67   Status Process() override { return status_to_return_; }
68 
GetPreservedAnalyses()69   Analysis GetPreservedAnalyses() override { return IRContext::kAnalysisBegin; }
70 
71  private:
72   Status status_to_return_;
73 };
74 
75 using IRContextTest = PassTest<::testing::Test>;
76 
TEST_F(IRContextTest,IndividualValidAfterBuild)77 TEST_F(IRContextTest, IndividualValidAfterBuild) {
78   std::unique_ptr<Module> module(new Module());
79   IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
80                          spvtools::MessageConsumer());
81 
82   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
83        i <<= 1) {
84     localContext.BuildInvalidAnalyses(i);
85     EXPECT_TRUE(localContext.AreAnalysesValid(i));
86   }
87 }
88 
TEST_F(IRContextTest,AllValidAfterBuild)89 TEST_F(IRContextTest, AllValidAfterBuild) {
90   std::unique_ptr<Module> module = MakeUnique<Module>();
91   IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
92                          spvtools::MessageConsumer());
93 
94   Analysis built_analyses = IRContext::kAnalysisNone;
95   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
96        i <<= 1) {
97     localContext.BuildInvalidAnalyses(i);
98     built_analyses |= i;
99   }
100   EXPECT_TRUE(localContext.AreAnalysesValid(built_analyses));
101 }
102 
TEST_F(IRContextTest,AllValidAfterPassNoChange)103 TEST_F(IRContextTest, AllValidAfterPassNoChange) {
104   std::unique_ptr<Module> module = MakeUnique<Module>();
105   IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
106                          spvtools::MessageConsumer());
107 
108   Analysis built_analyses = IRContext::kAnalysisNone;
109   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
110        i <<= 1) {
111     localContext.BuildInvalidAnalyses(i);
112     built_analyses |= i;
113   }
114 
115   DummyPassPreservesNothing pass(Pass::Status::SuccessWithoutChange);
116   Pass::Status s = pass.Run(&localContext);
117   EXPECT_EQ(s, Pass::Status::SuccessWithoutChange);
118   EXPECT_TRUE(localContext.AreAnalysesValid(built_analyses));
119 }
120 
TEST_F(IRContextTest,NoneValidAfterPassWithChange)121 TEST_F(IRContextTest, NoneValidAfterPassWithChange) {
122   std::unique_ptr<Module> module = MakeUnique<Module>();
123   IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
124                          spvtools::MessageConsumer());
125 
126   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
127        i <<= 1) {
128     localContext.BuildInvalidAnalyses(i);
129   }
130 
131   DummyPassPreservesNothing pass(Pass::Status::SuccessWithChange);
132   Pass::Status s = pass.Run(&localContext);
133   EXPECT_EQ(s, Pass::Status::SuccessWithChange);
134   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
135        i <<= 1) {
136     EXPECT_FALSE(localContext.AreAnalysesValid(i));
137   }
138 }
139 
TEST_F(IRContextTest,AllPreservedAfterPassWithChange)140 TEST_F(IRContextTest, AllPreservedAfterPassWithChange) {
141   std::unique_ptr<Module> module = MakeUnique<Module>();
142   IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
143                          spvtools::MessageConsumer());
144 
145   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
146        i <<= 1) {
147     localContext.BuildInvalidAnalyses(i);
148   }
149 
150   DummyPassPreservesAll pass(Pass::Status::SuccessWithChange);
151   Pass::Status s = pass.Run(&localContext);
152   EXPECT_EQ(s, Pass::Status::SuccessWithChange);
153   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
154        i <<= 1) {
155     EXPECT_TRUE(localContext.AreAnalysesValid(i));
156   }
157 }
158 
TEST_F(IRContextTest,PreserveFirstOnlyAfterPassWithChange)159 TEST_F(IRContextTest, PreserveFirstOnlyAfterPassWithChange) {
160   std::unique_ptr<Module> module = MakeUnique<Module>();
161   IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
162                          spvtools::MessageConsumer());
163 
164   for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
165        i <<= 1) {
166     localContext.BuildInvalidAnalyses(i);
167   }
168 
169   DummyPassPreservesFirst pass(Pass::Status::SuccessWithChange);
170   Pass::Status s = pass.Run(&localContext);
171   EXPECT_EQ(s, Pass::Status::SuccessWithChange);
172   EXPECT_TRUE(localContext.AreAnalysesValid(IRContext::kAnalysisBegin));
173   for (Analysis i = IRContext::kAnalysisBegin << 1; i < IRContext::kAnalysisEnd;
174        i <<= 1) {
175     EXPECT_FALSE(localContext.AreAnalysesValid(i));
176   }
177 }
178 
TEST_F(IRContextTest,KillMemberName)179 TEST_F(IRContextTest, KillMemberName) {
180   const std::string text = R"(
181               OpCapability Shader
182           %1 = OpExtInstImport "GLSL.std.450"
183                OpMemoryModel Logical GLSL450
184                OpEntryPoint Fragment %2 "main"
185                OpExecutionMode %2 OriginUpperLeft
186                OpSource GLSL 430
187                OpName %3 "stuff"
188                OpMemberName %3 0 "refZ"
189                OpMemberDecorate %3 0 Offset 0
190                OpDecorate %3 Block
191           %4 = OpTypeFloat 32
192           %3 = OpTypeStruct %4
193           %5 = OpTypeVoid
194           %6 = OpTypeFunction %5
195           %2 = OpFunction %5 None %6
196           %7 = OpLabel
197                OpReturn
198                OpFunctionEnd
199 )";
200 
201   std::unique_ptr<IRContext> context =
202       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
203 
204   // Build the decoration manager.
205   context->get_decoration_mgr();
206 
207   // Delete the OpTypeStruct.  Should delete the OpName, OpMemberName, and
208   // OpMemberDecorate associated with it.
209   context->KillDef(3);
210 
211   // Make sure all of the name are removed.
212   for (auto& inst : context->debugs2()) {
213     EXPECT_EQ(inst.opcode(), SpvOpNop);
214   }
215 
216   // Make sure all of the decorations are removed.
217   for (auto& inst : context->annotations()) {
218     EXPECT_EQ(inst.opcode(), SpvOpNop);
219   }
220 }
221 
TEST_F(IRContextTest,KillGroupDecoration)222 TEST_F(IRContextTest, KillGroupDecoration) {
223   const std::string text = R"(
224                OpCapability Shader
225           %1 = OpExtInstImport "GLSL.std.450"
226                OpMemoryModel Logical GLSL450
227                OpEntryPoint Fragment %2 "main"
228                OpExecutionMode %2 OriginUpperLeft
229                OpSource GLSL 430
230                OpDecorate %3 Restrict
231           %3 = OpDecorationGroup
232                OpGroupDecorate %3 %4 %5
233           %6 = OpTypeFloat 32
234           %7 = OpTypePointer Function %6
235           %8 = OpTypeStruct %6
236           %9 = OpTypeVoid
237          %10 = OpTypeFunction %9
238           %2 = OpFunction %9 None %10
239          %11 = OpLabel
240           %4 = OpVariable %7 Function
241           %5 = OpVariable %7 Function
242                OpReturn
243                OpFunctionEnd
244 )";
245 
246   std::unique_ptr<IRContext> context =
247       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
248 
249   // Build the decoration manager.
250   context->get_decoration_mgr();
251 
252   // Delete the second variable.
253   context->KillDef(5);
254 
255   // The three decorations instructions should still be there.  The first two
256   // should be the same, but the third should have %5 removed.
257 
258   // Check the OpDecorate instruction
259   auto inst = context->annotation_begin();
260   EXPECT_EQ(inst->opcode(), SpvOpDecorate);
261   EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
262 
263   // Check the OpDecorationGroup Instruction
264   ++inst;
265   EXPECT_EQ(inst->opcode(), SpvOpDecorationGroup);
266   EXPECT_EQ(inst->result_id(), 3);
267 
268   // Check that %5 is no longer part of the group.
269   ++inst;
270   EXPECT_EQ(inst->opcode(), SpvOpGroupDecorate);
271   EXPECT_EQ(inst->NumInOperands(), 2);
272   EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
273   EXPECT_EQ(inst->GetSingleWordInOperand(1), 4);
274 
275   // Check that we are at the end.
276   ++inst;
277   EXPECT_EQ(inst, context->annotation_end());
278 }
279 
TEST_F(IRContextTest,TakeNextUniqueIdIncrementing)280 TEST_F(IRContextTest, TakeNextUniqueIdIncrementing) {
281   const uint32_t NUM_TESTS = 1000;
282   IRContext localContext(SPV_ENV_UNIVERSAL_1_2, nullptr);
283   for (uint32_t i = 1; i < NUM_TESTS; ++i)
284     EXPECT_EQ(i, localContext.TakeNextUniqueId());
285 }
286 
TEST_F(IRContextTest,KillGroupDecorationWitNoDecorations)287 TEST_F(IRContextTest, KillGroupDecorationWitNoDecorations) {
288   const std::string text = R"(
289                OpCapability Shader
290           %1 = OpExtInstImport "GLSL.std.450"
291                OpMemoryModel Logical GLSL450
292                OpEntryPoint Fragment %2 "main"
293                OpExecutionMode %2 OriginUpperLeft
294                OpSource GLSL 430
295           %3 = OpDecorationGroup
296                OpGroupDecorate %3 %4 %5
297           %6 = OpTypeFloat 32
298           %7 = OpTypePointer Function %6
299           %8 = OpTypeStruct %6
300           %9 = OpTypeVoid
301          %10 = OpTypeFunction %9
302           %2 = OpFunction %9 None %10
303          %11 = OpLabel
304           %4 = OpVariable %7 Function
305           %5 = OpVariable %7 Function
306                OpReturn
307                OpFunctionEnd
308 )";
309 
310   std::unique_ptr<IRContext> context =
311       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
312 
313   // Build the decoration manager.
314   context->get_decoration_mgr();
315 
316   // Delete the second variable.
317   context->KillDef(5);
318 
319   // The two decoration instructions should still be there.  The first one
320   // should be the same, but the second should have %5 removed.
321 
322   // Check the OpDecorationGroup Instruction
323   auto inst = context->annotation_begin();
324   EXPECT_EQ(inst->opcode(), SpvOpDecorationGroup);
325   EXPECT_EQ(inst->result_id(), 3);
326 
327   // Check that %5 is no longer part of the group.
328   ++inst;
329   EXPECT_EQ(inst->opcode(), SpvOpGroupDecorate);
330   EXPECT_EQ(inst->NumInOperands(), 2);
331   EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
332   EXPECT_EQ(inst->GetSingleWordInOperand(1), 4);
333 
334   // Check that we are at the end.
335   ++inst;
336   EXPECT_EQ(inst, context->annotation_end());
337 }
338 
TEST_F(IRContextTest,KillDecorationGroup)339 TEST_F(IRContextTest, KillDecorationGroup) {
340   const std::string text = R"(
341                OpCapability Shader
342           %1 = OpExtInstImport "GLSL.std.450"
343                OpMemoryModel Logical GLSL450
344                OpEntryPoint Fragment %2 "main"
345                OpExecutionMode %2 OriginUpperLeft
346                OpSource GLSL 430
347           %3 = OpDecorationGroup
348                OpGroupDecorate %3 %4 %5
349           %6 = OpTypeFloat 32
350           %7 = OpTypePointer Function %6
351           %8 = OpTypeStruct %6
352           %9 = OpTypeVoid
353          %10 = OpTypeFunction %9
354           %2 = OpFunction %9 None %10
355          %11 = OpLabel
356           %4 = OpVariable %7 Function
357           %5 = OpVariable %7 Function
358                OpReturn
359                OpFunctionEnd
360 )";
361 
362   std::unique_ptr<IRContext> context =
363       BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
364 
365   // Build the decoration manager.
366   context->get_decoration_mgr();
367 
368   // Delete the second variable.
369   context->KillDef(3);
370 
371   // Check the OpDecorationGroup Instruction is still there.
372   EXPECT_TRUE(context->annotations().empty());
373 }
374 
TEST_F(IRContextTest,BasicVisitFromEntryPoint)375 TEST_F(IRContextTest, BasicVisitFromEntryPoint) {
376   // Make sure we visit the entry point, and the function it calls.
377   // Do not visit Dead or Exported.
378   const std::string text = R"(
379                OpCapability Shader
380                OpMemoryModel Logical GLSL450
381                OpEntryPoint Fragment %10 "main"
382                OpName %10 "main"
383                OpName %Dead "Dead"
384                OpName %11 "Constant"
385                OpName %ExportedFunc "ExportedFunc"
386                OpDecorate %ExportedFunc LinkageAttributes "ExportedFunc" Export
387        %void = OpTypeVoid
388           %6 = OpTypeFunction %void
389          %10 = OpFunction %void None %6
390          %14 = OpLabel
391          %15 = OpFunctionCall %void %11
392          %16 = OpFunctionCall %void %11
393                OpReturn
394                OpFunctionEnd
395          %11 = OpFunction %void None %6
396          %18 = OpLabel
397                OpReturn
398                OpFunctionEnd
399        %Dead = OpFunction %void None %6
400          %19 = OpLabel
401                OpReturn
402                OpFunctionEnd
403 %ExportedFunc = OpFunction %void None %7
404          %20 = OpLabel
405          %21 = OpFunctionCall %void %11
406                OpReturn
407                OpFunctionEnd
408 )";
409   // clang-format on
410 
411   std::unique_ptr<IRContext> localContext =
412       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
413                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
414   EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n"
415                                    << text << std::endl;
416   std::vector<uint32_t> processed;
417   Pass::ProcessFunction mark_visited = [&processed](Function* fp) {
418     processed.push_back(fp->result_id());
419     return false;
420   };
421   localContext->ProcessEntryPointCallTree(mark_visited);
422   EXPECT_THAT(processed, UnorderedElementsAre(10, 11));
423 }
424 
TEST_F(IRContextTest,BasicVisitReachable)425 TEST_F(IRContextTest, BasicVisitReachable) {
426   // Make sure we visit the entry point, exported function, and the function
427   // they call. Do not visit Dead.
428   const std::string text = R"(
429                OpCapability Shader
430                OpMemoryModel Logical GLSL450
431                OpEntryPoint Fragment %10 "main"
432                OpName %10 "main"
433                OpName %Dead "Dead"
434                OpName %11 "Constant"
435                OpName %12 "ExportedFunc"
436                OpName %13 "Constant2"
437                OpDecorate %12 LinkageAttributes "ExportedFunc" Export
438        %void = OpTypeVoid
439           %6 = OpTypeFunction %void
440          %10 = OpFunction %void None %6
441          %14 = OpLabel
442          %15 = OpFunctionCall %void %11
443          %16 = OpFunctionCall %void %11
444                OpReturn
445                OpFunctionEnd
446          %11 = OpFunction %void None %6
447          %18 = OpLabel
448                OpReturn
449                OpFunctionEnd
450        %Dead = OpFunction %void None %6
451          %19 = OpLabel
452                OpReturn
453                OpFunctionEnd
454          %12 = OpFunction %void None %6
455          %20 = OpLabel
456          %21 = OpFunctionCall %void %13
457                OpReturn
458                OpFunctionEnd
459          %13 = OpFunction %void None %6
460          %22 = OpLabel
461                OpReturn
462                OpFunctionEnd
463 )";
464   // clang-format on
465 
466   std::unique_ptr<IRContext> localContext =
467       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
468                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
469   EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n"
470                                    << text << std::endl;
471 
472   std::vector<uint32_t> processed;
473   Pass::ProcessFunction mark_visited = [&processed](Function* fp) {
474     processed.push_back(fp->result_id());
475     return false;
476   };
477   localContext->ProcessReachableCallTree(mark_visited);
478   EXPECT_THAT(processed, UnorderedElementsAre(10, 11, 12, 13));
479 }
480 
TEST_F(IRContextTest,BasicVisitOnlyOnce)481 TEST_F(IRContextTest, BasicVisitOnlyOnce) {
482   // Make sure we visit %12 only once, even if it is called from two different
483   // functions.
484   const std::string text = R"(
485                OpCapability Shader
486                OpMemoryModel Logical GLSL450
487                OpEntryPoint Fragment %10 "main"
488                OpName %10 "main"
489                OpName %Dead "Dead"
490                OpName %11 "Constant"
491                OpName %12 "ExportedFunc"
492                OpDecorate %12 LinkageAttributes "ExportedFunc" Export
493        %void = OpTypeVoid
494           %6 = OpTypeFunction %void
495          %10 = OpFunction %void None %6
496          %14 = OpLabel
497          %15 = OpFunctionCall %void %11
498          %16 = OpFunctionCall %void %12
499                OpReturn
500                OpFunctionEnd
501          %11 = OpFunction %void None %6
502          %18 = OpLabel
503          %19 = OpFunctionCall %void %12
504                OpReturn
505                OpFunctionEnd
506        %Dead = OpFunction %void None %6
507          %20 = OpLabel
508                OpReturn
509                OpFunctionEnd
510          %12 = OpFunction %void None %6
511          %21 = OpLabel
512                OpReturn
513                OpFunctionEnd
514 )";
515   // clang-format on
516 
517   std::unique_ptr<IRContext> localContext =
518       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
519                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
520   EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n"
521                                    << text << std::endl;
522 
523   std::vector<uint32_t> processed;
524   Pass::ProcessFunction mark_visited = [&processed](Function* fp) {
525     processed.push_back(fp->result_id());
526     return false;
527   };
528   localContext->ProcessReachableCallTree(mark_visited);
529   EXPECT_THAT(processed, UnorderedElementsAre(10, 11, 12));
530 }
531 
TEST_F(IRContextTest,BasicDontVisitExportedVariable)532 TEST_F(IRContextTest, BasicDontVisitExportedVariable) {
533   // Make sure we only visit functions and not exported variables.
534   const std::string text = R"(
535                OpCapability Shader
536                OpMemoryModel Logical GLSL450
537                OpEntryPoint Fragment %10 "main"
538                OpExecutionMode %10 OriginUpperLeft
539                OpSource GLSL 150
540                OpName %10 "main"
541                OpName %12 "export_var"
542                OpDecorate %12 LinkageAttributes "export_var" Export
543        %void = OpTypeVoid
544           %6 = OpTypeFunction %void
545       %float = OpTypeFloat 32
546   %float_1 = OpConstant %float 1
547          %12 = OpVariable %float Output
548          %10 = OpFunction %void None %6
549          %14 = OpLabel
550                OpStore %12 %float_1
551                OpReturn
552                OpFunctionEnd
553 )";
554   // clang-format on
555 
556   std::unique_ptr<IRContext> localContext =
557       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
558                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
559   EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n"
560                                    << text << std::endl;
561 
562   std::vector<uint32_t> processed;
563   Pass::ProcessFunction mark_visited = [&processed](Function* fp) {
564     processed.push_back(fp->result_id());
565     return false;
566   };
567   localContext->ProcessReachableCallTree(mark_visited);
568   EXPECT_THAT(processed, UnorderedElementsAre(10));
569 }
570 
TEST_F(IRContextTest,IdBoundTestAtLimit)571 TEST_F(IRContextTest, IdBoundTestAtLimit) {
572   const std::string text = R"(
573 OpCapability Shader
574 OpCapability Linkage
575 OpMemoryModel Logical GLSL450
576 %1 = OpTypeVoid
577 %2 = OpTypeFunction %1
578 %3 = OpFunction %1 None %2
579 %4 = OpLabel
580 OpReturn
581 OpFunctionEnd)";
582 
583   std::unique_ptr<IRContext> context =
584       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
585                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
586   uint32_t current_bound = context->module()->id_bound();
587   context->set_max_id_bound(current_bound);
588   uint32_t next_id_bound = context->TakeNextId();
589   EXPECT_EQ(next_id_bound, 0);
590   EXPECT_EQ(current_bound, context->module()->id_bound());
591   next_id_bound = context->TakeNextId();
592   EXPECT_EQ(next_id_bound, 0);
593 }
594 
TEST_F(IRContextTest,IdBoundTestBelowLimit)595 TEST_F(IRContextTest, IdBoundTestBelowLimit) {
596   const std::string text = R"(
597 OpCapability Shader
598 OpCapability Linkage
599 OpMemoryModel Logical GLSL450
600 %1 = OpTypeVoid
601 %2 = OpTypeFunction %1
602 %3 = OpFunction %1 None %2
603 %4 = OpLabel
604 OpReturn
605 OpFunctionEnd)";
606 
607   std::unique_ptr<IRContext> context =
608       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
609                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
610   uint32_t current_bound = context->module()->id_bound();
611   context->set_max_id_bound(current_bound + 100);
612   uint32_t next_id_bound = context->TakeNextId();
613   EXPECT_EQ(next_id_bound, current_bound);
614   EXPECT_EQ(current_bound + 1, context->module()->id_bound());
615   next_id_bound = context->TakeNextId();
616   EXPECT_EQ(next_id_bound, current_bound + 1);
617 }
618 
TEST_F(IRContextTest,IdBoundTestNearLimit)619 TEST_F(IRContextTest, IdBoundTestNearLimit) {
620   const std::string text = R"(
621 OpCapability Shader
622 OpCapability Linkage
623 OpMemoryModel Logical GLSL450
624 %1 = OpTypeVoid
625 %2 = OpTypeFunction %1
626 %3 = OpFunction %1 None %2
627 %4 = OpLabel
628 OpReturn
629 OpFunctionEnd)";
630 
631   std::unique_ptr<IRContext> context =
632       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
633                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
634   uint32_t current_bound = context->module()->id_bound();
635   context->set_max_id_bound(current_bound + 1);
636   uint32_t next_id_bound = context->TakeNextId();
637   EXPECT_EQ(next_id_bound, current_bound);
638   EXPECT_EQ(current_bound + 1, context->module()->id_bound());
639   next_id_bound = context->TakeNextId();
640   EXPECT_EQ(next_id_bound, 0);
641 }
642 
TEST_F(IRContextTest,IdBoundTestUIntMax)643 TEST_F(IRContextTest, IdBoundTestUIntMax) {
644   const std::string text = R"(
645 OpCapability Shader
646 OpCapability Linkage
647 OpMemoryModel Logical GLSL450
648 %1 = OpTypeVoid
649 %2 = OpTypeFunction %1
650 %3 = OpFunction %1 None %2
651 %4294967294 = OpLabel ; ID is UINT_MAX-1
652 OpReturn
653 OpFunctionEnd)";
654 
655   std::unique_ptr<IRContext> context =
656       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
657                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
658   uint32_t current_bound = context->module()->id_bound();
659 
660   // Expecting |BuildModule| to preserve the numeric ids.
661   EXPECT_EQ(current_bound, std::numeric_limits<uint32_t>::max());
662 
663   context->set_max_id_bound(current_bound);
664   uint32_t next_id_bound = context->TakeNextId();
665   EXPECT_EQ(next_id_bound, 0);
666   EXPECT_EQ(current_bound, context->module()->id_bound());
667 }
668 
TEST_F(IRContextTest,CfgAndDomAnalysis)669 TEST_F(IRContextTest, CfgAndDomAnalysis) {
670   const std::string text = R"(
671 OpCapability Shader
672 OpCapability Linkage
673 OpMemoryModel Logical GLSL450
674 %1 = OpTypeVoid
675 %2 = OpTypeFunction %1
676 %3 = OpFunction %1 None %2
677 %4 = OpLabel
678 OpReturn
679 OpFunctionEnd)";
680 
681   std::unique_ptr<IRContext> ctx =
682       BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
683                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
684 
685   // Building the dominator analysis should build the CFG.
686   ASSERT_TRUE(ctx->module()->begin() != ctx->module()->end());
687   ctx->GetDominatorAnalysis(&*ctx->module()->begin());
688 
689   EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
690   EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
691 
692   // Invalidating the CFG analysis should invalidate the dominator analysis.
693   ctx->InvalidateAnalyses(IRContext::kAnalysisCFG);
694   EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
695   EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
696 }
697 
TEST_F(IRContextTest,AsanErrorTest)698 TEST_F(IRContextTest, AsanErrorTest) {
699   std::string shader = R"(
700                OpCapability Shader
701           %1 = OpExtInstImport "GLSL.std.450"
702                OpMemoryModel Logical GLSL450
703                OpEntryPoint Fragment %4 "main"
704                OpExecutionMode %4 OriginUpperLeft
705                OpSource ESSL 310
706                OpName %4 "main"
707                OpName %8 "x"
708                OpName %10 "y"
709                OpDecorate %8 RelaxedPrecision
710                OpDecorate %10 RelaxedPrecision
711                OpDecorate %11 RelaxedPrecision
712           %2 = OpTypeVoid
713           %3 = OpTypeFunction %2
714           %6 = OpTypeInt 32 1
715           %7 = OpTypePointer Function %6
716           %9 = OpConstant %6 1
717           %4 = OpFunction %2 None %3
718           %5 = OpLabel
719           %8 = OpVariable %7 Function
720          %10 = OpVariable %7 Function
721                OpStore %8 %9
722          %11 = OpLoad %6 %8
723 	       OpBranch %20
724 	 %20 = OpLabel
725 	 %21 = OpPhi %6 %11 %5
726          OpStore %10 %21
727          OpReturn
728          OpFunctionEnd
729   )";
730 
731   const auto env = SPV_ENV_UNIVERSAL_1_3;
732   const auto consumer = nullptr;
733   const auto context = BuildModule(
734       env, consumer, shader, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
735 
736   opt::Function* fun =
737       context->cfg()->block(5)->GetParent();  // Computes the CFG analysis
738   opt::DominatorAnalysis* dom = nullptr;
739   dom = context->GetDominatorAnalysis(fun);  // Computes the dominator analysis,
740                                              // which depends on the CFG
741                                              // analysis
742   context->InvalidateAnalysesExceptFor(
743       opt::IRContext::Analysis::kAnalysisDominatorAnalysis);  // Invalidates the
744                                                               // CFG analysis
745   dom = context->GetDominatorAnalysis(
746       fun);  // Recompute the CFG analysis because the Dominator tree uses it.
747   auto bb = dom->ImmediateDominator(5);
748   std::cout
749       << bb->id();  // Make sure asan does not complain about use after free.
750 }
751 
752 }  // namespace
753 }  // namespace opt
754 }  // namespace spvtools
755