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