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 "OpenCLDebugInfo100.h"
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "source/opt/pass.h"
26 #include "test/opt/pass_fixture.h"
27 #include "test/opt/pass_utils.h"
28
29 static const uint32_t kDebugDeclareOperandVariableIndex = 5;
30 static const uint32_t kDebugValueOperandValueIndex = 5;
31
32 namespace spvtools {
33 namespace opt {
34 namespace {
35
36 using Analysis = IRContext::Analysis;
37 using ::testing::Each;
38 using ::testing::UnorderedElementsAre;
39
40 class NoopPassPreservesNothing : public Pass {
41 public:
NoopPassPreservesNothing(Status s)42 NoopPassPreservesNothing(Status s) : Pass(), status_to_return_(s) {}
43
name() const44 const char* name() const override { return "noop-pass"; }
Process()45 Status Process() override { return status_to_return_; }
46
47 private:
48 Status status_to_return_;
49 };
50
51 class NoopPassPreservesAll : public Pass {
52 public:
NoopPassPreservesAll(Status s)53 NoopPassPreservesAll(Status s) : Pass(), status_to_return_(s) {}
54
name() const55 const char* name() const override { return "noop-pass"; }
Process()56 Status Process() override { return status_to_return_; }
57
GetPreservedAnalyses()58 Analysis GetPreservedAnalyses() override {
59 return Analysis(IRContext::kAnalysisEnd - 1);
60 }
61
62 private:
63 Status status_to_return_;
64 };
65
66 class NoopPassPreservesFirst : public Pass {
67 public:
NoopPassPreservesFirst(Status s)68 NoopPassPreservesFirst(Status s) : Pass(), status_to_return_(s) {}
69
name() const70 const char* name() const override { return "noop-pass"; }
Process()71 Status Process() override { return status_to_return_; }
72
GetPreservedAnalyses()73 Analysis GetPreservedAnalyses() override { return IRContext::kAnalysisBegin; }
74
75 private:
76 Status status_to_return_;
77 };
78
79 using IRContextTest = PassTest<::testing::Test>;
80
TEST_F(IRContextTest,IndividualValidAfterBuild)81 TEST_F(IRContextTest, IndividualValidAfterBuild) {
82 std::unique_ptr<Module> module(new Module());
83 IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
84 spvtools::MessageConsumer());
85
86 for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
87 i <<= 1) {
88 localContext.BuildInvalidAnalyses(i);
89 EXPECT_TRUE(localContext.AreAnalysesValid(i));
90 }
91 }
92
TEST_F(IRContextTest,AllValidAfterBuild)93 TEST_F(IRContextTest, AllValidAfterBuild) {
94 std::unique_ptr<Module> module = MakeUnique<Module>();
95 IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
96 spvtools::MessageConsumer());
97
98 Analysis built_analyses = IRContext::kAnalysisNone;
99 for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
100 i <<= 1) {
101 localContext.BuildInvalidAnalyses(i);
102 built_analyses |= i;
103 }
104 EXPECT_TRUE(localContext.AreAnalysesValid(built_analyses));
105 }
106
TEST_F(IRContextTest,AllValidAfterPassNoChange)107 TEST_F(IRContextTest, AllValidAfterPassNoChange) {
108 std::unique_ptr<Module> module = MakeUnique<Module>();
109 IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
110 spvtools::MessageConsumer());
111
112 Analysis built_analyses = IRContext::kAnalysisNone;
113 for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
114 i <<= 1) {
115 localContext.BuildInvalidAnalyses(i);
116 built_analyses |= i;
117 }
118
119 NoopPassPreservesNothing pass(Pass::Status::SuccessWithoutChange);
120 Pass::Status s = pass.Run(&localContext);
121 EXPECT_EQ(s, Pass::Status::SuccessWithoutChange);
122 EXPECT_TRUE(localContext.AreAnalysesValid(built_analyses));
123 }
124
TEST_F(IRContextTest,NoneValidAfterPassWithChange)125 TEST_F(IRContextTest, NoneValidAfterPassWithChange) {
126 std::unique_ptr<Module> module = MakeUnique<Module>();
127 IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
128 spvtools::MessageConsumer());
129
130 for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
131 i <<= 1) {
132 localContext.BuildInvalidAnalyses(i);
133 }
134
135 NoopPassPreservesNothing pass(Pass::Status::SuccessWithChange);
136 Pass::Status s = pass.Run(&localContext);
137 EXPECT_EQ(s, Pass::Status::SuccessWithChange);
138 for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
139 i <<= 1) {
140 EXPECT_FALSE(localContext.AreAnalysesValid(i));
141 }
142 }
143
TEST_F(IRContextTest,AllPreservedAfterPassWithChange)144 TEST_F(IRContextTest, AllPreservedAfterPassWithChange) {
145 std::unique_ptr<Module> module = MakeUnique<Module>();
146 IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
147 spvtools::MessageConsumer());
148
149 for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
150 i <<= 1) {
151 localContext.BuildInvalidAnalyses(i);
152 }
153
154 NoopPassPreservesAll pass(Pass::Status::SuccessWithChange);
155 Pass::Status s = pass.Run(&localContext);
156 EXPECT_EQ(s, Pass::Status::SuccessWithChange);
157 for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
158 i <<= 1) {
159 EXPECT_TRUE(localContext.AreAnalysesValid(i));
160 }
161 }
162
TEST_F(IRContextTest,PreserveFirstOnlyAfterPassWithChange)163 TEST_F(IRContextTest, PreserveFirstOnlyAfterPassWithChange) {
164 std::unique_ptr<Module> module = MakeUnique<Module>();
165 IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module),
166 spvtools::MessageConsumer());
167
168 for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd;
169 i <<= 1) {
170 localContext.BuildInvalidAnalyses(i);
171 }
172
173 NoopPassPreservesFirst pass(Pass::Status::SuccessWithChange);
174 Pass::Status s = pass.Run(&localContext);
175 EXPECT_EQ(s, Pass::Status::SuccessWithChange);
176 EXPECT_TRUE(localContext.AreAnalysesValid(IRContext::kAnalysisBegin));
177 for (Analysis i = IRContext::kAnalysisBegin << 1; i < IRContext::kAnalysisEnd;
178 i <<= 1) {
179 EXPECT_FALSE(localContext.AreAnalysesValid(i));
180 }
181 }
182
TEST_F(IRContextTest,KillMemberName)183 TEST_F(IRContextTest, KillMemberName) {
184 const std::string text = R"(
185 OpCapability Shader
186 %1 = OpExtInstImport "GLSL.std.450"
187 OpMemoryModel Logical GLSL450
188 OpEntryPoint Fragment %2 "main"
189 OpExecutionMode %2 OriginUpperLeft
190 OpSource GLSL 430
191 OpName %3 "stuff"
192 OpMemberName %3 0 "refZ"
193 OpMemberDecorate %3 0 Offset 0
194 OpDecorate %3 Block
195 %4 = OpTypeFloat 32
196 %3 = OpTypeStruct %4
197 %5 = OpTypeVoid
198 %6 = OpTypeFunction %5
199 %2 = OpFunction %5 None %6
200 %7 = OpLabel
201 OpReturn
202 OpFunctionEnd
203 )";
204
205 std::unique_ptr<IRContext> context =
206 BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
207
208 // Build the decoration manager.
209 context->get_decoration_mgr();
210
211 // Delete the OpTypeStruct. Should delete the OpName, OpMemberName, and
212 // OpMemberDecorate associated with it.
213 context->KillDef(3);
214
215 // Make sure all of the name are removed.
216 for (auto& inst : context->debugs2()) {
217 EXPECT_EQ(inst.opcode(), SpvOpNop);
218 }
219
220 // Make sure all of the decorations are removed.
221 for (auto& inst : context->annotations()) {
222 EXPECT_EQ(inst.opcode(), SpvOpNop);
223 }
224 }
225
TEST_F(IRContextTest,KillGroupDecoration)226 TEST_F(IRContextTest, KillGroupDecoration) {
227 const std::string text = R"(
228 OpCapability Shader
229 %1 = OpExtInstImport "GLSL.std.450"
230 OpMemoryModel Logical GLSL450
231 OpEntryPoint Fragment %2 "main"
232 OpExecutionMode %2 OriginUpperLeft
233 OpSource GLSL 430
234 OpDecorate %3 Restrict
235 %3 = OpDecorationGroup
236 OpGroupDecorate %3 %4 %5
237 %6 = OpTypeFloat 32
238 %7 = OpTypePointer Function %6
239 %8 = OpTypeStruct %6
240 %9 = OpTypeVoid
241 %10 = OpTypeFunction %9
242 %2 = OpFunction %9 None %10
243 %11 = OpLabel
244 %4 = OpVariable %7 Function
245 %5 = OpVariable %7 Function
246 OpReturn
247 OpFunctionEnd
248 )";
249
250 std::unique_ptr<IRContext> context =
251 BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
252
253 // Build the decoration manager.
254 context->get_decoration_mgr();
255
256 // Delete the second variable.
257 context->KillDef(5);
258
259 // The three decorations instructions should still be there. The first two
260 // should be the same, but the third should have %5 removed.
261
262 // Check the OpDecorate instruction
263 auto inst = context->annotation_begin();
264 EXPECT_EQ(inst->opcode(), SpvOpDecorate);
265 EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
266
267 // Check the OpDecorationGroup Instruction
268 ++inst;
269 EXPECT_EQ(inst->opcode(), SpvOpDecorationGroup);
270 EXPECT_EQ(inst->result_id(), 3);
271
272 // Check that %5 is no longer part of the group.
273 ++inst;
274 EXPECT_EQ(inst->opcode(), SpvOpGroupDecorate);
275 EXPECT_EQ(inst->NumInOperands(), 2);
276 EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
277 EXPECT_EQ(inst->GetSingleWordInOperand(1), 4);
278
279 // Check that we are at the end.
280 ++inst;
281 EXPECT_EQ(inst, context->annotation_end());
282 }
283
TEST_F(IRContextTest,TakeNextUniqueIdIncrementing)284 TEST_F(IRContextTest, TakeNextUniqueIdIncrementing) {
285 const uint32_t NUM_TESTS = 1000;
286 IRContext localContext(SPV_ENV_UNIVERSAL_1_2, nullptr);
287 for (uint32_t i = 1; i < NUM_TESTS; ++i)
288 EXPECT_EQ(i, localContext.TakeNextUniqueId());
289 }
290
TEST_F(IRContextTest,KillGroupDecorationWitNoDecorations)291 TEST_F(IRContextTest, KillGroupDecorationWitNoDecorations) {
292 const std::string text = R"(
293 OpCapability Shader
294 %1 = OpExtInstImport "GLSL.std.450"
295 OpMemoryModel Logical GLSL450
296 OpEntryPoint Fragment %2 "main"
297 OpExecutionMode %2 OriginUpperLeft
298 OpSource GLSL 430
299 %3 = OpDecorationGroup
300 OpGroupDecorate %3 %4 %5
301 %6 = OpTypeFloat 32
302 %7 = OpTypePointer Function %6
303 %8 = OpTypeStruct %6
304 %9 = OpTypeVoid
305 %10 = OpTypeFunction %9
306 %2 = OpFunction %9 None %10
307 %11 = OpLabel
308 %4 = OpVariable %7 Function
309 %5 = OpVariable %7 Function
310 OpReturn
311 OpFunctionEnd
312 )";
313
314 std::unique_ptr<IRContext> context =
315 BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
316
317 // Build the decoration manager.
318 context->get_decoration_mgr();
319
320 // Delete the second variable.
321 context->KillDef(5);
322
323 // The two decoration instructions should still be there. The first one
324 // should be the same, but the second should have %5 removed.
325
326 // Check the OpDecorationGroup Instruction
327 auto inst = context->annotation_begin();
328 EXPECT_EQ(inst->opcode(), SpvOpDecorationGroup);
329 EXPECT_EQ(inst->result_id(), 3);
330
331 // Check that %5 is no longer part of the group.
332 ++inst;
333 EXPECT_EQ(inst->opcode(), SpvOpGroupDecorate);
334 EXPECT_EQ(inst->NumInOperands(), 2);
335 EXPECT_EQ(inst->GetSingleWordInOperand(0), 3);
336 EXPECT_EQ(inst->GetSingleWordInOperand(1), 4);
337
338 // Check that we are at the end.
339 ++inst;
340 EXPECT_EQ(inst, context->annotation_end());
341 }
342
TEST_F(IRContextTest,KillDecorationGroup)343 TEST_F(IRContextTest, KillDecorationGroup) {
344 const std::string text = R"(
345 OpCapability Shader
346 %1 = OpExtInstImport "GLSL.std.450"
347 OpMemoryModel Logical GLSL450
348 OpEntryPoint Fragment %2 "main"
349 OpExecutionMode %2 OriginUpperLeft
350 OpSource GLSL 430
351 %3 = OpDecorationGroup
352 OpGroupDecorate %3 %4 %5
353 %6 = OpTypeFloat 32
354 %7 = OpTypePointer Function %6
355 %8 = OpTypeStruct %6
356 %9 = OpTypeVoid
357 %10 = OpTypeFunction %9
358 %2 = OpFunction %9 None %10
359 %11 = OpLabel
360 %4 = OpVariable %7 Function
361 %5 = OpVariable %7 Function
362 OpReturn
363 OpFunctionEnd
364 )";
365
366 std::unique_ptr<IRContext> context =
367 BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
368
369 // Build the decoration manager.
370 context->get_decoration_mgr();
371
372 // Delete the second variable.
373 context->KillDef(3);
374
375 // Check the OpDecorationGroup Instruction is still there.
376 EXPECT_TRUE(context->annotations().empty());
377 }
378
TEST_F(IRContextTest,KillFunctionFromDebugFunction)379 TEST_F(IRContextTest, KillFunctionFromDebugFunction) {
380 const std::string text = R"(
381 OpCapability Shader
382 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
383 OpMemoryModel Logical GLSL450
384 OpEntryPoint Fragment %2 "main"
385 OpExecutionMode %2 OriginUpperLeft
386 %3 = OpString "ps.hlsl"
387 %4 = OpString "foo"
388 OpSource HLSL 600
389 %void = OpTypeVoid
390 %6 = OpTypeFunction %void
391 %7 = OpExtInst %void %1 DebugSource %3
392 %8 = OpExtInst %void %1 DebugCompilationUnit 1 4 %7 HLSL
393 %9 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void
394 %10 = OpExtInst %void %1 DebugFunction %4 %9 %7 1 1 %8 %4 FlagIsProtected|FlagIsPrivate 1 %11
395 %2 = OpFunction %void None %6
396 %12 = OpLabel
397 OpReturn
398 OpFunctionEnd
399 %11 = OpFunction %void None %6
400 %13 = OpLabel
401 OpReturn
402 OpFunctionEnd
403 )";
404
405 std::unique_ptr<IRContext> context =
406 BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
407
408 // Delete the second variable.
409 context->KillDef(11);
410
411 // Get DebugInfoNone id.
412 uint32_t debug_info_none_id = 0;
413 for (auto it = context->ext_inst_debuginfo_begin();
414 it != context->ext_inst_debuginfo_end(); ++it) {
415 if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
416 debug_info_none_id = it->result_id();
417 }
418 }
419 EXPECT_NE(0, debug_info_none_id);
420
421 // Check the Function operand of DebugFunction is DebugInfoNone.
422 const uint32_t kDebugFunctionOperandFunctionIndex = 13;
423 bool checked = false;
424 for (auto it = context->ext_inst_debuginfo_begin();
425 it != context->ext_inst_debuginfo_end(); ++it) {
426 if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) {
427 EXPECT_FALSE(checked);
428 EXPECT_EQ(it->GetOperand(kDebugFunctionOperandFunctionIndex).words[0],
429 debug_info_none_id);
430 checked = true;
431 }
432 }
433 EXPECT_TRUE(checked);
434 }
435
TEST_F(IRContextTest,KillVariableFromDebugGlobalVariable)436 TEST_F(IRContextTest, KillVariableFromDebugGlobalVariable) {
437 const std::string text = R"(
438 OpCapability Shader
439 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
440 OpMemoryModel Logical GLSL450
441 OpEntryPoint Fragment %2 "main"
442 OpExecutionMode %2 OriginUpperLeft
443 %3 = OpString "ps.hlsl"
444 %4 = OpString "foo"
445 %5 = OpString "int"
446 OpSource HLSL 600
447 %uint = OpTypeInt 32 0
448 %uint_32 = OpConstant %uint 32
449 %_ptr_Private_uint = OpTypePointer Private %uint
450 %void = OpTypeVoid
451 %10 = OpTypeFunction %void
452 %11 = OpVariable %_ptr_Private_uint Private
453 %12 = OpExtInst %void %1 DebugSource %3
454 %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL
455 %14 = OpExtInst %void %1 DebugTypeBasic %5 %uint_32 Signed
456 %15 = OpExtInst %void %1 DebugGlobalVariable %4 %14 %12 1 12 %13 %4 %11 FlagIsDefinition
457 %2 = OpFunction %void None %10
458 %16 = OpLabel
459 OpReturn
460 OpFunctionEnd
461 )";
462
463 std::unique_ptr<IRContext> context =
464 BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
465
466 // Delete the second variable.
467 context->KillDef(11);
468
469 // Get DebugInfoNone id.
470 uint32_t debug_info_none_id = 0;
471 for (auto it = context->ext_inst_debuginfo_begin();
472 it != context->ext_inst_debuginfo_end(); ++it) {
473 if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
474 debug_info_none_id = it->result_id();
475 }
476 }
477 EXPECT_NE(0, debug_info_none_id);
478
479 // Check the Function operand of DebugFunction is DebugInfoNone.
480 const uint32_t kDebugGlobalVariableOperandVariableIndex = 11;
481 bool checked = false;
482 for (auto it = context->ext_inst_debuginfo_begin();
483 it != context->ext_inst_debuginfo_end(); ++it) {
484 if (it->GetOpenCL100DebugOpcode() ==
485 OpenCLDebugInfo100DebugGlobalVariable) {
486 EXPECT_FALSE(checked);
487 EXPECT_EQ(
488 it->GetOperand(kDebugGlobalVariableOperandVariableIndex).words[0],
489 debug_info_none_id);
490 checked = true;
491 }
492 }
493 EXPECT_TRUE(checked);
494 }
495
TEST_F(IRContextTest,BasicVisitFromEntryPoint)496 TEST_F(IRContextTest, BasicVisitFromEntryPoint) {
497 // Make sure we visit the entry point, and the function it calls.
498 // Do not visit Dead or Exported.
499 const std::string text = R"(
500 OpCapability Shader
501 OpMemoryModel Logical GLSL450
502 OpEntryPoint Fragment %10 "main"
503 OpName %10 "main"
504 OpName %Dead "Dead"
505 OpName %11 "Constant"
506 OpName %ExportedFunc "ExportedFunc"
507 OpDecorate %ExportedFunc LinkageAttributes "ExportedFunc" Export
508 %void = OpTypeVoid
509 %6 = OpTypeFunction %void
510 %10 = OpFunction %void None %6
511 %14 = OpLabel
512 %15 = OpFunctionCall %void %11
513 %16 = OpFunctionCall %void %11
514 OpReturn
515 OpFunctionEnd
516 %11 = OpFunction %void None %6
517 %18 = OpLabel
518 OpReturn
519 OpFunctionEnd
520 %Dead = OpFunction %void None %6
521 %19 = OpLabel
522 OpReturn
523 OpFunctionEnd
524 %ExportedFunc = OpFunction %void None %7
525 %20 = OpLabel
526 %21 = OpFunctionCall %void %11
527 OpReturn
528 OpFunctionEnd
529 )";
530 // clang-format on
531
532 std::unique_ptr<IRContext> localContext =
533 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
534 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
535 EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n"
536 << text << std::endl;
537 std::vector<uint32_t> processed;
538 Pass::ProcessFunction mark_visited = [&processed](Function* fp) {
539 processed.push_back(fp->result_id());
540 return false;
541 };
542 localContext->ProcessEntryPointCallTree(mark_visited);
543 EXPECT_THAT(processed, UnorderedElementsAre(10, 11));
544 }
545
TEST_F(IRContextTest,BasicVisitReachable)546 TEST_F(IRContextTest, BasicVisitReachable) {
547 // Make sure we visit the entry point, exported function, and the function
548 // they call. Do not visit Dead.
549 const std::string text = R"(
550 OpCapability Shader
551 OpMemoryModel Logical GLSL450
552 OpEntryPoint Fragment %10 "main"
553 OpName %10 "main"
554 OpName %Dead "Dead"
555 OpName %11 "Constant"
556 OpName %12 "ExportedFunc"
557 OpName %13 "Constant2"
558 OpDecorate %12 LinkageAttributes "ExportedFunc" Export
559 %void = OpTypeVoid
560 %6 = OpTypeFunction %void
561 %10 = OpFunction %void None %6
562 %14 = OpLabel
563 %15 = OpFunctionCall %void %11
564 %16 = OpFunctionCall %void %11
565 OpReturn
566 OpFunctionEnd
567 %11 = OpFunction %void None %6
568 %18 = OpLabel
569 OpReturn
570 OpFunctionEnd
571 %Dead = OpFunction %void None %6
572 %19 = OpLabel
573 OpReturn
574 OpFunctionEnd
575 %12 = OpFunction %void None %6
576 %20 = OpLabel
577 %21 = OpFunctionCall %void %13
578 OpReturn
579 OpFunctionEnd
580 %13 = OpFunction %void None %6
581 %22 = OpLabel
582 OpReturn
583 OpFunctionEnd
584 )";
585 // clang-format on
586
587 std::unique_ptr<IRContext> localContext =
588 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
589 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
590 EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n"
591 << text << std::endl;
592
593 std::vector<uint32_t> processed;
594 Pass::ProcessFunction mark_visited = [&processed](Function* fp) {
595 processed.push_back(fp->result_id());
596 return false;
597 };
598 localContext->ProcessReachableCallTree(mark_visited);
599 EXPECT_THAT(processed, UnorderedElementsAre(10, 11, 12, 13));
600 }
601
TEST_F(IRContextTest,BasicVisitOnlyOnce)602 TEST_F(IRContextTest, BasicVisitOnlyOnce) {
603 // Make sure we visit %12 only once, even if it is called from two different
604 // functions.
605 const std::string text = R"(
606 OpCapability Shader
607 OpMemoryModel Logical GLSL450
608 OpEntryPoint Fragment %10 "main"
609 OpName %10 "main"
610 OpName %Dead "Dead"
611 OpName %11 "Constant"
612 OpName %12 "ExportedFunc"
613 OpDecorate %12 LinkageAttributes "ExportedFunc" Export
614 %void = OpTypeVoid
615 %6 = OpTypeFunction %void
616 %10 = OpFunction %void None %6
617 %14 = OpLabel
618 %15 = OpFunctionCall %void %11
619 %16 = OpFunctionCall %void %12
620 OpReturn
621 OpFunctionEnd
622 %11 = OpFunction %void None %6
623 %18 = OpLabel
624 %19 = OpFunctionCall %void %12
625 OpReturn
626 OpFunctionEnd
627 %Dead = OpFunction %void None %6
628 %20 = OpLabel
629 OpReturn
630 OpFunctionEnd
631 %12 = OpFunction %void None %6
632 %21 = OpLabel
633 OpReturn
634 OpFunctionEnd
635 )";
636 // clang-format on
637
638 std::unique_ptr<IRContext> localContext =
639 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
640 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
641 EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n"
642 << text << std::endl;
643
644 std::vector<uint32_t> processed;
645 Pass::ProcessFunction mark_visited = [&processed](Function* fp) {
646 processed.push_back(fp->result_id());
647 return false;
648 };
649 localContext->ProcessReachableCallTree(mark_visited);
650 EXPECT_THAT(processed, UnorderedElementsAre(10, 11, 12));
651 }
652
TEST_F(IRContextTest,BasicDontVisitExportedVariable)653 TEST_F(IRContextTest, BasicDontVisitExportedVariable) {
654 // Make sure we only visit functions and not exported variables.
655 const std::string text = R"(
656 OpCapability Shader
657 OpMemoryModel Logical GLSL450
658 OpEntryPoint Fragment %10 "main"
659 OpExecutionMode %10 OriginUpperLeft
660 OpSource GLSL 150
661 OpName %10 "main"
662 OpName %12 "export_var"
663 OpDecorate %12 LinkageAttributes "export_var" Export
664 %void = OpTypeVoid
665 %6 = OpTypeFunction %void
666 %float = OpTypeFloat 32
667 %float_1 = OpConstant %float 1
668 %12 = OpVariable %float Output
669 %10 = OpFunction %void None %6
670 %14 = OpLabel
671 OpStore %12 %float_1
672 OpReturn
673 OpFunctionEnd
674 )";
675 // clang-format on
676
677 std::unique_ptr<IRContext> localContext =
678 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
679 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
680 EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n"
681 << text << std::endl;
682
683 std::vector<uint32_t> processed;
684 Pass::ProcessFunction mark_visited = [&processed](Function* fp) {
685 processed.push_back(fp->result_id());
686 return false;
687 };
688 localContext->ProcessReachableCallTree(mark_visited);
689 EXPECT_THAT(processed, UnorderedElementsAre(10));
690 }
691
TEST_F(IRContextTest,IdBoundTestAtLimit)692 TEST_F(IRContextTest, IdBoundTestAtLimit) {
693 const std::string text = R"(
694 OpCapability Shader
695 OpCapability Linkage
696 OpMemoryModel Logical GLSL450
697 %1 = OpTypeVoid
698 %2 = OpTypeFunction %1
699 %3 = OpFunction %1 None %2
700 %4 = OpLabel
701 OpReturn
702 OpFunctionEnd)";
703
704 std::unique_ptr<IRContext> context =
705 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
706 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
707 uint32_t current_bound = context->module()->id_bound();
708 context->set_max_id_bound(current_bound);
709 uint32_t next_id_bound = context->TakeNextId();
710 EXPECT_EQ(next_id_bound, 0);
711 EXPECT_EQ(current_bound, context->module()->id_bound());
712 next_id_bound = context->TakeNextId();
713 EXPECT_EQ(next_id_bound, 0);
714 }
715
TEST_F(IRContextTest,IdBoundTestBelowLimit)716 TEST_F(IRContextTest, IdBoundTestBelowLimit) {
717 const std::string text = R"(
718 OpCapability Shader
719 OpCapability Linkage
720 OpMemoryModel Logical GLSL450
721 %1 = OpTypeVoid
722 %2 = OpTypeFunction %1
723 %3 = OpFunction %1 None %2
724 %4 = OpLabel
725 OpReturn
726 OpFunctionEnd)";
727
728 std::unique_ptr<IRContext> context =
729 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
730 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
731 uint32_t current_bound = context->module()->id_bound();
732 context->set_max_id_bound(current_bound + 100);
733 uint32_t next_id_bound = context->TakeNextId();
734 EXPECT_EQ(next_id_bound, current_bound);
735 EXPECT_EQ(current_bound + 1, context->module()->id_bound());
736 next_id_bound = context->TakeNextId();
737 EXPECT_EQ(next_id_bound, current_bound + 1);
738 }
739
TEST_F(IRContextTest,IdBoundTestNearLimit)740 TEST_F(IRContextTest, IdBoundTestNearLimit) {
741 const std::string text = R"(
742 OpCapability Shader
743 OpCapability Linkage
744 OpMemoryModel Logical GLSL450
745 %1 = OpTypeVoid
746 %2 = OpTypeFunction %1
747 %3 = OpFunction %1 None %2
748 %4 = OpLabel
749 OpReturn
750 OpFunctionEnd)";
751
752 std::unique_ptr<IRContext> context =
753 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
754 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
755 uint32_t current_bound = context->module()->id_bound();
756 context->set_max_id_bound(current_bound + 1);
757 uint32_t next_id_bound = context->TakeNextId();
758 EXPECT_EQ(next_id_bound, current_bound);
759 EXPECT_EQ(current_bound + 1, context->module()->id_bound());
760 next_id_bound = context->TakeNextId();
761 EXPECT_EQ(next_id_bound, 0);
762 }
763
TEST_F(IRContextTest,IdBoundTestUIntMax)764 TEST_F(IRContextTest, IdBoundTestUIntMax) {
765 const std::string text = R"(
766 OpCapability Shader
767 OpCapability Linkage
768 OpMemoryModel Logical GLSL450
769 %1 = OpTypeVoid
770 %2 = OpTypeFunction %1
771 %3 = OpFunction %1 None %2
772 %4294967294 = OpLabel ; ID is UINT_MAX-1
773 OpReturn
774 OpFunctionEnd)";
775
776 std::unique_ptr<IRContext> context =
777 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
778 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
779 uint32_t current_bound = context->module()->id_bound();
780
781 // Expecting |BuildModule| to preserve the numeric ids.
782 EXPECT_EQ(current_bound, std::numeric_limits<uint32_t>::max());
783
784 context->set_max_id_bound(current_bound);
785 uint32_t next_id_bound = context->TakeNextId();
786 EXPECT_EQ(next_id_bound, 0);
787 EXPECT_EQ(current_bound, context->module()->id_bound());
788 }
789
TEST_F(IRContextTest,CfgAndDomAnalysis)790 TEST_F(IRContextTest, CfgAndDomAnalysis) {
791 const std::string text = R"(
792 OpCapability Shader
793 OpCapability Linkage
794 OpMemoryModel Logical GLSL450
795 %1 = OpTypeVoid
796 %2 = OpTypeFunction %1
797 %3 = OpFunction %1 None %2
798 %4 = OpLabel
799 OpReturn
800 OpFunctionEnd)";
801
802 std::unique_ptr<IRContext> ctx =
803 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
804 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
805
806 // Building the dominator analysis should build the CFG.
807 ASSERT_TRUE(ctx->module()->begin() != ctx->module()->end());
808 ctx->GetDominatorAnalysis(&*ctx->module()->begin());
809
810 EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
811 EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
812
813 // Invalidating the CFG analysis should invalidate the dominator analysis.
814 ctx->InvalidateAnalyses(IRContext::kAnalysisCFG);
815 EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG));
816 EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis));
817 }
818
TEST_F(IRContextTest,AsanErrorTest)819 TEST_F(IRContextTest, AsanErrorTest) {
820 std::string shader = R"(
821 OpCapability Shader
822 %1 = OpExtInstImport "GLSL.std.450"
823 OpMemoryModel Logical GLSL450
824 OpEntryPoint Fragment %4 "main"
825 OpExecutionMode %4 OriginUpperLeft
826 OpSource ESSL 310
827 OpName %4 "main"
828 OpName %8 "x"
829 OpName %10 "y"
830 OpDecorate %8 RelaxedPrecision
831 OpDecorate %10 RelaxedPrecision
832 OpDecorate %11 RelaxedPrecision
833 %2 = OpTypeVoid
834 %3 = OpTypeFunction %2
835 %6 = OpTypeInt 32 1
836 %7 = OpTypePointer Function %6
837 %9 = OpConstant %6 1
838 %4 = OpFunction %2 None %3
839 %5 = OpLabel
840 %8 = OpVariable %7 Function
841 %10 = OpVariable %7 Function
842 OpStore %8 %9
843 %11 = OpLoad %6 %8
844 OpBranch %20
845 %20 = OpLabel
846 %21 = OpPhi %6 %11 %5
847 OpStore %10 %21
848 OpReturn
849 OpFunctionEnd
850 )";
851
852 const auto env = SPV_ENV_UNIVERSAL_1_3;
853 const auto consumer = nullptr;
854 const auto context = BuildModule(
855 env, consumer, shader, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
856
857 opt::Function* fun =
858 context->cfg()->block(5)->GetParent(); // Computes the CFG analysis
859 opt::DominatorAnalysis* dom = nullptr;
860 dom = context->GetDominatorAnalysis(fun); // Computes the dominator analysis,
861 // which depends on the CFG
862 // analysis
863 context->InvalidateAnalysesExceptFor(
864 opt::IRContext::Analysis::kAnalysisDominatorAnalysis); // Invalidates the
865 // CFG analysis
866 dom = context->GetDominatorAnalysis(
867 fun); // Recompute the CFG analysis because the Dominator tree uses it.
868 auto bb = dom->ImmediateDominator(5);
869 std::cout
870 << bb->id(); // Make sure asan does not complain about use after free.
871 }
872
TEST_F(IRContextTest,DebugInstructionReplaceSingleUse)873 TEST_F(IRContextTest, DebugInstructionReplaceSingleUse) {
874 const std::string text = R"(
875 OpCapability Shader
876 OpCapability Linkage
877 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
878 OpMemoryModel Logical GLSL450
879 %2 = OpString "test"
880 %3 = OpTypeVoid
881 %4 = OpTypeFunction %3
882 %5 = OpTypeFloat 32
883 %6 = OpTypePointer Function %5
884 %7 = OpConstant %5 0
885 %8 = OpTypeInt 32 0
886 %9 = OpConstant %8 32
887 %10 = OpExtInst %3 %1 DebugExpression
888 %11 = OpExtInst %3 %1 DebugSource %2
889 %12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
890 %13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
891 %14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
892 %15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float
893 %16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal
894 %17 = OpFunction %3 None %4
895 %18 = OpLabel
896 %19 = OpExtInst %3 %1 DebugScope %14
897 %20 = OpVariable %6 Function
898 %26 = OpVariable %6 Function
899 OpBranch %21
900 %21 = OpLabel
901 %22 = OpPhi %5 %7 %18
902 OpBranch %23
903 %23 = OpLabel
904 OpLine %2 0 0
905 OpStore %20 %7
906 %24 = OpExtInst %3 %1 DebugValue %16 %22 %10
907 %25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10
908 OpReturn
909 OpFunctionEnd)";
910
911 std::unique_ptr<IRContext> ctx =
912 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
913 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
914 ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
915 NoopPassPreservesAll pass(Pass::Status::SuccessWithChange);
916 pass.Run(ctx.get());
917 EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
918
919 auto* dbg_value = ctx->get_def_use_mgr()->GetDef(24);
920 EXPECT_TRUE(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
921 22);
922 EXPECT_TRUE(ctx->ReplaceAllUsesWith(22, 7));
923 dbg_value = ctx->get_def_use_mgr()->GetDef(24);
924 EXPECT_TRUE(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
925 7);
926
927 auto* dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
928 EXPECT_TRUE(
929 dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26);
930 EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20));
931 dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
932 EXPECT_TRUE(
933 dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
934 }
935
TEST_F(IRContextTest,DebugInstructionReplaceAllUses)936 TEST_F(IRContextTest, DebugInstructionReplaceAllUses) {
937 const std::string text = R"(
938 OpCapability Shader
939 OpCapability Linkage
940 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
941 OpMemoryModel Logical GLSL450
942 %2 = OpString "test"
943 %3 = OpTypeVoid
944 %4 = OpTypeFunction %3
945 %5 = OpTypeFloat 32
946 %6 = OpTypePointer Function %5
947 %7 = OpConstant %5 0
948 %8 = OpTypeInt 32 0
949 %9 = OpConstant %8 32
950 %10 = OpExtInst %3 %1 DebugExpression
951 %11 = OpExtInst %3 %1 DebugSource %2
952 %12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
953 %13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
954 %14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
955 %15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float
956 %16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal
957 %27 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 1 0 %14 FlagIsLocal
958 %17 = OpFunction %3 None %4
959 %18 = OpLabel
960 %19 = OpExtInst %3 %1 DebugScope %14
961 %20 = OpVariable %6 Function
962 %26 = OpVariable %6 Function
963 OpBranch %21
964 %21 = OpLabel
965 %22 = OpPhi %5 %7 %18
966 OpBranch %23
967 %23 = OpLabel
968 OpLine %2 0 0
969 OpStore %20 %7
970 %24 = OpExtInst %3 %1 DebugValue %16 %22 %10
971 %25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10
972 %28 = OpExtInst %3 %1 DebugValue %27 %22 %10
973 %29 = OpExtInst %3 %1 DebugDeclare %27 %26 %10
974 OpReturn
975 OpFunctionEnd)";
976
977 std::unique_ptr<IRContext> ctx =
978 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
979 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
980 ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
981 NoopPassPreservesAll pass(Pass::Status::SuccessWithChange);
982 pass.Run(ctx.get());
983 EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
984
985 auto* dbg_value0 = ctx->get_def_use_mgr()->GetDef(24);
986 auto* dbg_value1 = ctx->get_def_use_mgr()->GetDef(28);
987 EXPECT_TRUE(dbg_value0->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
988 22);
989 EXPECT_TRUE(dbg_value1->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
990 22);
991 EXPECT_TRUE(ctx->ReplaceAllUsesWith(22, 7));
992 dbg_value0 = ctx->get_def_use_mgr()->GetDef(24);
993 dbg_value1 = ctx->get_def_use_mgr()->GetDef(28);
994 EXPECT_TRUE(dbg_value0->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
995 7);
996 EXPECT_TRUE(dbg_value1->GetSingleWordOperand(kDebugValueOperandValueIndex) ==
997 7);
998
999 auto* dbg_decl0 = ctx->get_def_use_mgr()->GetDef(25);
1000 auto* dbg_decl1 = ctx->get_def_use_mgr()->GetDef(29);
1001 EXPECT_TRUE(
1002 dbg_decl0->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26);
1003 EXPECT_TRUE(
1004 dbg_decl1->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26);
1005 EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20));
1006 dbg_decl0 = ctx->get_def_use_mgr()->GetDef(25);
1007 dbg_decl1 = ctx->get_def_use_mgr()->GetDef(29);
1008 EXPECT_TRUE(
1009 dbg_decl0->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
1010 EXPECT_TRUE(
1011 dbg_decl1->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20);
1012 }
1013
TEST_F(IRContextTest,DebugInstructionReplaceDebugScopeAndDebugInlinedAt)1014 TEST_F(IRContextTest, DebugInstructionReplaceDebugScopeAndDebugInlinedAt) {
1015 const std::string text = R"(
1016 OpCapability Shader
1017 OpCapability Linkage
1018 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
1019 OpMemoryModel Logical GLSL450
1020 %2 = OpString "test"
1021 %3 = OpTypeVoid
1022 %4 = OpTypeFunction %3
1023 %5 = OpTypeFloat 32
1024 %6 = OpTypePointer Function %5
1025 %7 = OpConstant %5 0
1026 %8 = OpTypeInt 32 0
1027 %9 = OpConstant %8 32
1028 %10 = OpExtInst %3 %1 DebugExpression
1029 %11 = OpExtInst %3 %1 DebugSource %2
1030 %12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
1031 %13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
1032 %14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
1033 %15 = OpExtInst %3 %1 DebugInfoNone
1034 %16 = OpExtInst %3 %1 DebugFunction %2 %13 %11 10 10 %12 %2 FlagIsProtected|FlagIsPrivate 0 %15
1035 %25 = OpExtInst %3 %1 DebugInlinedAt 0 %14
1036 %26 = OpExtInst %3 %1 DebugInlinedAt 2 %14
1037 %17 = OpFunction %3 None %4
1038 %18 = OpLabel
1039 %19 = OpExtInst %3 %1 DebugScope %14
1040 %20 = OpVariable %6 Function
1041 OpBranch %21
1042 %21 = OpLabel
1043 %24 = OpExtInst %3 %1 DebugScope %16
1044 %22 = OpPhi %5 %7 %18
1045 OpBranch %23
1046 %23 = OpLabel
1047 %27 = OpExtInst %3 %1 DebugScope %16 %25
1048 OpLine %2 0 0
1049 %28 = OpFAdd %5 %7 %7
1050 OpStore %20 %28
1051 OpReturn
1052 OpFunctionEnd)";
1053
1054 std::unique_ptr<IRContext> ctx =
1055 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1056 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1057 ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
1058 NoopPassPreservesAll pass(Pass::Status::SuccessWithChange);
1059 pass.Run(ctx.get());
1060 EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
1061
1062 auto* inst0 = ctx->get_def_use_mgr()->GetDef(20);
1063 auto* inst1 = ctx->get_def_use_mgr()->GetDef(22);
1064 auto* inst2 = ctx->get_def_use_mgr()->GetDef(28);
1065 EXPECT_EQ(inst0->GetDebugScope().GetLexicalScope(), 14);
1066 EXPECT_EQ(inst1->GetDebugScope().GetLexicalScope(), 16);
1067 EXPECT_EQ(inst2->GetDebugScope().GetLexicalScope(), 16);
1068 EXPECT_EQ(inst2->GetDebugInlinedAt(), 25);
1069
1070 EXPECT_TRUE(ctx->ReplaceAllUsesWith(14, 12));
1071 EXPECT_TRUE(ctx->ReplaceAllUsesWith(16, 14));
1072 EXPECT_TRUE(ctx->ReplaceAllUsesWith(25, 26));
1073 EXPECT_EQ(inst0->GetDebugScope().GetLexicalScope(), 12);
1074 EXPECT_EQ(inst1->GetDebugScope().GetLexicalScope(), 14);
1075 EXPECT_EQ(inst2->GetDebugScope().GetLexicalScope(), 14);
1076 EXPECT_EQ(inst2->GetDebugInlinedAt(), 26);
1077 }
1078
TEST_F(IRContextTest,AddDebugValueAfterReplaceUse)1079 TEST_F(IRContextTest, AddDebugValueAfterReplaceUse) {
1080 const std::string text = R"(
1081 OpCapability Shader
1082 OpCapability Linkage
1083 %1 = OpExtInstImport "OpenCL.DebugInfo.100"
1084 OpMemoryModel Logical GLSL450
1085 %2 = OpString "test"
1086 %3 = OpTypeVoid
1087 %4 = OpTypeFunction %3
1088 %5 = OpTypeFloat 32
1089 %6 = OpTypePointer Function %5
1090 %7 = OpConstant %5 0
1091 %8 = OpTypeInt 32 0
1092 %9 = OpConstant %8 32
1093 %10 = OpExtInst %3 %1 DebugExpression
1094 %11 = OpExtInst %3 %1 DebugSource %2
1095 %12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL
1096 %13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3
1097 %14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17
1098 %15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float
1099 %16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal
1100 %17 = OpFunction %3 None %4
1101 %18 = OpLabel
1102 %19 = OpExtInst %3 %1 DebugScope %14
1103 %20 = OpVariable %6 Function
1104 %26 = OpVariable %6 Function
1105 OpBranch %21
1106 %21 = OpLabel
1107 %27 = OpExtInst %3 %1 DebugScope %14
1108 %22 = OpPhi %5 %7 %18
1109 OpBranch %23
1110 %23 = OpLabel
1111 %28 = OpExtInst %3 %1 DebugScope %14
1112 OpLine %2 0 0
1113 OpStore %20 %7
1114 %24 = OpExtInst %3 %1 DebugValue %16 %22 %10
1115 %25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10
1116 OpReturn
1117 OpFunctionEnd)";
1118
1119 std::unique_ptr<IRContext> ctx =
1120 BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
1121 SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
1122 ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo);
1123 NoopPassPreservesAll pass(Pass::Status::SuccessWithChange);
1124 pass.Run(ctx.get());
1125 EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo));
1126
1127 // Replace all uses of result it '26' with '20'
1128 auto* dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
1129 EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
1130 26);
1131 EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20));
1132 dbg_decl = ctx->get_def_use_mgr()->GetDef(25);
1133 EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
1134 20);
1135
1136 // No DebugValue should be added because result id '26' is not used for
1137 // DebugDeclare.
1138 ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 26, 22,
1139 dbg_decl, nullptr);
1140 EXPECT_EQ(dbg_decl->NextNode()->opcode(), SpvOpReturn);
1141
1142 // DebugValue should be added because result id '20' is used for DebugDeclare.
1143 ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 20, 22,
1144 dbg_decl, nullptr);
1145 EXPECT_EQ(dbg_decl->NextNode()->GetOpenCL100DebugOpcode(),
1146 OpenCLDebugInfo100DebugValue);
1147
1148 // Replace all uses of result it '20' with '26'
1149 EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
1150 20);
1151 EXPECT_TRUE(ctx->ReplaceAllUsesWith(20, 26));
1152 EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex),
1153 26);
1154
1155 // No DebugValue should be added because result id '20' is not used for
1156 // DebugDeclare.
1157 ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 20, 7,
1158 dbg_decl, nullptr);
1159 Instruction* dbg_value = dbg_decl->NextNode();
1160 EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue);
1161 EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 22);
1162
1163 // DebugValue should be added because result id '26' is used for DebugDeclare.
1164 ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 26, 7,
1165 dbg_decl, nullptr);
1166 dbg_value = dbg_decl->NextNode();
1167 EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue);
1168 EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 7);
1169 }
1170
1171 } // namespace
1172 } // namespace opt
1173 } // namespace spvtools
1174