Jlm
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 
18 #include <jlm/llvm/TestRvsdgs.hpp>
19 #include <jlm/rvsdg/control.hpp>
20 #include <jlm/rvsdg/gamma.hpp>
22 #include <jlm/rvsdg/TestType.hpp>
23 #include <jlm/rvsdg/theta.hpp>
24 #include <jlm/rvsdg/view.hpp>
25 #include <jlm/util/Statistics.hpp>
26 
27 static 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 
40 TEST(InvariantValueRedirectionTests, TestGamma)
41 {
42  using namespace jlm::llvm;
43 
44  // Arrange
45  auto valueType = jlm::rvsdg::TestType::createValueType();
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(),
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 
88 TEST(InvariantValueRedirectionTests, TestTheta)
89 {
90  // Arrange
91  using namespace jlm::llvm;
92 
93  auto ioStateType = IOStateType::Create();
94  auto valueType = jlm::rvsdg::TestType::createValueType();
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(),
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 
139 TEST(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 
226 TEST(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
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(
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),
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 
372 TEST(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
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(),
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(
446  { 1 });
447 
448  auto & lambdaExitMergeNode = LambdaExitMemoryStateMergeOperation::CreateNode(
449  *lambdaNode->subregion(),
450  outputs(&callExitSplitNode),
451  { 1 });
452 
453  lambdaOutputTest2 = lambdaNode->finalize({ callNode.output(0),
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 
493 TEST(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
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(
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(
645  { 0, 1, 2 });
646 
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(), 3);
661  ASSERT_EQ(callEntryMergeNodeA->ninputs(), 3);
662  ASSERT_EQ(callExitSplitNodeA->noutputs(), 3);
663  ASSERT_EQ(callEntryMergeNodeB->ninputs(), 3);
664  ASSERT_EQ(callExitSplitNodeB->noutputs(), 3);
665  ASSERT_EQ(lambdaExitMergeNode->ninputs(), 3);
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 
683 TEST(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 
702 TEST(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(),
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 
781 TEST(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(),
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)
static std::vector< rvsdg::Output * > create(std::shared_ptr< const rvsdg::Type > allocatedType, rvsdg::Output *count, const size_t alignment)
Definition: alloca.hpp:131
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, const std::vector< rvsdg::Output * > &operands, std::vector< MemoryNodeId > memoryNodeIds)
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &operand, std::vector< MemoryNodeId > memoryNodeIds)
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< CallOperation > callOperation, const std::vector< rvsdg::Output * > &operands)
Definition: call.hpp:489
static rvsdg::Input & GetMemoryStateInput(const rvsdg::Node &node) noexcept
Definition: call.hpp:357
static rvsdg::Output & GetIOStateOutput(const rvsdg::Node &node) noexcept
Definition: call.hpp:345
static rvsdg::Output & GetMemoryStateOutput(const rvsdg::Node &node) noexcept
Definition: call.hpp:369
static std::shared_ptr< const IOStateType > Create()
Definition: types.cpp:343
static rvsdg::Node & Create(rvsdg::Region &region, IntegerValueRepresentation representation)
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 rvsdg::SimpleNode & CreateNode(rvsdg::Output &operand, std::vector< MemoryNodeId > memoryNodeIds)
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, const std::vector< rvsdg::Output * > &operands, const std::vector< MemoryNodeId > &memoryNodeIds)
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)
static rvsdg::SimpleNode & CreateNode(rvsdg::Region &region, std::unique_ptr< LoadNonVolatileOperation > loadOperation, const std::vector< rvsdg::Output * > &operands)
Definition: Load.hpp:466
static std::shared_ptr< const MemoryStateType > Create()
Definition: types.cpp:379
static std::shared_ptr< const PointerType > Create()
Definition: types.cpp:45
jlm::llvm::LlvmRvsdgModule & module()
Definition: TestRvsdgs.hpp:34
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, rvsdg::Output &value, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition: Store.hpp:323
static constexpr NodeIndex externalMemoryNode
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:140
size_t ninputs() const noexcept
Definition: node.hpp:609
size_t noutputs() const noexcept
Definition: node.hpp:644
Graph & Rvsdg() noexcept
Definition: RvsdgModule.hpp:57
NodeInput * input(size_t index) const noexcept
Definition: simple-node.hpp:82
NodeOutput * output(size_t index) const noexcept
Definition: simple-node.hpp:88
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.
rvsdg::SimpleNode * tryGetMemoryStateEntrySplit(const rvsdg::LambdaNode &lambdaNode) noexcept
rvsdg::SimpleNode * tryGetMemoryStateExitMerge(const rvsdg::LambdaNode &lambdaNode) noexcept
std::string view(const rvsdg::Region *region)
Definition: view.cpp:142
static std::vector< jlm::rvsdg::Output * > outputs(const Node *node)
Definition: node.hpp:1058