Jlm
Loading...
Searching...
No Matches
RegionPredicateTraceTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2026 Helge Bahmann <hcb@chaoticmind.net>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
11#include <jlm/rvsdg/control.hpp>
12#include <jlm/rvsdg/gamma.hpp>
13#include <jlm/rvsdg/graph.hpp>
19#include <jlm/rvsdg/theta.hpp>
20
21TEST(RegionPredicateTraceTests, TestTracing)
22{
23 using namespace jlm;
24
25 auto valueType = rvsdg::TestType::createValueType();
26 auto ctl2 = rvsdg::ControlType::Create(2);
27
28 rvsdg::Graph rvsdg;
29 auto & pred1 = rvsdg::GraphImport::Create(rvsdg, ctl2, "pred1");
30 auto & pred2 = rvsdg::GraphImport::Create(rvsdg, ctl2, "pred2");
31
32 // First gamma, computes a predicate
33 auto gamma1 = rvsdg::GammaNode::create(&pred1, 2);
34 auto g1_left = gamma1->subregion(0);
35 auto g1_right = gamma1->subregion(1);
36 auto & g1_p0 = rvsdg::ControlConstantOperation::createTrue(*g1_left);
37 auto & g1_p1 = rvsdg::ControlConstantOperation::createFalse(*g1_right);
38 auto pred3 = gamma1->AddExitVar({ &g1_p0, &g1_p1 }).output;
39
40 // Second gamma, depends on that predicate.
41 auto gamma2 = rvsdg::GammaNode::create(pred3, 2);
42 auto g2_left = gamma2->subregion(0);
43 auto g2_right = gamma2->subregion(1);
44 auto r0 = rvsdg::TestOperation::createNode(g2_left, {}, { valueType });
45 auto r1 = rvsdg::TestOperation::createNode(g2_right, {}, { valueType });
46 auto r = gamma2->AddExitVar({ r0->output(0), r1->output(0) }).output;
47 rvsdg::GraphExport::Create(*r, "result1");
48
49 // Third gamma, depends on unrelated predicate.
50 auto gamma3 = rvsdg::GammaNode::create(&pred2, 2);
51 auto g3_left = gamma3->subregion(0);
52 auto g3_right = gamma3->subregion(1);
53 auto s0 = rvsdg::TestOperation::createNode(g3_left, {}, { valueType });
54 auto s1 = rvsdg::TestOperation::createNode(g3_right, {}, { valueType });
55 auto s = gamma3->AddExitVar({ s0->output(0), s1->output(0) }).output;
56 rvsdg::GraphExport::Create(*s, "result2");
57
59
60 // Since gamma1 dominates gamma2, not all cross-paths are possible.
61 EXPECT_TRUE(trace.isReachableFromRegion(*g2_right, *g1_left));
62 EXPECT_TRUE(trace.isReachableFromRegion(*g2_left, *g1_right));
63 EXPECT_FALSE(trace.isReachableFromRegion(*g2_left, *g1_left));
64 EXPECT_FALSE(trace.isReachableFromRegion(*g2_right, *g1_right));
65
66 // Since gamma1 and gamma3 are unrelated, all cross-paths are possible.
67 EXPECT_TRUE(trace.isReachableFromRegion(*g3_right, *g1_left));
68 EXPECT_TRUE(trace.isReachableFromRegion(*g3_left, *g1_right));
69 EXPECT_TRUE(trace.isReachableFromRegion(*g3_left, *g1_left));
70 EXPECT_TRUE(trace.isReachableFromRegion(*g3_right, *g1_right));
71
72 // Now change the graph, and check again.
73 gamma2->predicate()->divert_to(&pred2);
74 trace.clearCaches();
75
76 // Now, everything is uncorrelated.
77 EXPECT_TRUE(trace.isReachableFromRegion(*g2_right, *g1_left));
78 EXPECT_TRUE(trace.isReachableFromRegion(*g2_left, *g1_right));
79 EXPECT_TRUE(trace.isReachableFromRegion(*g2_left, *g1_left));
80 EXPECT_TRUE(trace.isReachableFromRegion(*g2_right, *g1_right));
81 EXPECT_TRUE(trace.isReachableFromRegion(*g3_right, *g1_left));
82 EXPECT_TRUE(trace.isReachableFromRegion(*g3_left, *g1_right));
83 EXPECT_TRUE(trace.isReachableFromRegion(*g3_left, *g1_left));
84 EXPECT_TRUE(trace.isReachableFromRegion(*g3_right, *g1_right));
85}
86
87TEST(RegionPredicateTraceTests, TraceOutOfTheta)
88{
116 using namespace jlm;
117
118 // Arrange
119 auto bit32 = rvsdg::BitType::Create(32);
120
121 rvsdg::Graph rvsdg;
122 auto theta0 = rvsdg::ThetaNode::create(&rvsdg.GetRootRegion());
123 auto undef0 =
124 rvsdg::CreateOpNode<rvsdg::TestNullaryOperation>(rvsdg.GetRootRegion(), bit32).output(0);
125 auto loopVar0 = theta0->AddLoopVar(undef0);
126
127 // theta1
128 auto theta1 = rvsdg::ThetaNode::create(theta0->subregion());
129 auto undef1 =
130 rvsdg::CreateOpNode<rvsdg::TestNullaryOperation>(*theta0->subregion(), bit32).output(0);
131 auto loopVar1 = theta1->AddLoopVar(undef1);
132 auto & int3Output = rvsdg::BitConstantOperation::create(*theta1->subregion(), { 32, 3 });
133 loopVar1.post->divert_to(&int3Output);
134
135 // theta2
136 auto theta2 = rvsdg::ThetaNode::create(theta0->subregion());
137 auto loopVar2 = theta2->AddLoopVar(loopVar1.output);
138
139 // theta3
140 auto theta3 = rvsdg::ThetaNode::create(theta0->subregion());
141 auto undef3 =
142 rvsdg::CreateOpNode<rvsdg::TestNullaryOperation>(*theta0->subregion(), bit32).output(0);
143 auto loopVar3 = theta3->AddLoopVar(undef3);
144
145 // theta4
146 auto theta4 = rvsdg::ThetaNode::create(theta3->subregion());
147 auto loopVar4 = theta4->AddLoopVar(loopVar3.pre);
148 auto & int7Output = rvsdg::BitConstantOperation::create(*theta4->subregion(), { 32, 7 });
149 loopVar4.post->divert_to(&int7Output);
150 loopVar3.post->divert_to(loopVar4.output);
151
152 // ADD inside theta0's subregion, combining outputs from theta2 and theta3/theta4
153 auto & addOutput = *rvsdg::bitadd_op::create(32, loopVar2.output, loopVar3.output);
154 loopVar0.post->divert_to(&addOutput);
155
156 rvsdg::GraphExport::Create(*loopVar0.output, "x");
157
158 // Assert
160
161 // Every region can be reached from the root region
162 EXPECT_TRUE(trace.isReachableFromRegion(*theta0->subregion(), rvsdg.GetRootRegion()));
163 EXPECT_TRUE(trace.isReachableFromRegion(*theta1->subregion(), rvsdg.GetRootRegion()));
164 EXPECT_TRUE(trace.isReachableFromRegion(*theta2->subregion(), rvsdg.GetRootRegion()));
165 EXPECT_TRUE(trace.isReachableFromRegion(*theta3->subregion(), rvsdg.GetRootRegion()));
166 EXPECT_TRUE(trace.isReachableFromRegion(*theta4->subregion(), rvsdg.GetRootRegion()));
167
168 // Every region can reach the root region
169 EXPECT_TRUE(trace.isReachableFromRegion(rvsdg.GetRootRegion(), *theta0->subregion()));
170 EXPECT_TRUE(trace.isReachableFromRegion(rvsdg.GetRootRegion(), *theta1->subregion()));
171 EXPECT_TRUE(trace.isReachableFromRegion(rvsdg.GetRootRegion(), *theta1->subregion()));
172 EXPECT_TRUE(trace.isReachableFromRegion(rvsdg.GetRootRegion(), *theta1->subregion()));
173 EXPECT_TRUE(trace.isReachableFromRegion(rvsdg.GetRootRegion(), *theta1->subregion()));
174
175 // theta0 can reach every region inside it
176 EXPECT_TRUE(trace.isReachableFromRegion(*theta1->subregion(), *theta0->subregion()));
177 EXPECT_TRUE(trace.isReachableFromRegion(*theta2->subregion(), *theta0->subregion()));
178 EXPECT_TRUE(trace.isReachableFromRegion(*theta3->subregion(), *theta0->subregion()));
179 EXPECT_TRUE(trace.isReachableFromRegion(*theta4->subregion(), *theta0->subregion()));
180
181 // theta0 can also be reached by every region inside it
182 EXPECT_TRUE(trace.isReachableFromRegion(*theta0->subregion(), *theta1->subregion()));
183 EXPECT_TRUE(trace.isReachableFromRegion(*theta0->subregion(), *theta2->subregion()));
184 EXPECT_TRUE(trace.isReachableFromRegion(*theta0->subregion(), *theta3->subregion()));
185 EXPECT_TRUE(trace.isReachableFromRegion(*theta0->subregion(), *theta4->subregion()));
186
187 // theta2 can be reached from theta1
188 EXPECT_TRUE(trace.isReachableFromRegion(*theta2->subregion(), *theta1->subregion()));
189
190 // theta3 and theta4 can reach each other
191 EXPECT_TRUE(trace.isReachableFromRegion(*theta4->subregion(), *theta3->subregion()));
192 EXPECT_TRUE(trace.isReachableFromRegion(*theta3->subregion(), *theta4->subregion()));
193}
194
195TEST(RegionPredicateTraceTests, TraceIntoGamma)
196{
222 using namespace jlm;
223
224 auto controlType = rvsdg::ControlType::Create(2);
225
226 rvsdg::Graph rvsdg;
227 auto & outerCtrl0 = rvsdg::ControlConstantOperation::createFalse(rvsdg.GetRootRegion());
228 auto & gamma0 = *rvsdg::GammaNode::create(&outerCtrl0, 2);
229
230 // Left subregion of gamma0
231 auto & leftCtrl0 = rvsdg::ControlConstantOperation::createFalse(*gamma0.subregion(0));
232 auto & leftCtrl1 = rvsdg::ControlConstantOperation::createTrue(*gamma0.subregion(0));
233
234 auto & gamma1 = *rvsdg::GammaNode::create(&leftCtrl0, 2);
235 auto gamma1Entry0 = gamma1.AddEntryVar(&leftCtrl0);
236 auto gamma1Entry1 = gamma1.AddEntryVar(&leftCtrl1);
237 auto gamma1Exit =
238 gamma1.AddExitVar({ gamma1Entry0.branchArgument[0], gamma1Entry1.branchArgument[1] });
239
240 // Right subregion of gamma1
241 auto & rightCtrl1 = rvsdg::ControlConstantOperation::createTrue(*gamma0.subregion(1));
242
243 auto gamma0Exit = gamma0.AddExitVar({ gamma1Exit.output, &rightCtrl1 });
244
245 auto & gamma2 = *rvsdg::GammaNode::create(gamma0Exit.output, 2);
246
247 // Assert
249
250 // targeting gamma2's left subregion
251 ASSERT_TRUE(trace.isReachableFromRegion(*gamma2.subregion(0), *gamma1.subregion(0)));
252 ASSERT_FALSE(trace.isReachableFromRegion(*gamma2.subregion(0), *gamma1.subregion(1)));
253 ASSERT_TRUE(trace.isReachableFromRegion(*gamma2.subregion(0), *gamma0.subregion(0)));
254 ASSERT_FALSE(trace.isReachableFromRegion(*gamma2.subregion(0), *gamma0.subregion(1)));
255
256 // targeting gamma2's right subregion
257 ASSERT_FALSE(trace.isReachableFromRegion(*gamma2.subregion(1), *gamma1.subregion(0)));
258 ASSERT_TRUE(trace.isReachableFromRegion(*gamma2.subregion(1), *gamma1.subregion(1)));
259 ASSERT_TRUE(trace.isReachableFromRegion(*gamma2.subregion(1), *gamma0.subregion(0)));
260 ASSERT_TRUE(trace.isReachableFromRegion(*gamma2.subregion(1), *gamma0.subregion(1)));
261}
262
263TEST(RegionPredicateTraceTests, TraceThroughGammas)
264{
291 using namespace jlm;
292
293 auto controlType2 = rvsdg::ControlType::Create(2);
294 auto controlType3 = rvsdg::ControlType::Create(3);
295
296 rvsdg::Graph rvsdg;
297
298 // gamma 0
299 auto & testOp0 =
300 rvsdg::CreateOpNode<rvsdg::TestNullaryOperation>(rvsdg.GetRootRegion(), controlType2);
301 auto & gamma0 = *rvsdg::GammaNode::create(testOp0.output(0), 2);
302 auto & gamma0Ctrl0 = rvsdg::ControlConstantOperation::create(*gamma0.subregion(0), { 0, 3 });
303 auto & gamma0Ctrl1 = rvsdg::ControlConstantOperation::create(*gamma0.subregion(1), { 1, 3 });
304 auto gamma0ExitVar = gamma0.AddExitVar({ &gamma0Ctrl0, &gamma0Ctrl1 });
305
306 // gamma 1
307 auto & testOp1 =
308 rvsdg::CreateOpNode<rvsdg::TestNullaryOperation>(rvsdg.GetRootRegion(), controlType2);
309 auto & gamma1 = *rvsdg::GammaNode::create(testOp1.output(0), 2);
310 auto gamma1EntryFromGamma0 = gamma1.AddEntryVar(gamma0ExitVar.output);
311 auto & gamma1Ctrl1 = rvsdg::ControlConstantOperation::create(rvsdg.GetRootRegion(), { 1, 3 });
312 auto gamma1EntryFromCtrl1 = gamma1.AddEntryVar(&gamma1Ctrl1);
313 auto gamma1ExitVar = gamma1.AddExitVar(
314 { gamma1EntryFromGamma0.branchArgument[0], gamma1EntryFromCtrl1.branchArgument[1] });
315
316 // gamma 2
317 auto & gamma2 = *rvsdg::GammaNode::create(gamma1ExitVar.output, 3);
318
319 // Assert
321
322 // gamma0 has no effect on the subregions of gamma1
323 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma1.subregion(0), *gamma0.subregion(0)));
324 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma1.subregion(1), *gamma0.subregion(0)));
325 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma1.subregion(0), *gamma0.subregion(1)));
326 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma1.subregion(1), *gamma0.subregion(1)));
327
328 // gamma0 has no effect on the choice between region 0 or 1 in gamma2 either
329 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma2.subregion(0), *gamma0.subregion(0)));
330 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma2.subregion(1), *gamma0.subregion(0)));
331 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma2.subregion(0), *gamma0.subregion(1)));
332 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma2.subregion(1), *gamma0.subregion(1)));
333
334 // From subregion 0 of gamma1 both subregions 0 and 1 can be reached in gamma2
335 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma2.subregion(0), *gamma1.subregion(0)));
336 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma2.subregion(1), *gamma1.subregion(0)));
337 // From subregion 1 of gamma1, however, only subregions 1 can be reached in gamma2
338 ASSERT_FALSE(tracer.isReachableFromRegion(*gamma2.subregion(0), *gamma1.subregion(1)));
339 ASSERT_TRUE(tracer.isReachableFromRegion(*gamma2.subregion(1), *gamma1.subregion(1)));
340
341 // region 2 of gamma2 is entriely unreachable, from any region, including the root
342 ASSERT_FALSE(tracer.isReachableFromRegion(*gamma2.subregion(2), *gamma0.subregion(0)));
343 ASSERT_FALSE(tracer.isReachableFromRegion(*gamma2.subregion(2), *gamma0.subregion(1)));
344 ASSERT_FALSE(tracer.isReachableFromRegion(*gamma2.subregion(2), *gamma1.subregion(0)));
345 ASSERT_FALSE(tracer.isReachableFromRegion(*gamma2.subregion(2), *gamma1.subregion(1)));
346 ASSERT_FALSE(tracer.isReachableFromRegion(*gamma2.subregion(2), rvsdg.GetRootRegion()));
347
348 // Using the old predicate tracer, the following assert fails
349 // rvsdg::RegionPredicateTrace oldTracer;
350 // ASSERT_TRUE(oldTracer.CheckPredicatesSatisfiable(*gamma0.subregion(0), *gamma2.subregion(1)));
351}
TEST(RegionPredicateTraceTests, TestTracing)
class providing guarantees about unreachability of regions from other regions.
bool isReachableFromRegion(Region &targetRegion, Region &originRegion)
Region & GetRootRegion() const noexcept
Definition graph.hpp:99