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, false);
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 =
512 jlm::llvm::traceOutput(*exitVar.branchResult[1]->origin(), false);
513 const auto loopVar2 = thetaNode.MapPreLoopVar(traced1stRegionOrigin);
514 EXPECT_EQ(loopVar.pre, loopVar2.pre);
515
516 const auto constInputInteger = jlm::llvm::tryGetConstantSignedInteger(*loopVar.input->origin());
517 EXPECT_EQ(constInputInteger, 40u);
518}
519
520TEST(StoreValueForwardingTests, RouteAroundLoadLoop)
521{
522 using namespace jlm;
523 using namespace jlm::llvm;
524
544 // Arrange
545 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
546 auto & graph = rvsdgModule.Rvsdg();
547 const auto pointerType = PointerType::Create();
548 const auto bits32Type = rvsdg::BitType::Create(32);
549 const auto ioStateType = IOStateType::Create();
550 const auto memoryStateType = MemoryStateType::Create();
551
552 const auto funcType = rvsdg::FunctionType::Create(
553 { pointerType, ioStateType, memoryStateType },
554 { bits32Type, ioStateType, memoryStateType });
555
556 // Setup the function "func"
557 auto & lambdaNode = *rvsdg::LambdaNode::Create(
558 graph.GetRootRegion(),
559 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
560
561 auto & q = *lambdaNode.GetFunctionArguments()[0];
562 auto & io0 = *lambdaNode.GetFunctionArguments()[1];
563 auto & mem0 = *lambdaNode.GetFunctionArguments()[2];
564
565 // mem1 = STORE[bits32] q, 40, mem0
566 auto & constantForty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 40);
567 auto & storeQ40Node =
568 StoreNonVolatileOperation::CreateNode(q, *constantForty.output(0), { &mem0 }, 4);
569 auto & mem1 = *StoreOperation::MemoryStateOutputs(storeQ40Node).begin();
570
571 // _, mem4, l2 = theta q, mem1, undef
572 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
573 auto qLoopVar = thetaNode.AddLoopVar(&q);
574 auto memLoopVar = thetaNode.AddLoopVar(&mem1);
575 auto undefL = UndefValueOperation::Create(*lambdaNode.subregion(), bits32Type);
576 auto lLoopVar = thetaNode.AddLoopVar(undefL);
577
578 auto & loadInLoopNode =
579 LoadNonVolatileOperation::CreateNode(*qLoopVar.pre, { memLoopVar.pre }, bits32Type, 4);
580 auto & l1 = LoadOperation::LoadedValueOutput(loadInLoopNode);
581 auto & mem3 = *LoadOperation::MemoryStateOutputs(loadInLoopNode).begin();
582
583 lLoopVar.post->divert_to(&l1);
584 memLoopVar.post->divert_to(&mem3);
585
586 // l3, mem5 = LOAD[bits32] q, mem4
587 auto & loadAfterLoopNode =
588 LoadNonVolatileOperation::CreateNode(q, { memLoopVar.output }, bits32Type, 4);
589 auto & l3 = LoadOperation::LoadedValueOutput(loadAfterLoopNode);
590 auto & mem5 = *LoadOperation::MemoryStateOutputs(loadAfterLoopNode).begin();
591
592 // add1 = ADD l2, l3
593 auto & addNode = rvsdg::CreateOpNode<IntegerAddOperation>({ lLoopVar.output, &l3 }, 32);
594 auto & add1 = *addNode.output(0);
595
596 // return add1, io0, mem5
597 lambdaNode.finalize({ &add1, &io0, &mem5 });
598
599 std::cout << rvsdg::view(&rvsdgModule.Rvsdg().GetRootRegion()) << std::endl;
600
601 // Act
602 RunStoreValueForwarding(rvsdgModule);
603
604 std::cout << rvsdg::view(&rvsdgModule.Rvsdg().GetRootRegion()) << std::endl;
605
606 // Assert
607
608 // The value replacing l2 should lead to a loop output variable,
609 // whose post origin is an invariant loop variable
610 const auto & addLhsOrigin = *addNode.input(0)->origin();
611 const auto loopVar1 = thetaNode.MapOutputLoopVar(addLhsOrigin);
612 const auto loopVar2 = thetaNode.MapPreLoopVar(*loopVar1.post->origin());
613 EXPECT_TRUE(rvsdg::ThetaLoopVarIsInvariant(loopVar2));
614 EXPECT_EQ(tryGetConstantSignedInteger(*loopVar2.input->origin()), 40u);
615
616 // The value replacing l3 should come from the constant directly, not a theta output.
617 const auto & addRhsOrigin = *addNode.input(1)->origin();
618 EXPECT_EQ(tryGetConstantSignedInteger(addRhsOrigin), 40u);
619 EXPECT_EQ(rvsdg::TryGetOwnerNode<rvsdg::ThetaNode>(addRhsOrigin), nullptr);
620}
621
622TEST(StoreValueForwardingTests, RouteUninitialized)
623{
624 using namespace jlm;
625 using namespace jlm::llvm;
626
649 // Arrange
650 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
651 auto & graph = rvsdgModule.Rvsdg();
652 const auto bits32Type = rvsdg::BitType::Create(32);
653 const auto ioStateType = IOStateType::Create();
654 const auto memoryStateType = MemoryStateType::Create();
655 const auto unitType = rvsdg::UnitType::Create();
656
657 const auto funcType = rvsdg::FunctionType::Create(
658 { ioStateType, memoryStateType },
659 { bits32Type, ioStateType, memoryStateType });
660
661 auto & lambdaNode = *rvsdg::LambdaNode::Create(
662 graph.GetRootRegion(),
663 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
664
665 auto & io0 = *lambdaNode.GetFunctionArguments()[0];
666 auto & mem0 = *lambdaNode.GetFunctionArguments()[1];
667
668 // a, mem1 = ALLOCA[bits32], 1
669 auto & constantOne = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 1);
670 auto allocaAOutputs = AllocaOperation::create(bits32Type, constantOne.output(0), 4);
671
672 // pred = CTRL(0)
673 auto & predicate = rvsdg::ControlConstantOperation::create(*lambdaNode.subregion(), 2, 0);
674
675 // mem5 = gamma pred, a, mem1
676 auto & gammaNode = rvsdg::GammaNode::Create(predicate, 2, { unitType, unitType });
677 auto aEntryVar = gammaNode.AddEntryVar(allocaAOutputs[0]);
678 auto memEntryVar = gammaNode.AddEntryVar(allocaAOutputs[1]);
679
680 // [_, a1, mem2] { mem3 = STORE a1, 20, mem2 }[mem3]
681 auto & gammaSubregion0 = *gammaNode.subregion(0);
682 auto & constantTwenty = IntegerConstantOperation::Create(gammaSubregion0, 32, 20);
683 auto & storeA20Node = StoreNonVolatileOperation::CreateNode(
684 *aEntryVar.branchArgument[0],
685 *constantTwenty.output(0),
686 { memEntryVar.branchArgument[0] },
687 4);
688 auto & mem3 = *StoreOperation::MemoryStateOutputs(storeA20Node).begin();
689
690 // [_, a2, mem4] { }[mem4]
691 auto memExitVar = gammaNode.AddExitVar({ &mem3, memEntryVar.branchArgument[1] });
692
693 // ld, mem6 = LOAD[bits32] a, mem5
694 auto & loadNode = LoadNonVolatileOperation::CreateNode(
695 *allocaAOutputs[0],
696 { memExitVar.output },
697 bits32Type,
698 4);
699 auto & ld = LoadOperation::LoadedValueOutput(loadNode);
700
701 lambdaNode.finalize({ &ld, &io0, &mem0 });
702
703 // Act
704 RunStoreValueForwarding(rvsdgModule);
705
706 // Assert
707 const auto & resultOrigin = *lambdaNode.GetFunctionResults()[0]->origin();
708 EXPECT_NE(rvsdg::TryGetOwnerNode<rvsdg::GammaNode>(resultOrigin), nullptr);
709
710 const auto exitVar = gammaNode.MapOutputExitVar(resultOrigin);
711 EXPECT_EQ(jlm::llvm::tryGetConstantSignedInteger(*exitVar.branchResult[0]->origin()), 20u);
712 const auto [undefNode, undefOperation] =
713 rvsdg::TryGetSimpleNodeAndOptionalOp<UndefValueOperation>(*exitVar.branchResult[1]->origin());
714 EXPECT_TRUE(undefNode && undefOperation);
715}
716
717TEST(StoreValueForwardingTests, GepInLoop)
718{
719 using namespace jlm;
720 using namespace jlm::llvm;
721
750 // Arrange
751 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
752 auto & graph = rvsdgModule.Rvsdg();
753 const auto pointerType = PointerType::Create();
754 const auto bits32Type = rvsdg::BitType::Create(32);
755 const auto intArrayType = ArrayType::Create(bits32Type, 4);
756 const auto ioStateType = IOStateType::Create();
757 const auto memoryStateType = MemoryStateType::Create();
758
759 const auto funcType = rvsdg::FunctionType::Create(
760 { ioStateType, memoryStateType },
761 { bits32Type, ioStateType, memoryStateType });
762
763 auto & lambdaNode = *rvsdg::LambdaNode::Create(
764 graph.GetRootRegion(),
765 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
766
767 auto & io0 = *lambdaNode.GetFunctionArguments()[0];
768 auto & mem0 = *lambdaNode.GetFunctionArguments()[1];
769
770 auto & constantZero = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0);
771 auto & constantOne = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 1);
772 auto & constantTwo = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2);
773 auto & constantThree = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 3);
774 auto & constantTwenty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 20);
775 auto & constantThirty = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 30);
776
777 // a, mem1 = ALLOCA[int[4]], 1
778 auto allocaAOutputs = AllocaOperation::create(intArrayType, constantOne.output(0), 4);
779
780 // a2 = &a[2], a3 = &a[3]
781 auto a2 = GetElementPtrOperation::create(
782 allocaAOutputs[0],
783 { constantZero.output(0), constantTwo.output(0) },
784 intArrayType);
785 auto a3 = GetElementPtrOperation::create(
786 allocaAOutputs[0],
787 { constantZero.output(0), constantThree.output(0) },
788 intArrayType);
789
790 // *a2 = 20; *a3 = 30;
791 auto & storeA220Node = StoreNonVolatileOperation::CreateNode(
792 *a2,
793 *constantTwenty.output(0),
794 { allocaAOutputs[1] },
795 4);
796 auto & mem1 = *StoreOperation::MemoryStateOutputs(storeA220Node).begin();
797 auto & storeA330Node =
798 StoreNonVolatileOperation::CreateNode(*a3, *constantThirty.output(0), { &mem1 }, 4);
799 auto & mem2 = *StoreOperation::MemoryStateOutputs(storeA330Node).begin();
800
801 // do { ... } while (0)
802 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
803 auto aLoopVar = thetaNode.AddLoopVar(allocaAOutputs[0]);
804 auto a2LoopVar = thetaNode.AddLoopVar(a2);
805 auto memLoopVar = thetaNode.AddLoopVar(&mem2);
806
807 // loaded = *a2;
808 auto & loadInLoopNode =
809 LoadNonVolatileOperation::CreateNode(*a2LoopVar.pre, { memLoopVar.pre }, bits32Type, 4);
810 auto & loadedValue = LoadOperation::LoadedValueOutput(loadInLoopNode);
811 auto & mem3 = *LoadOperation::MemoryStateOutputs(loadInLoopNode).begin();
812
813 // a1 = &a[1]; a22 = &a1[1];
814 auto & constantOneInLoop = IntegerConstantOperation::Create(*thetaNode.subregion(), 32, 1);
815 auto a1 =
816 GetElementPtrOperation::create(aLoopVar.pre, { constantOneInLoop.output(0) }, bits32Type);
817 auto a22 = GetElementPtrOperation::create(a1, { constantOneInLoop.output(0) }, bits32Type);
818
819 // *a22 = loaded + 1;
820 auto & addLoadedOneNode =
821 rvsdg::CreateOpNode<IntegerAddOperation>({ &loadedValue, constantOneInLoop.output(0) }, 32);
822 auto & incrementedValue = *addLoadedOneNode.output(0);
823 auto & storeA22Node = StoreNonVolatileOperation::CreateNode(*a22, incrementedValue, { &mem3 }, 4);
824 auto & mem4 = *StoreOperation::MemoryStateOutputs(storeA22Node).begin();
825
826 // *a1 = 10;
827 auto & constantTen = IntegerConstantOperation::Create(*thetaNode.subregion(), 32, 10);
828 auto & storeA1Node =
829 StoreNonVolatileOperation::CreateNode(*a1, *constantTen.output(0), { &mem4 }, 4);
830 auto & mem5 = *StoreOperation::MemoryStateOutputs(storeA1Node).begin();
831
832 memLoopVar.post->divert_to(&mem5);
833
834 // return *a2 + *a3;
835 auto & loadAfterLoopA2Node =
836 LoadNonVolatileOperation::CreateNode(*a2, { memLoopVar.output }, bits32Type, 4);
837 auto & loadedA2 = LoadOperation::LoadedValueOutput(loadAfterLoopA2Node);
838 auto & mem6 = *LoadOperation::MemoryStateOutputs(loadAfterLoopA2Node).begin();
839 auto & loadAfterLoopA3Node = LoadNonVolatileOperation::CreateNode(*a3, { &mem6 }, bits32Type, 4);
840 auto & loadedA3 = LoadOperation::LoadedValueOutput(loadAfterLoopA3Node);
841 auto & addResultNode = rvsdg::CreateOpNode<IntegerAddOperation>({ &loadedA2, &loadedA3 }, 32);
842 auto & resultValue = *addResultNode.output(0);
843
844 lambdaNode.finalize({ &resultValue, &io0, &mem0 });
845
846 std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
847
848 // Act
849 RunStoreValueForwarding(rvsdgModule);
850
851 std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
852
853 // Assert
854
855 // Check that the load of a[2] inside the loop is replaced by a loop variable
856 // which takes 20 as its initial value, and loaded + 1 as its post origin
857 const auto & loadedInLoopOrigin = *addLoadedOneNode.input(0)->origin();
858 const auto loadedLoopVar = thetaNode.MapPreLoopVar(loadedInLoopOrigin);
859 EXPECT_EQ(jlm::llvm::tryGetConstantSignedInteger(*loadedLoopVar.input->origin()), 20u);
860 EXPECT_EQ(loadedLoopVar.post->origin(), addLoadedOneNode.output(0));
861
862 // Check that the final load of a[2] is replaced by the value of loaded + 1 in the loop
863 const auto & addLhsOrigin = *addResultNode.input(0)->origin();
864 const auto a2ResultLoopVar = thetaNode.MapOutputLoopVar(addLhsOrigin);
865 EXPECT_EQ(a2ResultLoopVar.post->origin(), addLoadedOneNode.output(0));
866
867 // Check that the final load of a[3] is directly attached to the constant 30,
868 // and that it does not go via an invariant loop variable
869 const auto & addRhsOrigin = *addResultNode.input(1)->origin();
870 EXPECT_EQ(&addRhsOrigin, constantThirty.output(0));
871}
872
873TEST(StoreValueForwardingTests, LoadForwarding)
874{
875 using namespace jlm;
876 using namespace jlm::llvm;
877
890 // Arrange
891 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
892 auto & graph = rvsdgModule.Rvsdg();
893 const auto pointerType = PointerType::Create();
894 const auto bits32Type = rvsdg::BitType::Create(32);
895 const auto ioStateType = IOStateType::Create();
896 const auto memoryStateType = MemoryStateType::Create();
897
898 const auto funcType = rvsdg::FunctionType::Create(
899 { pointerType, ioStateType, memoryStateType },
900 { bits32Type, ioStateType, memoryStateType });
901
902 auto & lambdaNode = *rvsdg::LambdaNode::Create(
903 graph.GetRootRegion(),
904 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
905
906 auto & p = *lambdaNode.GetFunctionArguments()[0];
907 auto & io0 = *lambdaNode.GetFunctionArguments()[1];
908 auto & mem0 = *lambdaNode.GetFunctionArguments()[2];
909
910 auto & load1Node = LoadNonVolatileOperation::CreateNode(p, { &mem0 }, bits32Type, 4);
911 auto & l1 = LoadOperation::LoadedValueOutput(load1Node);
912 auto & mem1 = *LoadOperation::MemoryStateOutputs(load1Node).begin();
913
914 auto & load2Node = LoadNonVolatileOperation::CreateNode(p, { &mem1 }, bits32Type, 4);
915 auto & l2 = LoadOperation::LoadedValueOutput(load2Node);
916 auto & mem2 = *LoadOperation::MemoryStateOutputs(load2Node).begin();
917
918 auto & addNode = rvsdg::CreateOpNode<IntegerAddOperation>({ &l1, &l2 }, 32);
919 auto & add0 = *addNode.output(0);
920
921 lambdaNode.finalize({ &add0, &io0, &mem2 });
922
923 // Act
924 RunStoreValueForwarding(rvsdgModule);
925
926 // Assert
927 size_t loadCount = 0;
928 for (auto & node : lambdaNode.subregion()->Nodes())
929 {
930 if (is<LoadOperation>(&node))
931 loadCount++;
932 }
933 EXPECT_EQ(loadCount, 1u);
934
935 const auto & addLhsOrigin = jlm::llvm::traceOutput(*addNode.input(0)->origin(), false);
936 const auto & addRhsOrigin = jlm::llvm::traceOutput(*addNode.input(1)->origin(), false);
937 EXPECT_EQ(&addLhsOrigin, &l1);
938 EXPECT_EQ(&addRhsOrigin, &l1);
939
940 const auto & memoryResultOrigin =
941 jlm::llvm::traceOutput(*lambdaNode.GetFunctionResults()[2]->origin(), false);
942 EXPECT_EQ(&memoryResultOrigin, &mem1);
943}
944
945TEST(StoreValueForwardingTests, LoadForwardingIntoTheta)
946{
947 using namespace jlm;
948 using namespace jlm::llvm;
949
969 // Arrange
970 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
971 auto & graph = rvsdgModule.Rvsdg();
972 const auto pointerType = PointerType::Create();
973 const auto bits32Type = rvsdg::BitType::Create(32);
974 const auto ioStateType = IOStateType::Create();
975 const auto memoryStateType = MemoryStateType::Create();
976
977 const auto funcType = rvsdg::FunctionType::Create(
978 { pointerType, ioStateType, memoryStateType },
979 { bits32Type, ioStateType, memoryStateType });
980
981 auto & lambdaNode = *rvsdg::LambdaNode::Create(
982 graph.GetRootRegion(),
983 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
984
985 auto & p = *lambdaNode.GetFunctionArguments()[0];
986 auto & io0 = *lambdaNode.GetFunctionArguments()[1];
987 auto & mem0 = *lambdaNode.GetFunctionArguments()[2];
988
989 auto & load1Node = LoadNonVolatileOperation::CreateNode(p, { &mem0 }, bits32Type, 4);
990 auto & l1 = LoadOperation::LoadedValueOutput(load1Node);
991 auto & mem1 = *LoadOperation::MemoryStateOutputs(load1Node).begin();
992
993 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
994 auto pLoopVar = thetaNode.AddLoopVar(&p);
995 auto sumLoopVar = thetaNode.AddLoopVar(&l1);
996 auto memLoopVar = thetaNode.AddLoopVar(&mem1);
997
998 auto & load2Node =
999 LoadNonVolatileOperation::CreateNode(*pLoopVar.pre, { memLoopVar.pre }, bits32Type, 4);
1000 auto & l2 = LoadOperation::LoadedValueOutput(load2Node);
1001 auto & mem3 = *LoadOperation::MemoryStateOutputs(load2Node).begin();
1002
1003 auto & addNode = rvsdg::CreateOpNode<IntegerAddOperation>({ sumLoopVar.pre, &l2 }, 32);
1004 auto & sum2 = *addNode.output(0);
1005
1006 auto & constant100 = IntegerConstantOperation::Create(*thetaNode.subregion(), 32, 100);
1007 auto & sltNode = rvsdg::CreateOpNode<IntegerSltOperation>({ &sum2, constant100.output(0) }, 32);
1008 const auto predicate = rvsdg::MatchOperation::Create(*sltNode.output(0), { { 1, 1 } }, 0, 2);
1009
1010 thetaNode.set_predicate(predicate);
1011 sumLoopVar.post->divert_to(&sum2);
1012 memLoopVar.post->divert_to(&mem3);
1013
1014 lambdaNode.finalize({ sumLoopVar.output, &io0, memLoopVar.output });
1015
1016 // Act
1017 RunStoreValueForwarding(rvsdgModule);
1018
1019 // Assert
1020 size_t lambdaLoadCount = 0;
1021 for (auto & node : lambdaNode.subregion()->Nodes())
1022 {
1023 if (is<LoadOperation>(&node))
1024 lambdaLoadCount++;
1025 }
1026 EXPECT_EQ(lambdaLoadCount, 1u);
1027
1028 size_t thetaLoadCount = 0;
1029 for (auto & node : thetaNode.subregion()->Nodes())
1030 {
1031 if (is<LoadOperation>(&node))
1032 thetaLoadCount++;
1033 }
1034 EXPECT_EQ(thetaLoadCount, 0u);
1035
1036 const auto & addLhsOrigin = jlm::llvm::traceOutput(*addNode.input(0)->origin(), false);
1037 EXPECT_EQ(&addLhsOrigin, sumLoopVar.pre);
1038
1039 const auto & addRhsOrigin = *addNode.input(1)->origin();
1040 const auto forwardedLoopVar = thetaNode.MapPreLoopVar(addRhsOrigin);
1041 EXPECT_TRUE(rvsdg::ThetaLoopVarIsInvariant(forwardedLoopVar));
1042 EXPECT_EQ(forwardedLoopVar.input->origin(), &l1);
1043 EXPECT_EQ(&jlm::llvm::traceOutput(addRhsOrigin, false), &l1);
1044
1045 const auto & memoryResultOrigin =
1046 jlm::llvm::traceOutput(*lambdaNode.GetFunctionResults()[2]->origin(), false);
1047 EXPECT_EQ(&memoryResultOrigin, &mem1);
1048}
1049
1050TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithIntegerConstant)
1051{
1052 using namespace jlm::llvm;
1053 using namespace jlm::rvsdg;
1054
1055 // Arrange
1056 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1057 auto & graph = rvsdgModule.Rvsdg();
1058
1059 const auto pointerType = PointerType::Create();
1060 const auto bits8Type = BitType::Create(8);
1061 const auto bits32Type = BitType::Create(32);
1062 const auto functionType = FunctionType::Create(
1063 {},
1064 {
1065 bits32Type,
1066 bits8Type,
1067 });
1068
1069 auto deltaNode = DeltaNode::Create(
1070 &graph.GetRootRegion(),
1071 DeltaOperation::Create(bits32Type, true, pointerType));
1072 auto & four = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 4);
1073 auto & deltaOutput = deltaNode->finalize(four.output(0));
1074
1075 auto & lambdaNode = *LambdaNode::Create(
1076 graph.GetRootRegion(),
1077 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1078 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1079
1080 auto & load32Node = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits32Type, 4);
1081 auto & load8Node = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits8Type, 4);
1082
1083 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(load32Node),
1084 &LoadOperation::LoadedValueOutput(load8Node) });
1085
1086 // Act
1087 RunStoreValueForwarding(rvsdgModule);
1088
1089 // Assert
1090 {
1091 auto [intNode0, intOperation0] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1092 *lambdaNode.GetFunctionResults()[0]->origin());
1093 EXPECT_NE(intOperation0, nullptr);
1094 EXPECT_EQ(intOperation0->Representation().nbits(), 32u);
1095 EXPECT_EQ(intOperation0->Representation().to_uint(), 4u);
1096 }
1097
1098 {
1099 auto [truncNode, truncOperation] = TryGetSimpleNodeAndOptionalOp<TruncOperation>(
1100 *lambdaNode.GetFunctionResults()[1]->origin());
1101 EXPECT_NE(truncOperation, nullptr);
1102
1103 auto [intNode1, intOperation1] =
1104 TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(*truncNode->input(0)->origin());
1105 EXPECT_NE(intOperation1, nullptr);
1106 EXPECT_EQ(intOperation1->Representation().nbits(), 32u);
1107 EXPECT_EQ(intOperation1->Representation().to_uint(), 4u);
1108 }
1109}
1110
1111TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithAggregateZeroConstant)
1112{
1113 using namespace jlm::llvm;
1114 using namespace jlm::rvsdg;
1115
1116 // Arrange
1117 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1118 auto & graph = rvsdgModule.Rvsdg();
1119
1120 const auto pointerType = PointerType::Create();
1121 const auto bits32Type = BitType::Create(32);
1122 const auto fixedVectorType = FixedVectorType::Create(bits32Type, 4);
1123 const auto floatType = FloatingPointType::Create(fpsize::flt);
1124 const auto doubleType = FloatingPointType::Create(fpsize::dbl);
1125 const auto structType = StructType::CreateIdentified(
1126 "struct",
1127 { bits32Type, pointerType, fixedVectorType, floatType, doubleType },
1128 false);
1129 const auto functionType =
1130 FunctionType::Create({}, { bits32Type, pointerType, fixedVectorType, floatType, doubleType });
1131
1132 auto deltaNode = DeltaNode::Create(
1133 &graph.GetRootRegion(),
1134 DeltaOperation::Create(structType, true, pointerType));
1135 auto aggregateZero = ConstantAggregateZeroOperation::Create(*deltaNode->subregion(), structType);
1136 auto & deltaOutput = deltaNode->finalize(aggregateZero);
1137
1138 auto & lambdaNode = *LambdaNode::Create(
1139 graph.GetRootRegion(),
1140 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1141 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1142
1143 auto & zeroNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0);
1144 auto & oneNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 1);
1145 auto & twoNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2);
1146 auto & threeNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 3);
1147 auto & fourNode = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 4);
1148
1149 auto & gep0Node = GetElementPtrOperation::createNode(
1150 *ctxVar.inner,
1151 { zeroNode.output(0), zeroNode.output(0) },
1152 structType);
1153 auto & load32Node = LoadNonVolatileOperation::CreateNode(*gep0Node.output(0), {}, bits32Type, 4);
1154
1155 auto & gep1Node = GetElementPtrOperation::createNode(
1156 *ctxVar.inner,
1157 { zeroNode.output(0), oneNode.output(0) },
1158 structType);
1159 auto & loadPtrNode =
1160 LoadNonVolatileOperation::CreateNode(*gep1Node.output(0), {}, pointerType, 4);
1161
1162 auto & gep2Node = GetElementPtrOperation::createNode(
1163 *ctxVar.inner,
1164 { zeroNode.output(0), twoNode.output(0) },
1165 structType);
1166 auto & loadV32Node =
1167 LoadNonVolatileOperation::CreateNode(*gep2Node.output(0), {}, fixedVectorType, 4);
1168
1169 auto & gepFloatNode = GetElementPtrOperation::createNode(
1170 *ctxVar.inner,
1171 { zeroNode.output(0), threeNode.output(0) },
1172 structType);
1173 auto & loadFloatNode =
1174 LoadNonVolatileOperation::CreateNode(*gepFloatNode.output(0), {}, floatType, 4);
1175
1176 auto & gepDoubleNode = GetElementPtrOperation::createNode(
1177 *ctxVar.inner,
1178 { zeroNode.output(0), fourNode.output(0) },
1179 structType);
1180 auto & loadDoubleNode =
1181 LoadNonVolatileOperation::CreateNode(*gepDoubleNode.output(0), {}, doubleType, 8);
1182
1183 lambdaNode.finalize({
1184 &LoadOperation::LoadedValueOutput(load32Node),
1185 &LoadOperation::LoadedValueOutput(loadPtrNode),
1186 &LoadOperation::LoadedValueOutput(loadV32Node),
1187 &LoadOperation::LoadedValueOutput(loadFloatNode),
1188 &LoadOperation::LoadedValueOutput(loadDoubleNode),
1189 });
1190
1191 // Act
1192 RunStoreValueForwarding(rvsdgModule);
1193
1194 // Assert
1195 // We expect all load nodes to be forwarded
1196 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
1197
1198 {
1199 auto [intNode, intOperation] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1200 *lambdaNode.GetFunctionResults()[0]->origin());
1201 EXPECT_NE(intOperation, nullptr);
1202 EXPECT_EQ(intOperation->Representation().to_uint(), 0u);
1203 }
1204
1205 {
1206 auto [nullPtrNode, nullPtrOperation] =
1207 TryGetSimpleNodeAndOptionalOp<ConstantPointerNullOperation>(
1208 *lambdaNode.GetFunctionResults()[1]->origin());
1209 EXPECT_NE(nullPtrOperation, nullptr);
1210 }
1211
1212 {
1213 auto [aggZeroNode, aggZeroOperation] =
1214 TryGetSimpleNodeAndOptionalOp<ConstantAggregateZeroOperation>(
1215 *lambdaNode.GetFunctionResults()[2]->origin());
1216 EXPECT_NE(aggZeroOperation, nullptr);
1217 }
1218
1219 {
1220 auto [floatNode, floatOperation] =
1221 TryGetSimpleNodeAndOptionalOp<ConstantFP>(*lambdaNode.GetFunctionResults()[3]->origin());
1222 EXPECT_NE(floatOperation, nullptr);
1223 EXPECT_EQ(&floatOperation->constant().getSemantics(), &llvm::APFloat::IEEEsingle());
1224 EXPECT_TRUE(floatOperation->constant().isZero());
1225 }
1226
1227 {
1228 auto [doubleNode, doubleOperation] =
1229 TryGetSimpleNodeAndOptionalOp<ConstantFP>(*lambdaNode.GetFunctionResults()[4]->origin());
1230 EXPECT_NE(doubleOperation, nullptr);
1231 EXPECT_EQ(&doubleOperation->constant().getSemantics(), &llvm::APFloat::IEEEdouble());
1232 EXPECT_TRUE(doubleOperation->constant().isZero());
1233 }
1234}
1235
1236TEST(StoreValueForwardingTests, LoadForwardingFromDeltaCtxVar)
1237{
1238 using namespace jlm::llvm;
1239 using namespace jlm::rvsdg;
1240
1241 // Arrange
1242 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1243 auto & graph = rvsdgModule.Rvsdg();
1244 const auto pointerType = PointerType::Create();
1245 const auto bits32Type = BitType::Create(32);
1246 const auto functionType = FunctionType::Create(
1247 {},
1248 {
1249 pointerType,
1250 });
1251
1252 auto deltaNode1 = DeltaNode::Create(
1253 &graph.GetRootRegion(),
1254 DeltaOperation::Create(bits32Type, true, pointerType));
1255 auto & four = IntegerConstantOperation::Create(*deltaNode1->subregion(), 32, 4);
1256 auto & deltaOutput1 = deltaNode1->finalize(four.output(0));
1257
1258 auto deltaNode2 = DeltaNode::Create(
1259 &graph.GetRootRegion(),
1260 DeltaOperation::Create(pointerType, true, pointerType));
1261 auto deltaCtxVar = deltaNode2->AddContextVar(deltaOutput1);
1262 auto & deltaOutput2 = deltaNode2->finalize(deltaCtxVar.inner);
1263
1264 auto & lambdaNode = *LambdaNode::Create(
1265 graph.GetRootRegion(),
1266 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1267 auto ctxVar = lambdaNode.AddContextVar(deltaOutput2);
1268
1269 auto & loadNode = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, pointerType, 4);
1270
1271 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(loadNode) });
1272
1273 // Act
1274 RunStoreValueForwarding(rvsdgModule);
1275
1276 // Assert
1277 // We expect all load nodes to be forwarded
1278 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
1279 // We expect that deltaOutput1 has now lambdaNode as user on top of deltaNode2.
1280 EXPECT_EQ(deltaOutput1.nusers(), 2u);
1281}
1282
1283TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantFP)
1284{
1285 using namespace jlm::llvm;
1286 using namespace jlm::rvsdg;
1287
1288 // Arrange
1289 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1290 auto & graph = rvsdgModule.Rvsdg();
1291
1292 const auto pointerType = PointerType::Create();
1293 const auto floatType = FloatingPointType::Create(fpsize::flt);
1294 const auto functionType = FunctionType::Create(
1295 {},
1296 {
1297 floatType,
1298 });
1299
1300 auto deltaNode = DeltaNode::Create(
1301 &graph.GetRootRegion(),
1302 DeltaOperation::Create(floatType, true, pointerType));
1303 auto & fourNode =
1304 ConstantFP::createNode(*deltaNode->subregion(), fpsize::flt, llvm::APFloat(4.0f));
1305 auto & deltaOutput = deltaNode->finalize(fourNode.output(0));
1306
1307 auto & lambdaNode = *LambdaNode::Create(
1308 graph.GetRootRegion(),
1309 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1310 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1311
1312 auto & loadNode = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, floatType, 4);
1313
1314 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(loadNode) });
1315
1316 // Act
1317 RunStoreValueForwarding(rvsdgModule);
1318
1319 // Assert
1320 // We expect all load nodes to be forwarded
1321 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
1322}
1323
1324TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantPointerNull)
1325{
1326 using namespace jlm::llvm;
1327 using namespace jlm::rvsdg;
1328
1329 // Arrange
1330 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1331 auto & graph = rvsdgModule.Rvsdg();
1332
1333 const auto pointerType = PointerType::Create();
1334 const auto functionType = FunctionType::Create(
1335 {},
1336 {
1337 pointerType,
1338 });
1339
1340 auto deltaNode = DeltaNode::Create(
1341 &graph.GetRootRegion(),
1342 DeltaOperation::Create(pointerType, true, pointerType));
1343 auto & constantPointerNull = ConstantPointerNullOperation::createNode(*deltaNode->subregion());
1344 auto & deltaOutput = deltaNode->finalize(constantPointerNull.output(0));
1345
1346 auto & lambdaNode = *LambdaNode::Create(
1347 graph.GetRootRegion(),
1348 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1349 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1350
1351 auto & loadNode = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, pointerType, 4);
1352
1353 lambdaNode.finalize({ loadNode.output(0) });
1354
1355 // Act
1356 RunStoreValueForwarding(rvsdgModule);
1357
1358 // Assert
1359 // We expect all load nodes to be forwarded
1360 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
1361}
1362
1363TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantDataArray)
1364{
1365 using namespace jlm::llvm;
1366 using namespace jlm::rvsdg;
1367
1368 // Arrange
1369 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1370 auto & graph = rvsdgModule.Rvsdg();
1371 const auto pointerType = PointerType::Create();
1372 const auto bits8Type = BitType::Create(8);
1373 const auto bits32Type = BitType::Create(32);
1374 const auto bits64Type = BitType::Create(64);
1375 const auto arrayType = ArrayType::Create(bits32Type, 3);
1376 const auto functionType = FunctionType::Create(
1377 {},
1378 {
1379 bits32Type,
1380 bits32Type,
1381 bits32Type,
1382 bits32Type,
1383 bits32Type,
1384 bits64Type,
1385 });
1386
1387 auto deltaNode = DeltaNode::Create(
1388 &graph.GetRootRegion(),
1389 DeltaOperation::Create(arrayType, true, pointerType));
1390 auto & zeroNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 0);
1391 auto & oneNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 1);
1392 auto & twoNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 2);
1393 auto constantDataArrayResult = ConstantDataArrayOperation::Create(
1394 { zeroNode.output(0), oneNode.output(0), twoNode.output(0) });
1395 auto & deltaOutput = deltaNode->finalize(constantDataArrayResult);
1396
1397 auto & lambdaNode = *LambdaNode::Create(
1398 graph.GetRootRegion(),
1399 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
1400 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
1401
1402 auto zero = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0).output(0);
1403 auto two = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2).output(0);
1404 auto four = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 4).output(0);
1405
1406 auto & loadNode0 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits32Type, 4);
1407
1408 auto gepOutput1 = GetElementPtrOperation::create(ctxVar.inner, { zero }, bits32Type);
1409 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(*gepOutput1, {}, bits32Type, 4);
1410
1411 auto gepOutput2 = GetElementPtrOperation::create(ctxVar.inner, { zero, zero }, arrayType);
1412 auto & loadNode2 = LoadNonVolatileOperation::CreateNode(*gepOutput2, {}, bits32Type, 4);
1413
1414 auto gepOutput3 = GetElementPtrOperation::create(ctxVar.inner, { zero, two }, arrayType);
1415 auto & loadNode3 = LoadNonVolatileOperation::CreateNode(*gepOutput3, {}, bits32Type, 4);
1416
1417 auto gepOutput4 = GetElementPtrOperation::create(ctxVar.inner, { four }, bits8Type);
1418 auto & loadNode4 = LoadNonVolatileOperation::CreateNode(*gepOutput4, {}, bits32Type, 4);
1419
1420 auto & loadNode5 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits64Type, 4);
1421
1422 lambdaNode.finalize({
1423 &LoadOperation::LoadedValueOutput(loadNode0),
1424 &LoadOperation::LoadedValueOutput(loadNode1),
1425 &LoadOperation::LoadedValueOutput(loadNode2),
1426 &LoadOperation::LoadedValueOutput(loadNode3),
1427 &LoadOperation::LoadedValueOutput(loadNode4),
1428 &LoadOperation::LoadedValueOutput(loadNode5),
1429 });
1430
1431 // Act
1432 RunStoreValueForwarding(rvsdgModule);
1433
1434 // Assert
1435 auto [intNode0, intOperation0] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1436 *lambdaNode.GetFunctionResults()[0]->origin());
1437 EXPECT_NE(intOperation0, nullptr);
1438 EXPECT_EQ(intOperation0->Representation().to_uint(), 0u);
1439
1440 auto [intNode1, intOperation1] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1441 *lambdaNode.GetFunctionResults()[1]->origin());
1442 EXPECT_NE(intOperation1, nullptr);
1443 EXPECT_EQ(intOperation1->Representation().to_uint(), 0u);
1444
1445 auto [intNode2, intOperation2] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1446 *lambdaNode.GetFunctionResults()[2]->origin());
1447 EXPECT_NE(intOperation2, nullptr);
1448 EXPECT_EQ(intOperation2->Representation().to_uint(), 0u);
1449
1450 auto [intNode3, intOperation3] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1451 *lambdaNode.GetFunctionResults()[3]->origin());
1452 EXPECT_NE(intOperation3, nullptr);
1453 EXPECT_EQ(intOperation3->Representation().to_uint(), 2u);
1454
1455 auto [intNode4, intOperation4] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1456 *lambdaNode.GetFunctionResults()[4]->origin());
1457 EXPECT_NE(intOperation4, nullptr);
1458 EXPECT_EQ(intOperation4->Representation().to_uint(), 1u);
1459
1460 {
1461 // FIXME: Does currently not work at the types do not align
1462 auto [loadNode, loadOperation] = TryGetSimpleNodeAndOptionalOp<LoadNonVolatileOperation>(
1463 *lambdaNode.GetFunctionResults()[5]->origin());
1464 EXPECT_NE(loadOperation, nullptr);
1465#if 0
1466 auto [intNode5, intOperation5] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1467 *lambdaNode.GetFunctionResults()[5]->origin());
1468 EXPECT_NE(intOperation5, nullptr);
1469 EXPECT_EQ(intOperation5->Representation().to_uint(), 0x0000000100000000u);
1470#endif
1471 }
1472}
1473
1474TEST(StoreValueForwardingTests, RegionPredicatedValueForwarding)
1475{
1476 using namespace jlm;
1477 using namespace jlm::llvm;
1478
1511 // Arrange
1512 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1513 auto & graph = rvsdgModule.Rvsdg();
1514 const auto pointerType = PointerType::Create();
1515 const auto bit32Type = rvsdg::BitType::Create(32);
1516 const auto memoryStateType = MemoryStateType::Create();
1517
1518 const auto funcType =
1519 rvsdg::FunctionType::Create({ pointerType, memoryStateType }, { bit32Type, memoryStateType });
1520
1521 auto & lambdaNode = *rvsdg::LambdaNode::Create(
1522 graph.GetRootRegion(),
1523 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
1524
1525 auto & p = *lambdaNode.GetFunctionArguments()[0];
1526 auto & mem0 = *lambdaNode.GetFunctionArguments()[1];
1527
1528 // gamma1
1529 auto & ctrlZero = rvsdg::ControlConstantOperation::create(*lambdaNode.subregion(), 2, 0);
1530 auto & gamma1 = *rvsdg::GammaNode::create(&ctrlZero, 2);
1531
1532 auto gamma1PEntry = gamma1.AddEntryVar(&p);
1533 auto gamma1MemEntr = gamma1.AddEntryVar(&mem0);
1534
1535 // Left subregion of gamma1
1536 auto & constantForty = IntegerConstantOperation::Create(*gamma1.subregion(0), 32, 40);
1537 auto & storeP40Node = StoreNonVolatileOperation::CreateNode(
1538 *gamma1PEntry.branchArgument[0],
1539 *constantForty.output(0),
1540 { gamma1MemEntr.branchArgument[0] },
1541 4);
1542 auto & gamma1LeftMem = *StoreOperation::MemoryStateOutputs(storeP40Node).begin();
1543 auto & gamma1LeftCtrl = rvsdg::ControlConstantOperation::create(*gamma1.subregion(0), 2, 0);
1544
1545 // Right subregion of gamma1
1546 auto & gamma1RightCtrl = rvsdg::ControlConstantOperation::create(*gamma1.subregion(1), 2, 1);
1547
1548 // Exit variables of gamma1
1549 auto gamma1CtrlExit = gamma1.AddExitVar({ &gamma1LeftCtrl, &gamma1RightCtrl });
1550 auto gamma1MemExit = gamma1.AddExitVar({ &gamma1LeftMem, gamma1MemEntr.branchArgument[1] });
1551
1552 // gamma2 after gamma1
1553 auto & gamma2 = *rvsdg::GammaNode::create(gamma1CtrlExit.output, 2);
1554
1555 auto gamma2PEntry = gamma2.AddEntryVar(&p);
1556 auto gamma2MemEntry = gamma2.AddEntryVar(gamma1MemExit.output);
1557
1558 // Left subregion of gamma2
1559 auto & loadPLeft = LoadNonVolatileOperation::CreateNode(
1560 *gamma2PEntry.branchArgument[0],
1561 { gamma2MemEntry.branchArgument[0] },
1562 bit32Type,
1563 4);
1564 auto & loadedPLeft = LoadNonVolatileOperation::LoadedValueOutput(loadPLeft);
1565 auto & memLeft = *LoadNonVolatileOperation::MemoryStateOutputs(loadPLeft).begin();
1566
1567 // Right subregion of gamma2
1568 auto & loadPRight = LoadNonVolatileOperation::CreateNode(
1569 *gamma2PEntry.branchArgument[1],
1570 { gamma2MemEntry.branchArgument[1] },
1571 bit32Type,
1572 4);
1573 auto & loadedPRight = LoadNonVolatileOperation::LoadedValueOutput(loadPRight);
1574 auto & memRight = *LoadNonVolatileOperation::MemoryStateOutputs(loadPRight).begin();
1575
1576 // Exit variables of gamma2
1577 auto gamma2ExitLoadedP = gamma2.AddExitVar({ &loadedPLeft, &loadedPRight });
1578 auto gamma2ExitMem = gamma2.AddExitVar({ &memLeft, &memRight });
1579
1580 // Finalize lambda with the merged result from gamma2
1581 lambdaNode.finalize({ gamma2ExitLoadedP.output, gamma2ExitMem.output });
1582
1583 // Act
1584 RunStoreValueForwarding(rvsdgModule);
1585
1586 // Assert
1587
1588 // The LOAD in the left subregion of gamma2 should be gone,
1589 // and replaced by a value that originates from an entry variable into gamma2.
1590 const auto & leftPOrigin = *gamma2ExitLoadedP.branchResult[0]->origin();
1591 const auto & tracedLeftPOrigin = jlm::llvm::traceOutput(leftPOrigin, false);
1592 // the value of p should come from gamma1
1593 auto gamma = rvsdg::TryGetOwnerNode<rvsdg::GammaNode>(tracedLeftPOrigin);
1594 ASSERT_EQ(gamma, &gamma1);
1595
1596 auto exitVarP = gamma->MapOutputExitVar(tracedLeftPOrigin);
1597 // The left branch should give 40
1598 auto & leftG1Origin = *exitVarP.branchResult[0]->origin();
1599 ASSERT_TRUE(rvsdg::IsOwnerNodeOperation<IntegerConstantOperation>(leftG1Origin));
1600 ASSERT_EQ(jlm::llvm::tryGetConstantSignedInteger(leftG1Origin), 40);
1601
1602 // The right branch should be undef
1603 auto & rightG1Origin = *exitVarP.branchResult[1]->origin();
1604 ASSERT_TRUE(rvsdg::IsOwnerNodeOperation<UndefValueOperation>(rightG1Origin));
1605
1606 // The LOAD in the right subregion of gamma2 should still be untouched
1607 const auto & rightPOrigin = *gamma2ExitLoadedP.branchResult[1]->origin();
1608 ASSERT_TRUE(rvsdg::IsOwnerNodeOperation<LoadNonVolatileOperation>(rightPOrigin));
1609}
1610
1611TEST(StoreValueForwardingTests, LoadForwardingFromLoopExiting)
1612{
1613 using namespace jlm;
1614 using namespace jlm::llvm;
1615
1681 // Arrange
1682 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1683 auto & graph = rvsdgModule.Rvsdg();
1684 const auto pointerType = PointerType::Create();
1685 const auto bits32Type = rvsdg::BitType::Create(32);
1686 const auto ioStateType = IOStateType::Create();
1687 const auto memoryStateType = MemoryStateType::Create();
1688 const auto unitType = rvsdg::UnitType::Create();
1689 const auto controlType = rvsdg::ControlType::Create(2);
1690
1691 // opaque function type: void opaque()
1692 const auto opaqueFuncType = rvsdg::FunctionType::Create(
1693 { ioStateType, memoryStateType },
1694 { ioStateType, memoryStateType });
1695
1696 auto & opaqueImport = LlvmGraphImport::createFunctionImport(
1697 graph,
1698 opaqueFuncType,
1699 "opaque",
1700 Linkage::externalLinkage,
1701 CallingConvention::Default);
1702
1703 // func function type: int func(int* p, int* q)
1704 const auto funcType = rvsdg::FunctionType::Create(
1705 { pointerType, pointerType, ioStateType, memoryStateType },
1706 { bits32Type, ioStateType, memoryStateType });
1707
1708 auto & lambdaNode = *rvsdg::LambdaNode::Create(
1709 graph.GetRootRegion(),
1710 LlvmLambdaOperation::Create(funcType, "func", Linkage::internalLinkage));
1711
1712 auto & p = *lambdaNode.GetFunctionArguments()[0];
1713 auto & q = *lambdaNode.GetFunctionArguments()[1];
1714 auto & io0 = *lambdaNode.GetFunctionArguments()[2];
1715 auto & mem0 = *lambdaNode.GetFunctionArguments()[3];
1716
1717 // Add opaque import as context variable for use inside the lambda
1718 auto opaqueCtxVar = lambdaNode.AddContextVar(opaqueImport);
1719
1720 // theta node:
1721 // exitPred, _, _, mem8, io3, _ = theta undef, p, q, mem0, io0, opaque1
1722 auto & thetaNode = *rvsdg::ThetaNode::create(lambdaNode.subregion());
1723
1724 auto exitPredInit = UndefValueOperation::Create(*lambdaNode.subregion(), controlType);
1725 auto exitPredLoopVar = thetaNode.AddLoopVar(exitPredInit);
1726 auto pLoopVar = thetaNode.AddLoopVar(&p);
1727 auto qLoopVar = thetaNode.AddLoopVar(&q);
1728 auto memLoopVar = thetaNode.AddLoopVar(&mem0);
1729 auto ioLoopVar = thetaNode.AddLoopVar(&io0);
1730 auto opaqueLoopVar = thetaNode.AddLoopVar(opaqueCtxVar.inner);
1731
1732 // Inside theta subregion:
1733 // io2, mem2 = CALL opaque2 io1, mem1
1734 auto & callOpqNode = CallOperation::CreateNode(
1735 opaqueLoopVar.pre,
1736 opaqueFuncType,
1737 { ioLoopVar.pre, memLoopVar.pre });
1738 auto & io2 = CallOperation::GetIOStateOutput(callOpqNode);
1739 auto & mem2 = CallOperation::GetMemoryStateOutput(callOpqNode);
1740
1741 // pLoad, mem3 = LOAD p1, mem2
1742 auto & loadPNode = LoadNonVolatileOperation::CreateNode(*pLoopVar.pre, { &mem2 }, bits32Type, 4);
1743 auto & pLoad = LoadOperation::LoadedValueOutput(loadPNode);
1744 auto & mem3 = *LoadOperation::MemoryStateOutputs(loadPNode).begin();
1745
1746 // zero = IntegerConstant32(0)
1747 auto & constantZero = IntegerConstantOperation::Create(*thetaNode.subregion(), 32, 0);
1748
1749 // gamma1Cond = NEq pLoad, zero
1750 auto & neqCondNode =
1751 rvsdg::CreateOpNode<IntegerNeOperation>({ &pLoad, constantZero.output(0) }, 32);
1752 auto & gamma1Cond = *neqCondNode.output(0);
1753
1754 // gamma1Pred = MATCH[1->1, 0] gamma1Cond (maps 1 -> 1 (exit), default 0 (continue))
1755 const auto matchMapping = std::unordered_map<uint64_t, uint64_t>{ { 1, 1 } };
1756 auto & gamma1PredNode =
1757 rvsdg::CreateOpNode<rvsdg::MatchOperation>({ &gamma1Cond }, 1, matchMapping, 0, 2);
1758 auto & gamma1Pred = *gamma1PredNode.output(0);
1759
1760 // loopPred, exitPred1, mem7 = gamma gamma1Pred, q1, mem3
1761 // 2 branches: branch 0 (pLoad==0, continue), branch 1 (pLoad!=0, exit)
1762 auto & outerGammaNode = rvsdg::GammaNode::Create(gamma1Pred, 2, { unitType, unitType });
1763 auto qEntryVar = outerGammaNode.AddEntryVar(qLoopVar.pre);
1764 auto memEntryVar = outerGammaNode.AddEntryVar(&mem3);
1765
1766 // Branch 0 (exit loop and load *p): [_, mem4] {
1767 // ctrlZero = ControlConstant(0)
1768 // }[ctrlZero, ctrlZero, mem4]
1769 auto & gammaSubregion0 = *outerGammaNode.subregion(0);
1770 auto & ctrlZeroOuter = rvsdg::ControlConstantOperation::create(gammaSubregion0, 2, 0);
1771
1772 // Branch 1 (pLoad!=0, check *q): [q2, mem5] {
1773 auto & gammaSubregion1 = *outerGammaNode.subregion(1);
1774
1775 // qLoad, mem6 = LOAD q2, mem5
1776 auto & loadQNode = LoadNonVolatileOperation::CreateNode(
1777 *qEntryVar.branchArgument[1],
1778 { memEntryVar.branchArgument[1] },
1779 bits32Type,
1780 4);
1781 auto & qLoad = LoadOperation::LoadedValueOutput(loadQNode);
1782 auto & mem6 = *LoadOperation::MemoryStateOutputs(loadQNode).begin();
1783
1784 // zero = IntegerConstant32(0)
1785 auto & constantZeroInner = IntegerConstantOperation::Create(gammaSubregion1, 32, 0);
1786
1787 // gamma2Cond = NEq qLoad, zero
1788 auto & neqCondInnerNode =
1789 rvsdg::CreateOpNode<IntegerNeOperation>({ &qLoad, constantZeroInner.output(0) }, 32);
1790 auto & gamma2Cond = *neqCondInnerNode.output(0);
1791
1792 // gamma2Pred = MATCH[1->1, 0] gamma2Cond (maps 1 -> 1 (exit), default 0 (continue))
1793 auto & gamma2PredNode =
1794 rvsdg::CreateOpNode<rvsdg::MatchOperation>({ &gamma2Cond }, 1, matchMapping, 0, 2);
1795 auto & gamma2Pred = *gamma2PredNode.output(0);
1796
1797 // loopPred1, exitPred1 = gamma gamma2Pred (inner gamma with no entry vars)
1798 // Branch 0: exit loop to load *q again (loopPred=0, exitPred=1)
1799 // Branch 1: continue loop (loopPred=1, exitPred=DONTCARE)
1800 auto & innerGammaNode = rvsdg::GammaNode::Create(gamma2Pred, 2, {});
1801
1802 // Inner gamma branch 0 (continue): ctrlZero for loopPred, ctrlOne for exitPred...
1803 auto & innerGammaBranch0 = *innerGammaNode.subregion(0);
1804 auto & ctrlZero1 = rvsdg::ControlConstantOperation::create(innerGammaBranch0, 2, 0);
1805 auto & ctrlOne1 = rvsdg::ControlConstantOperation::create(innerGammaBranch0, 2, 1);
1806
1807 auto & innerGammaBranch1 = *innerGammaNode.subregion(1);
1808 auto & ctrlOne2 = rvsdg::ControlConstantOperation::create(innerGammaBranch1, 2, 1);
1809
1810 auto innerLoopPredExitVar = innerGammaNode.AddExitVar({ &ctrlZero1, &ctrlOne2 });
1811 auto innerExitPredExitVar = innerGammaNode.AddExitVar({ &ctrlOne1, &ctrlOne2 });
1812
1813 // Create exit variables for outer loop
1814 auto outerLoopPredExitVar =
1815 outerGammaNode.AddExitVar({ &ctrlZeroOuter, innerLoopPredExitVar.output });
1816 auto outerExitPredExitVar =
1817 outerGammaNode.AddExitVar({ &ctrlZeroOuter, innerExitPredExitVar.output });
1818
1819 // mem7: branch0 -> mem4 (memEntryVar.branchArgument[0]),
1820 // branch1 -> mem6
1821 auto memExitVar = outerGammaNode.AddExitVar({ memEntryVar.branchArgument[0], &mem6 });
1822
1823 // Wire up theta loop variable posts:
1824 thetaNode.predicate()->divert_to(outerLoopPredExitVar.output);
1825 exitPredLoopVar.post->divert_to(outerLoopPredExitVar.output);
1826 memLoopVar.post->divert_to(memExitVar.output);
1827 ioLoopVar.post->divert_to(&io2);
1828 opaqueLoopVar.post->divert_to(opaqueLoopVar.pre);
1829
1830 // Outside theta: gamma on exitPred for return values
1831 // ret, mem13 = gamma exitPred, p1, q1, mem8
1832 // [p2, _, mem9]{ pLoad2, mem10 = LOAD p2, mem9 }[pLoad2, mem10]
1833 // [_, q2, mem11]{ qLoad2, mem12 = LOAD q2, mem11 }[qLoad2, mem12]
1834
1835 auto & exitGammaNode = *rvsdg::GammaNode::create(exitPredLoopVar.output, 2);
1836 auto pEntryVarExit = exitGammaNode.AddEntryVar(&p);
1837 auto qEntryVarExit = exitGammaNode.AddEntryVar(&q);
1838 auto memEntryVarExit = exitGammaNode.AddEntryVar(memLoopVar.output);
1839
1840 // Branch 0: *p is true, return *p
1841 auto & loadP2Node = LoadNonVolatileOperation::CreateNode(
1842 *pEntryVarExit.branchArgument[0],
1843 { memEntryVarExit.branchArgument[0] },
1844 bits32Type,
1845 4);
1846 auto & pLoad2 = LoadOperation::LoadedValueOutput(loadP2Node);
1847 auto & mem10 = *LoadOperation::MemoryStateOutputs(loadP2Node).begin();
1848
1849 // Branch 1: *q is true, return *q
1850 auto & loadQ2Node = LoadNonVolatileOperation::CreateNode(
1851 *qEntryVarExit.branchArgument[1],
1852 { memEntryVarExit.branchArgument[1] },
1853 bits32Type,
1854 4);
1855 auto & qLoad2 = LoadOperation::LoadedValueOutput(loadQ2Node);
1856 auto & mem12 = *LoadOperation::MemoryStateOutputs(loadQ2Node).begin();
1857
1858 auto retExitVar = exitGammaNode.AddExitVar({ &pLoad2, &qLoad2 });
1859 auto memFinalExitVar = exitGammaNode.AddExitVar({ &mem10, &mem12 });
1860
1861 // Finalize lambda: [ret, mem13, io3]
1862 lambdaNode.finalize({ retExitVar.output, ioLoopVar.output, memFinalExitVar.output });
1863
1864 std::cout << rvsdg::view(&graph.GetRootRegion()) << std::endl;
1865
1866 // Act
1867 RunStoreValueForwarding(rvsdgModule);
1868
1869 // Assert
1870 // After StoreValueForwarding, the two LOADs outside the loop should be gone
1871 ASSERT_TRUE(
1872 rvsdg::TryGetRegionParentNode<rvsdg::GammaNode>(*retExitVar.branchResult[0]->origin()));
1873 ASSERT_TRUE(
1874 rvsdg::TryGetRegionParentNode<rvsdg::GammaNode>(*retExitVar.branchResult[1]->origin()));
1875 // The LOADs inside the loop should have two users each
1876 ASSERT_EQ(pLoad.nusers(), 2u);
1877 ASSERT_EQ(qLoad.nusers(), 2u);
1878}
1879
1880TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantStruct)
1881{
1882 using namespace jlm::llvm;
1883 using namespace jlm::rvsdg;
1884
1885 // Arrange
1886 const auto pointerType = PointerType::Create();
1887 const auto bits8Type = BitType::Create(8);
1888 const auto bits32Type = BitType::Create(32);
1889 const auto bits64Type = BitType::Create(64);
1890 const auto structType = StructType::CreateIdentified(
1891 { bits32Type, bits32Type, bits32Type, bits32Type, pointerType },
1892 false);
1893 auto functionType1 = FunctionType::Create({}, {});
1894 const auto functionType2 = FunctionType::Create(
1895 {},
1896 { bits32Type, bits32Type, bits32Type, bits32Type, bits32Type, bits64Type, pointerType });
1897
1898 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
1899 auto & graph = rvsdgModule.Rvsdg();
1900
1901 auto & i0 = GraphImport::Create(graph, functionType2, "fct");
1902
1903 auto deltaNode = DeltaNode::Create(
1904 &graph.GetRootRegion(),
1905 DeltaOperation::Create(structType, true, pointerType));
1906 {
1907 auto ctxVar = deltaNode->AddContextVar(i0);
1908 auto & zeroNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 0);
1909 auto & oneNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 1);
1910 auto & twoNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 2);
1911 auto & threeNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 3);
1912 auto & fnToPtrNode = FunctionToPointerOperation::createNode(*ctxVar.inner);
1913 auto & constantStructResult = ConstantStructOperation::Create(
1914 *deltaNode->subregion(),
1915 { zeroNode.output(0),
1916 oneNode.output(0),
1917 twoNode.output(0),
1918 threeNode.output(0),
1919 fnToPtrNode.output(0) },
1920 structType);
1921 deltaNode->finalize(&constantStructResult);
1922 }
1923
1924 auto & lambdaNode = *LambdaNode::Create(
1925 graph.GetRootRegion(),
1926 LlvmLambdaOperation::Create(functionType2, "func", Linkage::internalLinkage));
1927 auto ctxVar = lambdaNode.AddContextVar(deltaNode->output());
1928
1929 auto zero = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0).output(0);
1930 auto two = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2).output(0);
1931 auto four = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 4).output(0);
1932
1933 auto & loadNode0 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits32Type, 4);
1934
1935 auto gepOutput1 = GetElementPtrOperation::create(ctxVar.inner, { zero }, bits32Type);
1936 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(*gepOutput1, {}, bits32Type, 4);
1937
1938 auto gepOutput2 = GetElementPtrOperation::create(ctxVar.inner, { zero, zero }, structType);
1939 auto & loadNode2 = LoadNonVolatileOperation::CreateNode(*gepOutput2, {}, bits32Type, 4);
1940
1941 auto gepOutput3 = GetElementPtrOperation::create(ctxVar.inner, { zero, two }, structType);
1942 auto & loadNode3 = LoadNonVolatileOperation::CreateNode(*gepOutput3, {}, bits32Type, 4);
1943
1944 auto gepOutput4 = GetElementPtrOperation::create(ctxVar.inner, { four }, bits8Type);
1945 auto & loadNode4 = LoadNonVolatileOperation::CreateNode(*gepOutput4, {}, bits32Type, 4);
1946
1947 auto & loadNode5 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits64Type, 4);
1948
1949 auto gepOutput6 = GetElementPtrOperation::create(ctxVar.inner, { zero, four }, structType);
1950 auto & loadNode6 = LoadNonVolatileOperation::CreateNode(*gepOutput6, {}, pointerType, 4);
1951
1952 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(loadNode0),
1953 &LoadOperation::LoadedValueOutput(loadNode1),
1954 &LoadOperation::LoadedValueOutput(loadNode2),
1955 &LoadOperation::LoadedValueOutput(loadNode3),
1956 &LoadOperation::LoadedValueOutput(loadNode4),
1957 &LoadOperation::LoadedValueOutput(loadNode5),
1958 &LoadOperation::LoadedValueOutput(loadNode6) });
1959
1960 // Act
1961 RunStoreValueForwarding(rvsdgModule);
1962
1963 // Assert
1964 auto [intNode0, intOperation0] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1965 *lambdaNode.GetFunctionResults()[0]->origin());
1966 EXPECT_NE(intOperation0, nullptr);
1967 EXPECT_EQ(intOperation0->Representation().to_uint(), 0u);
1968
1969 auto [intNode1, intOperation1] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1970 *lambdaNode.GetFunctionResults()[1]->origin());
1971 EXPECT_NE(intOperation1, nullptr);
1972 EXPECT_EQ(intOperation1->Representation().to_uint(), 0u);
1973
1974 auto [intNode2, intOperation2] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1975 *lambdaNode.GetFunctionResults()[2]->origin());
1976 EXPECT_NE(intOperation2, nullptr);
1977 EXPECT_EQ(intOperation2->Representation().to_uint(), 0u);
1978
1979 auto [intNode3, intOperation3] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1980 *lambdaNode.GetFunctionResults()[3]->origin());
1981 EXPECT_NE(intOperation3, nullptr);
1982 EXPECT_EQ(intOperation3->Representation().to_uint(), 2u);
1983
1984 auto [intNode4, intOperation4] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1985 *lambdaNode.GetFunctionResults()[4]->origin());
1986 EXPECT_NE(intOperation4, nullptr);
1987 EXPECT_EQ(intOperation4->Representation().to_uint(), 1u);
1988
1989 {
1990 // FIXME: Does currently not work at the types do not align
1991 auto [loadNode, loadOperation] = TryGetSimpleNodeAndOptionalOp<LoadNonVolatileOperation>(
1992 *lambdaNode.GetFunctionResults()[5]->origin());
1993 EXPECT_NE(loadOperation, nullptr);
1994#if 0
1995 auto [intNode5, intOperation5] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
1996 *lambdaNode.GetFunctionResults()[5]->origin());
1997 EXPECT_NE(intOperation5, nullptr);
1998 EXPECT_EQ(intOperation5->Representation().to_uint(), 0x0000000100000000u);
1999#endif
2000 }
2001
2002 auto [fnToPtrNode, fnToPtrOperation] = TryGetSimpleNodeAndOptionalOp<FunctionToPointerOperation>(
2003 *lambdaNode.GetFunctionResults()[6]->origin());
2004 EXPECT_NE(fnToPtrOperation, nullptr);
2005}
2006
2007TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantArray)
2008{
2009 using namespace jlm::llvm;
2010 using namespace jlm::rvsdg;
2011
2012 // Arrange
2013 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
2014 auto & graph = rvsdgModule.Rvsdg();
2015 const auto pointerType = PointerType::Create();
2016 const auto bits8Type = BitType::Create(8);
2017 const auto bits32Type = BitType::Create(32);
2018 const auto bits64Type = BitType::Create(64);
2019 const auto arrayType = ArrayType::Create(bits32Type, 3);
2020 const auto functionType = FunctionType::Create(
2021 {},
2022 {
2023 bits32Type,
2024 bits32Type,
2025 bits32Type,
2026 bits32Type,
2027 bits32Type,
2028 bits64Type,
2029 });
2030
2031 auto deltaNode = DeltaNode::Create(
2032 &graph.GetRootRegion(),
2033 DeltaOperation::Create(arrayType, true, pointerType));
2034 auto & zeroNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 0);
2035 auto & oneNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 1);
2036 auto & twoNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 2);
2037 auto constantDataArrayResult =
2038 ConstantArrayOperation::Create({ zeroNode.output(0), oneNode.output(0), twoNode.output(0) });
2039 auto & deltaOutput = deltaNode->finalize(constantDataArrayResult);
2040
2041 auto & lambdaNode = *LambdaNode::Create(
2042 graph.GetRootRegion(),
2043 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
2044 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
2045
2046 auto zero = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 0).output(0);
2047 auto two = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 2).output(0);
2048 auto four = IntegerConstantOperation::Create(*lambdaNode.subregion(), 32, 4).output(0);
2049
2050 auto & loadNode0 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits32Type, 4);
2051
2052 auto gepOutput1 = GetElementPtrOperation::create(ctxVar.inner, { zero }, arrayType);
2053 auto & loadNode1 = LoadNonVolatileOperation::CreateNode(*gepOutput1, {}, bits32Type, 4);
2054
2055 auto gepOutput2 = GetElementPtrOperation::create(ctxVar.inner, { zero, zero }, arrayType);
2056 auto & loadNode2 = LoadNonVolatileOperation::CreateNode(*gepOutput2, {}, bits32Type, 4);
2057
2058 auto gepOutput3 = GetElementPtrOperation::create(ctxVar.inner, { zero, two }, arrayType);
2059 auto & loadNode3 = LoadNonVolatileOperation::CreateNode(*gepOutput3, {}, bits32Type, 4);
2060
2061 auto gepOutput4 = GetElementPtrOperation::create(ctxVar.inner, { four }, bits8Type);
2062 auto & loadNode4 = LoadNonVolatileOperation::CreateNode(*gepOutput4, {}, bits32Type, 4);
2063
2064 auto & loadNode5 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, bits64Type, 4);
2065
2066 lambdaNode.finalize({
2067 &LoadOperation::LoadedValueOutput(loadNode0),
2068 &LoadOperation::LoadedValueOutput(loadNode1),
2069 &LoadOperation::LoadedValueOutput(loadNode2),
2070 &LoadOperation::LoadedValueOutput(loadNode3),
2071 &LoadOperation::LoadedValueOutput(loadNode4),
2072 &LoadOperation::LoadedValueOutput(loadNode5),
2073 });
2074
2075 // Act
2076 RunStoreValueForwarding(rvsdgModule);
2077
2078 // Assert
2079 {
2080 auto [intNode0, intOperation0] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2081 *lambdaNode.GetFunctionResults()[0]->origin());
2082 EXPECT_NE(intOperation0, nullptr);
2083 EXPECT_EQ(intOperation0->Representation().to_uint(), 0);
2084 }
2085
2086 {
2087 auto [intNode1, intOperation1] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2088 *lambdaNode.GetFunctionResults()[1]->origin());
2089 EXPECT_NE(intOperation1, nullptr);
2090 EXPECT_EQ(intOperation1->Representation().to_uint(), 0);
2091 }
2092
2093 {
2094 auto [intNode2, intOperation2] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2095 *lambdaNode.GetFunctionResults()[2]->origin());
2096 EXPECT_NE(intOperation2, nullptr);
2097 EXPECT_EQ(intOperation2->Representation().to_uint(), 0);
2098 }
2099
2100 {
2101 auto [intNode3, intOperation3] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2102 *lambdaNode.GetFunctionResults()[3]->origin());
2103 EXPECT_NE(intOperation3, nullptr);
2104 EXPECT_EQ(intOperation3->Representation().to_uint(), 2);
2105 }
2106
2107 {
2108 auto [intNode4, intOperation4] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2109 *lambdaNode.GetFunctionResults()[4]->origin());
2110 EXPECT_NE(intOperation4, nullptr);
2111 EXPECT_EQ(intOperation4->Representation().to_uint(), 1);
2112 }
2113
2114 {
2115 // FIXME: Does currently not work at the types do not align
2116 auto [loadNode, loadOperation] = TryGetSimpleNodeAndOptionalOp<LoadNonVolatileOperation>(
2117 *lambdaNode.GetFunctionResults()[5]->origin());
2118 EXPECT_NE(loadOperation, nullptr);
2119#if 0
2120 auto [intNode5, intOperation5] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2121 *lambdaNode.GetFunctionResults()[5]->origin());
2122 EXPECT_NE(intOperation5, nullptr);
2123 EXPECT_EQ(intOperation5->Representation().to_uint(), 0x0000000100000000);
2124#endif
2125 }
2126}
2127
2128TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithIntToPtr)
2129{
2130 using namespace jlm::llvm;
2131 using namespace jlm::rvsdg;
2132
2133 // Arrange
2134 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
2135 auto & graph = rvsdgModule.Rvsdg();
2136 const auto pointerType = PointerType::Create();
2137 const auto functionType = FunctionType::Create(
2138 {},
2139 {
2140 pointerType,
2141 });
2142
2143 auto deltaNode = DeltaNode::Create(
2144 &graph.GetRootRegion(),
2145 DeltaOperation::Create(pointerType, true, pointerType));
2146 auto & oneNode = IntegerConstantOperation::Create(*deltaNode->subregion(), 32, 1);
2147 auto intToPtr = IntToPtrOperation::create(oneNode.output(0));
2148 auto & deltaOutput = deltaNode->finalize(intToPtr);
2149
2150 auto & lambdaNode = *LambdaNode::Create(
2151 graph.GetRootRegion(),
2152 LlvmLambdaOperation::Create(functionType, "func", Linkage::internalLinkage));
2153 auto ctxVar = lambdaNode.AddContextVar(deltaOutput);
2154
2155 auto & loadNode0 = LoadNonVolatileOperation::CreateNode(*ctxVar.inner, {}, pointerType, 4);
2156
2157 lambdaNode.finalize({
2158 &LoadOperation::LoadedValueOutput(loadNode0),
2159 });
2160
2161 // Act
2162 RunStoreValueForwarding(rvsdgModule);
2163
2164 // Assert
2165 {
2166 auto [intToPtrNode, intToPtrOp] = TryGetSimpleNodeAndOptionalOp<IntToPtrOperation>(
2167 *lambdaNode.GetFunctionResults()[0]->origin());
2168 EXPECT_NE(intToPtrOp, nullptr);
2169 }
2170}
2171
2172TEST(StoreValueForwardingTests, LoadForwardingFromDeltaWithConstantGep)
2173{
2174 using namespace jlm::llvm;
2175 using namespace jlm::rvsdg;
2176
2177 // %struct.s = type { i32, i32 }
2178 // @v = global ptr getelementptr (i8, ptr @s0, i64 4)
2179 // @s0 = global %struct.s { i32 1, i32 2 }
2180 // %p = load ptr, ptr @v
2181 // %x = load i32, ptr %p
2182
2183 // Arrange
2184 LlvmRvsdgModule rvsdgModule(jlm::util::FilePath(""), "", "");
2185 auto & graph = rvsdgModule.Rvsdg();
2186
2187 const auto pointerType = PointerType::Create();
2188 const auto bits8Type = BitType::Create(8);
2189 const auto bits32Type = BitType::Create(32);
2190 const auto structType =
2191 StructType::CreateIdentified("struct.s", { bits32Type, bits32Type }, false);
2192
2193 // @s0 = { 1, 2 }
2194 auto s0 = DeltaNode::Create(
2195 &graph.GetRootRegion(),
2196 DeltaOperation::Create(structType, true, pointerType));
2197 auto & one = IntegerConstantOperation::Create(*s0->subregion(), 32, 1);
2198 auto & two = IntegerConstantOperation::Create(*s0->subregion(), 32, 2);
2199 auto & s0Value = ConstantStructOperation::Create(
2200 *s0->subregion(),
2201 { one.output(0), two.output(0) },
2202 structType);
2203 auto & s0Output = s0->finalize(&s0Value);
2204
2205 // @v = getelementptr i8, ptr @s0, i64 4
2206 auto v = DeltaNode::Create(
2207 &graph.GetRootRegion(),
2208 DeltaOperation::Create(pointerType, true, pointerType));
2209 auto vContext = v->AddContextVar(s0Output);
2210 auto four = IntegerConstantOperation::Create(*v->subregion(), 64, 4).output(0);
2211 auto vValue = GetElementPtrOperation::create(vContext.inner, { four }, bits8Type);
2212 auto & vOutput = v->finalize(vValue);
2213
2214 auto & lambdaNode = *LambdaNode::Create(
2215 graph.GetRootRegion(),
2216 LlvmLambdaOperation::Create(
2217 FunctionType::Create({}, { bits32Type }),
2218 "f",
2219 Linkage::internalLinkage));
2220 auto vContextVar = lambdaNode.AddContextVar(vOutput);
2221
2222 // %p = load ptr, ptr @v
2223 auto & pointerLoad = LoadNonVolatileOperation::CreateNode(*vContextVar.inner, {}, pointerType, 8);
2224 // %x = load i32, ptr %p
2225 auto & valueLoad =
2226 LoadNonVolatileOperation::CreateNode(*pointerLoad.output(0), {}, bits32Type, 4);
2227 lambdaNode.finalize({ &LoadOperation::LoadedValueOutput(valueLoad) });
2228
2229 // Act
2230 RunStoreValueForwarding(rvsdgModule);
2231
2232 // Assert
2233 EXPECT_FALSE(Region::containsOperation<LoadNonVolatileOperation>(graph.GetRootRegion(), true));
2234
2235 auto [intNode, intOperation] = TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
2236 *lambdaNode.GetFunctionResults()[0]->origin());
2237 EXPECT_NE(intNode, nullptr);
2238 EXPECT_NE(intOperation, nullptr);
2239 EXPECT_EQ(intOperation->Representation().to_uint(), 2u);
2240}
static jlm::util::StatisticsCollector statisticsCollector
Definition PullTests.cpp:17
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, bool mayEnterSubregions, const rvsdg::Region *withinRegion)
Definition Trace.cpp:84
std::optional< int64_t > tryGetConstantSignedInteger(const rvsdg::Output &output)
Definition Trace.cpp:97