Jlm
Loading...
Searching...
No Matches
PushTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2017 Nico Reißmann <nico.reissmann@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
14#include <jlm/llvm/ir/types.hpp>
15#include <jlm/llvm/opt/push.hpp>
17#include <jlm/rvsdg/gamma.hpp>
18#include <jlm/rvsdg/lambda.hpp>
21#include <jlm/rvsdg/theta.hpp>
22#include <jlm/rvsdg/view.hpp>
24
25TEST(NodeHoistingTests, simpleGamma)
26{
27 using namespace jlm::llvm;
28 using namespace jlm::rvsdg;
29
30 // Arrange
31 const auto controlType = ControlType::Create(2);
32 const auto valueType = TestType::createValueType();
33 const auto functionType = FunctionType::Create(
34 {
35 controlType,
36 valueType,
37 },
38 { valueType });
39
40 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
41 auto & rvsdg = rvsdgModule.Rvsdg();
42
43 auto lambdaNode = LambdaNode::Create(
44 rvsdg.GetRootRegion(),
45 LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
46 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
47 auto valueArgument = lambdaNode->GetFunctionArguments()[1];
48
49 auto gammaNode = GammaNode::create(controlArgument, 2);
50 auto entryVar = gammaNode->AddEntryVar(valueArgument);
51
52 // gamma subregion 0
53 auto constantNode = TestOperation::createNode(gammaNode->subregion(0), {}, { valueType });
54 auto binaryNode = TestOperation::createNode(
55 gammaNode->subregion(0),
56 { entryVar.branchArgument[0], constantNode->output(0) },
57 { valueType });
58
59 // gamma subregion 1
60 auto unaryNode = TestOperation::createNode(
61 gammaNode->subregion(1),
62 { entryVar.branchArgument[1] },
63 { valueType });
64
65 auto exitVar = gammaNode->AddExitVar({ binaryNode->output(0), unaryNode->output(0) });
66
67 auto lambdaOutput = lambdaNode->finalize({ exitVar.output });
68
69 GraphExport::Create(*lambdaOutput, "x");
70
71 view(rvsdg, stdout);
72
73 // Act
74 NodeHoisting nodeHoisting;
76 nodeHoisting.Run(rvsdgModule, statisticsCollector);
77
78 view(rvsdg, stdout);
79
80 // Assert
81 // All nodes from the gamma subregions should have been hoisted to the lambda subregion
82 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 4u);
83
84 // The original nodes in the gamma subregions should have been removed
85 EXPECT_EQ(gammaNode->subregion(0)->numNodes(), 0u);
86 EXPECT_EQ(gammaNode->subregion(1)->numNodes(), 0u);
87}
88
89TEST(NodeHoistingTests, nestedGamma)
90{
91 using namespace jlm::llvm;
92 using namespace jlm::rvsdg;
93
94 // Arrange
95 const auto controlType = ControlType::Create(2);
96 const auto valueType = TestType::createValueType();
97 const auto functionType = FunctionType::Create(
98 {
99 controlType,
100 valueType,
101 },
102 { valueType });
103
104 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
105 auto & rvsdg = rvsdgModule.Rvsdg();
106
107 auto lambdaNode = LambdaNode::Create(
108 rvsdg.GetRootRegion(),
109 LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
110 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
111 auto valueArgument = lambdaNode->GetFunctionArguments()[1];
112
113 auto gammaNode1 = GammaNode::create(controlArgument, 2);
114 auto controlEntryVar = gammaNode1->AddEntryVar(controlArgument);
115 auto valueEntryVar1 = gammaNode1->AddEntryVar(valueArgument);
116
117 // gamma1 subregion 0
118 auto constantNode1 = TestOperation::createNode(gammaNode1->subregion(0), {}, { valueType });
119
120 auto gammaNode2 = GammaNode::create(controlEntryVar.branchArgument[0], 2);
121 auto valueEntryVar2 = gammaNode2->AddEntryVar(valueEntryVar1.branchArgument[0]);
122 auto valueEntryVar3 = gammaNode2->AddEntryVar(constantNode1->output(0));
123
124 // gamma2 subregion 0
125 auto binaryNode = TestOperation::createNode(
126 gammaNode1->subregion(0),
127 { valueEntryVar2.branchArgument[0], valueEntryVar3.branchArgument[0] },
128 { valueType });
129
130 // gamma2 subregion 1
131 auto unaryNode = TestOperation::createNode(
132 gammaNode1->subregion(1),
133 { valueEntryVar2.branchArgument[1] },
134 { valueType });
135
136 auto exitVar1 = gammaNode2->AddExitVar({ binaryNode->output(0), unaryNode->output(0) });
137
138 // gamma1 subregion 1
139 auto constantNode2 = TestOperation::createNode(gammaNode1->subregion(1), {}, { valueType });
140
141 auto exitVar2 = gammaNode1->AddExitVar({ exitVar1.output, constantNode2->output(0) });
142
143 auto lambdaOutput = lambdaNode->finalize({ exitVar2.output });
144
145 GraphExport::Create(*lambdaOutput, "x");
146
147 view(rvsdg, stdout);
148
149 // Act
150 NodeHoisting nodeHoisting;
152 nodeHoisting.Run(rvsdgModule, statisticsCollector);
153
154 view(rvsdg, stdout);
155
156 // Assert
157 // All simple nodes from both gamma subregions should have been hoisted to the lambda subregion
158 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 5u);
159
160 // Only gamma node 2 should be left in gamma node 1 subregion 0
161 EXPECT_EQ(gammaNode1->subregion(0)->numNodes(), 1u);
162 EXPECT_EQ(gammaNode1->subregion(1)->numNodes(), 0u);
163
164 // All nodes should have been hoisted out
165 EXPECT_EQ(gammaNode2->subregion(0)->numNodes(), 0u);
166 EXPECT_EQ(gammaNode2->subregion(1)->numNodes(), 0u);
167}
168
169TEST(NodeHoistingTests, simpleTheta)
170{
171 using namespace jlm::llvm;
172 using namespace jlm::rvsdg;
173
174 // Arrange
175 auto controlType = ControlType::Create(2);
176 const auto valueType = TestType::createValueType();
177 const auto functionType = FunctionType::Create(
178 {
179 controlType,
180 valueType,
181 },
182 { valueType });
183
184 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
185 auto & rvsdg = rvsdgModule.Rvsdg();
186
187 auto lambdaNode = LambdaNode::Create(
188 rvsdg.GetRootRegion(),
189 LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
190 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
191 auto valueArgument = lambdaNode->GetFunctionArguments()[1];
192
193 auto thetaNode = ThetaNode::create(lambdaNode->subregion());
194
195 auto lv1 = thetaNode->AddLoopVar(controlArgument);
196 auto lv2 = thetaNode->AddLoopVar(valueArgument);
197 auto lv3 = thetaNode->AddLoopVar(valueArgument);
198 auto lv4 = thetaNode->AddLoopVar(valueArgument);
199
200 auto node1 = TestOperation::createNode(thetaNode->subregion(), {}, { valueType });
201 auto node2 = TestOperation::createNode(
202 thetaNode->subregion(),
203 { node1->output(0), lv3.pre },
204 { valueType });
205 auto node3 = TestOperation::createNode(
206 thetaNode->subregion(),
207 { lv2.pre, node2->output(0) },
208 { valueType });
209 auto node4 =
210 TestOperation::createNode(thetaNode->subregion(), { lv3.pre, lv4.pre }, { valueType });
211
212 lv2.post->divert_to(node3->output(0));
213 lv4.post->divert_to(node4->output(0));
214
215 thetaNode->set_predicate(lv1.pre);
216
217 lambdaNode->finalize({ thetaNode->output(1) });
218
219 view(rvsdg, stdout);
220
221 // Act
222 NodeHoisting nodeHoisting;
224 nodeHoisting.Run(rvsdgModule, statisticsCollector);
225
226 view(rvsdg, stdout);
227
228 // Assert
229 // We expect node1 and node2 to be hoisted out of the theta subregion
230 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 3u);
231 EXPECT_EQ(thetaNode->subregion()->numNodes(), 2u);
232
233 EXPECT_EQ(lv2.post->origin(), node3->output(0));
234 EXPECT_EQ(lv4.post->origin(), node4->output(0));
235}
236
237TEST(NodeHoistingTests, invariantMemoryOperation)
238{
239 using namespace jlm::llvm;
240 using namespace jlm::rvsdg;
241
242 // Arrange
243 const auto memoryStateType = MemoryStateType::Create();
244 const auto pointerType = PointerType::Create();
245 const auto controlType = ControlType::Create(2);
246 const auto valueType = TestType::createValueType();
247 const auto functionType = FunctionType::Create(
248 { controlType, pointerType, valueType, memoryStateType },
249 { memoryStateType });
250
251 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
252 auto & rvsdg = rvsdgModule.Rvsdg();
253
254 auto lambdaNode = LambdaNode::Create(
255 rvsdg.GetRootRegion(),
256 LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
257 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
258 auto pointerArgument = lambdaNode->GetFunctionArguments()[1];
259 auto valueArgument = lambdaNode->GetFunctionArguments()[2];
260 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[3];
261
262 auto thetaNode = ThetaNode::create(lambdaNode->subregion());
263
264 auto lvc = thetaNode->AddLoopVar(controlArgument);
265 auto lva = thetaNode->AddLoopVar(pointerArgument);
266 auto lvv = thetaNode->AddLoopVar(valueArgument);
267 auto lvs = thetaNode->AddLoopVar(memoryStateArgument);
268
269 auto & storeNode = StoreNonVolatileOperation::CreateNode(*lva.pre, *lvv.pre, { lvs.pre }, 4);
270
271 lvs.post->divert_to(storeNode.output(0));
272 thetaNode->set_predicate(lvc.pre);
273
274 lambdaNode->finalize({ lvs.output });
275
276 view(rvsdg, stdout);
277
278 // Act
279 NodeHoisting nodeHoisting;
281 nodeHoisting.Run(rvsdgModule, statisticsCollector);
282
283 view(rvsdg, stdout);
284
285 // Assert
286 // We expect the store node hoisted out of the theta subregion
287 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 2u);
288 EXPECT_EQ(thetaNode->subregion()->numNodes(), 0u);
289}
290
291TEST(NodeHoistingTests, statefulOperations)
292{
293 // Arrange
294 using namespace jlm::llvm;
295 using namespace jlm::rvsdg;
296
297 auto controlType = ControlType::Create(2);
298 auto valueType = TestType::createValueType();
299 auto stateType = TestType::createStateType();
300 const auto functionType = FunctionType::Create(
301 {
302 controlType,
303 valueType,
304 stateType,
305 },
306 { valueType });
307
308 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
309 auto & rvsdg = rvsdgModule.Rvsdg();
310
311 auto lambdaNode = LambdaNode::Create(
312 rvsdg.GetRootRegion(),
313 LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
314 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
315 auto valueArgument = lambdaNode->GetFunctionArguments()[1];
316 auto stateArgument = lambdaNode->GetFunctionArguments()[2];
317
318 auto gammaNode1 = GammaNode::create(controlArgument, 2);
319 auto controlEntryVar = gammaNode1->AddEntryVar(controlArgument);
320 auto valueEntryVar1 = gammaNode1->AddEntryVar(valueArgument);
321 auto stateEntryVar = gammaNode1->AddEntryVar(stateArgument);
322
323 auto stateNode = TestOperation::createNode(
324 gammaNode1->subregion(0),
325 { valueEntryVar1.branchArgument[0], stateEntryVar.branchArgument[0] },
326 { valueType });
327
328 auto gammaNode2 = GammaNode::create(controlEntryVar.branchArgument[0], 2);
329 auto valueEntryVar2 = gammaNode2->AddEntryVar(stateNode->output(0));
330 auto valueEntryVar3 = gammaNode2->AddEntryVar(valueEntryVar1.branchArgument[0]);
331
332 auto binaryNode = TestOperation::createNode(
333 gammaNode2->subregion(0),
334 { valueEntryVar2.branchArgument[0], valueEntryVar3.branchArgument[0] },
335 { valueType });
336
337 auto exitVar2 =
338 gammaNode2->AddExitVar({ binaryNode->output(0), valueEntryVar2.branchArgument[1] });
339
340 auto exitVar = gammaNode1->AddExitVar({ exitVar2.output, valueEntryVar1.branchArgument[1] });
341
342 lambdaNode->finalize({ exitVar.output });
343
344 view(rvsdg, stdout);
345
346 // Act
347 NodeHoisting nodeHoisting;
349 nodeHoisting.Run(rvsdgModule, statisticsCollector);
350
351 view(rvsdg, stdout);
352
353 // Assert
354 // We expect that stateNode stays where it is and only the binaryNode is hoisted to the same
355 // region as stateNode
356
357 // Gamma node and undef node
358 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 1u);
359
360 // stateNode, gammaNode2, and binaryNode
361 EXPECT_EQ(gammaNode1->subregion(0)->numNodes(), 3u);
362 EXPECT_EQ(gammaNode1->subregion(1)->numNodes(), 0u);
363
364 EXPECT_EQ(gammaNode2->subregion(0)->numNodes(), 0u);
365 EXPECT_EQ(gammaNode2->subregion(1)->numNodes(), 0u);
366}
367
368TEST(NodeHoistingTests, controlConstants)
369{
399 // Arrange
400 using namespace jlm::llvm;
401 using namespace jlm::rvsdg;
402
403 auto controlType = ControlType::Create(2);
404 auto int32Type = BitType::Create(32);
405 auto memoryStateType = MemoryStateType::Create();
406 auto ioStateType = IOStateType::Create();
407 const auto functionType = FunctionType::Create(
408 { ioStateType, memoryStateType },
409 { int32Type, ioStateType, memoryStateType });
410
411 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
412 auto & rvsdg = rvsdgModule.Rvsdg();
413
414 auto lambdaNode = LambdaNode::Create(
415 rvsdg.GetRootRegion(),
416 LlvmLambdaOperation::Create(functionType, "func", Linkage::externalLinkage));
417
418 auto ioStateArgument = lambdaNode->GetFunctionArguments()[0];
419 auto memStateArgument = lambdaNode->GetFunctionArguments()[1];
420
421 // Theta node
422 auto thetaNode = ThetaNode::create(lambdaNode->subregion());
423 auto thetaUndef = UndefValueOperation::Create(*lambdaNode->subregion(), controlType);
424 auto thetaCtrlLoopVar = thetaNode->AddLoopVar(thetaUndef);
425 auto & thetaInnerCtrl1 = ControlConstantOperation::create(*thetaNode->subregion(), 2, 1);
426 thetaCtrlLoopVar.post->divert_to(&thetaInnerCtrl1);
427
428 // gamma1: takes theta's control loop var output as its predicate
429 auto & gamma1 = GammaNode::Create(*thetaCtrlLoopVar.output, 2, {});
430
431 // gamma1 exit variable taking Ctrl(0) and Ctrl(1) in the respective subregions
432 auto & gamma1Ctrl1 = ControlConstantOperation::create(*gamma1.subregion(0), 2, 1);
433 auto & gamma1Ctrl0 = ControlConstantOperation::create(*gamma1.subregion(1), 2, 0);
434 auto gamma1Exit = gamma1.AddExitVar({ &gamma1Ctrl1, &gamma1Ctrl0 });
435
436 // gamma2: takes gamma1's exit (control) as its predicate
437 auto & gamma2 = GammaNode::Create(*gamma1Exit.output, 2, {});
438
439 // gamma2 exit variable takes integer constants
440 auto & gamma2Int3 = *IntegerConstantOperation::Create(*gamma2.subregion(0), 32, 3).output(0);
441 auto & gamma2Int7 = *IntegerConstantOperation::Create(*gamma2.subregion(1), 32, 7).output(0);
442
443 auto gamma2Exit = gamma2.AddExitVar({ &gamma2Int3, &gamma2Int7 });
444
445 lambdaNode->finalize({ gamma2Exit.output, ioStateArgument, memStateArgument });
446
447 view(rvsdg, stdout);
448
449 // Act
450 NodeHoisting nodeHoisting;
452 nodeHoisting.Run(rvsdgModule, statisticsCollector);
453
454 view(rvsdg, stdout);
455
456 // Assert
457 // Control constants must stay in their original regions and not be hoisted.
458 // Bit constants (Int32) should be hoisted to the lambda subregion.
459
460 // Lambda subregion: theta node + gamma1 + gamma2 + CTL(0) (loop var entry) + two hoisted Int32
461 // constants
462 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 6u);
463
464 // Theta subregion: The Ctrl(0) and Ctrl(1) remain inside the theta
465 EXPECT_EQ(thetaNode->subregion()->numNodes(), 2u);
466 auto thetaPredicateOwner = TryGetOwnerNode<SimpleNode>(*thetaNode->predicate()->origin());
467 EXPECT_TRUE(thetaPredicateOwner);
468 EXPECT_EQ(thetaPredicateOwner->region(), thetaNode->subregion());
469 auto thetaPostOwner = TryGetOwnerNode<SimpleNode>(*thetaCtrlLoopVar.post->origin());
470 EXPECT_TRUE(thetaPostOwner);
471 EXPECT_EQ(thetaPostOwner->region(), thetaNode->subregion());
472
473 // Gamma1 subregions: control constants stay in place (no structural nodes inside)
474 EXPECT_EQ(gamma1.subregion(0)->numNodes(), 1u); // Ctrl(1) only
475 auto gamma1LeftCtrlOwner = TryGetOwnerNode<SimpleNode>(*gamma1Exit.branchResult[0]->origin());
476 EXPECT_TRUE(gamma1LeftCtrlOwner);
477 EXPECT_EQ(gamma1LeftCtrlOwner->region(), gamma1.subregion(0));
478
479 EXPECT_EQ(gamma1.subregion(1)->numNodes(), 1u); // Ctrl(0) only
480 auto gamma1RightCtrlOwner = TryGetOwnerNode<SimpleNode>(*gamma1Exit.branchResult[1]->origin());
481 EXPECT_TRUE(gamma1RightCtrlOwner);
482 EXPECT_EQ(gamma1RightCtrlOwner->region(), gamma1.subregion(1));
483
484 // Gamma2 subregions: Int32 constants should have been hoisted out to lambda level
485 EXPECT_EQ(gamma2.subregion(0)->numNodes(), 0u);
486 EXPECT_EQ(gamma2.subregion(1)->numNodes(), 0u);
487}
static jlm::util::StatisticsCollector statisticsCollector
TEST(NodeHoistingTests, simpleGamma)
Definition PushTests.cpp:25
Node Hoisting Transformation.
Definition push.hpp:37
void Run(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
Definition push.cpp:389
Global memory state passed between functions.