Jlm
Loading...
Searching...
No Matches
InvariantValueRedirectionTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2017 Nico Reißmann <nico.reissmann@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
19#include <jlm/rvsdg/control.hpp>
20#include <jlm/rvsdg/gamma.hpp>
23#include <jlm/rvsdg/theta.hpp>
24#include <jlm/rvsdg/view.hpp>
26
27static void
29{
30 jlm::rvsdg::view(rvsdgModule.Rvsdg(), stdout);
31
34 jlm::llvm::InvariantValueRedirection invariantValueRedirection(std::move(configuration));
35 invariantValueRedirection.Run(rvsdgModule, statisticsCollector);
36
37 jlm::rvsdg::view(rvsdgModule.Rvsdg(), stdout);
38}
39
40TEST(InvariantValueRedirectionTests, TestGamma)
41{
42 using namespace jlm::llvm;
43
44 // Arrange
46 auto controlType = jlm::rvsdg::ControlType::Create(2);
47 auto functionType = jlm::rvsdg::FunctionType::Create(
48 { controlType, valueType, valueType },
49 { valueType, valueType });
50
51 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
52 auto & rvsdg = rvsdgModule->Rvsdg();
53
54 auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
55 rvsdg.GetRootRegion(),
56 jlm::llvm::LlvmLambdaOperation::Create(functionType, "test", Linkage::externalLinkage));
57
58 auto c = lambdaNode->GetFunctionArguments()[0];
59 auto x = lambdaNode->GetFunctionArguments()[1];
60 auto y = lambdaNode->GetFunctionArguments()[2];
61
62 auto gammaNode1 = jlm::rvsdg::GammaNode::create(c, 2);
63 auto gammaInput1 = gammaNode1->AddEntryVar(c);
64 auto gammaInput2 = gammaNode1->AddEntryVar(x);
65 auto gammaInput3 = gammaNode1->AddEntryVar(y);
66
67 auto gammaNode2 = jlm::rvsdg::GammaNode::create(gammaInput1.branchArgument[0], 2);
68 auto gammaInput4 = gammaNode2->AddEntryVar(gammaInput2.branchArgument[0]);
69 auto gammaInput5 = gammaNode2->AddEntryVar(gammaInput3.branchArgument[0]);
70 gammaNode2->AddExitVar(gammaInput4.branchArgument);
71 gammaNode2->AddExitVar(gammaInput5.branchArgument);
72
73 gammaNode1->AddExitVar({ gammaNode2->output(0), gammaInput2.branchArgument[1] });
74 gammaNode1->AddExitVar({ gammaNode2->output(1), gammaInput3.branchArgument[1] });
75
76 auto lambdaOutput = lambdaNode->finalize({ gammaNode1->output(0), gammaNode1->output(1) });
77
78 jlm::rvsdg::GraphExport::Create(*lambdaOutput, "test");
79
80 // Act
81 RunInvariantValueRedirection(*rvsdgModule);
82
83 // Assert
84 EXPECT_EQ(lambdaNode->GetFunctionResults()[0]->origin(), x);
85 EXPECT_EQ(lambdaNode->GetFunctionResults()[1]->origin(), y);
86}
87
88TEST(InvariantValueRedirectionTests, TestTheta)
89{
90 // Arrange
91 using namespace jlm::llvm;
92
93 auto ioStateType = IOStateType::Create();
95 auto controlType = jlm::rvsdg::ControlType::Create(2);
96 auto functionType = jlm::rvsdg::FunctionType::Create(
97 { controlType, valueType, ioStateType },
98 { controlType, valueType, ioStateType });
99
100 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
101 auto & rvsdg = rvsdgModule->Rvsdg();
102
103 auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
104 rvsdg.GetRootRegion(),
105 jlm::llvm::LlvmLambdaOperation::Create(functionType, "test", Linkage::externalLinkage));
106
107 auto c = lambdaNode->GetFunctionArguments()[0];
108 auto x = lambdaNode->GetFunctionArguments()[1];
109 auto l = lambdaNode->GetFunctionArguments()[2];
110
111 auto thetaNode1 = jlm::rvsdg::ThetaNode::create(lambdaNode->subregion());
112 auto thetaVar1 = thetaNode1->AddLoopVar(c);
113 auto thetaVar2 = thetaNode1->AddLoopVar(x);
114 auto thetaVar3 = thetaNode1->AddLoopVar(l);
115
116 auto thetaNode2 = jlm::rvsdg::ThetaNode::create(thetaNode1->subregion());
117 auto thetaVar4 = thetaNode2->AddLoopVar(thetaVar1.pre);
118 thetaNode2->AddLoopVar(thetaVar2.pre);
119 auto thetaVar5 = thetaNode2->AddLoopVar(thetaVar3.pre);
120 thetaNode2->set_predicate(thetaVar4.pre);
121
122 thetaVar3.post->divert_to(thetaVar5.output);
123 thetaNode1->set_predicate(thetaVar1.pre);
124
125 auto lambdaOutput =
126 lambdaNode->finalize({ thetaVar1.output, thetaVar2.output, thetaVar3.output });
127
128 jlm::rvsdg::GraphExport::Create(*lambdaOutput, "test");
129
130 // Act
131 RunInvariantValueRedirection(*rvsdgModule);
132
133 // Assert
134 EXPECT_EQ(lambdaNode->GetFunctionResults()[0]->origin(), c);
135 EXPECT_EQ(lambdaNode->GetFunctionResults()[1]->origin(), x);
136 EXPECT_EQ(lambdaNode->GetFunctionResults()[2]->origin(), thetaVar3.output);
137}
138
139TEST(InvariantValueRedirectionTests, TestCall)
140{
141 // Arrange
142 using namespace jlm::llvm;
143
144 auto ioStateType = IOStateType::Create();
145 auto memoryStateType = MemoryStateType::Create();
146 auto valueType = jlm::rvsdg::TestType::createValueType();
147 auto controlType = jlm::rvsdg::ControlType::Create(2);
148 auto functionTypeTest1 = jlm::rvsdg::FunctionType::Create(
149 { controlType, valueType, valueType, ioStateType, memoryStateType },
150 { valueType, valueType, ioStateType, memoryStateType });
151
152 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
153 auto & rvsdg = rvsdgModule->Rvsdg();
154
155 jlm::rvsdg::Output * lambdaOutputTest1 = nullptr;
156 {
157 auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
158 rvsdg.GetRootRegion(),
159 LlvmLambdaOperation::Create(functionTypeTest1, "test1", Linkage::externalLinkage));
160
161 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
162 auto xArgument = lambdaNode->GetFunctionArguments()[1];
163 auto yArgument = lambdaNode->GetFunctionArguments()[2];
164 auto ioStateArgument = lambdaNode->GetFunctionArguments()[3];
165 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[4];
166
167 auto gammaNode = jlm::rvsdg::GammaNode::create(controlArgument, 2);
168 auto gammaInputX = gammaNode->AddEntryVar(xArgument);
169 auto gammaInputY = gammaNode->AddEntryVar(yArgument);
170 auto gammaInputIOState = gammaNode->AddEntryVar(ioStateArgument);
171 auto gammaInputMemoryState = gammaNode->AddEntryVar(memoryStateArgument);
172 auto gammaOutputX =
173 gammaNode->AddExitVar({ gammaInputY.branchArgument[0], gammaInputY.branchArgument[1] });
174 auto gammaOutputY =
175 gammaNode->AddExitVar({ gammaInputX.branchArgument[0], gammaInputX.branchArgument[1] });
176 auto gammaOutputIOState = gammaNode->AddExitVar(
177 { gammaInputIOState.branchArgument[0], gammaInputIOState.branchArgument[1] });
178 auto gammaOutputMemoryState = gammaNode->AddExitVar(
179 { gammaInputMemoryState.branchArgument[0], gammaInputMemoryState.branchArgument[1] });
180
181 lambdaOutputTest1 = lambdaNode->finalize({ gammaOutputX.output,
182 gammaOutputY.output,
183 gammaOutputIOState.output,
184 gammaOutputMemoryState.output });
185 }
186
187 jlm::rvsdg::Output * lambdaOutputTest2 = nullptr;
188 {
189 auto functionType = jlm::rvsdg::FunctionType::Create(
190 { valueType, valueType, ioStateType, memoryStateType },
191 { valueType, valueType, ioStateType, memoryStateType });
192
193 auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
194 rvsdg.GetRootRegion(),
195 LlvmLambdaOperation::Create(functionType, "test2", Linkage::externalLinkage));
196 auto xArgument = lambdaNode->GetFunctionArguments()[0];
197 auto yArgument = lambdaNode->GetFunctionArguments()[1];
198 auto ioStateArgument = lambdaNode->GetFunctionArguments()[2];
199 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[3];
200 auto lambdaArgumentTest1 = lambdaNode->AddContextVar(*lambdaOutputTest1).inner;
201
202 auto controlResult =
203 &jlm::rvsdg::ControlConstantOperation::create(*lambdaNode->subregion(), 2, 0);
204
205 auto & callNode = CallOperation::CreateNode(
206 lambdaArgumentTest1,
207 functionTypeTest1,
208 { controlResult, xArgument, yArgument, ioStateArgument, memoryStateArgument });
209
210 lambdaOutputTest2 = lambdaNode->finalize(outputs(&callNode));
211 jlm::rvsdg::GraphExport::Create(*lambdaOutputTest2, "test2");
212 }
213
214 // Act
215 RunInvariantValueRedirection(*rvsdgModule);
216
217 // Assert
218 auto & lambdaNode = jlm::rvsdg::AssertGetOwnerNode<jlm::rvsdg::LambdaNode>(*lambdaOutputTest2);
219 EXPECT_EQ(lambdaNode.GetFunctionResults().size(), 4u);
220 EXPECT_EQ(lambdaNode.GetFunctionResults()[0]->origin(), lambdaNode.GetFunctionArguments()[1]);
221 EXPECT_EQ(lambdaNode.GetFunctionResults()[1]->origin(), lambdaNode.GetFunctionArguments()[0]);
222 EXPECT_EQ(lambdaNode.GetFunctionResults()[2]->origin(), lambdaNode.GetFunctionArguments()[2]);
223 EXPECT_EQ(lambdaNode.GetFunctionResults()[3]->origin(), lambdaNode.GetFunctionArguments()[3]);
224}
225
226TEST(InvariantValueRedirectionTests, TestCallWithMemoryStateNodes)
227{
228 // Arrange
229 using namespace jlm::llvm;
230
262 // The memory node representing external has index 0, and is avoided in this test
263 EXPECT_EQ(aa::PointsToGraph::externalMemoryNode, 0u);
264
265 auto ioStateType = IOStateType::Create();
266 auto memoryStateType = MemoryStateType::Create();
267 auto valueType = jlm::rvsdg::TestType::createValueType();
268 auto controlType = jlm::rvsdg::ControlType::Create(2);
269 auto functionTypeTest1 = jlm::rvsdg::FunctionType::Create(
270 { controlType, valueType, ioStateType, memoryStateType },
271 { valueType, ioStateType, memoryStateType });
272
273 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
274 auto & rvsdg = rvsdgModule->Rvsdg();
275
276 jlm::rvsdg::Output * lambdaOutputTest1 = nullptr;
277 {
278 auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
279 rvsdg.GetRootRegion(),
280 LlvmLambdaOperation::Create(functionTypeTest1, "test1", Linkage::externalLinkage));
281
282 auto controlArgument = lambdaNode->GetFunctionArguments()[0];
283 auto xArgument = lambdaNode->GetFunctionArguments()[1];
284 auto ioStateArgument = lambdaNode->GetFunctionArguments()[2];
285 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[3];
286
287 auto & lambdaEntrySplitNode =
288 LambdaEntryMemoryStateSplitOperation::CreateNode(*memoryStateArgument, { 1, 2 });
289
290 auto gammaNode = jlm::rvsdg::GammaNode::create(controlArgument, 2);
291
292 auto gammaInputX = gammaNode->AddEntryVar(xArgument);
293 auto gammaInputMemoryState1 = gammaNode->AddEntryVar(lambdaEntrySplitNode.output(0));
294 auto gammaInputMemoryState2 = gammaNode->AddEntryVar(lambdaEntrySplitNode.output(1));
295
296 auto gammaOutputX = gammaNode->AddExitVar(gammaInputX.branchArgument);
297 auto gammaOutputMemoryState1 = gammaNode->AddExitVar(gammaInputMemoryState1.branchArgument);
298 auto gammaOutputMemoryState2 = gammaNode->AddExitVar(gammaInputMemoryState2.branchArgument);
299
300 auto & lambdaExitMergeNode = LambdaExitMemoryStateMergeOperation::CreateNode(
301 *lambdaNode->subregion(),
302 { gammaOutputMemoryState1.output, gammaOutputMemoryState2.output },
303 { 1, 2 });
304
305 lambdaOutputTest1 = lambdaNode->finalize(
306 { gammaOutputX.output, ioStateArgument, lambdaExitMergeNode.output(0) });
307 }
308
309 jlm::rvsdg::Output * lambdaOutputTest2 = nullptr;
310 {
311 auto functionType = jlm::rvsdg::FunctionType::Create(
312 { valueType, ioStateType, memoryStateType },
313 { valueType, ioStateType, memoryStateType });
314
315 auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
316 rvsdg.GetRootRegion(),
317 LlvmLambdaOperation::Create(functionType, "test2", Linkage::externalLinkage));
318 auto xArgument = lambdaNode->GetFunctionArguments()[0];
319 auto ioStateArgument = lambdaNode->GetFunctionArguments()[1];
320 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[2];
321 auto lambdaArgumentTest1 = lambdaNode->AddContextVar(*lambdaOutputTest1).inner;
322
323 auto & lambdaEntrySplitNode =
324 LambdaEntryMemoryStateSplitOperation::CreateNode(*memoryStateArgument, { 1, 2 });
325
326 auto & callEntryMergeNode = CallEntryMemoryStateMergeOperation::CreateNode(
327 *lambdaNode->subregion(),
328 outputs(&lambdaEntrySplitNode),
329 { 1, 2 });
330
331 auto controlResult =
332 &jlm::rvsdg::ControlConstantOperation::create(*lambdaNode->subregion(), 2, 0);
333
334 auto & callNode = CallOperation::CreateNode(
335 lambdaArgumentTest1,
336 functionTypeTest1,
337 { controlResult, xArgument, ioStateArgument, callEntryMergeNode.output(0) });
338
339 auto & callExitSplitNode = CallExitMemoryStateSplitOperation::CreateNode(
340 CallOperation::GetMemoryStateOutput(callNode),
341 { 2, 1 });
342
343 auto & lambdaExitMergeNode = LambdaExitMemoryStateMergeOperation::CreateNode(
344 *lambdaNode->subregion(),
345 outputs(&callExitSplitNode),
346 { 2, 1 });
347
348 lambdaOutputTest2 = lambdaNode->finalize({ callNode.output(0),
349 &CallOperation::GetIOStateOutput(callNode),
350 lambdaExitMergeNode.output(0) });
351 jlm::rvsdg::GraphExport::Create(*lambdaOutputTest2, "test2");
352 }
353
354 // Act
355 RunInvariantValueRedirection(*rvsdgModule);
356
357 // Assert
358 auto & lambdaNode = jlm::rvsdg::AssertGetOwnerNode<jlm::rvsdg::LambdaNode>(*lambdaOutputTest2);
359 EXPECT_EQ(lambdaNode.GetFunctionResults().size(), 3u);
360 EXPECT_EQ(lambdaNode.GetFunctionResults()[0]->origin(), lambdaNode.GetFunctionArguments()[0]);
361 EXPECT_EQ(lambdaNode.GetFunctionResults()[1]->origin(), lambdaNode.GetFunctionArguments()[1]);
362
363 auto lambdaEntrySplit = tryGetMemoryStateEntrySplit(lambdaNode);
364 auto lambdaExitMerge = tryGetMemoryStateExitMerge(lambdaNode);
365
366 EXPECT_TRUE(lambdaEntrySplit && lambdaEntrySplit->noutputs() == 2);
367 EXPECT_TRUE(lambdaExitMerge && lambdaExitMerge->ninputs() == 2);
368 EXPECT_EQ(lambdaExitMerge->input(0)->origin(), lambdaEntrySplit->output(1));
369 EXPECT_EQ(lambdaExitMerge->input(1)->origin(), lambdaEntrySplit->output(0));
370}
371
372TEST(InvariantValueRedirectionTests, TestCallWithMissingMemoryStateNodes)
373{
374 // Arrange
375 using namespace jlm::llvm;
376 using namespace jlm::rvsdg;
377
378 // The memory node representing external has index 0, and is avoided in this test
379 EXPECT_EQ(aa::PointsToGraph::externalMemoryNode, 0u);
380
381 auto ioStateType = IOStateType::Create();
382 auto memoryStateType = MemoryStateType::Create();
383 auto valueType = TestType::createValueType();
384 auto int32Type = BitType::Create(32);
385 auto functionType = FunctionType::Create(
386 { valueType, ioStateType, memoryStateType },
387 { int32Type, ioStateType, memoryStateType });
388
389 auto rvsdgModule = jlm::llvm::LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
390 auto & rvsdg = rvsdgModule->Rvsdg();
391
392 Output * lambdaOutputTest1 = nullptr;
393 {
394 auto lambdaNode = LambdaNode::Create(
395 rvsdg.GetRootRegion(),
396 LlvmLambdaOperation::Create(functionType, "test", Linkage::externalLinkage));
397
398 auto xArgument = lambdaNode->GetFunctionArguments()[0];
399 auto ioStateArgument = lambdaNode->GetFunctionArguments()[1];
400 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[2];
401
402 auto & zeroNode = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 0);
403 auto & oneNode = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 1);
404 auto allocaResults = AllocaOperation::create(valueType, oneNode.output(0), 4);
405
406 auto & storeNode = StoreNonVolatileOperation::CreateNode(
407 *allocaResults[0],
408 *xArgument,
409 { memoryStateArgument },
410 4);
411
412 auto & lambdaExitMergeNode = LambdaExitMemoryStateMergeOperation::CreateNode(
413 *lambdaNode->subregion(),
414 { storeNode.output(0) },
415 { 1 });
416
417 lambdaOutputTest1 = lambdaNode->finalize(
418 { zeroNode.output(0), ioStateArgument, lambdaExitMergeNode.output(0) });
419 }
420
421 Output * lambdaOutputTest2 = nullptr;
422 {
423 auto lambdaNode = LambdaNode::Create(
424 rvsdg.GetRootRegion(),
425 LlvmLambdaOperation::Create(functionType, "test2", Linkage::externalLinkage));
426 auto xArgument = lambdaNode->GetFunctionArguments()[0];
427 auto ioStateArgument = lambdaNode->GetFunctionArguments()[1];
428 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[2];
429 auto lambdaArgumentTest = lambdaNode->AddContextVar(*lambdaOutputTest1).inner;
430
431 auto & lambdaEntrySplitNode =
432 LambdaEntryMemoryStateSplitOperation::CreateNode(*memoryStateArgument, { 1 });
433
434 auto & callEntryMergeNode = CallEntryMemoryStateMergeOperation::CreateNode(
435 *lambdaNode->subregion(),
436 outputs(&lambdaEntrySplitNode),
437 { 1 });
438
439 auto & callNode = CallOperation::CreateNode(
440 lambdaArgumentTest,
441 functionType,
442 { xArgument, ioStateArgument, callEntryMergeNode.output(0) });
443
444 auto & callExitSplitNode = CallExitMemoryStateSplitOperation::CreateNode(
445 CallOperation::GetMemoryStateOutput(callNode),
446 { 1 });
447
448 auto & lambdaExitMergeNode = LambdaExitMemoryStateMergeOperation::CreateNode(
449 *lambdaNode->subregion(),
450 outputs(&callExitSplitNode),
451 { 1 });
452
453 lambdaOutputTest2 = lambdaNode->finalize({ callNode.output(0),
454 &CallOperation::GetIOStateOutput(callNode),
455 lambdaExitMergeNode.output(0) });
456 GraphExport::Create(*lambdaOutputTest2, "test2");
457 }
458
459 std::cout << view(&rvsdg.GetRootRegion()) << std::flush;
460
461 // Act
462 RunInvariantValueRedirection(*rvsdgModule);
463 std::cout << view(&rvsdg.GetRootRegion()) << std::flush;
464
465 // Assert
466 // Nothing should have been redirected
467 const auto & lambdaNode1 = AssertGetOwnerNode<LambdaNode>(*lambdaOutputTest1);
468 const auto lambdaEntrySplit1 = tryGetMemoryStateEntrySplit(lambdaNode1);
469 const auto lambdaExitMerge1 = tryGetMemoryStateExitMerge(lambdaNode1);
470 EXPECT_EQ(lambdaEntrySplit1, nullptr);
471 EXPECT_TRUE(lambdaExitMerge1 && lambdaExitMerge1->ninputs() == 1);
472
473 const auto & lambdaNode2 = AssertGetOwnerNode<LambdaNode>(*lambdaOutputTest2);
474 const auto lambdaEntrySplit2 = tryGetMemoryStateEntrySplit(lambdaNode2);
475 const auto lambdaExitMerge2 = tryGetMemoryStateExitMerge(lambdaNode2);
476 EXPECT_TRUE(lambdaEntrySplit2 && lambdaEntrySplit2->noutputs() == 1);
477 EXPECT_TRUE(lambdaExitMerge2 && lambdaExitMerge2->ninputs() == 1);
478 const auto & [callExitSplitNode, _] =
479 TryGetSimpleNodeAndOptionalOp<CallExitMemoryStateSplitOperation>(
480 *lambdaExitMerge2->input(0)->origin());
481 EXPECT_EQ(callExitSplitNode->noutputs(), 1u);
482 const auto & [callNode, calOperation] =
483 TryGetSimpleNodeAndOptionalOp<CallOperation>(*callExitSplitNode->input(0)->origin());
484 EXPECT_EQ(callNode->noutputs(), 3u);
485 EXPECT_EQ(callNode->ninputs(), 4u);
486 const auto & memoryStateInput = CallOperation::GetMemoryStateInput(*callNode);
487 const auto & [callEntryMergeNode, callEntryMergeOperation] =
488 TryGetSimpleNodeAndOptionalOp<CallEntryMemoryStateMergeOperation>(*memoryStateInput.origin());
489 EXPECT_EQ(callEntryMergeNode->ninputs(), 1u);
490 EXPECT_EQ(callEntryMergeNode->input(0)->origin(), lambdaEntrySplit2->output(0));
491}
492
493TEST(InvariantValueRedirectionTests, TestCallWithDifferentExternalCompression)
494{
495 // Arrange
496 using namespace jlm::llvm;
497 using namespace jlm::rvsdg;
498
543 // The memory node representing external has index 0
544 EXPECT_EQ(aa::PointsToGraph::externalMemoryNode, 0u);
545
546 const auto ioStateType = IOStateType::Create();
547 const auto memoryStateType = MemoryStateType::Create();
548 const auto functionType =
549 FunctionType::Create({ ioStateType, memoryStateType }, { ioStateType, memoryStateType });
550
551 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
552 auto & rvsdg = rvsdgModule->Rvsdg();
553
554 Output * callee0Output = nullptr;
555 {
556 auto lambdaNode = LambdaNode::Create(
557 rvsdg.GetRootRegion(),
558 LlvmLambdaOperation::Create(functionType, "callee0", Linkage::externalLinkage));
559
560 auto ioStateArgument = lambdaNode->GetFunctionArguments()[0];
561 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[1];
562
563 auto & lambdaEntrySplitNode =
564 LambdaEntryMemoryStateSplitOperation::CreateNode(*memoryStateArgument, { 0, 1, 3 });
565 auto modifiedExternal = TestOperation::createNode(
566 lambdaNode->subregion(),
567 { lambdaEntrySplitNode.output(0) },
568 { memoryStateType });
569 auto & lambdaExitMergeNode = LambdaExitMemoryStateMergeOperation::CreateNode(
570 *lambdaNode->subregion(),
571 { modifiedExternal->output(0),
572 lambdaEntrySplitNode.output(1),
573 lambdaEntrySplitNode.output(2) },
574 { 0, 1, 3 });
575
576 callee0Output = lambdaNode->finalize({ ioStateArgument, lambdaExitMergeNode.output(0) });
577 }
578
579 Output * callee3Output = nullptr;
580 {
581 auto lambdaNode = LambdaNode::Create(
582 rvsdg.GetRootRegion(),
583 LlvmLambdaOperation::Create(functionType, "callee3", Linkage::externalLinkage));
584
585 auto ioStateArgument = lambdaNode->GetFunctionArguments()[0];
586 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[1];
587
588 auto & lambdaEntrySplitNode =
589 LambdaEntryMemoryStateSplitOperation::CreateNode(*memoryStateArgument, { 0, 1, 3 });
590 auto modifiedMemory3 = TestOperation::createNode(
591 lambdaNode->subregion(),
592 { lambdaEntrySplitNode.output(2) },
593 { memoryStateType });
594 auto & lambdaExitMergeNode = LambdaExitMemoryStateMergeOperation::CreateNode(
595 *lambdaNode->subregion(),
596 { lambdaEntrySplitNode.output(0),
597 lambdaEntrySplitNode.output(1),
598 modifiedMemory3->output(0) },
599 { 0, 1, 3 });
600
601 callee3Output = lambdaNode->finalize({ ioStateArgument, lambdaExitMergeNode.output(0) });
602 }
603
604 SimpleNode * lambdaEntrySplitNode = nullptr;
605 SimpleNode * callEntryMergeNodeA = nullptr;
606 SimpleNode * callExitSplitNodeA = nullptr;
607 SimpleNode * callEntryMergeNodeB = nullptr;
608 SimpleNode * callExitSplitNodeB = nullptr;
609 SimpleNode * lambdaExitMergeNode = nullptr;
610 {
611 auto lambdaNode = LambdaNode::Create(
612 rvsdg.GetRootRegion(),
613 LlvmLambdaOperation::Create(functionType, "caller", Linkage::externalLinkage));
614
615 auto ioStateArgument = lambdaNode->GetFunctionArguments()[0];
616 auto memoryStateArgument = lambdaNode->GetFunctionArguments()[1];
617 auto callee0Argument = lambdaNode->AddContextVar(*callee0Output).inner;
618 auto callee3Argument = lambdaNode->AddContextVar(*callee3Output).inner;
619
620 lambdaEntrySplitNode =
621 &LambdaEntryMemoryStateSplitOperation::CreateNode(*memoryStateArgument, { 0, 1, 2 });
622
623 callEntryMergeNodeA = &CallEntryMemoryStateMergeOperation::CreateNode(
624 *lambdaNode->subregion(),
625 outputs(lambdaEntrySplitNode),
626 { 0, 1, 2 });
627 auto & callNodeA = CallOperation::CreateNode(
628 callee0Argument,
629 functionType,
630 { ioStateArgument, callEntryMergeNodeA->output(0) });
631 callExitSplitNodeA = &CallExitMemoryStateSplitOperation::CreateNode(
632 CallOperation::GetMemoryStateOutput(callNodeA),
633 { 0, 1, 2 });
634
635 callEntryMergeNodeB = &CallEntryMemoryStateMergeOperation::CreateNode(
636 *lambdaNode->subregion(),
637 outputs(callExitSplitNodeA),
638 { 0, 1, 2 });
639 auto & callNodeB = CallOperation::CreateNode(
640 callee3Argument,
641 functionType,
642 { &CallOperation::GetIOStateOutput(callNodeA), callEntryMergeNodeB->output(0) });
643 callExitSplitNodeB = &CallExitMemoryStateSplitOperation::CreateNode(
644 CallOperation::GetMemoryStateOutput(callNodeB),
645 { 0, 1, 2 });
646
647 lambdaExitMergeNode = &LambdaExitMemoryStateMergeOperation::CreateNode(
648 *lambdaNode->subregion(),
649 outputs(callExitSplitNodeB),
650 { 0, 1, 2 });
651
652 lambdaNode->finalize(
653 { &CallOperation::GetIOStateOutput(callNodeB), lambdaExitMergeNode->output(0) });
654 }
655
656 // Act
657 RunInvariantValueRedirection(*rvsdgModule);
658
659 // Assert
660 ASSERT_EQ(lambdaEntrySplitNode->noutputs(), 3u);
661 ASSERT_EQ(callEntryMergeNodeA->ninputs(), 3u);
662 ASSERT_EQ(callExitSplitNodeA->noutputs(), 3u);
663 ASSERT_EQ(callEntryMergeNodeB->ninputs(), 3u);
664 ASSERT_EQ(callExitSplitNodeB->noutputs(), 3u);
665 ASSERT_EQ(lambdaExitMergeNode->ninputs(), 3u);
666
667 // the memory state edge representing the external node has not been re-routed around anything
668 EXPECT_EQ(callEntryMergeNodeA->input(0)->origin(), lambdaEntrySplitNode->output(0));
669 EXPECT_EQ(callEntryMergeNodeB->input(0)->origin(), callExitSplitNodeA->output(0));
670 EXPECT_EQ(lambdaExitMergeNode->input(0)->origin(), callExitSplitNodeB->output(0));
671
672 // the memory state edge representing memory node 1 has been re-routed to the entry
673 EXPECT_EQ(callEntryMergeNodeA->input(1)->origin(), lambdaEntrySplitNode->output(1));
674 EXPECT_EQ(callEntryMergeNodeB->input(1)->origin(), lambdaEntrySplitNode->output(1));
675 EXPECT_EQ(lambdaExitMergeNode->input(1)->origin(), lambdaEntrySplitNode->output(1));
676
677 // the memory state edge representing memory node 2 only been re-routed around callee3
678 EXPECT_EQ(callEntryMergeNodeA->input(2)->origin(), lambdaEntrySplitNode->output(2));
679 EXPECT_EQ(callEntryMergeNodeB->input(2)->origin(), callExitSplitNodeA->output(2));
680 EXPECT_EQ(lambdaExitMergeNode->input(2)->origin(), callExitSplitNodeA->output(2));
681}
682
683TEST(InvariantValueRedirectionTests, TestLambdaCallArgumentMismatch)
684{
685 // Arrange
687
688 // Act
690
691 // Assert
692 auto & callNode = test.GetCall();
693 auto & lambdaNode = test.GetLambdaMain();
694
695 EXPECT_EQ(lambdaNode.GetFunctionResults().size(), 3u);
696 EXPECT_EQ(lambdaNode.GetFunctionResults().size(), callNode.noutputs());
697 EXPECT_EQ(lambdaNode.GetFunctionResults()[0]->origin(), callNode.output(0));
698 EXPECT_EQ(lambdaNode.GetFunctionResults()[1]->origin(), callNode.output(1));
699 EXPECT_EQ(lambdaNode.GetFunctionResults()[2]->origin(), callNode.output(2));
700}
701
702TEST(InvariantValueRedirectionTests, testThetaGammaRedirection)
703{
704 // Arrange
705 using namespace jlm::llvm;
706 using namespace jlm::rvsdg;
707
708 auto valueType = TestType::createValueType();
709 auto controlType = ControlType::Create(2);
710 const auto functionType = FunctionType::Create({ valueType, valueType }, { valueType });
711
712 auto rvsdgModule = jlm::llvm::LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
713 auto & rvsdg = rvsdgModule->Rvsdg();
714
715 auto lambdaNode = LambdaNode::Create(
716 rvsdg.GetRootRegion(),
717 LlvmLambdaOperation::Create(functionType, "test", Linkage::externalLinkage));
718
719 auto functionArgument0 = lambdaNode->GetFunctionArguments()[0];
720 auto functionArgument1 = lambdaNode->GetFunctionArguments()[1];
721
722 auto thetaNode = ThetaNode::create(lambdaNode->subregion());
723 auto loopVar0 = thetaNode->AddLoopVar(functionArgument0);
724 auto loopVar1 = thetaNode->AddLoopVar(functionArgument1);
725
726 auto dummyNodeTheta = TestOperation::createNode(thetaNode->subregion(), {}, { valueType });
727
728 auto predicate =
729 TestOperation::createNode(thetaNode->subregion(), {}, { controlType })->output(0);
730 auto gammaNode = GammaNode::create(predicate, 2);
731 auto entryVar0 = gammaNode->AddEntryVar(loopVar0.pre);
732 auto entryVar1 = gammaNode->AddEntryVar(dummyNodeTheta->output(0));
733
734 auto dummyNodeGamma0 = TestOperation::createNode(gammaNode->subregion(0), {}, { valueType });
735 auto dummyNodeGamma1 = TestOperation::createNode(gammaNode->subregion(1), {}, { valueType });
736
737 auto controlConstant0 =
738 &ControlConstantOperation::create(*gammaNode->subregion(0), ControlValueRepresentation(0, 2));
739 auto controlConstant1 =
740 &ControlConstantOperation::create(*gammaNode->subregion(1), ControlValueRepresentation(1, 2));
741
742 auto controlExitVar = gammaNode->AddExitVar({ controlConstant0, controlConstant1 });
743 auto exitVar0 =
744 gammaNode->AddExitVar({ dummyNodeGamma0->output(0), entryVar0.branchArgument[1] });
745 auto exitVar1 =
746 gammaNode->AddExitVar({ entryVar1.branchArgument[0], dummyNodeGamma1->output(0) });
747
748 thetaNode->predicate()->divert_to(controlExitVar.output);
749 loopVar0.post->divert_to(exitVar0.output);
750 loopVar1.post->divert_to(exitVar1.output);
751
752 auto lambdaOutput = lambdaNode->finalize({ loopVar1.output });
753
754 GraphExport::Create(*lambdaOutput, "test");
755
756 // Act
757 RunInvariantValueRedirection(*rvsdgModule);
758
759 // Assert
760 // We expect that the post value of both loop variables does not originate from the gamma any
761 // longer.
762 auto loopVars = thetaNode->GetLoopVars();
763 EXPECT_EQ(loopVars.size(), 2u);
764
765 // Loop variable 0 was dead after the loop, which means it is irrelevant what happens to it in
766 // the last iteration of the loop. As the loop predicate originates from a control constant in
767 // one of the gamma nodes' subregions, the loop variables' value is always the same as the one
768 // from the gamma subregion with control constant 1 (i.e. loop repetition). This means we could
769 // redirect the loop variable from the gamma to the respective entry variables' origin.
770 EXPECT_EQ(loopVars[0].post->origin(), loopVars[0].pre);
771
772 // Loop variable 1 was dead at the beginning of each loop iteration, which means it is irrelevant
773 // what happens to it except in the last iteration of the loop. As the loop predicate originates
774 // from a control constant in a one of the gamma nodes' subregions, the loop variables' value is
775 // always the same as the one from the gamma subregion with control constant 0 (i.e. loop exit).
776 // This means we could redirect the loop variable from the gamma to the respective entry
777 // variables' origin.
778 EXPECT_EQ(loopVars[1].post->origin(), dummyNodeTheta->output(0));
779}
780
781TEST(InvariantValueRedirectionTests, testLoadWithDeadLoadedValue)
782{
783 // Arrange
784 using namespace jlm::llvm;
785 using namespace jlm::rvsdg;
786
787 const auto valueType = TestType::createValueType();
788 const auto pointerType = PointerType::Create();
789 const auto memoryStateType = MemoryStateType::Create();
790 const auto functionType = FunctionType::Create(
791 { pointerType, memoryStateType, memoryStateType },
792 { memoryStateType, memoryStateType });
793
794 auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
795 auto & rvsdg = rvsdgModule->Rvsdg();
796
797 auto lambdaNode = LambdaNode::Create(
798 rvsdg.GetRootRegion(),
799 LlvmLambdaOperation::Create(functionType, "test", Linkage::externalLinkage));
800
801 auto addressArgument = lambdaNode->GetFunctionArguments()[0];
802 auto memoryStateArgument1 = lambdaNode->GetFunctionArguments()[1];
803 auto memoryStateArgument2 = lambdaNode->GetFunctionArguments()[2];
804
805 auto & loadNode = LoadNonVolatileOperation::CreateNode(
806 *addressArgument,
807 { memoryStateArgument1, memoryStateArgument2 },
808 valueType,
809 4);
810
811 auto lambdaOutput = lambdaNode->finalize({ loadNode.output(1), loadNode.output(2) });
812
813 GraphExport::Create(*lambdaOutput, "test");
814
815 // Act
816 RunInvariantValueRedirection(*rvsdgModule);
817
818 // Assert
819 // We expect that the users of the memory state outputs of the load node were redirected to the
820 // origins of the respective inputs, which in turn rendered the load node dead. Consequently, it
821 // was pruned from the lambda subregion.
822 EXPECT_EQ(lambdaNode->subregion()->numNodes(), 0u);
823 EXPECT_EQ(lambdaNode->GetFunctionResults()[0]->origin(), memoryStateArgument1);
824 EXPECT_EQ(lambdaNode->GetFunctionResults()[1]->origin(), memoryStateArgument2);
825}
static jlm::util::StatisticsCollector statisticsCollector
TEST(InvariantValueRedirectionTests, TestGamma)
static void RunInvariantValueRedirection(jlm::llvm::LlvmRvsdgModule &rvsdgModule)
void Run(rvsdg::RvsdgModule &module, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
RVSDG module containing a static function that is called with the wrong number of arguments.
const rvsdg::SimpleNode & GetCall() const noexcept
const rvsdg::LambdaNode & GetLambdaMain() const noexcept
static std::unique_ptr< LlvmLambdaOperation > Create(std::shared_ptr< const jlm::rvsdg::FunctionType > type, std::string name, const jlm::llvm::Linkage &linkage, jlm::llvm::CallingConvention callingConvention, jlm::llvm::AttributeSet attributes)
Definition lambda.hpp:84
static std::unique_ptr< LlvmRvsdgModule > Create(const util::FilePath &sourceFileName, const std::string &targetTriple, const std::string &dataLayout)
jlm::llvm::LlvmRvsdgModule & module()
static Output & create(Region &region, ControlValueRepresentation value)
Definition control.hpp:122
static std::shared_ptr< const ControlType > Create(std::size_t nalternatives)
Instantiates control type.
Definition control.cpp:50
static std::shared_ptr< const FunctionType > Create(std::vector< std::shared_ptr< const jlm::rvsdg::Type > > argumentTypes, std::vector< std::shared_ptr< const jlm::rvsdg::Type > > resultTypes)
static GammaNode * create(jlm::rvsdg::Output *predicate, size_t nalternatives)
Definition gamma.hpp:161
static GraphExport & Create(Output &origin, std::string name)
Definition graph.cpp:62
Output * origin() const noexcept
Definition node.hpp:58
static LambdaNode * Create(rvsdg::Region &parent, std::unique_ptr< LambdaOperation > operation)
Definition lambda.cpp:141
size_t ninputs() const noexcept
Definition node.hpp:609
size_t noutputs() const noexcept
Definition node.hpp:644
Graph & Rvsdg() noexcept
NodeInput * input(size_t index) const noexcept
NodeOutput * output(size_t index) const noexcept
static std::shared_ptr< const TestType > createValueType()
Definition TestType.cpp:67
static ThetaNode * create(rvsdg::Region *parent)
Definition theta.hpp:73
Global memory state passed between functions.
std::string view(const rvsdg::Region *region)
Definition view.cpp:142
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872