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());
58 const auto & tracedX1 = traceOutputIntraProcedurally(*x1.origin());
59 const auto & tracedX2 = traceOutputIntraProcedurally(*x2.origin());
60 // Trace from within one of the context variables of the gamma
61 const auto & traceGammaEntry = traceOutputIntraProcedurally(*entryVar1.branchArgument[0]);
62 const auto & tracedNodeInput = traceOutputIntraProcedurally(*node->input(0)->origin());
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());
101 const auto & tracedX1 = traceOutputIntraProcedurally(*x1.origin());
102 const auto & traceLoopVar0Pre = traceOutputIntraProcedurally(*loopVar0.pre);
103 const auto & traceLoopVar1Pre = traceOutputIntraProcedurally(*loopVar1.pre);
104 const auto & tracedNodeInput = traceOutputIntraProcedurally(*node->input(0)->origin());
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());
161 const auto & tracedX1 = traceOutputIntraProcedurally(*x1.origin());
162 const auto & tracedX2 = traceOutputIntraProcedurally(*x2.origin());
163 const auto & traceExitVar0 = traceOutputIntraProcedurally(*exitVar0.output);
164 const auto & traceExitVar1 = traceOutputIntraProcedurally(*exitVar1.output);
165 const auto & traceBranchArgument0 = traceOutputIntraProcedurally(*entryVar0.branchArgument[0]);
166 const auto & traceBranchArgument1 = traceOutputIntraProcedurally(*entryVar1.branchArgument[1]);
167 const auto & traceLoopVar0Pre = traceOutputIntraProcedurally(*loopVar0.pre);
168 const auto & traceLoopVar1Pre = traceOutputIntraProcedurally(*loopVar1.pre);
169 const auto & traceLoopVar2Pre = traceOutputIntraProcedurally(*loopVar2.pre);
170
171 EXPECT_EQ(&tracedX0, &i0);
172 EXPECT_EQ(&tracedX1, loopVar1.output);
173 EXPECT_EQ(&tracedX2, &i2);
174 EXPECT_EQ(&traceExitVar0, &i0);
175 EXPECT_EQ(&traceExitVar1, loopVar1.pre);
176 EXPECT_EQ(&traceBranchArgument0, &i0);
177 EXPECT_EQ(&traceBranchArgument1, loopVar1.pre);
178 EXPECT_EQ(&traceLoopVar0Pre, &i0);
179 EXPECT_EQ(&traceLoopVar1Pre, loopVar1.pre);
180 EXPECT_EQ(&traceLoopVar2Pre, &i2);
181 }
182
183 // Act & Assert 2
184 {
185 // Create an alternative tracer that does not perform deep tracing
186 constexpr bool enableCaching = false;
187 OutputTracer shallowTracer(enableCaching);
188 shallowTracer.setTraceThroughStructuralNodes(false);
189
190 const auto & tracedX0 = shallowTracer.trace(*x0.origin());
191 const auto & tracedX1 = shallowTracer.trace(*x1.origin());
192 const auto & tracedX2 = shallowTracer.trace(*x2.origin());
193 const auto & traceExitVar0 = shallowTracer.trace(*exitVar0.output);
194 const auto & traceExitVar1 = shallowTracer.trace(*exitVar1.output);
195 const auto & traceBranchArgument0 = shallowTracer.trace(*entryVar0.branchArgument[0]);
196 const auto & traceBranchArgument1 = shallowTracer.trace(*entryVar1.branchArgument[1]);
197 const auto & traceLoopVar0Pre = shallowTracer.trace(*loopVar0.pre);
198 const auto & traceLoopVar1Pre = shallowTracer.trace(*loopVar1.pre);
199 const auto & traceLoopVar2Pre = shallowTracer.trace(*loopVar2.pre);
200
201 EXPECT_EQ(&tracedX0, loopVar0.output);
202 EXPECT_EQ(&tracedX1, loopVar1.output);
203 EXPECT_EQ(&tracedX2, &i2); // loopVar2 can still be traced through as it is trivially invariant
204 EXPECT_EQ(&traceExitVar0, loopVar0.pre);
205 EXPECT_EQ(&traceExitVar1, loopVar1.pre);
206 EXPECT_EQ(&traceBranchArgument0, loopVar0.pre);
207 EXPECT_EQ(&traceBranchArgument1, loopVar1.pre);
208 EXPECT_EQ(&traceLoopVar0Pre, loopVar0.pre);
209 EXPECT_EQ(&traceLoopVar1Pre, loopVar1.pre);
210 EXPECT_EQ(&traceLoopVar2Pre, &i2);
211 }
212}
213
219TEST(TraceTests, TestIndirectLoopInvariantOutput)
220{
221 using namespace jlm::rvsdg;
222
243 Graph rvsdg;
244 auto & c20 = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 20 });
245 auto & c40 = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 40 });
246
247 const auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
248 auto invariantLoopVar = thetaNode->AddLoopVar(&c20);
249 auto indirectLoopVar = thetaNode->AddLoopVar(&c40);
250
251 // USER1 uses the pre-iteration value of the non-invariant loop variable.
252 auto user1 = TestOperation::createNode(thetaNode->subregion(), { indirectLoopVar.pre }, {});
253
254 // Make the post value of indirectLoopVar come from the trivially invariant loop var.
255 indirectLoopVar.post->divert_to(invariantLoopVar.pre);
256
257 // USER2 uses the loop ouput value of the non-trivially invariant loop variable.
258 auto user2 = TestOperation::createNode(&rvsdg.GetRootRegion(), { indirectLoopVar.output }, {});
259
260 view(&rvsdg.GetRootRegion(), stdout);
261
262 // Act
263 const auto & tracedUser1 = traceOutputIntraProcedurally(*user1->input(0)->origin());
264 const auto & tracedUser2 = traceOutputIntraProcedurally(*user2->input(0)->origin());
265
266 // Assert
267 EXPECT_TRUE(ThetaLoopVarIsInvariant(invariantLoopVar));
268 EXPECT_FALSE(ThetaLoopVarIsInvariant(indirectLoopVar));
269 EXPECT_EQ(&tracedUser1, indirectLoopVar.pre);
270 EXPECT_EQ(&tracedUser2, &c20);
271}
272
277TEST(TraceTests, TestIndirectLoopInvariance)
278{
279 using namespace jlm::rvsdg;
280
301 Graph rvsdg;
302 auto & c20 = BitConstantOperation::create(rvsdg.GetRootRegion(), { 32, 20 });
303
304 const auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
305 auto invariantLoopVar = thetaNode->AddLoopVar(&c20);
306 auto indirectLoopVar = thetaNode->AddLoopVar(&c20);
307
308 // USER1 uses the pre-iteration value of the non-trivially invariant loop variable.
309 auto user1 = TestOperation::createNode(thetaNode->subregion(), { indirectLoopVar.pre }, {});
310
311 // Make the post value of indirectLoopVar come from the trivially invariant loop var.
312 indirectLoopVar.post->divert_to(invariantLoopVar.pre);
313
314 // USER2 uses the loop ouput value of the indirectly invariant loop variable.
315 auto user2 = TestOperation::createNode(&rvsdg.GetRootRegion(), { indirectLoopVar.output }, {});
316
317 view(&rvsdg.GetRootRegion(), stdout);
318
319 // Act
320 const auto & tracedUser1 = traceOutputIntraProcedurally(*user1->input(0)->origin());
321 const auto & tracedUser2 = traceOutputIntraProcedurally(*user2->input(0)->origin());
322
323 // Assert
324 EXPECT_TRUE(ThetaLoopVarIsInvariant(invariantLoopVar));
325 EXPECT_FALSE(ThetaLoopVarIsInvariant(indirectLoopVar));
326 EXPECT_EQ(&tracedUser1, &c20);
327 EXPECT_EQ(&tracedUser2, &c20);
328}
329
330TEST(TraceTests, GammaCachingTest)
331{
332 using namespace jlm::rvsdg;
333
334 // Arrange
335 const auto controlType = ControlType::Create(2);
336 const auto valueType = TestType::createValueType();
337
338 Graph rvsdg;
339
340 auto & predicate = GraphImport::Create(rvsdg, controlType, "predicate");
341 auto & i1 = GraphImport::Create(rvsdg, valueType, "i1");
342 auto & i2 = GraphImport::Create(rvsdg, valueType, "i2");
343
344 auto gammaNode = GammaNode::create(&predicate, 2);
345 auto i1EntryVar = gammaNode->AddEntryVar(&i1);
346 auto i2EntryVar = gammaNode->AddEntryVar(&i2);
347
348 auto exitVar =
349 gammaNode->AddExitVar({ i1EntryVar.branchArgument[0], i1EntryVar.branchArgument[1] });
350
351 auto & graphExport = GraphExport::Create(*exitVar.output, "export");
352
353 constexpr bool enableCaching = true;
354 OutputTracer tracer(enableCaching);
355
356 // Act & Assert
357 // This is the first time we are tracing this output. We expect it to arrive at i1.
358 auto traceResult = &tracer.trace(*graphExport.origin());
359 assert(traceResult == &i1);
360
361 // Divert the origins of the exit variable results to i2.
362 exitVar.branchResult[0]->divert_to(i2EntryVar.branchArgument[0]);
363 exitVar.branchResult[1]->divert_to(i2EntryVar.branchArgument[1]);
364
365 // Since we traced graphExport already and had caching enabled in the tracer, we expect the tracer
366 // to still return i1 even though we redirected the origins of the exit variable results to i2.
367 traceResult = &tracer.trace(*graphExport.origin());
368 assert(traceResult == &i1);
369
370 // Clear the tracing cache. We should now arrive at i2.
371 tracer.clearCache();
372 traceResult = &tracer.trace(*graphExport.origin());
373 assert(traceResult == &i2);
374}
375
376TEST(TraceTests, ThetaCachingTest)
377{
378 using namespace jlm::rvsdg;
379
380 // Arrange
381 const auto controlType = ControlType::Create(2);
382 const auto valueType = TestType::createValueType();
383
384 Graph rvsdg;
385
386 auto & i1 = GraphImport::Create(rvsdg, valueType, "i1");
387 auto & i2 = GraphImport::Create(rvsdg, valueType, "i2");
388
389 auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
390 auto loopVar1 = thetaNode->AddLoopVar(&i1);
391 auto loopVar2 = thetaNode->AddLoopVar(&i2);
392
393 auto & graphExport = GraphExport::Create(*loopVar1.output, "export");
394
395 constexpr bool enableCaching = true;
396 OutputTracer tracer(enableCaching);
397
398 // Act & Assert
399 // This is the first time we are tracing this output. We expect it to arrive at i1.
400 auto traceResult = &tracer.trace(*graphExport.origin());
401 assert(traceResult == &i1);
402
403 // Divert the origins of the loop variables' post value
404 loopVar1.post->divert_to(loopVar2.pre);
405 loopVar2.post->divert_to(loopVar1.pre);
406
407 // Since we traced graphExport already and had caching enabled in the tracer, we expect the tracer
408 // to still return i1 even though we redirected loopVar1.
409 traceResult = &tracer.trace(*graphExport.origin());
410 assert(traceResult == &i1);
411
412 // Clear the tracing cache. We should now arrive at the output of loopVar1.
413 tracer.clearCache();
414 traceResult = &tracer.trace(*graphExport.origin());
415 assert(traceResult == loopVar1.output);
416}
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
void setTraceThroughStructuralNodes(bool value) noexcept
Definition Trace.hpp:59
Output & trace(Output &output)
Definition Trace.cpp:22
TEST(TraceTests, testTracingIOBarrier)
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872