Jlm
Loading...
Searching...
No Matches
TraceTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2025 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
9#include <jlm/rvsdg/gamma.hpp>
13#include <jlm/rvsdg/theta.hpp>
14#include <jlm/rvsdg/Trace.hpp>
15#include <jlm/rvsdg/view.hpp>
16
21TEST(TraceTests, TestTraceOutputIntraProcedural_Gamma)
22{
23 using namespace jlm::rvsdg;
24
25 // Assert
26 const auto controlType = ControlType::Create(2);
27 const auto valueType = TestType::createValueType();
28
29 Graph rvsdg;
30 auto & i0 = GraphImport::Create(rvsdg, controlType, "i0");
31 auto & i1 = GraphImport::Create(rvsdg, valueType, "i1");
32 auto & i2 = GraphImport::Create(rvsdg, valueType, "i2");
33
34 const auto gammaNode = GammaNode::create(&i0, 2);
35 auto entryVar1 = gammaNode->AddEntryVar(&i1);
36 auto entryVar1Copy = gammaNode->AddEntryVar(&i1);
37 auto entryVar2 = gammaNode->AddEntryVar(&i2);
38
39 auto node = TestOperation::createNode(
40 gammaNode->subregion(1),
41 { entryVar2.branchArgument[1] },
42 { valueType });
43
44 auto exitVar0 =
45 gammaNode->AddExitVar({ entryVar1.branchArgument[0], entryVar1Copy.branchArgument[1] });
46 auto exitVar1 =
47 gammaNode->AddExitVar({ entryVar1.branchArgument[0], entryVar2.branchArgument[1] });
48 auto exitVar2 = gammaNode->AddExitVar({ entryVar2.branchArgument[0], node->output(0) });
49
50 auto & x0 = GraphExport::Create(*exitVar0.output, "x0");
51 auto & x1 = GraphExport::Create(*exitVar1.output, "x1");
52 auto & x2 = GraphExport::Create(*exitVar2.output, "x2");
53
54 view(&rvsdg.GetRootRegion(), stdout);
55
56 // Act
57 const auto & tracedX0 = traceOutputIntraProcedurally(*x0.origin(), false);
58 const auto & tracedX1 = traceOutputIntraProcedurally(*x1.origin(), false);
59 const auto & tracedX2 = traceOutputIntraProcedurally(*x2.origin(), false);
60 // Trace from within one of the context variables of the gamma
61 const auto & traceGammaEntry = traceOutputIntraProcedurally(*entryVar1.branchArgument[0], false);
62 const auto & tracedNodeInput = traceOutputIntraProcedurally(*node->input(0)->origin(), false);
63
64 // Assert
65 EXPECT_EQ(&tracedX0, &i1);
66 EXPECT_EQ(&tracedX1, x1.origin());
67 EXPECT_EQ(&tracedX2, x2.origin());
68 EXPECT_EQ(&traceGammaEntry, &i1);
69 EXPECT_EQ(&tracedNodeInput, &i2);
70}
71
75TEST(TraceTests, TestTraceOutputIntraProcedural_Theta)
76{
77 using namespace jlm::rvsdg;
78
79 // Assert
80 const auto valueType = TestType::createValueType();
81
82 Graph rvsdg;
83 auto & i0 = GraphImport::Create(rvsdg, valueType, "i0");
84 auto & i1 = GraphImport::Create(rvsdg, valueType, "i1");
85
86 const auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
87 // loopVar0 is trivially loop invariant
88 auto loopVar0 = thetaNode->AddLoopVar(&i0);
89 auto loopVar1 = thetaNode->AddLoopVar(&i1);
90
91 auto node = TestOperation::createNode(thetaNode->subregion(), { loopVar1.pre }, { valueType });
92 loopVar1.post->divert_to(node->output(0));
93
94 auto & x0 = GraphExport::Create(*loopVar0.output, "x0");
95 auto & x1 = GraphExport::Create(*loopVar1.output, "x1");
96
97 view(&rvsdg.GetRootRegion(), stdout);
98
99 // Act
100 const auto & tracedX0 = traceOutputIntraProcedurally(*x0.origin(), false);
101 const auto & tracedX1 = traceOutputIntraProcedurally(*x1.origin(), false);
102 const auto & traceLoopVar0Pre = traceOutputIntraProcedurally(*loopVar0.pre, false);
103 const auto & traceLoopVar1Pre = traceOutputIntraProcedurally(*loopVar1.pre, false);
104 const auto & tracedNodeInput = traceOutputIntraProcedurally(*node->input(0)->origin(), false);
105
106 // Assert
107 EXPECT_EQ(&tracedX0, &i0);
108 EXPECT_EQ(&tracedX1, x1.origin());
109 EXPECT_EQ(&traceLoopVar0Pre, &i0);
110 EXPECT_EQ(&traceLoopVar1Pre, loopVar1.pre);
111 EXPECT_EQ(&tracedNodeInput, loopVar1.pre);
112}
113
117TEST(TraceTests, TestTraceNestedStructuralNodes)
118{
119 using namespace jlm::rvsdg;
120
121 // Assert
122 const auto controlType = ControlType::Create(2);
123 const auto valueType = TestType::createValueType();
124
125 Graph rvsdg;
126 auto & i0 = GraphImport::Create(rvsdg, valueType, "i0");
127 auto & i1 = GraphImport::Create(rvsdg, valueType, "i1");
128 auto & i2 = GraphImport::Create(rvsdg, valueType, "i2");
129
130 const auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
131 // loop variables 0 and 1 go through the inner gamma. loopVar2 is trivially loop invariant
132 auto loopVar0 = thetaNode->AddLoopVar(&i0);
133 auto loopVar1 = thetaNode->AddLoopVar(&i1);
134 auto loopVar2 = thetaNode->AddLoopVar(&i2);
135
136 // Create the gamma that sends loopVar0 and loopVar1 directly through
137 auto & undefNode =
138 jlm::rvsdg::CreateOpNode<TestNullaryOperation>(*thetaNode->subregion(), controlType);
139 const auto gammaNode = GammaNode::create(undefNode.output(0), 2);
140 auto entryVar0 = gammaNode->AddEntryVar(loopVar0.pre);
141 auto entryVar1 = gammaNode->AddEntryVar(loopVar1.pre);
142 auto exitVar0 =
143 gammaNode->AddExitVar({ entryVar0.branchArgument[0], entryVar0.branchArgument[1] });
144 auto exitVar1 =
145 gammaNode->AddExitVar({ entryVar1.branchArgument[0], entryVar1.branchArgument[1] });
146
147 // Both loopVar0 and loopVar1 get loopVar0.pre as their indirect origin
148 // This means only loopVar0 is loop invariant
149 loopVar0.post->divert_to(exitVar0.output);
150 loopVar1.post->divert_to(exitVar0.output);
151
152 auto & x0 = GraphExport::Create(*loopVar0.output, "x0");
153 auto & x1 = GraphExport::Create(*loopVar1.output, "x1");
154 auto & x2 = GraphExport::Create(*loopVar2.output, "x2");
155
156 view(&rvsdg.GetRootRegion(), stdout);
157
158 // Act & Assert 1
159 {
160 const auto & tracedX0 = traceOutputIntraProcedurally(*x0.origin(), false);
161 const auto & tracedX1 = traceOutputIntraProcedurally(*x1.origin(), false);
162 const auto & tracedX2 = traceOutputIntraProcedurally(*x2.origin(), false);
163 const auto & traceExitVar0 = traceOutputIntraProcedurally(*exitVar0.output, false);
164 const auto & traceExitVar1 = traceOutputIntraProcedurally(*exitVar1.output, false);
165 const auto & traceBranchArgument0 =
166 traceOutputIntraProcedurally(*entryVar0.branchArgument[0], false);
167 const auto & traceBranchArgument1 =
168 traceOutputIntraProcedurally(*entryVar1.branchArgument[1], false);
169 const auto & traceLoopVar0Pre = traceOutputIntraProcedurally(*loopVar0.pre, false);
170 const auto & traceLoopVar1Pre = traceOutputIntraProcedurally(*loopVar1.pre, false);
171 const auto & traceLoopVar2Pre = traceOutputIntraProcedurally(*loopVar2.pre, false);
172
173 EXPECT_EQ(&tracedX0, &i0);
174 EXPECT_EQ(&tracedX1, loopVar1.output);
175 EXPECT_EQ(&tracedX2, &i2);
176 EXPECT_EQ(&traceExitVar0, &i0);
177 EXPECT_EQ(&traceExitVar1, loopVar1.pre);
178 EXPECT_EQ(&traceBranchArgument0, &i0);
179 EXPECT_EQ(&traceBranchArgument1, loopVar1.pre);
180 EXPECT_EQ(&traceLoopVar0Pre, &i0);
181 EXPECT_EQ(&traceLoopVar1Pre, loopVar1.pre);
182 EXPECT_EQ(&traceLoopVar2Pre, &i2);
183 }
184
185 // Act & Assert 2
186 {
187 // Create an alternative tracer that does not perform deep tracing
188 OutputTracer shallowTracer;
189 shallowTracer.setStructuralNodePolicy(
190 OutputTracer::StructuralNodePolicy::traceThroughTriviallyInvariant);
191
192 const auto & tracedX0 = shallowTracer.trace(*x0.origin());
193 const auto & tracedX1 = shallowTracer.trace(*x1.origin());
194 const auto & tracedX2 = shallowTracer.trace(*x2.origin());
195 const auto & traceExitVar0 = shallowTracer.trace(*exitVar0.output);
196 const auto & traceExitVar1 = shallowTracer.trace(*exitVar1.output);
197 const auto & traceBranchArgument0 = shallowTracer.trace(*entryVar0.branchArgument[0]);
198 const auto & traceBranchArgument1 = shallowTracer.trace(*entryVar1.branchArgument[1]);
199 const auto & traceLoopVar0Pre = shallowTracer.trace(*loopVar0.pre);
200 const auto & traceLoopVar1Pre = shallowTracer.trace(*loopVar1.pre);
201 const auto & traceLoopVar2Pre = shallowTracer.trace(*loopVar2.pre);
202
203 EXPECT_EQ(&tracedX0, loopVar0.output);
204 EXPECT_EQ(&tracedX1, loopVar1.output);
205 EXPECT_EQ(&tracedX2, &i2); // loopVar2 can still be traced through as it is trivially invariant
206 EXPECT_EQ(&traceExitVar0, loopVar0.pre);
207 EXPECT_EQ(&traceExitVar1, loopVar1.pre);
208 EXPECT_EQ(&traceBranchArgument0, loopVar0.pre);
209 EXPECT_EQ(&traceBranchArgument1, loopVar1.pre);
210 EXPECT_EQ(&traceLoopVar0Pre, loopVar0.pre);
211 EXPECT_EQ(&traceLoopVar1Pre, loopVar1.pre);
212 EXPECT_EQ(&traceLoopVar2Pre, &i2);
213 }
214}
215
221TEST(TraceTests, TestIndirectLoopInvariantOutput)
222{
223 using namespace jlm::rvsdg;
224
245 Graph rvsdg;
246 auto & c20 = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 20 });
247 auto & c40 = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 40 });
248
249 const auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
250 auto invariantLoopVar = thetaNode->AddLoopVar(&c20);
251 auto indirectLoopVar = thetaNode->AddLoopVar(&c40);
252
253 // USER1 uses the pre-iteration value of the non-invariant loop variable.
254 auto user1 = TestOperation::createNode(thetaNode->subregion(), { indirectLoopVar.pre }, {});
255
256 // Make the post value of indirectLoopVar come from the trivially invariant loop var.
257 indirectLoopVar.post->divert_to(invariantLoopVar.pre);
258
259 // USER2 uses the loop ouput value of the non-trivially invariant loop variable.
260 auto user2 = TestOperation::createNode(&rvsdg.GetRootRegion(), { indirectLoopVar.output }, {});
261
262 view(&rvsdg.GetRootRegion(), stdout);
263
264 // Act
265 const auto & tracedUser1 = traceOutputIntraProcedurally(*user1->input(0)->origin(), false);
266 const auto & tracedUser2 = traceOutputIntraProcedurally(*user2->input(0)->origin(), false);
267
268 // Assert
269 EXPECT_TRUE(ThetaLoopVarIsInvariant(invariantLoopVar));
270 EXPECT_FALSE(ThetaLoopVarIsInvariant(indirectLoopVar));
271 EXPECT_EQ(&tracedUser1, indirectLoopVar.pre);
272 EXPECT_EQ(&tracedUser2, &c20);
273}
274
279TEST(TraceTests, TestIndirectLoopInvariance)
280{
281 using namespace jlm::rvsdg;
282
303 Graph rvsdg;
304 auto & c20 = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 20 });
305
306 const auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
307 auto invariantLoopVar = thetaNode->AddLoopVar(&c20);
308 auto indirectLoopVar = thetaNode->AddLoopVar(&c20);
309
310 // USER1 uses the pre-iteration value of the non-trivially invariant loop variable.
311 auto user1 = TestOperation::createNode(thetaNode->subregion(), { indirectLoopVar.pre }, {});
312
313 // Make the post value of indirectLoopVar come from the trivially invariant loop var.
314 indirectLoopVar.post->divert_to(invariantLoopVar.pre);
315
316 // USER2 uses the loop ouput value of the indirectly invariant loop variable.
317 auto user2 = TestOperation::createNode(&rvsdg.GetRootRegion(), { indirectLoopVar.output }, {});
318
319 view(&rvsdg.GetRootRegion(), stdout);
320
321 // Act
322 const auto & tracedUser1 = traceOutputIntraProcedurally(*user1->input(0)->origin(), false);
323 const auto & tracedUser2 = traceOutputIntraProcedurally(*user2->input(0)->origin(), false);
324
325 // Assert
326 EXPECT_TRUE(ThetaLoopVarIsInvariant(invariantLoopVar));
327 EXPECT_FALSE(ThetaLoopVarIsInvariant(indirectLoopVar));
328 EXPECT_EQ(&tracedUser1, &c20);
329 EXPECT_EQ(&tracedUser2, &c20);
330}
331
336TEST(TraceTests, TestEnterThetaSubregion)
337{
338 using namespace jlm::rvsdg;
339
363 // Arrange
364 const auto valueType = TestType::createValueType();
365
366 Graph rvsdg;
367 auto & i1 = GraphImport::Create(rvsdg, valueType, "i1");
368
369 auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
370 auto loopVar = thetaNode->AddLoopVar(&i1);
371
372 auto node = TestOperation::createNode(thetaNode->subregion(), { loopVar.pre }, { valueType });
373 loopVar.post->divert_to(node->output(0));
374
375 auto & x1 = GraphExport::Create(*loopVar.output, "x1");
376
377 // Act & Assert
378 // When tracing may enter subregions, tracing goes into the theta subregion
379 const auto & tracedIn = traceOutputIntraProcedurally(*x1.origin(), true);
380 EXPECT_EQ(&tracedIn, node->output(0));
381 EXPECT_EQ(tracedIn.region(), thetaNode->subregion());
382
383 // When tracing may not enter subregions, tracing stops at the theta output
384 const auto & tracedOut = traceOutputIntraProcedurally(*x1.origin(), false);
385 EXPECT_EQ(&tracedOut, loopVar.output);
386 EXPECT_EQ(tracedOut.region(), &rvsdg.GetRootRegion());
387}
388
389TEST(TraceTests, GammaCachingTest)
390{
391 using namespace jlm::rvsdg;
392
393 // Arrange
394 const auto controlType = ControlType::Create(2);
395 const auto valueType = TestType::createValueType();
396
397 Graph rvsdg;
398
399 auto & predicate = GraphImport::Create(rvsdg, controlType, "predicate");
400 auto & i1 = GraphImport::Create(rvsdg, valueType, "i1");
401 auto & i2 = GraphImport::Create(rvsdg, valueType, "i2");
402
403 auto gammaNode = GammaNode::create(&predicate, 2);
404 auto i1EntryVar = gammaNode->AddEntryVar(&i1);
405 auto i2EntryVar = gammaNode->AddEntryVar(&i2);
406
407 auto exitVar =
408 gammaNode->AddExitVar({ i1EntryVar.branchArgument[0], i1EntryVar.branchArgument[1] });
409
410 auto & graphExport = GraphExport::Create(*exitVar.output, "export");
411
412 OutputTracer tracer;
413 tracer.setInvarianceCaching(true);
414 // predicate checking reduces caching of gamma nodes, so disable it for this test
416
417 // Act & Assert
418 // This is the first time we are tracing this output. We expect it to arrive at i1.
419 auto traceResult = &tracer.trace(*graphExport.origin());
420 assert(traceResult == &i1);
421
422 // Divert the origins of the exit variable results to i2.
423 exitVar.branchResult[0]->divert_to(i2EntryVar.branchArgument[0]);
424 exitVar.branchResult[1]->divert_to(i2EntryVar.branchArgument[1]);
425
426 // Since we traced graphExport already and had caching enabled in the tracer, we expect the tracer
427 // to still return i1 even though we redirected the origins of the exit variable results to i2.
428 traceResult = &tracer.trace(*graphExport.origin());
429 assert(traceResult == &i1);
430
431 // Clear the tracing cache. We should now arrive at i2.
432 tracer.clearInvarianceCache();
433 traceResult = &tracer.trace(*graphExport.origin());
434 assert(traceResult == &i2);
435}
436
437TEST(TraceTests, ThetaCachingTest)
438{
439 using namespace jlm::rvsdg;
440
441 // Arrange
442 const auto controlType = ControlType::Create(2);
443 const auto valueType = TestType::createValueType();
444
445 Graph rvsdg;
446
447 auto & i1 = GraphImport::Create(rvsdg, valueType, "i1");
448 auto & i2 = GraphImport::Create(rvsdg, valueType, "i2");
449
450 auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
451 auto loopVar1 = thetaNode->AddLoopVar(&i1);
452 auto loopVar2 = thetaNode->AddLoopVar(&i2);
453
454 auto & graphExport = GraphExport::Create(*loopVar1.output, "export");
455
456 OutputTracer tracer;
457 tracer.setInvarianceCaching(true);
458
459 // Act & Assert
460 // This is the first time we are tracing this output. We expect it to arrive at i1.
461 auto traceResult = &tracer.trace(*graphExport.origin());
462 assert(traceResult == &i1);
463
464 // Divert the origins of the loop variables' post value
465 loopVar1.post->divert_to(loopVar2.pre);
466 loopVar2.post->divert_to(loopVar1.pre);
467
468 // Since we traced graphExport already and had caching enabled in the tracer, we expect the tracer
469 // to still return i1 even though we redirected loopVar1.
470 traceResult = &tracer.trace(*graphExport.origin());
471 assert(traceResult == &i1);
472
473 // Clear the tracing cache. We should now arrive at the output of loopVar1.
474 tracer.clearInvarianceCache();
475 traceResult = &tracer.trace(*graphExport.origin());
476 assert(traceResult == loopVar1.output);
477}
478
479TEST(TraceTests, RegionPredicationThetaTest)
480{
481 using namespace jlm::rvsdg;
482
509 // Arrange
510 const auto controlType = ControlType::Create(2);
511 const auto int32Type = BitType::Create(32);
512
513 Graph rvsdg;
514
515 auto & int1Outer = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 1 });
516 auto & int2Outer = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 2 });
517 auto & int3Outer = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 3 });
518
519 auto & thetaNode = *ThetaNode::create(&rvsdg.GetRootRegion());
520 auto loopVar1 = thetaNode.AddLoopVar(&int1Outer);
521 auto loopVar2 = thetaNode.AddLoopVar(&int2Outer);
522 auto loopVar3 = thetaNode.AddLoopVar(&int3Outer);
523
524 auto & testOp =
525 jlm::rvsdg::CreateOpNode<TestNullaryOperation>(*thetaNode.subregion(), controlType);
526
527 auto & gammaNode = *GammaNode::create(testOp.output(0), 2);
528 auto gammaEntry1 = gammaNode.AddEntryVar(loopVar1.pre);
529 auto gammaEntry2 = gammaNode.AddEntryVar(loopVar2.pre);
530 auto gammaEntry3 = gammaNode.AddEntryVar(loopVar3.pre);
531
532 // left subregion
533 auto & gammaCtrl0 = ControlConstantOperation::createFalse(*gammaNode.subregion(0));
534 auto & gammaInt4 = BitConstantOperation::create(*gammaNode.subregion(0), { 32, 4 });
535
536 // right subregion
537 auto & gammaCtrl1 = ControlConstantOperation::createTrue(*gammaNode.subregion(1));
538 auto & gammaInt5 = BitConstantOperation::create(*gammaNode.subregion(1), { 32, 5 });
539
540 auto gammaCtrlExit = gammaNode.AddExitVar({ &gammaCtrl0, &gammaCtrl1 });
541 auto gammaExit1 =
542 gammaNode.AddExitVar({ gammaEntry1.branchArgument[0], gammaEntry1.branchArgument[1] });
543 auto gammaExit2 = gammaNode.AddExitVar({ gammaEntry2.branchArgument[0], &gammaInt5 });
544 auto gammaExit3 = gammaNode.AddExitVar({ &gammaInt4, gammaEntry3.branchArgument[1] });
545
546 // Divert theta results
547 thetaNode.predicate()->divert_to(gammaCtrlExit.output);
548 loopVar1.post->divert_to(gammaExit1.output);
549 loopVar2.post->divert_to(gammaExit2.output);
550 loopVar3.post->divert_to(gammaExit3.output);
551
552 // exports
553 auto & exportX = GraphExport::Create(*loopVar1.output, "x");
554 auto & exportY = GraphExport::Create(*loopVar2.output, "y");
555 auto & exportZ = GraphExport::Create(*loopVar3.output, "z");
556
557 // Act
558 OutputTracer tracer;
559 // Enable caching to check that the inner gamma is not incorrectly cached when not invariant
560 tracer.setInvarianceCaching(true);
562 tracer.setStructuralNodePolicy(OutputTracer::StructuralNodePolicy::traceIntoSubregions);
563
564 // Assert
565
566 // Tracing from x leads to the theta's input
567 ASSERT_EQ(&tracer.trace(*exportX.origin()), &int1Outer);
568
569 // Tracing from y leads to the loop variable pre,
570 // since the loop variable is invariant in the last loop iteration,
571 // but not in any other loop iterations
572 ASSERT_EQ(&tracer.trace(*exportY.origin()), loopVar2.pre);
573
574 // Tracing from z leads to the Int(4),
575 // since the last iteration always goes through the left subregion
576 ASSERT_EQ(&tracer.trace(*exportZ.origin()), &gammaInt4);
577
578 // Also try tracing from the gamma exits inside the theta
579 // gamma exit 1 can be traced all the way out of the theta
580 ASSERT_EQ(&tracer.trace(*gammaExit1.output), &int1Outer);
581
582 // gamma exit 2 and 3 can not be traced any further
583 ASSERT_EQ(&tracer.trace(*gammaExit2.output), gammaExit2.output);
584 ASSERT_EQ(&tracer.trace(*gammaExit3.output), gammaExit3.output);
585
586 // Among the theta pre variables, only loop variable 1 can be traced anywhere
587 ASSERT_EQ(&tracer.trace(*loopVar1.pre), &int1Outer);
588 ASSERT_EQ(&tracer.trace(*loopVar2.pre), loopVar2.pre);
589 ASSERT_EQ(&tracer.trace(*loopVar3.pre), loopVar3.pre);
590}
591
592TEST(TraceTests, RegionPredicationThetaToGammaTest)
593{
594 using namespace jlm::rvsdg;
595
625 // Arrange
626 const auto control2Type = ControlType::Create(2);
627 const auto control3Type = ControlType::Create(3);
628 const auto int32Type = BitType::Create(32);
629
630 Graph rvsdg;
631
632 auto & testOpCtrlOuter =
634 auto & testOpIntOuter =
636
637 auto & thetaNode = *ThetaNode::create(&rvsdg.GetRootRegion());
638 auto loopVarCtrl = thetaNode.AddLoopVar(testOpCtrlOuter.output(0));
639 auto loopVarInt = thetaNode.AddLoopVar(testOpIntOuter.output(0));
640
641 auto & testOpCtrlInner =
642 jlm::rvsdg::CreateOpNode<TestNullaryOperation>(*thetaNode.subregion(), control3Type);
643 auto & gammaInner = *GammaNode::create(testOpCtrlInner.output(0), 3);
644
645 // subregion 0
646 auto & sub0ctrl0 = ControlConstantOperation::createFalse(*gammaInner.subregion(0));
647 auto & sub0Int1 = BitConstantOperation::create(*gammaInner.subregion(0), { 32, 1 });
648
649 // subregion 1
650 auto & sub1ctrl0 = ControlConstantOperation::createFalse(*gammaInner.subregion(1));
651 auto & sub1ctrl1 = ControlConstantOperation::createTrue(*gammaInner.subregion(1));
652 auto & sub1Int2 = BitConstantOperation::create(*gammaInner.subregion(1), { 32, 2 });
653
654 // subregion 2
655 auto & sub2ctrl0 = ControlConstantOperation::createFalse(*gammaInner.subregion(2));
656 auto & sub2ctrl1 = ControlConstantOperation::createTrue(*gammaInner.subregion(2));
657 auto & sub2Int3 = BitConstantOperation::create(*gammaInner.subregion(2), { 32, 3 });
658
659 // add exit variables
660 auto thetaPredExit = gammaInner.AddExitVar({ &sub0ctrl0, &sub1ctrl0, &sub2ctrl1 });
661 auto innerToOuterGammaCtrlExit = gammaInner.AddExitVar({ &sub0ctrl0, &sub1ctrl1, &sub2ctrl0 });
662 auto innerIntExit = gammaInner.AddExitVar({ &sub0Int1, &sub1Int2, &sub2Int3 });
663
664 // divert theta results
665 thetaNode.predicate()->divert_to(thetaPredExit.output);
666 loopVarCtrl.post->divert_to(innerToOuterGammaCtrlExit.output);
667 loopVarInt.post->divert_to(innerIntExit.output);
668
669 // create outer gamma
670 auto & outerGamma = *GammaNode::create(loopVarCtrl.output, 2);
671 auto outerIntEntry = outerGamma.AddEntryVar(loopVarInt.output);
672
673 // Act
674 OutputTracer tracer;
675 // Enable caching to check that the inner gamma is not incorrectly cached when not invariant
676 tracer.setInvarianceCaching(true);
678 tracer.setStructuralNodePolicy(OutputTracer::StructuralNodePolicy::traceIntoSubregions);
679
680 // Assert
681 // Going from the subregions of the outer gamma leads to specific values in the inner gamma
682 ASSERT_EQ(&tracer.trace(*outerIntEntry.branchArgument[0]), &sub0Int1);
683 ASSERT_EQ(&tracer.trace(*outerIntEntry.branchArgument[1]), &sub1Int2);
684
685 // Tracing directly from the theta output leads to the output of the inner gamma
686 ASSERT_EQ(&tracer.trace(*loopVarInt.output), innerIntExit.output);
687}
688
689TEST(TraceTests, ImpossibleSubregions)
690{
691 using namespace jlm::rvsdg;
692
726 // Arrange
727 const auto control2Type = ControlType::Create(2);
728 const auto int32Type = BitType::Create(32);
729
730 Graph rvsdg;
731
732 auto & testOpCtrlOuter =
734 auto & int10 = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 10 });
735
736 auto & theta = *ThetaNode::create(&rvsdg.GetRootRegion());
737 auto ctrlLoopVar = theta.AddLoopVar(testOpCtrlOuter.output(0));
738 auto intLoopVar = theta.AddLoopVar(&int10);
739
740 auto & testOpCtrlGamma0 =
741 jlm::rvsdg::CreateOpNode<TestNullaryOperation>(*theta.subregion(), control2Type);
742 auto & gamma0 = *GammaNode::create(testOpCtrlGamma0.output(0), 2);
743 auto gamma0IntEntryVar = gamma0.AddEntryVar(intLoopVar.pre);
744
745 // gamma0's left subregion
746 auto & testOpCtrlGamma1 =
747 jlm::rvsdg::CreateOpNode<TestNullaryOperation>(*gamma0.subregion(0), control2Type);
748 auto & gamma1 = *GammaNode::create(testOpCtrlGamma1.output(0), 2);
749
750 // gamma1's left subregion
751 auto & gamma1leftCtrl0 = ControlConstantOperation::createFalse(*gamma1.subregion(0));
752 auto & gamma1leftCtrl1 = ControlConstantOperation::createTrue(*gamma1.subregion(0));
753 auto & gamma1leftInt20 = BitConstantOperation::create(*gamma1.subregion(0), { 32, 20 });
754
755 // gamma1's right subregion
756 auto & gamma1rightCtrl0 = ControlConstantOperation::createFalse(*gamma1.subregion(1));
757 auto & gamma1rightCtrl1 = ControlConstantOperation::createTrue(*gamma1.subregion(1));
758 auto & gamma1rightInt30 = BitConstantOperation::create(*gamma1.subregion(1), { 32, 30 });
759
760 // gamma1's exit variables
761 auto gamma1Exit0 = gamma1.AddExitVar({ &gamma1leftCtrl0, &gamma1rightCtrl1 });
762 auto gamma1Exit1 = gamma1.AddExitVar({ &gamma1leftCtrl1, &gamma1rightCtrl0 });
763 auto gamma1ExitInt = gamma1.AddExitVar({ &gamma1leftInt20, &gamma1rightInt30 });
764
765 // gamma 0's right subregion
766 auto & gamma0rightCtrl0 = ControlConstantOperation::createFalse(*gamma0.subregion(1));
767
768 // gamma 0's exit variables
769 auto gamma0Exit0 = gamma0.AddExitVar({ gamma1Exit0.output, &gamma0rightCtrl0 });
770 auto gamma0Exit1 = gamma0.AddExitVar({ gamma1Exit1.output, &gamma0rightCtrl0 });
771 auto gamma0ExitInt =
772 gamma0.AddExitVar({ gamma1ExitInt.output, gamma0IntEntryVar.branchArgument[1] });
773
774 // route gamma0 exit variables to theta predicate and loop vars
775 theta.predicate()->divert_to(gamma0Exit0.output);
776 ctrlLoopVar.post->divert_to(gamma0Exit1.output);
777 intLoopVar.post->divert_to(gamma0ExitInt.output);
778
779 // create gamma 2 outside the theta
780 auto & gamma2 = *GammaNode::create(ctrlLoopVar.output, 2);
781 auto gamma2EntryInt = gamma2.AddEntryVar(intLoopVar.output);
782
783 // Assert
784 OutputTracer tracer;
786 tracer.setStructuralNodePolicy(OutputTracer::StructuralNodePolicy::traceIntoSubregions);
787
788 // Tracing from target region A traces through all of gamma0 and gamma1,
789 // but stops at the loop variable pre since the loop variable is not invariant.
790 ASSERT_EQ(&tracer.trace(*gamma2EntryInt.branchArgument[0]), intLoopVar.pre);
791
792 // Tracing from target region B traces to Int(20) inside gamma1
793 ASSERT_EQ(&tracer.trace(*gamma2EntryInt.branchArgument[1]), &gamma1leftInt20);
794}
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
void setRegionPredicateCheckingEnabled(bool value) noexcept
Definition Trace.hpp:165
void setInvarianceCaching(bool value) noexcept
Definition Trace.hpp:194
Output & trace(Output &output)
Definition Trace.cpp:21
void setStructuralNodePolicy(StructuralNodePolicy value) noexcept
Definition Trace.hpp:106
TEST(TraceTests, testTracingMemoryHoistBarrier)
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872