Jlm
Loading...
Searching...
No Matches
StoreValueForwardingTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2026 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
17#include <jlm/llvm/ir/Trace.hpp>
19#include <jlm/rvsdg/control.hpp>
20#include <jlm/rvsdg/delta.hpp>
21#include <jlm/rvsdg/gamma.hpp>
22#include <jlm/rvsdg/node.hpp>
23#include <jlm/rvsdg/region.hpp>
26#include <jlm/rvsdg/theta.hpp>
28#include <jlm/rvsdg/view.hpp>
30#include <llvm/ADT/APFloat.h>
31#include <llvm/IR/Constants.h>
32
33static void
40
41TEST(StoreValueForwardingTests, NestedAllocas)
42{
43 using namespace jlm;
44 using namespace jlm::llvm;
45
61 // Arrange
62 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
63 auto & graph = rvsdgModule.Rvsdg();
64 const auto pointerType = PointerType::Create();
65 const auto intType = rvsdg::BitType::Create(32);
66 const auto ioStateType = IOStateType::Create();
67 const auto memoryStateType = MemoryStateType::Create();
68
69 const auto funcType = rvsdg::FunctionType::Create(
70 { ioStateType, memoryStateType },
71 { intType, ioStateType, memoryStateType });
72
73 // Setup the function "func"
74 auto & lambdaNode = *rvsdg::LambdaNode::Create(
75 graph.GetRootRegion(),
76 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
77
78 const auto io0 = lambdaNode.GetFunctionArguments()[0];
79 const auto mem0 = lambdaNode.GetFunctionArguments()[1];
80
81 auto & constantOne = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 1);
82 auto allocaPOutputs = AllocaOperation::create(intType, constantOne.output(0), 4);
83 auto allocaAOutputs = AllocaOperation::create(intType, constantOne.output(0), 4);
84
85 // Create constant 20 for the second STORE
86 auto & constantTwenty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 20);
87
88 // STORE p, a, memP0, memA0
89 auto & storePANode = StoreNonVolatileOperation::CreateNode(
90 *allocaPOutputs[0],
91 *allocaAOutputs[0],
92 { allocaPOutputs[1], allocaAOutputs[1] },
93 4);
94
95 // STORE a, 20, memP1, memA1
96 auto & storeA20Node = StoreNonVolatileOperation::CreateNode(
97 *allocaAOutputs[0],
98 *constantTwenty.output(0),
99 { storePANode.output(0), storePANode.output(1) },
100 4);
101
102 // LOAD p, memP2, memA2
103 auto & loadPNode = LoadNonVolatileOperation::CreateNode(
104 *allocaPOutputs[0],
105 { storeA20Node.output(0), storeA20Node.output(1) },
106 pointerType,
107 8);
108
109 // LOAD a0, memP3, memA3
110 auto & loadA0Node = LoadNonVolatileOperation::CreateNode(
111 *loadPNode.output(0),
112 { loadPNode.output(1), loadPNode.output(2) },
113 intType,
114 4);
115
116 lambdaNode.finalize({ loadA0Node.output(0), io0, mem0 });
117
118 // std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
119
120 // Act
121 RunStoreValueForwarding(rvsdgModule);
122
123 // std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
124
125 // Assert
126
127 // Both ALLOCAs should be gone, and only the LOAD of q should remain.
128 size_t allocaCount = 0;
129 size_t storeCount = 0;
130 size_t loadCount = 0;
131
132 for (auto & node : lambdaNode.subregion()->Nodes())
133 {
134 if (is<AllocaOperation>(&node))
135 allocaCount++;
136 else if (is<StoreOperation>(&node))
137 storeCount++;
138 else if (is<LoadOperation>(&node))
139 loadCount++;
140 }
141 EXPECT_EQ(allocaCount, 0u);
142 EXPECT_EQ(storeCount, 0u);
143 EXPECT_EQ(loadCount, 0u);
144
145 // Verify that the return value is a constant 20
146 const auto & result = *lambdaNode.GetFunctionResults()[0]->origin();
147 const auto resultValue = tryGetConstantSignedInteger(result);
148 EXPECT_EQ(resultValue, 20u);
149}
150
151TEST(StoreValueForwardingTests, GetElementPointerOffsets)
152{
153 using namespace jlm;
154 using namespace jlm::llvm;
155
174 // Arrange
175 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
176 auto & graph = rvsdgModule.Rvsdg();
177 const auto bits32Type = rvsdg::BitType::Create(32);
178 const auto bits64Type = rvsdg::BitType::Create(64);
179 const auto byteType = rvsdg::BitType::Create(8);
180 const auto ioStateType = IOStateType::Create();
181 const auto memoryStateType = MemoryStateType::Create();
182
183 const auto funcType = rvsdg::FunctionType::Create(
184 { ioStateType, memoryStateType },
185 { bits64Type, bits32Type, bits32Type, ioStateType, memoryStateType });
186
187 // Setup the function "func"
188 auto & lambdaNode = *rvsdg::LambdaNode::Create(
189 graph.GetRootRegion(),
190 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
191
192 const auto io0 = lambdaNode.GetFunctionArguments()[0];
193 const auto mem0 = lambdaNode.GetFunctionArguments()[1];
194
195 auto & constantTwo = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2);
196 auto allocaAOutputs = AllocaOperation::create(bits32Type, constantTwo.output(0), 4);
197
198 // Create constant 40 and 20 for the STOREs
199 auto & constantTwenty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 20);
200 auto & constantForty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 40);
201
202 // STORE a, 40, memA0
203 auto & storeA40Node = StoreNonVolatileOperation::CreateNode(
204 *allocaAOutputs[0],
205 *constantForty.output(0),
206 { allocaAOutputs[1] },
207 4);
208
209 // b = GetElementPointer a, bits32[1]
210 auto & constantOne = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 1);
211 auto gepBOutput =
212 GetElementPtrOperation::create(allocaAOutputs[0], { constantOne.output(0) }, bits32Type);
213
214 // STORE b, 20, memA1
215 auto & storeB20Node = StoreNonVolatileOperation::CreateNode(
216 *gepBOutput,
217 *constantTwenty.output(0),
218 { storeA40Node.output(0) },
219 4);
220
221 // l1, memA3 = LOAD[bits64] a, memA2
222 auto & loadL1Node = LoadNonVolatileOperation::CreateNode(
223 *allocaAOutputs[0],
224 { storeB20Node.output(0) },
225 bits64Type,
226 8);
227
228 // l2, memA4 = LOAD[bits32] a, memA3
229 auto & loadL2Node = LoadNonVolatileOperation::CreateNode(
230 *allocaAOutputs[0],
231 { loadL1Node.output(1) },
232 bits32Type,
233 4);
234
235 // c = GetElementPointer[byte] a, 4
236 auto & constantFour = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 4);
237 auto gepCOutput =
238 GetElementPtrOperation::create(allocaAOutputs[0], { constantFour.output(0) }, byteType);
239
240 // l3, memA5 = LOAD[bits32] c, memA4
241 auto & loadL3Node =
242 LoadNonVolatileOperation::CreateNode(*gepCOutput, { loadL2Node.output(1) }, bits32Type, 4);
243
244 lambdaNode.finalize(
245 { loadL1Node.output(0), loadL2Node.output(0), loadL3Node.output(0), io0, mem0 });
246
247 // Act
248 RunStoreValueForwarding(rvsdgModule);
249
250 // std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
251
252 // Assert
253
254 // The two stores should still be there, but only l1 should be the only LOAD
255 size_t storeCount = 0;
256 size_t loadCount = 0;
257
258 for (auto & node : lambdaNode.subregion()->Nodes())
259 {
260 if (is<StoreOperation>(&node))
261 storeCount++;
262 else if (is<LoadOperation>(&node))
263 loadCount++;
264 }
265 EXPECT_EQ(storeCount, 2u);
266 EXPECT_EQ(loadCount, 1u);
267
268 // Verify that the last two return values are constants 40 and 20
269 const auto results = lambdaNode.GetFunctionResults();
270 const auto r1 = tryGetConstantSignedInteger(*results[0]->origin());
271 const auto r2 = tryGetConstantSignedInteger(*results[1]->origin());
272 const auto r3 = tryGetConstantSignedInteger(*results[2]->origin());
273 EXPECT_FALSE(r1.has_value());
274 EXPECT_EQ(r2, 40u);
275 EXPECT_EQ(r3, 20u);
276}
277
278TEST(StoreValueForwardingTests, RoutingIn)
279{
280 using namespace jlm;
281 using namespace jlm::llvm;
282
304 // Arrange
305 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
306 auto & graph = rvsdgModule.Rvsdg();
307 const auto pointerType = PointerType::Create();
308 const auto bits32Type = rvsdg::BitType::Create(32);
309 const auto ioStateType = IOStateType::Create();
310 const auto memoryStateType = MemoryStateType::Create();
311 const auto unitType = rvsdg::UnitType::Create();
312
313 const auto funcType = rvsdg::FunctionType::Create(
314 { pointerType, ioStateType, memoryStateType },
315 { bits32Type, ioStateType, memoryStateType });
316
317 // Setup the function "func"
318 auto & lambdaNode = *rvsdg::LambdaNode::Create(
319 graph.GetRootRegion(),
320 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
321
322 auto & q = *lambdaNode.GetFunctionArguments()[0];
323 auto & io0 = *lambdaNode.GetFunctionArguments()[1];
324 auto & mem0 = *lambdaNode.GetFunctionArguments()[2];
325
326 // Create constant 40 for the STORE
327 auto & constantForty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 40);
328
329 // mem1 = STORE[bits32] q, 40, mem0
330 auto & storeQ40Node =
331 StoreNonVolatileOperation::CreateNode(q, *constantForty.output(0), { &mem0 }, 4);
332 auto & mem1 = *StoreOperation::MemoryStateOutputs(storeQ40Node).begin();
333
334 // Create theta node structure
335 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
336
337 auto qLoopVar = thetaNode.AddLoopVar(&q);
338 auto memLoopVar = thetaNode.AddLoopVar(&mem1);
339 auto undefL = UndefValueOperation::Create(*lambdaNode.subregion(), bits32Type);
340 auto lLoopVar = thetaNode.AddLoopVar(undefL);
341
342 // Create gamma node inside theta
343 auto & predicate = *thetaNode.predicate()->origin();
344 auto & gammaNode = rvsdg::GammaNode::Create(predicate, 2, { unitType, unitType });
345
346 auto qEntryVar = gammaNode.AddEntryVar(qLoopVar.pre);
347 auto memEntryVar = gammaNode.AddEntryVar(memLoopVar.pre);
348
349 // Create first gamma case: LOAD operation
350 auto & loadNode = LoadNonVolatileOperation::CreateNode(
351 *qEntryVar.branchArgument[0],
352 { memEntryVar.branchArgument[0] },
353 bits32Type,
354 4);
355 auto & loadedValue = LoadOperation::LoadedValueOutput(loadNode);
356 auto & mem4 = *LoadOperation::MemoryStateOutputs(loadNode).begin();
357
358 // Create second gamma case: constant 70
359 auto & gammaSubregion1 = *gammaNode.subregion(1);
360 auto & constantSeventy = IntegerConstantOperation::Create(gammaSubregion1, 32, 70);
361
362 // Create gamma exit variables
363 auto lExitVar = gammaNode.AddExitVar({ &loadedValue, constantSeventy.output(0) });
364 auto memExitVar = gammaNode.AddExitVar({ &mem4, memEntryVar.branchArgument[1] });
365
366 // route theta results
367 memLoopVar.post->divert_to(memExitVar.output);
368 lLoopVar.post->divert_to(lExitVar.output);
369
370 // Finalize lambda node
371 lambdaNode.finalize({ lLoopVar.output, &io0, memLoopVar.output });
372
373 std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
374
375 // Act
376 RunStoreValueForwarding(rvsdgModule);
377
378 std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
379
380 // Assert
381
382 // The result in gamma region 0 should be the constant 40
383 auto & branch0Result = *lExitVar.branchResult[0]->origin();
384 auto resultValue = tryGetConstantSignedInteger(branch0Result);
385 EXPECT_EQ(resultValue, 40u);
386
387 // The result should be routed in from the constant 40 node
388 auto & resultTraced = jlm::llvm::traceOutput(branch0Result);
389 EXPECT_EQ(&resultTraced, constantForty.output(0));
390}
391
392TEST(StoreValueForwardingTests, RouteOut)
393{
394 using namespace jlm;
395 using namespace jlm::llvm;
396
421 // Arrange
422 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
423 auto & graph = rvsdgModule.Rvsdg();
424 const auto pointerType = PointerType::Create();
425 const auto bits32Type = rvsdg::BitType::Create(32);
426 const auto ioStateType = IOStateType::Create();
427 const auto memoryStateType = MemoryStateType::Create();
428 const auto unitType = rvsdg::UnitType::Create();
429
430 const auto funcType = rvsdg::FunctionType::Create(
431 { pointerType, ioStateType, memoryStateType },
432 { bits32Type, ioStateType, memoryStateType });
433
434 // Setup the function "func"
435 auto & lambdaNode = *rvsdg::LambdaNode::Create(
436 graph.GetRootRegion(),
437 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
438
439 auto & q = *lambdaNode.GetFunctionArguments()[0];
440 auto & io0 = *lambdaNode.GetFunctionArguments()[1];
441 auto & mem0 = *lambdaNode.GetFunctionArguments()[2];
442
443 // Create constant 40 for the first STORE
444 auto & constantForty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 40);
445
446 // mem1 = STORE[bits32] q, 40, mem0
447 auto & storeQ40Node =
448 StoreNonVolatileOperation::CreateNode(q, *constantForty.output(0), { &mem0 }, 4);
449 auto & mem1 = *StoreOperation::MemoryStateOutputs(storeQ40Node).begin();
450
451 // Create theta node structure
452 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
453
454 auto qLoopVar = thetaNode.AddLoopVar(&q);
455 auto memLoopVar = thetaNode.AddLoopVar(&mem1);
456
457 // Create gamma node inside theta
458 auto & predicate = *thetaNode.predicate()->origin();
459 auto & gammaNode = rvsdg::GammaNode::Create(predicate, 2, { unitType, unitType });
460
461 auto qEntryVar = gammaNode.AddEntryVar(qLoopVar.pre);
462 auto memEntryVar = gammaNode.AddEntryVar(memLoopVar.pre);
463
464 // Create first gamma case: STORE operation
465 auto & gammaSubregion0 = *gammaNode.subregion(0);
466 auto & constantTwenty = IntegerConstantOperation::Create(gammaSubregion0, 32, 20);
467 auto & storeQ20Node = StoreNonVolatileOperation::CreateNode(
468 *qEntryVar.branchArgument[0],
469 *constantTwenty.output(0),
470 { memEntryVar.branchArgument[0] },
471 4);
472 auto & mem4 = *StoreOperation::MemoryStateOutputs(storeQ20Node).begin();
473
474 // Create gamma exit variables
475 auto memExitVar = gammaNode.AddExitVar({ &mem4, memEntryVar.branchArgument[1] });
476
477 // route theta results
478 memLoopVar.post->divert_to(memExitVar.output);
479
480 // l1, mem8 = LOAD[bits32] q, mem7
481 auto & loadNode =
482 LoadNonVolatileOperation::CreateNode(*qLoopVar.output, { memLoopVar.output }, bits32Type, 4);
483 auto & loadedValue = LoadOperation::LoadedValueOutput(loadNode);
484 auto & mem8 = *LoadOperation::MemoryStateOutputs(loadNode).begin();
485
486 // Finalize lambda node
487 lambdaNode.finalize({ &loadedValue, &io0, &mem8 });
488
489 // std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
490
491 // Act
492 RunStoreValueForwarding(rvsdgModule);
493
494 // std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
495
496 // Assert
497
498 // The LOAD should be gone, and the result should be routed through the gamma
499 const auto & resultOrigin = *lambdaNode.GetFunctionResults()[0]->origin();
500 const auto loopVar = thetaNode.MapOutputLoopVar(resultOrigin);
501
502 // Inside the theta, the loop variable should come straight from the gamma
503 const auto & postOrigin = *loopVar.post->origin();
504 const auto exitVar = gammaNode.MapOutputExitVar(postOrigin);
505 // In the 0th subregion, the output should be a constant integer
506 const auto constInteger =
507 jlm::llvm::tryGetConstantSignedInteger(*exitVar.branchResult[0]->origin());
508 EXPECT_EQ(constInteger, 20u);
509
510 // In the 1st subregion, the output should be traced back to the loop var input
511 const auto & traced1stRegionOrigin = jlm::llvm::traceOutput(*exitVar.branchResult[1]->origin());
512 const auto loopVar2 = thetaNode.MapPreLoopVar(traced1stRegionOrigin);
513 EXPECT_EQ(loopVar.pre, loopVar2.pre);
514
515 const auto constInputInteger = jlm::llvm::tryGetConstantSignedInteger(*loopVar.input->origin());
516 EXPECT_EQ(constInputInteger, 40u);
517}
518
519TEST(StoreValueForwardingTests, RouteAroundLoadLoop)
520{
521 using namespace jlm;
522 using namespace jlm::llvm;
523
543 // Arrange
544 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
545 auto & graph = rvsdgModule.Rvsdg();
546 const auto pointerType = PointerType::Create();
547 const auto bits32Type = rvsdg::BitType::Create(32);
548 const auto ioStateType = IOStateType::Create();
549 const auto memoryStateType = MemoryStateType::Create();
550
551 const auto funcType = rvsdg::FunctionType::Create(
552 { pointerType, ioStateType, memoryStateType },
553 { bits32Type, ioStateType, memoryStateType });
554
555 // Setup the function "func"
556 auto & lambdaNode = *rvsdg::LambdaNode::Create(
557 graph.GetRootRegion(),
558 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
559
560 auto & q = *lambdaNode.GetFunctionArguments()[0];
561 auto & io0 = *lambdaNode.GetFunctionArguments()[1];
562 auto & mem0 = *lambdaNode.GetFunctionArguments()[2];
563
564 // mem1 = STORE[bits32] q, 40, mem0
565 auto & constantForty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 40);
566 auto & storeQ40Node =
567 StoreNonVolatileOperation::CreateNode(q, *constantForty.output(0), { &mem0 }, 4);
568 auto & mem1 = *StoreOperation::MemoryStateOutputs(storeQ40Node).begin();
569
570 // _, mem4, l2 = theta q, mem1, undef
571 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
572 auto qLoopVar = thetaNode.AddLoopVar(&q);
573 auto memLoopVar = thetaNode.AddLoopVar(&mem1);
574 auto undefL = UndefValueOperation::Create(*lambdaNode.subregion(), bits32Type);
575 auto lLoopVar = thetaNode.AddLoopVar(undefL);
576
577 auto & loadInLoopNode =
578 LoadNonVolatileOperation::CreateNode(*qLoopVar.pre, { memLoopVar.pre }, bits32Type, 4);
579 auto & l1 = LoadOperation::LoadedValueOutput(loadInLoopNode);
580 auto & mem3 = *LoadOperation::MemoryStateOutputs(loadInLoopNode).begin();
581
582 lLoopVar.post->divert_to(&l1);
583 memLoopVar.post->divert_to(&mem3);
584
585 // l3, mem5 = LOAD[bits32] q, mem4
586 auto & loadAfterLoopNode =
587 LoadNonVolatileOperation::CreateNode(q, { memLoopVar.output }, bits32Type, 4);
588 auto & l3 = LoadOperation::LoadedValueOutput(loadAfterLoopNode);
589 auto & mem5 = *LoadOperation::MemoryStateOutputs(loadAfterLoopNode).begin();
590
591 // add1 = ADD l2, l3
592 auto & addNode = rvsdg::CreateOpNode<IntegerAddOperation>({ lLoopVar.output, &l3 }, 32);
593 auto & add1 = *addNode.output(0);
594
595 // return add1, io0, mem5
596 lambdaNode.finalize({ &add1, &io0, &mem5 });
597
598 std::cout << rvsdg::view(&rvsdgModule.Rvsdg().GetRootRegion()) << std::endl;
599
600 // Act
601 RunStoreValueForwarding(rvsdgModule);
602
603 std::cout << rvsdg::view(&rvsdgModule.Rvsdg().GetRootRegion()) << std::endl;
604
605 // Assert
606
607 // The value replacing l2 should lead to a loop output variable,
608 // whose post origin is an invariant loop variable
609 const auto & addLhsOrigin = *addNode.input(0)->origin();
610 const auto loopVar1 = thetaNode.MapOutputLoopVar(addLhsOrigin);
611 const auto loopVar2 = thetaNode.MapPreLoopVar(*loopVar1.post->origin());
612 EXPECT_TRUE(rvsdg::ThetaLoopVarIsInvariant(loopVar2));
613 EXPECT_EQ(tryGetConstantSignedInteger(*loopVar2.input->origin()), 40u);
614
615 // The value replacing l3 should come from the constant directly, not a theta output.
616 const auto & addRhsOrigin = *addNode.input(1)->origin();
617 EXPECT_EQ(tryGetConstantSignedInteger(addRhsOrigin), 40u);
618 EXPECT_EQ(rvsdg::TryGetOwnerNode<rvsdg::ThetaNode>(addRhsOrigin), nullptr);
619}
620
621TEST(StoreValueForwardingTests, RouteUninitialized)
622{
623 using namespace jlm;
624 using namespace jlm::llvm;
625
648 // Arrange
649 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
650 auto & graph = rvsdgModule.Rvsdg();
651 const auto bits32Type = rvsdg::BitType::Create(32);
652 const auto ioStateType = IOStateType::Create();
653 const auto memoryStateType = MemoryStateType::Create();
654 const auto unitType = rvsdg::UnitType::Create();
655
656 const auto funcType = rvsdg::FunctionType::Create(
657 { ioStateType, memoryStateType },
658 { bits32Type, ioStateType, memoryStateType });
659
660 auto & lambdaNode = *rvsdg::LambdaNode::Create(
661 graph.GetRootRegion(),
662 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
663
664 auto & io0 = *lambdaNode.GetFunctionArguments()[0];
665 auto & mem0 = *lambdaNode.GetFunctionArguments()[1];
666
667 // a, mem1 = ALLOCA[bits32], 1
668 auto & constantOne = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 1);
669 auto allocaAOutputs = AllocaOperation::create(bits32Type, constantOne.output(0), 4);
670
671 // pred = CTRL(0)
672 auto & predicate = rvsdg::ControlConstantOperation::create(*lambdaNode.subregion(), 2, 0);
673
674 // mem5 = gamma pred, a, mem1
675 auto & gammaNode = rvsdg::GammaNode::Create(predicate, 2, { unitType, unitType });
676 auto aEntryVar = gammaNode.AddEntryVar(allocaAOutputs[0]);
677 auto memEntryVar = gammaNode.AddEntryVar(allocaAOutputs[1]);
678
679 // [_, a1, mem2] { mem3 = STORE a1, 20, mem2 }[mem3]
680 auto & gammaSubregion0 = *gammaNode.subregion(0);
681 auto & constantTwenty = IntegerConstantOperation::Create(gammaSubregion0, 32, 20);
682 auto & storeA20Node = StoreNonVolatileOperation::CreateNode(
683 *aEntryVar.branchArgument[0],
684 *constantTwenty.output(0),
685 { memEntryVar.branchArgument[0] },
686 4);
687 auto & mem3 = *StoreOperation::MemoryStateOutputs(storeA20Node).begin();
688
689 // [_, a2, mem4] { }[mem4]
690 auto memExitVar = gammaNode.AddExitVar({ &mem3, memEntryVar.branchArgument[1] });
691
692 // ld, mem6 = LOAD[bits32] a, mem5
693 auto & loadNode = LoadNonVolatileOperation::CreateNode(
694 *allocaAOutputs[0],
695 { memExitVar.output },
696 bits32Type,
697 4);
698 auto & ld = LoadOperation::LoadedValueOutput(loadNode);
699
700 lambdaNode.finalize({ &ld, &io0, &mem0 });
701
702 // Act
703 RunStoreValueForwarding(rvsdgModule);
704
705 // Assert
706 const auto & resultOrigin = *lambdaNode.GetFunctionResults()[0]->origin();
707 EXPECT_NE(rvsdg::TryGetOwnerNode<rvsdg::GammaNode>(resultOrigin), nullptr);
708
709 const auto exitVar = gammaNode.MapOutputExitVar(resultOrigin);
710 EXPECT_EQ(jlm::llvm::tryGetConstantSignedInteger(*exitVar.branchResult[0]->origin()), 20u);
711 const auto [undefNode, undefOperation] =
712 rvsdg::TryGetSimpleNodeAndOptionalOp<UndefValueOperation>(*exitVar.branchResult[1]->origin());
713 EXPECT_TRUE(undefNode && undefOperation);
714}
715
716TEST(StoreValueForwardingTests, GepInLoop)
717{
718 using namespace jlm;
719 using namespace jlm::llvm;
720
749 // Arrange
750 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
751 auto & graph = rvsdgModule.Rvsdg();
752 const auto pointerType = PointerType::Create();
753 const auto bits32Type = rvsdg::BitType::Create(32);
754 const auto intArrayType = ArrayType::Create(bits32Type, 4);
755 const auto ioStateType = IOStateType::Create();
756 const auto memoryStateType = MemoryStateType::Create();
757
758 const auto funcType = rvsdg::FunctionType::Create(
759 { ioStateType, memoryStateType },
760 { bits32Type, ioStateType, memoryStateType });
761
762 auto & lambdaNode = *rvsdg::LambdaNode::Create(
763 graph.GetRootRegion(),
764 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
765
766 auto & io0 = *lambdaNode.GetFunctionArguments()[0];
767 auto & mem0 = *lambdaNode.GetFunctionArguments()[1];
768
769 auto & constantZero = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0);
770 auto & constantOne = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 1);
771 auto & constantTwo = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2);
772 auto & constantThree = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 3);
773 auto & constantTwenty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 20);
774 auto & constantThirty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 30);
775
776 // a, mem1 = ALLOCA[int[4]], 1
777 auto allocaAOutputs = AllocaOperation::create(intArrayType, constantOne.output(0), 4);
778
779 // a2 = &a[2], a3 = &a[3]
780 auto a2 = GetElementPtrOperation::create(
781 allocaAOutputs[0],
782 { constantZero.output(0), constantTwo.output(0) },
783 intArrayType);
784 auto a3 = GetElementPtrOperation::create(
785 allocaAOutputs[0],
786 { constantZero.output(0), constantThree.output(0) },
787 intArrayType);
788
789 // *a2 = 20; *a3 = 30;
790 auto & storeA220Node = StoreNonVolatileOperation::CreateNode(
791 *a2,
792 *constantTwenty.output(0),
793 { allocaAOutputs[1] },
794 4);
795 auto & mem1 = *StoreOperation::MemoryStateOutputs(storeA220Node).begin();
796 auto & storeA330Node =
797 StoreNonVolatileOperation::CreateNode(*a3, *constantThirty.output(0), { &mem1 }, 4);
798 auto & mem2 = *StoreOperation::MemoryStateOutputs(storeA330Node).begin();
799
800 // do { ... } while (0)
801 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
802 auto aLoopVar = thetaNode.AddLoopVar(allocaAOutputs[0]);
803 auto a2LoopVar = thetaNode.AddLoopVar(a2);
804 auto memLoopVar = thetaNode.AddLoopVar(&mem2);
805
806 // loaded = *a2;
807 auto & loadInLoopNode =
808 LoadNonVolatileOperation::CreateNode(*a2LoopVar.pre, { memLoopVar.pre }, bits32Type, 4);
809 auto & loadedValue = LoadOperation::LoadedValueOutput(loadInLoopNode);
810 auto & mem3 = *LoadOperation::MemoryStateOutputs(loadInLoopNode).begin();
811
812 // a1 = &a[1]; a22 = &a1[1];
813 auto & constantOneInLoop = IntegerConstantOperation::Create(*thetaNode.subregion(), 32, 1);
814 auto a1 =
815 GetElementPtrOperation::create(aLoopVar.pre, { constantOneInLoop.output(0) }, bits32Type);
816 auto a22 = GetElementPtrOperation::create(a1, { constantOneInLoop.output(0) }, bits32Type);
817
818 // *a22 = loaded + 1;
819 auto & addLoadedOneNode =
820 rvsdg::CreateOpNode<IntegerAddOperation>({ &loadedValue, constantOneInLoop.output(0) }, 32);
821 auto & incrementedValue = *addLoadedOneNode.output(0);
822 auto & storeA22Node = StoreNonVolatileOperation::CreateNode(*a22, incrementedValue, { &mem3 }, 4);
823 auto & mem4 = *StoreOperation::MemoryStateOutputs(storeA22Node).begin();
824
825 // *a1 = 10;
826 auto & constantTen = IntegerConstantOperation::Create(*thetaNode.subregion(), 32, 10);
827 auto & storeA1Node =
828 StoreNonVolatileOperation::CreateNode(*a1, *constantTen.output(0), { &mem4 }, 4);
829 auto & mem5 = *StoreOperation::MemoryStateOutputs(storeA1Node).begin();
830
831 memLoopVar.post->divert_to(&mem5);
832
833 // return *a2 + *a3;
834 auto & loadAfterLoopA2Node =
835 LoadNonVolatileOperation::CreateNode(*a2, { memLoopVar.output }, bits32Type, 4);
836 auto & loadedA2 = LoadOperation::LoadedValueOutput(loadAfterLoopA2Node);
837 auto & mem6 = *LoadOperation::MemoryStateOutputs(loadAfterLoopA2Node).begin();
838 auto & loadAfterLoopA3Node = LoadNonVolatileOperation::CreateNode(*a3, { &mem6 }, bits32Type, 4);
839 auto & loadedA3 = LoadOperation::LoadedValueOutput(loadAfterLoopA3Node);
840 auto & addResultNode = rvsdg::CreateOpNode<IntegerAddOperation>({ &loadedA2, &loadedA3 }, 32);
841 auto & resultValue = *addResultNode.output(0);
842
843 lambdaNode.finalize({ &resultValue, &io0, &mem0 });
844
845 std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
846
847 // Act
848 RunStoreValueForwarding(rvsdgModule);
849
850 std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
851
852 // Assert
853
854 // Check that the load of a[2] inside the loop is replaced by a loop variable
855 // which takes 20 as its initial value, and loaded + 1 as its post origin
856 const auto & loadedInLoopOrigin = *addLoadedOneNode.input(0)->origin();
857 const auto loadedLoopVar = thetaNode.MapPreLoopVar(loadedInLoopOrigin);
858 EXPECT_EQ(jlm::llvm::tryGetConstantSignedInteger(*loadedLoopVar.input->origin()), 20u);
859 EXPECT_EQ(loadedLoopVar.post->origin(), addLoadedOneNode.output(0));
860
861 // Check that the final load of a[2] is replaced by the value of loaded + 1 in the loop
862 const auto & addLhsOrigin = *addResultNode.input(0)->origin();
863 const auto a2ResultLoopVar = thetaNode.MapOutputLoopVar(addLhsOrigin);
864 EXPECT_EQ(a2ResultLoopVar.post->origin(), addLoadedOneNode.output(0));
865
866 // Check that the final load of a[3] is directly attached to the constant 30,
867 // and that it does not go via an invariant loop variable
868 const auto & addRhsOrigin = *addResultNode.input(1)->origin();
869 EXPECT_EQ(&addRhsOrigin, constantThirty.output(0));
870}
871
872TEST(StoreValueForwardingTests, LoadForwarding)
873{
874 using namespace jlm;
875 using namespace jlm::llvm;
876
889 // Arrange
890 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
891 auto & graph = rvsdgModule.Rvsdg();
892 const auto pointerType = PointerType::Create();
893 const auto bits32Type = rvsdg::BitType::Create(32);
894 const auto ioStateType = IOStateType::Create();
895 const auto memoryStateType = MemoryStateType::Create();
896
897 const auto funcType = rvsdg::FunctionType::Create(
898 { pointerType, ioStateType, memoryStateType },
899 { bits32Type, ioStateType, memoryStateType });
900
901 auto & lambdaNode = *rvsdg::LambdaNode::Create(
902 graph.GetRootRegion(),
903 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
904
905 auto & p = *lambdaNode.GetFunctionArguments()[0];
906 auto & io0 = *lambdaNode.GetFunctionArguments()[1];
907 auto & mem0 = *lambdaNode.GetFunctionArguments()[2];
908
909 auto & load1Node = LoadNonVolatileOperation::CreateNode(p, { &mem0 }, bits32Type, 4);
910 auto & l1 = LoadOperation::LoadedValueOutput(load1Node);
911 auto & mem1 = *LoadOperation::MemoryStateOutputs(load1Node).begin();
912
913 auto & load2Node = LoadNonVolatileOperation::CreateNode(p, { &mem1 }, bits32Type, 4);
914 auto & l2 = LoadOperation::LoadedValueOutput(load2Node);
915 auto & mem2 = *LoadOperation::MemoryStateOutputs(load2Node).begin();
916
917 auto & addNode = rvsdg::CreateOpNode<IntegerAddOperation>({ &l1, &l2 }, 32);
918 auto & add0 = *addNode.output(0);
919
920 lambdaNode.finalize({ &add0, &io0, &mem2 });
921
922 // Act
923 RunStoreValueForwarding(rvsdgModule);
924
925 // Assert
926 size_t loadCount = 0;
927 for (auto & node : lambdaNode.subregion()->Nodes())
928 {
929 if (is<LoadOperation>(&node))
930 loadCount++;
931 }
932 EXPECT_EQ(loadCount, 1u);
933
934 const auto & addLhsOrigin = jlm::llvm::traceOutput(*addNode.input(0)->origin());
935 const auto & addRhsOrigin = jlm::llvm::traceOutput(*addNode.input(1)->origin());
936 EXPECT_EQ(&addLhsOrigin, &l1);
937 EXPECT_EQ(&addRhsOrigin, &l1);
938
939 const auto & memoryResultOrigin =
940 jlm::llvm::traceOutput(*lambdaNode.GetFunctionResults()[2]->origin());
941 EXPECT_EQ(&memoryResultOrigin, &mem1);
942}
943
944TEST(StoreValueForwardingTests, LoadForwardingIntoTheta)
945{
946 using namespace jlm;
947 using namespace jlm::llvm;
948
968 // Arrange
969 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
970 auto & graph = rvsdgModule.Rvsdg();
971 const auto pointerType = PointerType::Create();
972 const auto bits32Type = rvsdg::BitType::Create(32);
973 const auto ioStateType = IOStateType::Create();
974 const auto memoryStateType = MemoryStateType::Create();
975
976 const auto funcType = rvsdg::FunctionType::Create(
977 { pointerType, ioStateType, memoryStateType },
978 { bits32Type, ioStateType, memoryStateType });
979
980 auto & lambdaNode = *rvsdg::LambdaNode::Create(
981 graph.GetRootRegion(),
982 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
983
984 auto & p = *lambdaNode.GetFunctionArguments()[0];
985 auto & io0 = *lambdaNode.GetFunctionArguments()[1];
986 auto & mem0 = *lambdaNode.GetFunctionArguments()[2];
987
988 auto & load1Node = LoadNonVolatileOperation::CreateNode(p, { &mem0 }, bits32Type, 4);
989 auto & l1 = LoadOperation::LoadedValueOutput(load1Node);
990 auto & mem1 = *LoadOperation::MemoryStateOutputs(load1Node).begin();
991
992 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
993 auto pLoopVar = thetaNode.AddLoopVar(&p);
994 auto sumLoopVar = thetaNode.AddLoopVar(&l1);
995 auto memLoopVar = thetaNode.AddLoopVar(&mem1);
996
997 auto & load2Node =
998 LoadNonVolatileOperation::CreateNode(*pLoopVar.pre, { memLoopVar.pre }, bits32Type, 4);
999 auto & l2 = LoadOperation::LoadedValueOutput(load2Node);
1000 auto & mem3 = *LoadOperation::MemoryStateOutputs(load2Node).begin();
1001
1002 auto & addNode = rvsdg::CreateOpNode<IntegerAddOperation>({ sumLoopVar.pre, &l2 }, 32);
1003 auto & sum2 = *addNode.output(0);
1004
1005 auto & constant100 = IntegerConstantOperation::Create(*thetaNode.subregion(), 32, 100);
1006 auto & sltNode = rvsdg::CreateOpNode<IntegerSltOperation>({ &sum2, constant100.output(0) }, 32);
1007 const auto predicate = rvsdg::MatchOperation::Create(*sltNode.output(0), { { 1, 1 } }, 0, 2);
1008
1009 thetaNode.set_predicate(predicate);
1010 sumLoopVar.post->divert_to(&sum2);
1011 memLoopVar.post->divert_to(&mem3);
1012
1013 lambdaNode.finalize({ sumLoopVar.output, &io0, memLoopVar.output });
1014
1015 // Act
1016 RunStoreValueForwarding(rvsdgModule);
1017
1018 // Assert
1019 size_t lambdaLoadCount = 0;
1020 for (auto & node : lambdaNode.subregion()->Nodes())
1021 {
1022 if (is<LoadOperation>(&node))
1023 lambdaLoadCount++;
1024 }
1025 EXPECT_EQ(lambdaLoadCount, 1u);
1026
1027 size_t thetaLoadCount = 0;
1028 for (auto & node : thetaNode.subregion()->Nodes())
1029 {
1030 if (is<LoadOperation>(&node))
1031 thetaLoadCount++;
1032 }
1033 EXPECT_EQ(thetaLoadCount, 0u);
1034
1035 const auto & addLhsOrigin = jlm::llvm::traceOutput(*addNode.input(0)->origin());
1036 EXPECT_EQ(&addLhsOrigin, sumLoopVar.pre);
1037
1038 const auto & addRhsOrigin = *addNode.input(1)->origin();
1039 const auto forwardedLoopVar = thetaNode.MapPreLoopVar(addRhsOrigin);
1040 EXPECT_TRUE(rvsdg::ThetaLoopVarIsInvariant(forwardedLoopVar));
1041 EXPECT_EQ(forwardedLoopVar.input->origin(), &l1);
1042 EXPECT_EQ(&jlm::llvm::traceOutput(addRhsOrigin), &l1);
1043
1044 const auto & memoryResultOrigin =
1045 jlm::llvm::traceOutput(*lambdaNode.GetFunctionResults()[2]->origin());
1046 EXPECT_EQ(&memoryResultOrigin, &mem1);
1047}
1048
1049TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithIntegerConstant)
1050{
1051 using namespace jlm::llvm;
1052 using namespace jlm::rvsdg;
1053
1054 // Arrange
1055 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1056 auto & graph = rvsdgModule.Rvsdg();
1057
1058 const auto pointerType = PointerType::Create();
1059 const auto bits8Type = BitType::Create(8);
1060 const auto bits32Type = BitType::Create(32);
1061 const auto functionType = FunctionType::Create(
1062 {},
1063 {
1064 bits32Type,
1065 bits8Type,
1066 });
1067
1068 auto deltaNode = DeltaNode::Create(
1069 &graph.GetRootRegion(),
1070 DeltaOperation::Create(bits32Type, true, pointerType));
1071 auto & four = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 4);
1072 auto & deltaOutput = deltaNode->finalize(four.output(0));
1073
1074 auto & lambdaNode = *LambdaNode::Create(
1075 graph.GetRootRegion(),
1076 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1077 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1078
1079 auto & load32Node = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits32Type, 4);
1080 auto & load8Node = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits8Type, 4);
1081
1082 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(load32Node),
1083 &LoadOperation::LoadedValueOutput(load8Node) });
1084
1085 // Act
1086 RunStoreValueForwarding(rvsdgModule);
1087
1088 // Assert
1089 {
1090 auto [intNode0, intOperation0] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1091 *lambdaNode.GetFunctionResults()[0]->origin());
1092 EXPECT_NE(intOperation0, nullptr);
1093 EXPECT_EQ(intOperation0->Representation().nbits(), 32u);
1094 EXPECT_EQ(intOperation0->Representation().to_uint(), 4u);
1095 }
1096
1097 {
1098 auto [truncNode, truncOperation] = TryGetSimpleNodeAndOptionalOp<TruncOperation>(
1099 *lambdaNode.GetFunctionResults()[1]->origin());
1100 EXPECT_NE(truncOperation, nullptr);
1101
1102 auto [intNode1, intOperation1] =
1103 TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(*truncNode->input(0)->origin());
1104 EXPECT_NE(intOperation1, nullptr);
1105 EXPECT_EQ(intOperation1->Representation().nbits(), 32u);
1106 EXPECT_EQ(intOperation1->Representation().to_uint(), 4u);
1107 }
1108}
1109
1110TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithAggregateZeroConstant)
1111{
1112 using namespace jlm::llvm;
1113 using namespace jlm::rvsdg;
1114
1115 // Arrange
1116 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1117 auto & graph = rvsdgModule.Rvsdg();
1118
1119 const auto pointerType = PointerType::Create();
1120 const auto bits32Type = BitType::Create(32);
1121 const auto fixedVectorType = FixedVectorType::Create(bits32Type, 4);
1122 const auto floatType = FloatingPointType::Create(fpsize::flt);
1123 const auto doubleType = FloatingPointType::Create(fpsize::dbl);
1124 const auto structType = StructType::CreateIdentified(
1125 "struct",
1126 { bits32Type, pointerType, fixedVectorType, floatType, doubleType },
1127 false);
1128 const auto functionType =
1129 FunctionType::Create({}, { bits32Type, pointerType, fixedVectorType, floatType, doubleType });
1130
1131 auto deltaNode = DeltaNode::Create(
1132 &graph.GetRootRegion(),
1133 DeltaOperation::Create(structType, true, pointerType));
1134 auto aggregateZero = ConstantAggregateZeroOperation::Create(*deltaNode->subregion(), structType);
1135 auto & deltaOutput = deltaNode->finalize(aggregateZero);
1136
1137 auto & lambdaNode = *LambdaNode::Create(
1138 graph.GetRootRegion(),
1139 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1140 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1141
1142 auto & zeroNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0);
1143 auto & oneNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 1);
1144 auto & twoNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2);
1145 auto & threeNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 3);
1146 auto & fourNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 4);
1147
1148 auto & gep0Node = GetElementPtrOperation::createNode(
1149 *ctxVar.inner,
1150 { zeroNode.output(0), zeroNode.output(0) },
1151 structType);
1152 auto & load32Node = LoadNonVolatileOperation::CreateNode(*gep0Node.output(0), {}, bits32Type, 4);
1153
1154 auto & gep1Node = GetElementPtrOperation::createNode(
1155 *ctxVar.inner,
1156 { zeroNode.output(0), oneNode.output(0) },
1157 structType);
1158 auto & loadPtrNode =
1159 LoadNonVolatileOperation::CreateNode(*gep1Node.output(0), {}, pointerType, 4);
1160
1161 auto & gep2Node = GetElementPtrOperation::createNode(
1162 *ctxVar.inner,
1163 { zeroNode.output(0), twoNode.output(0) },
1164 structType);
1165 auto & loadV32Node =
1166 LoadNonVolatileOperation::CreateNode(*gep2Node.output(0), {}, fixedVectorType, 4);
1167
1168 auto & gepFloatNode = GetElementPtrOperation::createNode(
1169 *ctxVar.inner,
1170 { zeroNode.output(0), threeNode.output(0) },
1171 structType);
1172 auto & loadFloatNode =
1173 LoadNonVolatileOperation::CreateNode(*gepFloatNode.output(0), {}, floatType, 4);
1174
1175 auto & gepDoubleNode = GetElementPtrOperation::createNode(
1176 *ctxVar.inner,
1177 { zeroNode.output(0), fourNode.output(0) },
1178 structType);
1179 auto & loadDoubleNode =
1180 LoadNonVolatileOperation::CreateNode(*gepDoubleNode.output(0), {}, doubleType, 8);
1181
1182 lambdaNode.finalize({
1183 &LoadOperation::LoadedValueOutput(load32Node),
1184 &LoadOperation::LoadedValueOutput(loadPtrNode),
1185 &LoadOperation::LoadedValueOutput(loadV32Node),
1186 &LoadOperation::LoadedValueOutput(loadFloatNode),
1187 &LoadOperation::LoadedValueOutput(loadDoubleNode),
1188 });
1189
1190 // Act
1191 RunStoreValueForwarding(rvsdgModule);
1192
1193 // Assert
1194 // We expect all load nodes to be forwarded
1195 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
1196
1197 {
1198 auto [intNode, intOperation] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1199 *lambdaNode.GetFunctionResults()[0]->origin());
1200 EXPECT_NE(intOperation, nullptr);
1201 EXPECT_EQ(intOperation->Representation().to_uint(), 0u);
1202 }
1203
1204 {
1205 auto [nullPtrNode, nullPtrOperation] =
1206 TryGetSimpleNodeAndOptionalOp<ConstantPointerNullOperation>(
1207 *lambdaNode.GetFunctionResults()[1]->origin());
1208 EXPECT_NE(nullPtrOperation, nullptr);
1209 }
1210
1211 {
1212 auto [aggZeroNode, aggZeroOperation] =
1213 TryGetSimpleNodeAndOptionalOp<ConstantAggregateZeroOperation>(
1214 *lambdaNode.GetFunctionResults()[2]->origin());
1215 EXPECT_NE(aggZeroOperation, nullptr);
1216 }
1217
1218 {
1219 auto [floatNode, floatOperation] =
1220 TryGetSimpleNodeAndOptionalOp<ConstantFP>(*lambdaNode.GetFunctionResults()[3]->origin());
1221 EXPECT_NE(floatOperation, nullptr);
1222 EXPECT_EQ(&floatOperation->constant().getSemantics(), &llvm::APFloat::IEEEsingle());
1223 EXPECT_TRUE(floatOperation->constant().isZero());
1224 }
1225
1226 {
1227 auto [doubleNode, doubleOperation] =
1228 TryGetSimpleNodeAndOptionalOp<ConstantFP>(*lambdaNode.GetFunctionResults()[4]->origin());
1229 EXPECT_NE(doubleOperation, nullptr);
1230 EXPECT_EQ(&doubleOperation->constant().getSemantics(), &llvm::APFloat::IEEEdouble());
1231 EXPECT_TRUE(doubleOperation->constant().isZero());
1232 }
1233}
1234
1235TEST(StoreValueForwardingTests, LoadForwardingFromDeltaCtxVar)
1236{
1237 using namespace jlm::llvm;
1238 using namespace jlm::rvsdg;
1239
1240 // Arrange
1241 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1242 auto & graph = rvsdgModule.Rvsdg();
1243 const auto pointerType = PointerType::Create();
1244 const auto bits32Type = BitType::Create(32);
1245 const auto functionType = FunctionType::Create(
1246 {},
1247 {
1248 pointerType,
1249 });
1250
1251 auto deltaNode1 = DeltaNode::Create(
1252 &graph.GetRootRegion(),
1253 DeltaOperation::Create(bits32Type, true, pointerType));
1254 auto & four = IntegerConstantOperation::Create(*deltaNode1->subregion(), 32, 4);
1255 auto & deltaOutput1 = deltaNode1->finalize(four.output(0));
1256
1257 auto deltaNode2 = DeltaNode::Create(
1258 &graph.GetRootRegion(),
1259 DeltaOperation::Create(pointerType, true, pointerType));
1260 auto deltaCtxVar = deltaNode2->AddContextVar(deltaOutput1);
1261 auto & deltaOutput2 = deltaNode2->finalize(deltaCtxVar.inner);
1262
1263 auto & lambdaNode = *LambdaNode::Create(
1264 graph.GetRootRegion(),
1265 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1266 auto ctxVar = lambdaNode.AddContextVar(deltaOutput2);
1267
1268 auto & loadNode = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, pointerType, 4);
1269
1270 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(loadNode) });
1271
1272 // Act
1273 RunStoreValueForwarding(rvsdgModule);
1274
1275 // Assert
1276 // We expect all load nodes to be forwarded
1277 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
1278 // We expect that deltaOutput1 has now lambdaNode as user on top of deltaNode2.
1279 EXPECT_EQ(deltaOutput1.nusers(), 2u);
1280}
1281
1282TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantFP)
1283{
1284 using namespace jlm::llvm;
1285 using namespace jlm::rvsdg;
1286
1287 // Arrange
1288 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1289 auto & graph = rvsdgModule.Rvsdg();
1290
1291 const auto pointerType = PointerType::Create();
1292 const auto floatType = FloatingPointType::Create(fpsize::flt);
1293 const auto functionType = FunctionType::Create(
1294 {},
1295 {
1296 floatType,
1297 });
1298
1299 auto deltaNode = DeltaNode::Create(
1300 &graph.GetRootRegion(),
1301 DeltaOperation::Create(floatType, true, pointerType));
1302 auto & fourNode =
1303 ConstantFP::createNode(*deltaNode->subregion(), fpsize::flt, llvm::APFloat(4.0f));
1304 auto & deltaOutput = deltaNode->finalize(fourNode.output(0));
1305
1306 auto & lambdaNode = *LambdaNode::Create(
1307 graph.GetRootRegion(),
1308 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1309 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1310
1311 auto & loadNode = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, floatType, 4);
1312
1313 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(loadNode) });
1314
1315 // Act
1316 RunStoreValueForwarding(rvsdgModule);
1317
1318 // Assert
1319 // We expect all load nodes to be forwarded
1320 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
1321}
1322
1323TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantPointerNull)
1324{
1325 using namespace jlm::llvm;
1326 using namespace jlm::rvsdg;
1327
1328 // Arrange
1329 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1330 auto & graph = rvsdgModule.Rvsdg();
1331
1332 const auto pointerType = PointerType::Create();
1333 const auto functionType = FunctionType::Create(
1334 {},
1335 {
1336 pointerType,
1337 });
1338
1339 auto deltaNode = DeltaNode::Create(
1340 &graph.GetRootRegion(),
1341 DeltaOperation::Create(pointerType, true, pointerType));
1342 auto & constantPointerNull = ConstantPointerNullOperation::createNode(*deltaNode->subregion());
1343 auto & deltaOutput = deltaNode->finalize(constantPointerNull.output(0));
1344
1345 auto & lambdaNode = *LambdaNode::Create(
1346 graph.GetRootRegion(),
1347 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1348 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1349
1350 auto & loadNode = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, pointerType, 4);
1351
1352 lambdaNode.finalize({ loadNode.output(0) });
1353
1354 // Act
1355 RunStoreValueForwarding(rvsdgModule);
1356
1357 // Assert
1358 // We expect all load nodes to be forwarded
1359 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
1360}
1361
1362TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantDataArray)
1363{
1364 using namespace jlm::llvm;
1365 using namespace jlm::rvsdg;
1366
1367 // Arrange
1368 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1369 auto & graph = rvsdgModule.Rvsdg();
1370 const auto pointerType = PointerType::Create();
1371 const auto bits8Type = BitType::Create(8);
1372 const auto bits32Type = BitType::Create(32);
1373 const auto bits64Type = BitType::Create(64);
1374 const auto arrayType = ArrayType::Create(bits32Type, 3);
1375 const auto functionType = FunctionType::Create(
1376 {},
1377 {
1378 bits32Type,
1379 bits32Type,
1380 bits32Type,
1381 bits32Type,
1382 bits32Type,
1383 bits64Type,
1384 });
1385
1386 auto deltaNode = DeltaNode::Create(
1387 &graph.GetRootRegion(),
1388 DeltaOperation::Create(arrayType, true, pointerType));
1389 auto & zeroNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 0);
1390 auto & oneNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 1);
1391 auto & twoNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 2);
1392 auto constantDataArrayResult = ConstantDataArrayOperation::Create(
1393 { zeroNode.output(0), oneNode.output(0), twoNode.output(0) });
1394 auto & deltaOutput = deltaNode->finalize(constantDataArrayResult);
1395
1396 auto & lambdaNode = *LambdaNode::Create(
1397 graph.GetRootRegion(),
1398 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1399 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1400
1401 auto zero = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0).output(0);
1402 auto two = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2).output(0);
1403 auto four = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 4).output(0);
1404
1405 auto & loadNode0 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits32Type, 4);
1406
1407 auto gepOutput1 = GetElementPtrOperation::create(ctxVar.inner, { zero }, bits32Type);
1408 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(*gepOutput1, {}, bits32Type, 4);
1409
1410 auto gepOutput2 = GetElementPtrOperation::create(ctxVar.inner, { zero, zero }, arrayType);
1411 auto & loadNode2 = LoadNonVolatileOperation::CreateNode(*gepOutput2, {}, bits32Type, 4);
1412
1413 auto gepOutput3 = GetElementPtrOperation::create(ctxVar.inner, { zero, two }, arrayType);
1414 auto & loadNode3 = LoadNonVolatileOperation::CreateNode(*gepOutput3, {}, bits32Type, 4);
1415
1416 auto gepOutput4 = GetElementPtrOperation::create(ctxVar.inner, { four }, bits8Type);
1417 auto & loadNode4 = LoadNonVolatileOperation::CreateNode(*gepOutput4, {}, bits32Type, 4);
1418
1419 auto & loadNode5 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits64Type, 4);
1420
1421 lambdaNode.finalize({
1422 &LoadOperation::LoadedValueOutput(loadNode0),
1423 &LoadOperation::LoadedValueOutput(loadNode1),
1424 &LoadOperation::LoadedValueOutput(loadNode2),
1425 &LoadOperation::LoadedValueOutput(loadNode3),
1426 &LoadOperation::LoadedValueOutput(loadNode4),
1427 &LoadOperation::LoadedValueOutput(loadNode5),
1428 });
1429
1430 // Act
1431 RunStoreValueForwarding(rvsdgModule);
1432
1433 // Assert
1434 auto [intNode0, intOperation0] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1435 *lambdaNode.GetFunctionResults()[0]->origin());
1436 EXPECT_NE(intOperation0, nullptr);
1437 EXPECT_EQ(intOperation0->Representation().to_uint(), 0u);
1438
1439 auto [intNode1, intOperation1] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1440 *lambdaNode.GetFunctionResults()[1]->origin());
1441 EXPECT_NE(intOperation1, nullptr);
1442 EXPECT_EQ(intOperation1->Representation().to_uint(), 0u);
1443
1444 auto [intNode2, intOperation2] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1445 *lambdaNode.GetFunctionResults()[2]->origin());
1446 EXPECT_NE(intOperation2, nullptr);
1447 EXPECT_EQ(intOperation2->Representation().to_uint(), 0u);
1448
1449 auto [intNode3, intOperation3] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1450 *lambdaNode.GetFunctionResults()[3]->origin());
1451 EXPECT_NE(intOperation3, nullptr);
1452 EXPECT_EQ(intOperation3->Representation().to_uint(), 2u);
1453
1454 auto [intNode4, intOperation4] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1455 *lambdaNode.GetFunctionResults()[4]->origin());
1456 EXPECT_NE(intOperation4, nullptr);
1457 EXPECT_EQ(intOperation4->Representation().to_uint(), 1u);
1458
1459 {
1460 // FIXME: Does currently not work at the types do not align
1461 auto [loadNode, loadOperation] = TryGetSimpleNodeAndOptionalOp<LoadNonVolatileOperation>(
1462 *lambdaNode.GetFunctionResults()[5]->origin());
1463 EXPECT_NE(loadOperation, nullptr);
1464#if 0
1465 auto [intNode5, intOperation5] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1466 *lambdaNode.GetFunctionResults()[5]->origin());
1467 EXPECT_NE(intOperation5, nullptr);
1468 EXPECT_EQ(intOperation5->Representation().to_uint(), 0x0000000100000000u);
1469#endif
1470 }
1471}
1472
1473TEST(StoreValueForwardingTests, RegionPredicatedValueForwarding)
1474{
1475 using namespace jlm;
1476 using namespace jlm::llvm;
1477
1510 // Arrange
1511 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1512 auto & graph = rvsdgModule.Rvsdg();
1513 const auto pointerType = PointerType::Create();
1514 const auto bit32Type = rvsdg::BitType::Create(32);
1515 const auto memoryStateType = MemoryStateType::Create();
1516
1517 const auto funcType =
1518 rvsdg::FunctionType::Create({ pointerType, memoryStateType }, { bit32Type, memoryStateType });
1519
1520 auto & lambdaNode = *rvsdg::LambdaNode::Create(
1521 graph.GetRootRegion(),
1522 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
1523
1524 auto & p = *lambdaNode.GetFunctionArguments()[0];
1525 auto & mem0 = *lambdaNode.GetFunctionArguments()[1];
1526
1527 // gamma1
1528 auto & ctrlZero = rvsdg::ControlConstantOperation::create(*lambdaNode.subregion(), 2, 0);
1529 auto & gamma1 = *rvsdg::GammaNode::create(&ctrlZero, 2);
1530
1531 auto gamma1PEntry = gamma1.AddEntryVar(&p);
1532 auto gamma1MemEntr = gamma1.AddEntryVar(&mem0);
1533
1534 // Left subregion of gamma1
1535 auto & constantForty = IntegerConstantOperation::Create(*gamma1.subregion(0), 32, 40);
1536 auto & storeP40Node = StoreNonVolatileOperation::CreateNode(
1537 *gamma1PEntry.branchArgument[0],
1538 *constantForty.output(0),
1539 { gamma1MemEntr.branchArgument[0] },
1540 4);
1541 auto & gamma1LeftMem = *StoreOperation::MemoryStateOutputs(storeP40Node).begin();
1542 auto & gamma1LeftCtrl = rvsdg::ControlConstantOperation::create(*gamma1.subregion(0), 2, 0);
1543
1544 // Right subregion of gamma1
1545 auto & gamma1RightCtrl = rvsdg::ControlConstantOperation::create(*gamma1.subregion(1), 2, 1);
1546
1547 // Exit variables of gamma1
1548 auto gamma1CtrlExit = gamma1.AddExitVar({ &gamma1LeftCtrl, &gamma1RightCtrl });
1549 auto gamma1MemExit = gamma1.AddExitVar({ &gamma1LeftMem, gamma1MemEntr.branchArgument[1] });
1550
1551 // gamma2 after gamma1
1552 auto & gamma2 = *rvsdg::GammaNode::create(gamma1CtrlExit.output, 2);
1553
1554 auto gamma2PEntry = gamma2.AddEntryVar(&p);
1555 auto gamma2MemEntry = gamma2.AddEntryVar(gamma1MemExit.output);
1556
1557 // Left subregion of gamma2
1558 auto & loadPLeft = LoadNonVolatileOperation::CreateNode(
1559 *gamma2PEntry.branchArgument[0],
1560 { gamma2MemEntry.branchArgument[0] },
1561 bit32Type,
1562 4);
1563 auto & loadedPLeft = LoadNonVolatileOperation::LoadedValueOutput(loadPLeft);
1564 auto & memLeft = *LoadNonVolatileOperation::MemoryStateOutputs(loadPLeft).begin();
1565
1566 // Right subregion of gamma2
1567 auto & loadPRight = LoadNonVolatileOperation::CreateNode(
1568 *gamma2PEntry.branchArgument[1],
1569 { gamma2MemEntry.branchArgument[1] },
1570 bit32Type,
1571 4);
1572 auto & loadedPRight = LoadNonVolatileOperation::LoadedValueOutput(loadPRight);
1573 auto & memRight = *LoadNonVolatileOperation::MemoryStateOutputs(loadPRight).begin();
1574
1575 // Exit variables of gamma2
1576 auto gamma2ExitLoadedP = gamma2.AddExitVar({ &loadedPLeft, &loadedPRight });
1577 auto gamma2ExitMem = gamma2.AddExitVar({ &memLeft, &memRight });
1578
1579 // Finalize lambda with the merged result from gamma2
1580 lambdaNode.finalize({ gamma2ExitLoadedP.output, gamma2ExitMem.output });
1581
1582 // Act
1583 RunStoreValueForwarding(rvsdgModule);
1584
1585 // Assert
1586
1587 // The LOAD in the left subregion of gamma2 should be gone,
1588 // and replaced by a value that originates from an entry variable into gamma2.
1589 const auto & leftPOrigin = *gamma2ExitLoadedP.branchResult[0]->origin();
1590 const auto & tracedLeftPOrigin = jlm::llvm::traceOutput(leftPOrigin);
1591 // the value of p should come from gamma1
1592 auto gamma = rvsdg::TryGetOwnerNode<rvsdg::GammaNode>(tracedLeftPOrigin);
1593 ASSERT_EQ(gamma, &gamma1);
1594
1595 auto exitVarP = gamma->MapOutputExitVar(tracedLeftPOrigin);
1596 // The left branch should give 40
1597 auto & leftG1Origin = *exitVarP.branchResult[0]->origin();
1598 ASSERT_TRUE(rvsdg::IsOwnerNodeOperation<IntegerConstantOperation>(leftG1Origin));
1599 ASSERT_EQ(jlm::llvm::tryGetConstantSignedInteger(leftG1Origin), 40);
1600
1601 // The right branch should be undef
1602 auto & rightG1Origin = *exitVarP.branchResult[1]->origin();
1603 ASSERT_TRUE(rvsdg::IsOwnerNodeOperation<UndefValueOperation>(rightG1Origin));
1604
1605 // The LOAD in the right subregion of gamma2 should still be untouched
1606 const auto & rightPOrigin = *gamma2ExitLoadedP.branchResult[1]->origin();
1607 ASSERT_TRUE(rvsdg::IsOwnerNodeOperation<LoadNonVolatileOperation>(rightPOrigin));
1608}
1609
1610TEST(StoreValueForwardingTests, LoadForwardingFromLoopExiting)
1611{
1612 using namespace jlm;
1613 using namespace jlm::llvm;
1614
1680 // Arrange
1681 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1682 auto & graph = rvsdgModule.Rvsdg();
1683 const auto pointerType = PointerType::Create();
1684 const auto bits32Type = rvsdg::BitType::Create(32);
1685 const auto ioStateType = IOStateType::Create();
1686 const auto memoryStateType = MemoryStateType::Create();
1687 const auto unitType = rvsdg::UnitType::Create();
1688 const auto controlType = rvsdg::ControlType::Create(2);
1689
1690 // opaque function type: void opaque()
1691 const auto opaqueFuncType = rvsdg::FunctionType::Create(
1692 { ioStateType, memoryStateType },
1693 { ioStateType, memoryStateType });
1694
1695 auto & opaqueImport = LlvmGraphImport::createFunctionImport(
1696 graph,
1697 opaqueFuncType,
1698 "opaque",
1699 Linkage::externalLinkage,
1700 CallingConvention::Default);
1701
1702 // func function type: int func(int* p, int* q)
1703 const auto funcType = rvsdg::FunctionType::Create(
1704 { pointerType, pointerType, ioStateType, memoryStateType },
1705 { bits32Type, ioStateType, memoryStateType });
1706
1707 auto & lambdaNode = *rvsdg::LambdaNode::Create(
1708 graph.GetRootRegion(),
1709 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
1710
1711 auto & p = *lambdaNode.GetFunctionArguments()[0];
1712 auto & q = *lambdaNode.GetFunctionArguments()[1];
1713 auto & io0 = *lambdaNode.GetFunctionArguments()[2];
1714 auto & mem0 = *lambdaNode.GetFunctionArguments()[3];
1715
1716 // Add opaque import as context variable for use inside the lambda
1717 auto opaqueCtxVar = lambdaNode.AddContextVar(opaqueImport);
1718
1719 // theta node:
1720 // exitPred, _, _, mem8, io3, _ = theta undef, p, q, mem0, io0, opaque1
1721 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
1722
1723 auto exitPredInit = UndefValueOperation::Create(*lambdaNode.subregion(), controlType);
1724 auto exitPredLoopVar = thetaNode.AddLoopVar(exitPredInit);
1725 auto pLoopVar = thetaNode.AddLoopVar(&p);
1726 auto qLoopVar = thetaNode.AddLoopVar(&q);
1727 auto memLoopVar = thetaNode.AddLoopVar(&mem0);
1728 auto ioLoopVar = thetaNode.AddLoopVar(&io0);
1729 auto opaqueLoopVar = thetaNode.AddLoopVar(opaqueCtxVar.inner);
1730
1731 // Inside theta subregion:
1732 // io2, mem2 = CALL opaque2 io1, mem1
1733 auto & callOpqNode = CallOperation::CreateNode(
1734 opaqueLoopVar.pre,
1735 opaqueFuncType,
1736 { ioLoopVar.pre, memLoopVar.pre });
1737 auto & io2 = CallOperation::GetIOStateOutput(callOpqNode);
1738 auto & mem2 = CallOperation::GetMemoryStateOutput(callOpqNode);
1739
1740 // pLoad, mem3 = LOAD p1, mem2
1741 auto & loadPNode = LoadNonVolatileOperation::CreateNode(*pLoopVar.pre, { &mem2 }, bits32Type, 4);
1742 auto & pLoad = LoadOperation::LoadedValueOutput(loadPNode);
1743 auto & mem3 = *LoadOperation::MemoryStateOutputs(loadPNode).begin();
1744
1745 // zero = IntegerConstant32(0)
1746 auto & constantZero = IntegerConstantOperation::Create(*thetaNode.subregion(), 32, 0);
1747
1748 // gamma1Cond = NEq pLoad, zero
1749 auto & neqCondNode =
1750 rvsdg::CreateOpNode<IntegerNeOperation>({ &pLoad, constantZero.output(0) }, 32);
1751 auto & gamma1Cond = *neqCondNode.output(0);
1752
1753 // gamma1Pred = MATCH[1->1, 0] gamma1Cond (maps 1 -> 1 (exit), default 0 (continue))
1754 const auto matchMapping = std::unordered_map<uint64_t, uint64_t>{ { 1, 1 } };
1755 auto & gamma1PredNode =
1756 rvsdg::CreateOpNode<rvsdg::MatchOperation>({ &gamma1Cond }, 1, matchMapping, 0, 2);
1757 auto & gamma1Pred = *gamma1PredNode.output(0);
1758
1759 // loopPred, exitPred1, mem7 = gamma gamma1Pred, q1, mem3
1760 // 2 branches: branch 0 (pLoad==0, continue), branch 1 (pLoad!=0, exit)
1761 auto & outerGammaNode = rvsdg::GammaNode::Create(gamma1Pred, 2, { unitType, unitType });
1762 auto qEntryVar = outerGammaNode.AddEntryVar(qLoopVar.pre);
1763 auto memEntryVar = outerGammaNode.AddEntryVar(&mem3);
1764
1765 // Branch 0 (exit loop and load *p): [_, mem4] {
1766 // ctrlZero = ControlConstant(0)
1767 // }[ctrlZero, ctrlZero, mem4]
1768 auto & gammaSubregion0 = *outerGammaNode.subregion(0);
1769 auto & ctrlZeroOuter = rvsdg::ControlConstantOperation::create(gammaSubregion0, 2, 0);
1770
1771 // Branch 1 (pLoad!=0, check *q): [q2, mem5] {
1772 auto & gammaSubregion1 = *outerGammaNode.subregion(1);
1773
1774 // qLoad, mem6 = LOAD q2, mem5
1775 auto & loadQNode = LoadNonVolatileOperation::CreateNode(
1776 *qEntryVar.branchArgument[1],
1777 { memEntryVar.branchArgument[1] },
1778 bits32Type,
1779 4);
1780 auto & qLoad = LoadOperation::LoadedValueOutput(loadQNode);
1781 auto & mem6 = *LoadOperation::MemoryStateOutputs(loadQNode).begin();
1782
1783 // zero = IntegerConstant32(0)
1784 auto & constantZeroInner = IntegerConstantOperation::Create(gammaSubregion1, 32, 0);
1785
1786 // gamma2Cond = NEq qLoad, zero
1787 auto & neqCondInnerNode =
1788 rvsdg::CreateOpNode<IntegerNeOperation>({ &qLoad, constantZeroInner.output(0) }, 32);
1789 auto & gamma2Cond = *neqCondInnerNode.output(0);
1790
1791 // gamma2Pred = MATCH[1->1, 0] gamma2Cond (maps 1 -> 1 (exit), default 0 (continue))
1792 auto & gamma2PredNode =
1793 rvsdg::CreateOpNode<rvsdg::MatchOperation>({ &gamma2Cond }, 1, matchMapping, 0, 2);
1794 auto & gamma2Pred = *gamma2PredNode.output(0);
1795
1796 // loopPred1, exitPred1 = gamma gamma2Pred (inner gamma with no entry vars)
1797 // Branch 0: exit loop to load *q again (loopPred=0, exitPred=1)
1798 // Branch 1: continue loop (loopPred=1, exitPred=DONTCARE)
1799 auto & innerGammaNode = rvsdg::GammaNode::Create(gamma2Pred, 2, {});
1800
1801 // Inner gamma branch 0 (continue): ctrlZero for loopPred, ctrlOne for exitPred...
1802 auto & innerGammaBranch0 = *innerGammaNode.subregion(0);
1803 auto & ctrlZero1 = rvsdg::ControlConstantOperation::create(innerGammaBranch0, 2, 0);
1804 auto & ctrlOne1 = rvsdg::ControlConstantOperation::create(innerGammaBranch0, 2, 1);
1805
1806 auto & innerGammaBranch1 = *innerGammaNode.subregion(1);
1807 auto & ctrlOne2 = rvsdg::ControlConstantOperation::create(innerGammaBranch1, 2, 1);
1808
1809 auto innerLoopPredExitVar = innerGammaNode.AddExitVar({ &ctrlZero1, &ctrlOne2 });
1810 auto innerExitPredExitVar = innerGammaNode.AddExitVar({ &ctrlOne1, &ctrlOne2 });
1811
1812 // Create exit variables for outer loop
1813 auto outerLoopPredExitVar =
1814 outerGammaNode.AddExitVar({ &ctrlZeroOuter, innerLoopPredExitVar.output });
1815 auto outerExitPredExitVar =
1816 outerGammaNode.AddExitVar({ &ctrlZeroOuter, innerExitPredExitVar.output });
1817
1818 // mem7: branch0 -> mem4 (memEntryVar.branchArgument[0]),
1819 // branch1 -> mem6
1820 auto memExitVar = outerGammaNode.AddExitVar({ memEntryVar.branchArgument[0], &mem6 });
1821
1822 // Wire up theta loop variable posts:
1823 thetaNode.predicate()->divert_to(outerLoopPredExitVar.output);
1824 exitPredLoopVar.post->divert_to(outerLoopPredExitVar.output);
1825 memLoopVar.post->divert_to(memExitVar.output);
1826 ioLoopVar.post->divert_to(&io2);
1827 opaqueLoopVar.post->divert_to(opaqueLoopVar.pre);
1828
1829 // Outside theta: gamma on exitPred for return values
1830 // ret, mem13 = gamma exitPred, p1, q1, mem8
1831 // [p2, _, mem9]{ pLoad2, mem10 = LOAD p2, mem9 }[pLoad2, mem10]
1832 // [_, q2, mem11]{ qLoad2, mem12 = LOAD q2, mem11 }[qLoad2, mem12]
1833
1834 auto & exitGammaNode = *rvsdg::GammaNode::create(exitPredLoopVar.output, 2);
1835 auto pEntryVarExit = exitGammaNode.AddEntryVar(&p);
1836 auto qEntryVarExit = exitGammaNode.AddEntryVar(&q);
1837 auto memEntryVarExit = exitGammaNode.AddEntryVar(memLoopVar.output);
1838
1839 // Branch 0: *p is true, return *p
1840 auto & loadP2Node = LoadNonVolatileOperation::CreateNode(
1841 *pEntryVarExit.branchArgument[0],
1842 { memEntryVarExit.branchArgument[0] },
1843 bits32Type,
1844 4);
1845 auto & pLoad2 = LoadOperation::LoadedValueOutput(loadP2Node);
1846 auto & mem10 = *LoadOperation::MemoryStateOutputs(loadP2Node).begin();
1847
1848 // Branch 1: *q is true, return *q
1849 auto & loadQ2Node = LoadNonVolatileOperation::CreateNode(
1850 *qEntryVarExit.branchArgument[1],
1851 { memEntryVarExit.branchArgument[1] },
1852 bits32Type,
1853 4);
1854 auto & qLoad2 = LoadOperation::LoadedValueOutput(loadQ2Node);
1855 auto & mem12 = *LoadOperation::MemoryStateOutputs(loadQ2Node).begin();
1856
1857 auto retExitVar = exitGammaNode.AddExitVar({ &pLoad2, &qLoad2 });
1858 auto memFinalExitVar = exitGammaNode.AddExitVar({ &mem10, &mem12 });
1859
1860 // Finalize lambda: [ret, mem13, io3]
1861 lambdaNode.finalize({ retExitVar.output, ioLoopVar.output, memFinalExitVar.output });
1862
1863 std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
1864
1865 // Act
1866 RunStoreValueForwarding(rvsdgModule);
1867
1868 // Assert
1869 // After StoreValueForwarding, the two LOADs outside the loop should be gone
1870 ASSERT_TRUE(
1871 rvsdg::TryGetRegionParentNode<rvsdg::GammaNode>(*retExitVar.branchResult[0]->origin()));
1872 ASSERT_TRUE(
1873 rvsdg::TryGetRegionParentNode<rvsdg::GammaNode>(*retExitVar.branchResult[1]->origin()));
1874 // The LOADs inside the loop should have two users each
1875 ASSERT_EQ(pLoad.nusers(), 2u);
1876 ASSERT_EQ(qLoad.nusers(), 2u);
1877}
1878
1879TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantStruct)
1880{
1881 using namespace jlm::llvm;
1882 using namespace jlm::rvsdg;
1883
1884 // Arrange
1885 const auto pointerType = PointerType::Create();
1886 const auto bits8Type = BitType::Create(8);
1887 const auto bits32Type = BitType::Create(32);
1888 const auto bits64Type = BitType::Create(64);
1889 const auto structType = StructType::CreateIdentified(
1890 { bits32Type, bits32Type, bits32Type, bits32Type, pointerType },
1891 false);
1892 auto functionType1 = FunctionType::Create({}, {});
1893 const auto functionType2 = FunctionType::Create(
1894 {},
1895 { bits32Type, bits32Type, bits32Type, bits32Type, bits32Type, bits64Type, pointerType });
1896
1897 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1898 auto & graph = rvsdgModule.Rvsdg();
1899
1900 auto & i0 = GraphImport::Create(graph, functionType2, "fct");
1901
1902 auto deltaNode = DeltaNode::Create(
1903 &graph.GetRootRegion(),
1904 DeltaOperation::Create(structType, true, pointerType));
1905 {
1906 auto ctxVar = deltaNode->AddContextVar(i0);
1907 auto & zeroNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 0);
1908 auto & oneNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 1);
1909 auto & twoNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 2);
1910 auto & threeNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 3);
1911 auto & fnToPtrNode = FunctionToPointerOperation::createNode(*ctxVar.inner);
1912 auto & constantStructResult = ConstantStructOperation::Create(
1913 *deltaNode->subregion(),
1914 { zeroNode.output(0),
1915 oneNode.output(0),
1916 twoNode.output(0),
1917 threeNode.output(0),
1918 fnToPtrNode.output(0) },
1919 structType);
1920 deltaNode->finalize(&constantStructResult);
1921 }
1922
1923 auto & lambdaNode = *LambdaNode::Create(
1924 graph.GetRootRegion(),
1925 LlvmLambdaOperation::Create(functionType2, "func", Linkage::internalLinkage));
1926 auto ctxVar = lambdaNode.AddContextVar(deltaNode->output());
1927
1928 auto zero = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0).output(0);
1929 auto two = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2).output(0);
1930 auto four = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 4).output(0);
1931
1932 auto & loadNode0 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits32Type, 4);
1933
1934 auto gepOutput1 = GetElementPtrOperation::create(ctxVar.inner, { zero }, bits32Type);
1935 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(*gepOutput1, {}, bits32Type, 4);
1936
1937 auto gepOutput2 = GetElementPtrOperation::create(ctxVar.inner, { zero, zero }, structType);
1938 auto & loadNode2 = LoadNonVolatileOperation::CreateNode(*gepOutput2, {}, bits32Type, 4);
1939
1940 auto gepOutput3 = GetElementPtrOperation::create(ctxVar.inner, { zero, two }, structType);
1941 auto & loadNode3 = LoadNonVolatileOperation::CreateNode(*gepOutput3, {}, bits32Type, 4);
1942
1943 auto gepOutput4 = GetElementPtrOperation::create(ctxVar.inner, { four }, bits8Type);
1944 auto & loadNode4 = LoadNonVolatileOperation::CreateNode(*gepOutput4, {}, bits32Type, 4);
1945
1946 auto & loadNode5 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits64Type, 4);
1947
1948 auto gepOutput6 = GetElementPtrOperation::create(ctxVar.inner, { zero, four }, structType);
1949 auto & loadNode6 = LoadNonVolatileOperation::CreateNode(*gepOutput6, {}, pointerType, 4);
1950
1951 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(loadNode0),
1952 &LoadOperation::LoadedValueOutput(loadNode1),
1953 &LoadOperation::LoadedValueOutput(loadNode2),
1954 &LoadOperation::LoadedValueOutput(loadNode3),
1955 &LoadOperation::LoadedValueOutput(loadNode4),
1956 &LoadOperation::LoadedValueOutput(loadNode5),
1957 &LoadOperation::LoadedValueOutput(loadNode6) });
1958
1959 // Act
1960 RunStoreValueForwarding(rvsdgModule);
1961
1962 // Assert
1963 auto [intNode0, intOperation0] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1964 *lambdaNode.GetFunctionResults()[0]->origin());
1965 EXPECT_NE(intOperation0, nullptr);
1966 EXPECT_EQ(intOperation0->Representation().to_uint(), 0u);
1967
1968 auto [intNode1, intOperation1] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1969 *lambdaNode.GetFunctionResults()[1]->origin());
1970 EXPECT_NE(intOperation1, nullptr);
1971 EXPECT_EQ(intOperation1->Representation().to_uint(), 0u);
1972
1973 auto [intNode2, intOperation2] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1974 *lambdaNode.GetFunctionResults()[2]->origin());
1975 EXPECT_NE(intOperation2, nullptr);
1976 EXPECT_EQ(intOperation2->Representation().to_uint(), 0u);
1977
1978 auto [intNode3, intOperation3] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1979 *lambdaNode.GetFunctionResults()[3]->origin());
1980 EXPECT_NE(intOperation3, nullptr);
1981 EXPECT_EQ(intOperation3->Representation().to_uint(), 2u);
1982
1983 auto [intNode4, intOperation4] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1984 *lambdaNode.GetFunctionResults()[4]->origin());
1985 EXPECT_NE(intOperation4, nullptr);
1986 EXPECT_EQ(intOperation4->Representation().to_uint(), 1u);
1987
1988 {
1989 // FIXME: Does currently not work at the types do not align
1990 auto [loadNode, loadOperation] = TryGetSimpleNodeAndOptionalOp<LoadNonVolatileOperation>(
1991 *lambdaNode.GetFunctionResults()[5]->origin());
1992 EXPECT_NE(loadOperation, nullptr);
1993#if 0
1994 auto [intNode5, intOperation5] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1995 *lambdaNode.GetFunctionResults()[5]->origin());
1996 EXPECT_NE(intOperation5, nullptr);
1997 EXPECT_EQ(intOperation5->Representation().to_uint(), 0x0000000100000000u);
1998#endif
1999 }
2000
2001 auto [fnToPtrNode, fnToPtrOperation] = TryGetSimpleNodeAndOptionalOp<FunctionToPointerOperation>(
2002 *lambdaNode.GetFunctionResults()[6]->origin());
2003 EXPECT_NE(fnToPtrOperation, nullptr);
2004}
2005
2006TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantArray)
2007{
2008 using namespace jlm::llvm;
2009 using namespace jlm::rvsdg;
2010
2011 // Arrange
2012 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
2013 auto & graph = rvsdgModule.Rvsdg();
2014 const auto pointerType = PointerType::Create();
2015 const auto bits8Type = BitType::Create(8);
2016 const auto bits32Type = BitType::Create(32);
2017 const auto bits64Type = BitType::Create(64);
2018 const auto arrayType = ArrayType::Create(bits32Type, 3);
2019 const auto functionType = FunctionType::Create(
2020 {},
2021 {
2022 bits32Type,
2023 bits32Type,
2024 bits32Type,
2025 bits32Type,
2026 bits32Type,
2027 bits64Type,
2028 });
2029
2030 auto deltaNode = DeltaNode::Create(
2031 &graph.GetRootRegion(),
2032 DeltaOperation::Create(arrayType, true, pointerType));
2033 auto & zeroNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 0);
2034 auto & oneNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 1);
2035 auto & twoNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 2);
2036 auto constantDataArrayResult =
2037 ConstantArrayOperation::Create({ zeroNode.output(0), oneNode.output(0), twoNode.output(0) });
2038 auto & deltaOutput = deltaNode->finalize(constantDataArrayResult);
2039
2040 auto & lambdaNode = *LambdaNode::Create(
2041 graph.GetRootRegion(),
2042 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
2043 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
2044
2045 auto zero = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0).output(0);
2046 auto two = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2).output(0);
2047 auto four = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 4).output(0);
2048
2049 auto & loadNode0 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits32Type, 4);
2050
2051 auto gepOutput1 = GetElementPtrOperation::create(ctxVar.inner, { zero }, arrayType);
2052 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(*gepOutput1, {}, bits32Type, 4);
2053
2054 auto gepOutput2 = GetElementPtrOperation::create(ctxVar.inner, { zero, zero }, arrayType);
2055 auto & loadNode2 = LoadNonVolatileOperation::CreateNode(*gepOutput2, {}, bits32Type, 4);
2056
2057 auto gepOutput3 = GetElementPtrOperation::create(ctxVar.inner, { zero, two }, arrayType);
2058 auto & loadNode3 = LoadNonVolatileOperation::CreateNode(*gepOutput3, {}, bits32Type, 4);
2059
2060 auto gepOutput4 = GetElementPtrOperation::create(ctxVar.inner, { four }, bits8Type);
2061 auto & loadNode4 = LoadNonVolatileOperation::CreateNode(*gepOutput4, {}, bits32Type, 4);
2062
2063 auto & loadNode5 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits64Type, 4);
2064
2065 lambdaNode.finalize({
2066 &LoadOperation::LoadedValueOutput(loadNode0),
2067 &LoadOperation::LoadedValueOutput(loadNode1),
2068 &LoadOperation::LoadedValueOutput(loadNode2),
2069 &LoadOperation::LoadedValueOutput(loadNode3),
2070 &LoadOperation::LoadedValueOutput(loadNode4),
2071 &LoadOperation::LoadedValueOutput(loadNode5),
2072 });
2073
2074 // Act
2075 RunStoreValueForwarding(rvsdgModule);
2076
2077 // Assert
2078 {
2079 auto [intNode0, intOperation0] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2080 *lambdaNode.GetFunctionResults()[0]->origin());
2081 EXPECT_NE(intOperation0, nullptr);
2082 EXPECT_EQ(intOperation0->Representation().to_uint(), 0);
2083 }
2084
2085 {
2086 auto [intNode1, intOperation1] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2087 *lambdaNode.GetFunctionResults()[1]->origin());
2088 EXPECT_NE(intOperation1, nullptr);
2089 EXPECT_EQ(intOperation1->Representation().to_uint(), 0);
2090 }
2091
2092 {
2093 auto [intNode2, intOperation2] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2094 *lambdaNode.GetFunctionResults()[2]->origin());
2095 EXPECT_NE(intOperation2, nullptr);
2096 EXPECT_EQ(intOperation2->Representation().to_uint(), 0);
2097 }
2098
2099 {
2100 auto [intNode3, intOperation3] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2101 *lambdaNode.GetFunctionResults()[3]->origin());
2102 EXPECT_NE(intOperation3, nullptr);
2103 EXPECT_EQ(intOperation3->Representation().to_uint(), 2);
2104 }
2105
2106 {
2107 auto [intNode4, intOperation4] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2108 *lambdaNode.GetFunctionResults()[4]->origin());
2109 EXPECT_NE(intOperation4, nullptr);
2110 EXPECT_EQ(intOperation4->Representation().to_uint(), 1);
2111 }
2112
2113 {
2114 // FIXME: Does currently not work at the types do not align
2115 auto [loadNode, loadOperation] = TryGetSimpleNodeAndOptionalOp<LoadNonVolatileOperation>(
2116 *lambdaNode.GetFunctionResults()[5]->origin());
2117 EXPECT_NE(loadOperation, nullptr);
2118#if 0
2119 auto [intNode5, intOperation5] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2120 *lambdaNode.GetFunctionResults()[5]->origin());
2121 EXPECT_NE(intOperation5, nullptr);
2122 EXPECT_EQ(intOperation5->Representation().to_uint(), 0x0000000100000000);
2123#endif
2124 }
2125}
2126
2127TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithIntToPtr)
2128{
2129 using namespace jlm::llvm;
2130 using namespace jlm::rvsdg;
2131
2132 // Arrange
2133 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
2134 auto & graph = rvsdgModule.Rvsdg();
2135 const auto pointerType = PointerType::Create();
2136 const auto functionType = FunctionType::Create(
2137 {},
2138 {
2139 pointerType,
2140 });
2141
2142 auto deltaNode = DeltaNode::Create(
2143 &graph.GetRootRegion(),
2144 DeltaOperation::Create(pointerType, true, pointerType));
2145 auto & oneNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 1);
2146 auto intToPtr = IntToPtrOperation::create(oneNode.output(0));
2147 auto & deltaOutput = deltaNode->finalize(intToPtr);
2148
2149 auto & lambdaNode = *LambdaNode::Create(
2150 graph.GetRootRegion(),
2151 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
2152 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
2153
2154 auto & loadNode0 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, pointerType, 4);
2155
2156 lambdaNode.finalize({
2157 &LoadOperation::LoadedValueOutput(loadNode0),
2158 });
2159
2160 // Act
2161 RunStoreValueForwarding(rvsdgModule);
2162
2163 // Assert
2164 {
2165 auto [intToPtrNode, intToPtrOp] = TryGetSimpleNodeAndOptionalOp<IntToPtrOperation>(
2166 *lambdaNode.GetFunctionResults()[0]->origin());
2167 EXPECT_NE(intToPtrOp, nullptr);
2168 }
2169}
2170
2171TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantGep)
2172{
2173 using namespace jlm::llvm;
2174 using namespace jlm::rvsdg;
2175
2176 // %struct.s = type { i32, i32 }
2177 // @v = global ptr getelementptr (i8, ptr @s0, i64 4)
2178 // @s0 = global %struct.s { i32 1, i32 2 }
2179 // %p = load ptr, ptr @v
2180 // %x = load i32, ptr %p
2181
2182 // Arrange
2183 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
2184 auto & graph = rvsdgModule.Rvsdg();
2185
2186 const auto pointerType = PointerType::Create();
2187 const auto bits8Type = BitType::Create(8);
2188 const auto bits32Type = BitType::Create(32);
2189 const auto structType =
2190 StructType::CreateIdentified("struct.s", { bits32Type, bits32Type }, false);
2191
2192 // @s0 = { 1, 2 }
2193 auto s0 = DeltaNode::Create(
2194 &graph.GetRootRegion(),
2195 DeltaOperation::Create(structType, true, pointerType));
2196 auto & one = IntegerConstantOperation::Create(*s0->subregion(), 32, 1);
2197 auto & two = IntegerConstantOperation::Create(*s0->subregion(), 32, 2);
2198 auto & s0Value = ConstantStructOperation::Create(
2199 *s0->subregion(),
2200 { one.output(0), two.output(0) },
2201 structType);
2202 auto & s0Output = s0->finalize(&s0Value);
2203
2204 // @v = getelementptr i8, ptr @s0, i64 4
2205 auto v = DeltaNode::Create(
2206 &graph.GetRootRegion(),
2207 DeltaOperation::Create(pointerType, true, pointerType));
2208 auto vContext = v->AddContextVar(s0Output);
2209 auto four = IntegerConstantOperation::Create(*v->subregion(), 64, 4).output(0);
2210 auto vValue = GetElementPtrOperation::create(vContext.inner, { four }, bits8Type);
2211 auto & vOutput = v->finalize(vValue);
2212
2213 auto & lambdaNode = *LambdaNode::Create(
2214 graph.GetRootRegion(),
2215 LlvmLambdaOperation::Create(
2216 FunctionType::Create({}, { bits32Type }),
2217 "f",
2218 Linkage::internalLinkage));
2219 auto vContextVar = lambdaNode.AddContextVar(vOutput);
2220
2221 // %p = load ptr, ptr @v
2222 auto & pointerLoad = LoadNonVolatileOperation::CreateNode(*vContextVar.inner, {}, pointerType, 8);
2223 // %x = load i32, ptr %p
2224 auto & valueLoad =
2225 LoadNonVolatileOperation::CreateNode(*pointerLoad.output(0), {}, bits32Type, 4);
2226 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(valueLoad) });
2227
2228 // Act
2229 RunStoreValueForwarding(rvsdgModule);
2230
2231 // Assert
2232 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
2233
2234 auto [intNode, intOperation] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2235 *lambdaNode.GetFunctionResults()[0]->origin());
2236 EXPECT_NE(intNode, nullptr);
2237 EXPECT_NE(intOperation, nullptr);
2238 EXPECT_EQ(intOperation->Representation().to_uint(), 2u);
2239}
static jlm::util::StatisticsCollector statisticsCollector
TEST(StoreValueForwardingTests, NestedAllocas)
static void RunStoreValueForwarding(jlm::llvm::LlvmRvsdgModule &rvsdgModule)
Store Value Forwarding Optimization.
void Run(rvsdg::RvsdgModule &module, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
Region & GetRootRegion() const noexcept
Definition graph.hpp:99
Graph & Rvsdg() noexcept
Global memory state passed between functions.
rvsdg::Output & traceOutput(rvsdg::Output &output, const rvsdg::Region *withinRegion)
Definition Trace.cpp:62
std::optional< int64_t > tryGetConstantSignedInteger(const rvsdg::Output &output)
Definition Trace.cpp:70