Jlm
Loading...
Searching...
No Matches
NodeHoistingTests.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
15#include <jlm/llvm/ir/types.hpp>
18#include <jlm/rvsdg/gamma.hpp>
19#include <jlm/rvsdg/lambda.hpp>
22#include <jlm/rvsdg/theta.hpp>
23#include <jlm/rvsdg/view.hpp>
25
26namespace jlm::llvm
27{
28TEST(NodeHoistingTests, simpleGamma)
29{
30 using namespace jlm::llvm;
31 using namespace jlm::rvsdg;
32
33 // Arrange
34 const auto controlType = ControlType::Create(2);
35 const auto valueType = TestType::createValueType();
36 const auto functionType = FunctionType::Create(
37 {
38 controlType,
39 valueType,
40 },
41 { valueType });
42
43 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
44 auto & rvsdg = rvsdgModule.Rvsdg();
45
46 auto lambdaNode = LambdaNode::Create(
47 rvsdg.GetRootRegion(),
49 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
50 auto valueArgument = lambdaNode->GetFunctionArguments()[1];
51
52 auto gammaNode = GammaNode::create(controlArgument, 2);
53 auto entryVar = gammaNode->AddEntryVar(valueArgument);
54
55 // gamma subregion 0
56 auto constantNode = TestOperation::createNode(gammaNode->subregion(0), {}, { valueType });
57 auto binaryNode = TestOperation::createNode(
58 gammaNode->subregion(0),
59 { entryVar.branchArgument[0], constantNode->output(0) },
60 { valueType });
61
62 // gamma subregion 1
63 auto unaryNode = TestOperation::createNode(
64 gammaNode->subregion(1),
65 { entryVar.branchArgument[1] },
66 { valueType });
67
68 auto exitVar = gammaNode->AddExitVar({ binaryNode->output(0), unaryNode->output(0) });
69
70 auto lambdaOutput = lambdaNode->finalize({ exitVar.output });
71
72 GraphExport::Create(*lambdaOutput, "x");
73
74 view(rvsdg, stdout);
75
76 // Act
77 NodeHoisting nodeHoisting;
79 nodeHoisting.Run(rvsdgModule, statisticsCollector);
80
81 view(rvsdg, stdout);
82
83 // Assert
84 // All nodes from the gamma subregions should have been hoisted to the lambda subregion
85 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 4u);
86
87 // The original nodes in the gamma subregions should have been removed
88 EXPECT_EQ(gammaNode->subregion(0)->numNodes(), 0u);
89 EXPECT_EQ(gammaNode->subregion(1)->numNodes(), 0u);
90}
91
92TEST(NodeHoistingTests, nestedGamma)
93{
94 using namespace jlm::llvm;
95 using namespace jlm::rvsdg;
96
97 // Arrange
98 const auto controlType = ControlType::Create(2);
99 const auto valueType = TestType::createValueType();
100 const auto functionType = FunctionType::Create(
101 {
102 controlType,
103 valueType,
104 },
105 { valueType });
106
107 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
108 auto & rvsdg = rvsdgModule.Rvsdg();
109
110 auto lambdaNode = LambdaNode::Create(
111 rvsdg.GetRootRegion(),
113 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
114 auto valueArgument = lambdaNode->GetFunctionArguments()[1];
115
116 auto gammaNode1 = GammaNode::create(controlArgument, 2);
117 auto controlEntryVar = gammaNode1->AddEntryVar(controlArgument);
118 auto valueEntryVar1 = gammaNode1->AddEntryVar(valueArgument);
119
120 // gamma1 subregion 0
121 auto constantNode1 = TestOperation::createNode(gammaNode1->subregion(0), {}, { valueType });
122
123 auto gammaNode2 = GammaNode::create(controlEntryVar.branchArgument[0], 2);
124 auto valueEntryVar2 = gammaNode2->AddEntryVar(valueEntryVar1.branchArgument[0]);
125 auto valueEntryVar3 = gammaNode2->AddEntryVar(constantNode1->output(0));
126
127 // gamma2 subregion 0
128 auto binaryNode = TestOperation::createNode(
129 gammaNode1->subregion(0),
130 { valueEntryVar2.branchArgument[0], valueEntryVar3.branchArgument[0] },
131 { valueType });
132
133 // gamma2 subregion 1
134 auto unaryNode = TestOperation::createNode(
135 gammaNode1->subregion(1),
136 { valueEntryVar2.branchArgument[1] },
137 { valueType });
138
139 auto exitVar1 = gammaNode2->AddExitVar({ binaryNode->output(0), unaryNode->output(0) });
140
141 // gamma1 subregion 1
142 auto constantNode2 = TestOperation::createNode(gammaNode1->subregion(1), {}, { valueType });
143
144 auto exitVar2 = gammaNode1->AddExitVar({ exitVar1.output, constantNode2->output(0) });
145
146 auto lambdaOutput = lambdaNode->finalize({ exitVar2.output });
147
148 GraphExport::Create(*lambdaOutput, "x");
149
150 view(rvsdg, stdout);
151
152 // Act
153 NodeHoisting nodeHoisting;
155 nodeHoisting.Run(rvsdgModule, statisticsCollector);
156
157 view(rvsdg, stdout);
158
159 // Assert
160 // All simple nodes from both gamma subregions should have been hoisted to the lambda subregion
161 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 5u);
162
163 // Only gamma node 2 should be left in gamma node 1 subregion 0
164 EXPECT_EQ(gammaNode1->subregion(0)->numNodes(), 1u);
165 EXPECT_EQ(gammaNode1->subregion(1)->numNodes(), 0u);
166
167 // All nodes should have been hoisted out
168 EXPECT_EQ(gammaNode2->subregion(0)->numNodes(), 0u);
169 EXPECT_EQ(gammaNode2->subregion(1)->numNodes(), 0u);
170}
171
172TEST(NodeHoistingTests, simpleTheta)
173{
174 using namespace jlm::llvm;
175 using namespace jlm::rvsdg;
176
177 // Arrange
178 auto controlType = ControlType::Create(2);
179 const auto valueType = TestType::createValueType();
180 const auto functionType = FunctionType::Create(
181 {
182 controlType,
183 valueType,
184 },
185 { valueType });
186
187 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
188 auto & rvsdg = rvsdgModule.Rvsdg();
189
190 auto lambdaNode = LambdaNode::Create(
191 rvsdg.GetRootRegion(),
193 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
194 auto valueArgument = lambdaNode->GetFunctionArguments()[1];
195
196 auto thetaNode = ThetaNode::create(lambdaNode->subregion());
197
198 auto lv1 = thetaNode->AddLoopVar(controlArgument);
199 auto lv2 = thetaNode->AddLoopVar(valueArgument);
200 auto lv3 = thetaNode->AddLoopVar(valueArgument);
201 auto lv4 = thetaNode->AddLoopVar(valueArgument);
202
203 auto node1 = TestOperation::createNode(thetaNode->subregion(), {}, { valueType });
204 auto node2 = TestOperation::createNode(
205 thetaNode->subregion(),
206 { node1->output(0), lv3.pre },
207 { valueType });
208 auto node3 = TestOperation::createNode(
209 thetaNode->subregion(),
210 { lv2.pre, node2->output(0) },
211 { valueType });
212 auto node4 =
213 TestOperation::createNode(thetaNode->subregion(), { lv3.pre, lv4.pre }, { valueType });
214
215 lv2.post->divert_to(node3->output(0));
216 lv4.post->divert_to(node4->output(0));
217
218 thetaNode->set_predicate(lv1.pre);
219
220 lambdaNode->finalize({ thetaNode->output(1) });
221
222 view(rvsdg, stdout);
223
224 // Act
225 NodeHoisting nodeHoisting;
227 nodeHoisting.Run(rvsdgModule, statisticsCollector);
228
229 view(rvsdg, stdout);
230
231 // Assert
232 // We expect node1 and node2 to be hoisted out of the theta subregion
233 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 3u);
234 EXPECT_EQ(thetaNode->subregion()->numNodes(), 2u);
235
236 EXPECT_EQ(lv2.post->origin(), node3->output(0));
237 EXPECT_EQ(lv4.post->origin(), node4->output(0));
238}
239
240TEST(NodeHoistingTests, invariantMemoryOperation)
241{
242 using namespace jlm::llvm;
243 using namespace jlm::rvsdg;
244
245 // Arrange
246 const auto memoryStateType = MemoryStateType::Create();
247 const auto pointerType = PointerType::Create();
248 const auto controlType = ControlType::Create(2);
249 const auto valueType = TestType::createValueType();
250 const auto functionType = FunctionType::Create(
251 { controlType, pointerType, valueType, memoryStateType },
252 { memoryStateType });
253
254 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
255 auto & rvsdg = rvsdgModule.Rvsdg();
256
257 auto lambdaNode = LambdaNode::Create(
258 rvsdg.GetRootRegion(),
260 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
261 auto pointerArgument = lambdaNode->GetFunctionArguments()[1];
262 auto valueArgument = lambdaNode->GetFunctionArguments()[2];
263 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[3];
264
265 auto thetaNode = ThetaNode::create(lambdaNode->subregion());
266
267 auto lvc = thetaNode->AddLoopVar(controlArgument);
268 auto lva = thetaNode->AddLoopVar(pointerArgument);
269 auto lvv = thetaNode->AddLoopVar(valueArgument);
270 auto lvs = thetaNode->AddLoopVar(memoryStateArgument);
271
272 auto & storeNode = StoreNonVolatileOperation::CreateNode(*lva.pre, *lvv.pre, { lvs.pre }, 4);
273
274 lvs.post->divert_to(storeNode.output(0));
275 thetaNode->set_predicate(lvc.pre);
276
277 lambdaNode->finalize({ lvs.output });
278
279 view(rvsdg, stdout);
280
281 // Act
282 NodeHoisting nodeHoisting;
284 nodeHoisting.Run(rvsdgModule, statisticsCollector);
285
286 view(rvsdg, stdout);
287
288 // Assert
289 // We expect the store node hoisted out of the theta subregion
290 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 2u);
291 EXPECT_EQ(thetaNode->subregion()->numNodes(), 0u);
292
293 // We expect no new input to be added to the theta node as the store node should have been
294 // "hoisted along" its memory state edges.
295 EXPECT_EQ(thetaNode->ninputs(), 4u);
296}
297
298TEST(NodeHoistingTests, statefulOperations)
299{
300 // Arrange
301 using namespace jlm::llvm;
302 using namespace jlm::rvsdg;
303
304 auto controlType = ControlType::Create(2);
305 auto valueType = TestType::createValueType();
306 auto stateType = TestType::createStateType();
307 const auto functionType = FunctionType::Create(
308 {
309 controlType,
310 valueType,
311 stateType,
312 },
313 { valueType });
314
315 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
316 auto & rvsdg = rvsdgModule.Rvsdg();
317
318 auto lambdaNode = LambdaNode::Create(
319 rvsdg.GetRootRegion(),
321 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
322 auto valueArgument = lambdaNode->GetFunctionArguments()[1];
323 auto stateArgument = lambdaNode->GetFunctionArguments()[2];
324
325 auto gammaNode1 = GammaNode::create(controlArgument, 2);
326 auto controlEntryVar = gammaNode1->AddEntryVar(controlArgument);
327 auto valueEntryVar1 = gammaNode1->AddEntryVar(valueArgument);
328 auto stateEntryVar = gammaNode1->AddEntryVar(stateArgument);
329
330 auto stateNode = TestOperation::createNode(
331 gammaNode1->subregion(0),
332 { valueEntryVar1.branchArgument[0], stateEntryVar.branchArgument[0] },
333 { valueType });
334
335 auto gammaNode2 = GammaNode::create(controlEntryVar.branchArgument[0], 2);
336 auto valueEntryVar2 = gammaNode2->AddEntryVar(stateNode->output(0));
337 auto valueEntryVar3 = gammaNode2->AddEntryVar(valueEntryVar1.branchArgument[0]);
338
339 auto binaryNode = TestOperation::createNode(
340 gammaNode2->subregion(0),
341 { valueEntryVar2.branchArgument[0], valueEntryVar3.branchArgument[0] },
342 { valueType });
343
344 auto exitVar2 =
345 gammaNode2->AddExitVar({ binaryNode->output(0), valueEntryVar2.branchArgument[1] });
346
347 auto exitVar = gammaNode1->AddExitVar({ exitVar2.output, valueEntryVar1.branchArgument[1] });
348
349 lambdaNode->finalize({ exitVar.output });
350
351 view(rvsdg, stdout);
352
353 // Act
354 NodeHoisting nodeHoisting;
356 nodeHoisting.Run(rvsdgModule, statisticsCollector);
357
358 view(rvsdg, stdout);
359
360 // Assert
361 // We expect that stateNode stays where it is and only the binaryNode is hoisted to the same
362 // region as stateNode
363
364 // Gamma node and undef node
365 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 1u);
366
367 // stateNode, gammaNode2, and binaryNode
368 EXPECT_EQ(gammaNode1->subregion(0)->numNodes(), 3u);
369 EXPECT_EQ(gammaNode1->subregion(1)->numNodes(), 0u);
370
371 EXPECT_EQ(gammaNode2->subregion(0)->numNodes(), 0u);
372 EXPECT_EQ(gammaNode2->subregion(1)->numNodes(), 0u);
373}
374
375TEST(NodeHoistingTests, controlConstants)
376{
406 // Arrange
407 using namespace jlm::llvm;
408 using namespace jlm::rvsdg;
409
410 auto controlType = ControlType::Create(2);
411 auto int32Type = BitType::Create(32);
412 auto memoryStateType = MemoryStateType::Create();
413 auto ioStateType = IOStateType::Create();
414 const auto functionType = FunctionType::Create(
415 { ioStateType, memoryStateType },
416 { int32Type, ioStateType, memoryStateType });
417
418 jlm::llvm::LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
419 auto & rvsdg = rvsdgModule.Rvsdg();
420
421 auto lambdaNode = LambdaNode::Create(
422 rvsdg.GetRootRegion(),
424
425 auto ioStateArgument = lambdaNode->GetFunctionArguments()[0];
426 auto memStateArgument = lambdaNode->GetFunctionArguments()[1];
427
428 // Theta node
429 auto thetaNode = ThetaNode::create(lambdaNode->subregion());
430 auto thetaUndef = UndefValueOperation::Create(*lambdaNode->subregion(), controlType);
431 auto thetaCtrlLoopVar = thetaNode->AddLoopVar(thetaUndef);
432 auto & thetaInnerCtrl1 = ControlConstantOperation::create(*thetaNode->subregion(), 2, 1);
433 thetaCtrlLoopVar.post->divert_to(&thetaInnerCtrl1);
434
435 // gamma1: takes theta's control loop var output as its predicate
436 auto & gamma1 = GammaNode::Create(*thetaCtrlLoopVar.output, 2, {});
437
438 // gamma1 exit variable taking Ctrl(0) and Ctrl(1) in the respective subregions
439 auto & gamma1Ctrl1 = ControlConstantOperation::create(*gamma1.subregion(0), 2, 1);
440 auto & gamma1Ctrl0 = ControlConstantOperation::create(*gamma1.subregion(1), 2, 0);
441 auto gamma1Exit = gamma1.AddExitVar({ &gamma1Ctrl1, &gamma1Ctrl0 });
442
443 // gamma2: takes gamma1's exit (control) as its predicate
444 auto & gamma2 = GammaNode::Create(*gamma1Exit.output, 2, {});
445
446 // gamma2 exit variable takes integer constants
447 auto & gamma2Int3 = *IntegerConstantOperation::Create(*gamma2.subregion(0), 32, 3).output(0);
448 auto & gamma2Int7 = *IntegerConstantOperation::Create(*gamma2.subregion(1), 32, 7).output(0);
449
450 auto gamma2Exit = gamma2.AddExitVar({ &gamma2Int3, &gamma2Int7 });
451
452 lambdaNode->finalize({ gamma2Exit.output, ioStateArgument, memStateArgument });
453
454 view(rvsdg, stdout);
455
456 // Act
457 NodeHoisting nodeHoisting;
459 nodeHoisting.Run(rvsdgModule, statisticsCollector);
460
461 view(rvsdg, stdout);
462
463 // Assert
464 // Control constants must stay in their original regions and not be hoisted.
465 // Bit constants (Int32) should be hoisted to the lambda subregion.
466
467 // Lambda subregion: theta node + gamma1 + gamma2 + CTL(0) (loop var entry) + two hoisted Int32
468 // constants
469 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 6u);
470
471 // Theta subregion: The Ctrl(0) and Ctrl(1) remain inside the theta
472 EXPECT_EQ(thetaNode->subregion()->numNodes(), 2u);
473 auto thetaPredicateOwner = TryGetOwnerNode<SimpleNode>(*thetaNode->predicate()->origin());
474 EXPECT_TRUE(thetaPredicateOwner);
475 EXPECT_EQ(thetaPredicateOwner->region(), thetaNode->subregion());
476 auto thetaPostOwner = TryGetOwnerNode<SimpleNode>(*thetaCtrlLoopVar.post->origin());
477 EXPECT_TRUE(thetaPostOwner);
478 EXPECT_EQ(thetaPostOwner->region(), thetaNode->subregion());
479
480 // Gamma1 subregions: control constants stay in place (no structural nodes inside)
481 EXPECT_EQ(gamma1.subregion(0)->numNodes(), 1u); // Ctrl(1) only
482 auto gamma1LeftCtrlOwner = TryGetOwnerNode<SimpleNode>(*gamma1Exit.branchResult[0]->origin());
483 EXPECT_TRUE(gamma1LeftCtrlOwner);
484 EXPECT_EQ(gamma1LeftCtrlOwner->region(), gamma1.subregion(0));
485
486 EXPECT_EQ(gamma1.subregion(1)->numNodes(), 1u); // Ctrl(0) only
487 auto gamma1RightCtrlOwner = TryGetOwnerNode<SimpleNode>(*gamma1Exit.branchResult[1]->origin());
488 EXPECT_TRUE(gamma1RightCtrlOwner);
489 EXPECT_EQ(gamma1RightCtrlOwner->region(), gamma1.subregion(1));
490
491 // Gamma2 subregions: Int32 constants should have been hoisted out to lambda level
492 EXPECT_EQ(gamma2.subregion(0)->numNodes(), 0u);
493 EXPECT_EQ(gamma2.subregion(1)->numNodes(), 0u);
494}
495
496TEST(NodeHoistingTests, hoistLoadNodesOutOfGamma)
497{
498 using namespace jlm::rvsdg;
499
500 // Arrange
501 const auto ptrType = PointerType::Create();
502 const auto i32Type = BitType::Create(32);
503 const auto ioStateType = IOStateType::Create();
504 const auto memoryStateType = MemoryStateType::Create();
505 const auto controlType = ControlType::Create(2);
506 const auto functionType = FunctionType::Create(
507 { controlType, ptrType, ioStateType, memoryStateType },
508 { i32Type, ioStateType, memoryStateType });
509
510 LlvmRvsdgModule rvsdgModule(util::FilePath(""), "", "");
511 auto & rvsdg = rvsdgModule.Rvsdg();
512
513 auto lambdaNode = LambdaNode::Create(
514 rvsdg.GetRootRegion(),
516 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
517 auto ptrArgument = lambdaNode->GetFunctionArguments()[1];
518 auto ioStateArgument = lambdaNode->GetFunctionArguments()[2];
519 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[3];
520
521 auto gammaNode = GammaNode::create(controlArgument, 2);
522 auto ptrEntryVar = gammaNode->AddEntryVar(ptrArgument);
523 auto ioStateEntryVar = gammaNode->AddEntryVar(ioStateArgument);
524 auto memoryStateEntryVar = gammaNode->AddEntryVar(memoryStateArgument);
525
526 // gamma subregion 0
527 auto & hoistBarrierNode = MemoryHoistBarrierOperation::createNode(
528 *ptrEntryVar.branchArgument[0],
529 *ioStateEntryVar.branchArgument[0],
530 0);
531 auto & loadNode0 = LoadNonVolatileOperation::CreateNode(
532 *hoistBarrierNode.output(0),
533 { memoryStateEntryVar.branchArgument[0] },
534 i32Type,
535 4);
536
537 // gamma subregion 1
538 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(
539 *ptrEntryVar.branchArgument[1],
540 { memoryStateEntryVar.branchArgument[1] },
541 i32Type,
542 4);
543 auto & storeNode1 = StoreNonVolatileOperation::CreateNode(
544 *ptrEntryVar.branchArgument[1],
545 *loadNode1.output(0),
546 { loadNode1.output(1) },
547 4);
548
549 auto i32ExitVar = gammaNode->AddExitVar({ loadNode0.output(0), loadNode1.output(0) });
550 auto ioStateExitVar = gammaNode->AddExitVar(
551 { ioStateEntryVar.branchArgument[0], ioStateEntryVar.branchArgument[1] });
552 auto memoryStateExitVar = gammaNode->AddExitVar({ loadNode0.output(1), storeNode1.output(0) });
553
554 auto lambdaOutput =
555 lambdaNode->finalize({ i32ExitVar.output, ioStateExitVar.output, memoryStateExitVar.output });
556
557 GraphExport::Create(*lambdaOutput, "x");
558
559 // Act
560 NodeHoisting nodeHoisting;
562 nodeHoisting.Run(rvsdgModule, statisticsCollector);
563
564 // Assert
565 // We expect the load node from gamma subregion 1 to be hoisted out
566 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 2u);
567 EXPECT_EQ(gammaNode->subregion(0)->numNodes(), 2u);
568 EXPECT_EQ(gammaNode->subregion(1)->numNodes(), 1u);
569
570 // We expect that only one input was added to the gamma node: the loaded value of the hoisted load
571 // node. We do not expect that a new input for the memory state of the load node was added as the
572 // load node should have been "hoisted along" its state edge.
573 EXPECT_EQ(gammaNode->ninputs(), 5u);
574}
575
576TEST(NodeHoistingTests, hoistLoadNodesOutofNestedGamma)
577{
578 using namespace jlm::rvsdg;
579
580 // Arrange
581 const auto ptrType = PointerType::Create();
582 const auto i32Type = BitType::Create(32);
583 const auto ioStateType = IOStateType::Create();
584 const auto memoryStateType = MemoryStateType::Create();
585 const auto controlType = ControlType::Create(2);
586 const auto functionType = FunctionType::Create(
587 { controlType, ptrType, ptrType, ioStateType, memoryStateType },
588 { i32Type, ioStateType, memoryStateType });
589
590 LlvmRvsdgModule rvsdgModule(util::FilePath(""), "", "");
591 auto & rvsdg = rvsdgModule.Rvsdg();
592
593 auto lambdaNode = LambdaNode::Create(
594 rvsdg.GetRootRegion(),
596 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
597 auto ptrArgument1 = lambdaNode->GetFunctionArguments()[1];
598 auto ptrArgument2 = lambdaNode->GetFunctionArguments()[2];
599 auto ioStateArgument = lambdaNode->GetFunctionArguments()[3];
600 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[4];
601
602 auto outerGammaNode = GammaNode::create(controlArgument, 2);
603 auto ctlEntryVar = outerGammaNode->AddEntryVar(controlArgument);
604 auto outerPtr1EntryVar = outerGammaNode->AddEntryVar(ptrArgument1);
605 auto outerPtr2EntryVar = outerGammaNode->AddEntryVar(ptrArgument2);
606 auto outerIOStateEntryVar = outerGammaNode->AddEntryVar(ioStateArgument);
607 auto outerMemoryStateEntryVar = outerGammaNode->AddEntryVar(memoryStateArgument);
608
609 // outerGammaNode - subregion 0
610 auto & hoistBarrierNode = MemoryHoistBarrierOperation::createNode(
611 *outerPtr1EntryVar.branchArgument[0],
612 *outerIOStateEntryVar.branchArgument[0],
613 0);
614
615 auto innerGammaNode = GammaNode::create(ctlEntryVar.branchArgument[0], 2);
616 auto innerPtr1EntryVar = innerGammaNode->AddEntryVar(hoistBarrierNode.output(0));
617 auto innerPtr2EntryVar = innerGammaNode->AddEntryVar(outerPtr2EntryVar.branchArgument[0]);
618 auto innerMemoryStateEntryVar =
619 innerGammaNode->AddEntryVar(outerMemoryStateEntryVar.branchArgument[0]);
620
621 // inner0GammaNode - subregion 0
622 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(
623 *innerPtr1EntryVar.branchArgument[0],
624 { innerMemoryStateEntryVar.branchArgument[0] },
625 i32Type,
626 4);
627
628 // inner0GammaNode - subregion 1
629 auto & loadNode2 = LoadNonVolatileOperation::CreateNode(
630 *innerPtr2EntryVar.branchArgument[1],
631 { innerMemoryStateEntryVar.branchArgument[1] },
632 i32Type,
633 4);
634
635 // inner0GammaNode - finalize
636 auto innerI32ExitVar = innerGammaNode->AddExitVar({ loadNode1.output(0), loadNode2.output(0) });
637 auto innerMemoryStateExitVar = innerGammaNode->AddExitVar(
638 { loadNode1.output(1), innerMemoryStateEntryVar.branchArgument[1] });
639
640 // outerGammaNode - subregion 1
641 auto test1 = TestOperation::createNode(outerGammaNode->subregion(1), {}, { i32Type });
642
643 // outerGammaNode - finalize
644 auto outerI32ExitVar = outerGammaNode->AddExitVar({ innerI32ExitVar.output, test1->output(0) });
645 auto outerIOStateExitVar = outerGammaNode->AddExitVar(
646 { outerIOStateEntryVar.branchArgument[0], outerIOStateEntryVar.branchArgument[1] });
647 auto outerMemoryStateExitVar = outerGammaNode->AddExitVar(
648 { innerMemoryStateExitVar.output, outerMemoryStateEntryVar.branchArgument[1] });
649
650 // lambdaNode - finalize
651 auto lambdaOutput = lambdaNode->finalize(
652 { outerI32ExitVar.output, outerIOStateExitVar.output, outerMemoryStateExitVar.output });
653
654 GraphExport::Create(*lambdaOutput, "x");
655
656 // Act
657 NodeHoisting nodeHoisting;
659 nodeHoisting.Run(rvsdgModule, statisticsCollector);
660
661 // Assert
662 // We expect the following to happen:
663 // 1. loadNode1 is hoisted into subregion 0 of outerGammaNode, where the hoisting is stopped by
664 // the MemoryHoistBarrierOperation node
665 // 2. loadNode2 is hoisted into the lambda subregion
666 // 3. The hoisting of loadNode2 needs get past the already hoisted loadNode1. As loadNode1 is
667 // hoisted out first, its outgoing memory state is indirectly connected to loadNode2, which is
668 // still in subregion 1 of the innerGammaNode. Once loadNode2 is hoisted, it is first hoisted into
669 // subregion 0 of the outerGammaNode, where it is sequentialized behind loadNode1, and then needs
670 // to be hoisted above loadNode1 into the lambda subregion.
671 EXPECT_FALSE(
672 Region::containsOperation<LoadNonVolatileOperation>(*innerGammaNode->subregion(0), false));
673 EXPECT_FALSE(
674 Region::containsOperation<LoadNonVolatileOperation>(*innerGammaNode->subregion(1), false));
675
676 EXPECT_TRUE(
677 Region::containsOperation<LoadNonVolatileOperation>(*outerGammaNode->subregion(0), false));
678 EXPECT_EQ(outerGammaNode->subregion(0)->numNodes(), 3u);
679
680 EXPECT_TRUE(Region::containsOperation<LoadNonVolatileOperation>(*lambdaNode->subregion(), false));
681 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 3u);
682
683 {
684 auto [hoistedLoadNode1, loadOp] =
686 *innerMemoryStateEntryVar.input->origin());
687 EXPECT_NE(loadOp, nullptr);
688 EXPECT_EQ(LoadOperation::AddressInput(*hoistedLoadNode1).origin(), hoistBarrierNode.output(0));
689 }
690
691 {
692 auto [hoistedLoadNode2, loadOp] =
694 *outerMemoryStateEntryVar.input->origin());
695 EXPECT_NE(loadOp, nullptr);
696 EXPECT_EQ(LoadOperation::AddressInput(*hoistedLoadNode2).origin(), ptrArgument2);
697 }
698}
699
700TEST(NodeHoistingTests, hoistLoadNodeOutOfGammaInTheta)
701{
702 using namespace jlm::rvsdg;
703
704 // Arrange
705 const auto ptrType = PointerType::Create();
706 const auto i32Type = BitType::Create(32);
707 const auto memoryStateType = MemoryStateType::Create();
708 const auto controlType = ControlType::Create(2);
709 const auto functionType =
710 FunctionType::Create({ controlType, ptrType, memoryStateType }, { ptrType, memoryStateType });
711
712 LlvmRvsdgModule rvsdgModule(util::FilePath(""), "", "");
713 auto & rvsdg = rvsdgModule.Rvsdg();
714
715 auto lambdaNode = LambdaNode::Create(
716 rvsdg.GetRootRegion(),
718 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
719 auto ptrArgument = lambdaNode->GetFunctionArguments()[1];
720 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[2];
721
722 auto thetaNode = ThetaNode::create(lambdaNode->subregion());
723 auto ptrLoopVar = thetaNode->AddLoopVar(ptrArgument);
724 auto memoryStateLoopVar = thetaNode->AddLoopVar(memoryStateArgument);
725 auto controlLoopVar = thetaNode->AddLoopVar(controlArgument);
726
727 auto gammaNode = GammaNode::create(controlLoopVar.pre, 2);
728 auto ptrEntryVar = gammaNode->AddEntryVar(ptrLoopVar.pre);
729 auto memoryStateEntryVar = gammaNode->AddEntryVar(memoryStateLoopVar.pre);
730
731 // gamma subregion 0
732 auto testNode = TestOperation::createNode(gammaNode->subregion(0), {}, { i32Type });
733
734 // gamma subregion 1
735 auto & loadNode = LoadNonVolatileOperation::CreateNode(
736 *ptrEntryVar.branchArgument[1],
737 { memoryStateEntryVar.branchArgument[1] },
738 i32Type,
739 4);
740
741 auto i32ExitVar = gammaNode->AddExitVar({ testNode->output(0), loadNode.output(0) });
742 auto memoryStateExitVar =
743 gammaNode->AddExitVar({ memoryStateEntryVar.branchArgument[0], loadNode.output(1) });
744
745 memoryStateLoopVar.post->divert_to(memoryStateExitVar.output);
746
747 auto lambdaOutput = lambdaNode->finalize({ ptrLoopVar.output, memoryStateLoopVar.output });
748
749 GraphExport::Create(*lambdaOutput, "x");
750
751 // Act
752 NodeHoisting nodeHoisting;
754 nodeHoisting.Run(rvsdgModule, statisticsCollector);
755
756 // Assert
757 // We expect the load node from gamma subregion 1 to be hoisted out to the theta subregion
758 EXPECT_TRUE(Region::containsOperation<LoadNonVolatileOperation>(*thetaNode->subregion(), false));
759
760 // We expect that two inputs was added to the gamma node: the loaded value of the hoisted load
761 // node and the result of the test node.
762 EXPECT_EQ(gammaNode->ninputs(), 5u);
763}
764
765}
static std::shared_ptr< const IOStateType > Create()
Definition types.cpp:343
static rvsdg::Node & Create(rvsdg::Region &region, IntegerValueRepresentation representation)
static std::unique_ptr< LlvmLambdaOperation > Create(std::shared_ptr< const jlm::rvsdg::FunctionType > type, std::string name, const jlm::llvm::Linkage &linkage, jlm::llvm::CallingConvention callingConvention, jlm::llvm::AttributeSet attributes)
Definition lambda.hpp:84
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< LoadNonVolatileOperation > loadOperation, const std::vector< rvsdg::Output * > &operands)
Definition Load.hpp:470
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition Load.hpp:75
static rvsdg::SimpleNode & createNode(rvsdg::Output &address, rvsdg::Output &ioState, const std::size_t dereferenceableSize)
static std::shared_ptr< const MemoryStateType > Create()
Definition types.cpp:379
Node Hoisting Transformation.
void Run(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
static std::shared_ptr< const PointerType > Create()
Definition types.cpp:45
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, rvsdg::Output &value, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition Store.hpp:360
static jlm::rvsdg::Output * Create(rvsdg::Region &region, std::shared_ptr< const jlm::rvsdg::Type > type)
Output * origin() const noexcept
Definition node.hpp:58
NodeOutput * output(size_t index) const noexcept
Definition node.hpp:650
Global memory state passed between functions.
static util::StatisticsCollector statisticsCollector
TEST(ControlOperationsTests, foldConstants)
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872