Jlm
UnusedStateRemovalTests.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2023 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #include <gtest/gtest.h>
7 
13 #include <jlm/rvsdg/control.hpp>
14 #include <jlm/rvsdg/gamma.hpp>
16 #include <jlm/rvsdg/TestType.hpp>
17 #include <jlm/rvsdg/theta.hpp>
18 #include <jlm/rvsdg/view.hpp>
19 
20 TEST(UnusedStateRemovalTests, TestGamma)
21 {
22  using namespace jlm::llvm;
23 
24  // Arrange
25  auto valueType = jlm::rvsdg::TestType::createValueType();
26 
27  auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
28  auto & rvsdg = rvsdgModule->Rvsdg();
29 
31  auto x = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "x");
32  auto y = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "y");
33  auto z = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "z");
34 
35  auto gammaNode = jlm::rvsdg::GammaNode::create(p, 2);
36 
37  auto gammaInput1 = gammaNode->AddEntryVar(x);
38  auto gammaInput2 = gammaNode->AddEntryVar(y);
39  auto gammaInput3 = gammaNode->AddEntryVar(z);
40  auto gammaInput4 = gammaNode->AddEntryVar(x);
41  auto gammaInput5 = gammaNode->AddEntryVar(x);
42  auto gammaInput6 = gammaNode->AddEntryVar(x);
43  auto gammaInput7 = gammaNode->AddEntryVar(x);
44 
45  auto gammaOutput1 = gammaNode->AddExitVar(gammaInput1.branchArgument);
46  auto gammaOutput2 =
47  gammaNode->AddExitVar({ gammaInput2.branchArgument[0], gammaInput3.branchArgument[1] });
48  auto gammaOutput3 =
49  gammaNode->AddExitVar({ gammaInput4.branchArgument[0], gammaInput5.branchArgument[1] });
50  auto gammaOutput4 =
51  gammaNode->AddExitVar({ gammaInput6.branchArgument[0], gammaInput6.branchArgument[1] });
52  auto gammaOutput5 =
53  gammaNode->AddExitVar({ gammaInput6.branchArgument[0], gammaInput7.branchArgument[1] });
54 
55  jlm::rvsdg::GraphExport::Create(*gammaOutput1.output, "");
56  jlm::rvsdg::GraphExport::Create(*gammaOutput2.output, "");
57  jlm::rvsdg::GraphExport::Create(*gammaOutput3.output, "");
58  jlm::rvsdg::GraphExport::Create(*gammaOutput4.output, "");
59  jlm::rvsdg::GraphExport::Create(*gammaOutput5.output, "");
60 
61  // Act
64 
65  // Assert
66  EXPECT_EQ(gammaNode->ninputs(), 7); // gammaInput1 was removed
67  EXPECT_EQ(gammaNode->noutputs(), 4); // gammaOutput1 was removed
68  EXPECT_EQ(gammaInput2.input->index(), 1);
69  EXPECT_EQ(gammaOutput2.output->index(), 0);
70  // FIXME: The transformation is way too conservative here. The only input and output it removes
71  // are gammaInput1 and gammaOutput1, respectively. However, it could also remove gammaOutput3,
72  // gammaOutput4, and gammaOutput5 as they are all invariant. This in turn would also render some
73  // more inputs dead.
74 }
75 
76 TEST(UnusedStateRemovalTests, TestTheta)
77 {
78  using namespace jlm::llvm;
79  using namespace jlm::rvsdg;
80 
81  // Arrange
82  auto valueType = jlm::rvsdg::TestType::createValueType();
83  auto functionType = FunctionType::Create(
84  { ControlType::Create(2), valueType, valueType, valueType },
85  { valueType });
86 
87  auto rvsdgModule = jlm::llvm::LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
88  auto & rvsdg = rvsdgModule->Rvsdg();
89 
90  auto importP = &jlm::rvsdg::GraphImport::Create(rvsdg, ControlType::Create(2), "p");
91  auto importX = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "x");
92  auto importY = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "y");
93  auto importZ = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "z");
94 
95  auto thetaNode = ThetaNode::create(&rvsdg.GetRootRegion());
96 
97  auto loopVarP = thetaNode->AddLoopVar(importP);
98  auto loopVarX = thetaNode->AddLoopVar(importX);
99  auto loopVarY = thetaNode->AddLoopVar(importY);
100  auto loopVarZ = thetaNode->AddLoopVar(importZ);
101 
102  loopVarY.post->divert_to(loopVarZ.pre);
103  loopVarZ.post->divert_to(loopVarY.pre);
104  thetaNode->set_predicate(loopVarP.pre);
105 
106  auto & exportP = GraphExport::Create(*loopVarP.output, "p");
107  auto & exportX = GraphExport::Create(*loopVarX.output, "x");
108  auto & exportY = GraphExport::Create(*loopVarY.output, "y");
109  auto & exportZ = GraphExport::Create(*loopVarZ.output, "z");
110 
111  // Act
114 
115  // Assert
116  EXPECT_EQ(thetaNode->ninputs(), 3);
117  EXPECT_EQ(thetaNode->noutputs(), 3);
118 
119  EXPECT_EQ(TryGetOwnerNode<ThetaNode>(*exportP.origin()), thetaNode);
120  EXPECT_EQ(exportX.origin(), importX);
121  EXPECT_EQ(TryGetOwnerNode<ThetaNode>(*exportY.origin()), thetaNode);
122  EXPECT_EQ(TryGetOwnerNode<ThetaNode>(*exportZ.origin()), thetaNode);
123 }
124 
125 TEST(UnusedStateRemovalTests, TestLambda)
126 {
127  using namespace jlm::llvm;
128  using namespace jlm::rvsdg;
129 
130  // Arrange
131  auto valueType = jlm::rvsdg::TestType::createValueType();
132  auto functionType = jlm::rvsdg::FunctionType::Create(
133  { valueType, valueType },
134  { valueType, valueType, valueType, valueType });
135 
136  auto rvsdgModule = jlm::llvm::LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
137  auto & rvsdg = rvsdgModule->Rvsdg();
138 
139  auto x = &jlm::rvsdg::GraphImport::Create(rvsdg, valueType, "x");
140 
141  auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
142  rvsdg.GetRootRegion(),
144  auto argument0 = lambdaNode->GetFunctionArguments()[0];
145  auto argument1 = lambdaNode->GetFunctionArguments()[1];
146  auto argument2 = lambdaNode->AddContextVar(*x).inner;
147  auto argument3 = lambdaNode->AddContextVar(*x).inner;
148 
149  auto result1 = jlm::rvsdg::CreateOpNode<TestOperation>(
150  { argument1 },
151  std::vector<std::shared_ptr<const Type>>{ valueType },
152  std::vector<std::shared_ptr<const Type>>{ valueType })
153  .output(0);
154 
155  auto result3 = jlm::rvsdg::CreateOpNode<TestOperation>(
156  { argument3 },
157  std::vector<std::shared_ptr<const Type>>{ valueType },
158  std::vector<std::shared_ptr<const Type>>{ valueType })
159  .output(0);
160 
161  auto lambdaOutput = lambdaNode->finalize({ argument0, result1, argument2, result3 });
162 
163  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "f");
164 
165  // Act
168 
169  // Assert
170  EXPECT_EQ(rvsdg.GetRootRegion().numNodes(), 1);
171  auto & newLambdaNode =
172  dynamic_cast<const jlm::rvsdg::LambdaNode &>(*rvsdg.GetRootRegion().Nodes().begin());
173  EXPECT_EQ(newLambdaNode.ninputs(), 2);
174  EXPECT_EQ(newLambdaNode.subregion()->narguments(), 3);
175  EXPECT_EQ(newLambdaNode.subregion()->nresults(), 2);
176  // FIXME For lambdas, the transformation has the following issues:
177  // 1. It works only for lambda nodes in the root region. It throws an assert for all other lambdas
178  // 2. It does not check whether the lambda is only exported. Removing passthrough values works
179  // only for lambda nodes that do not have any calls.
180  // 3. It removes all pass through values, regardless of whether they are value or state types.
181  // Removing value types does change the signature of a lambda node.
182  // 4. It does not remove the arguments and inputs of context variables that are just passed
183  // through. It only renders them dead.
184  //
185  // There might be more issues.
186 }
187 
188 TEST(UnusedStateRemovalTests, TestUsedMemoryState)
189 {
190  using namespace jlm::llvm;
191  using namespace jlm::hls;
192 
193  auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
194 
195  // Setup the function
196  std::cout << "Function Setup" << std::endl;
197  auto functionType = jlm::rvsdg::FunctionType::Create(
200 
201  auto lambda = jlm::rvsdg::LambdaNode::Create(
202  rvsdgModule->Rvsdg().GetRootRegion(),
204 
205  // Load node
206  auto functionArguments = lambda->GetFunctionArguments();
207  auto loadOutput = LoadNonVolatileOperation::Create(
208  functionArguments[0],
209  { functionArguments[1] },
211  32);
212 
213  auto lambdaOutput = lambda->finalize({ loadOutput[1] });
214  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "f");
215 
216  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
217 
218  // Act
220  UnusedStateRemoval::CreateAndRun(*rvsdgModule, statisticsCollector);
221 
222  // Assert
223  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
224  auto * node = jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::Node>(
225  *rvsdgModule->Rvsdg().GetRootRegion().result(0)->origin());
226  auto lambdaSubregion = jlm::util::assertedCast<jlm::rvsdg::LambdaNode>(node)->subregion();
227  EXPECT_EQ(lambdaSubregion->nresults(), 1);
228  EXPECT_TRUE(is<MemoryStateType>(lambdaSubregion->result(0)->Type()));
229 }
230 
231 TEST(UnusedStateRemovalTests, TestUnusedMemoryState)
232 {
233  using namespace jlm::llvm;
234  using namespace jlm::hls;
235 
236  auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
237 
238  // Setup the function
239  std::cout << "Function Setup" << std::endl;
240  auto functionType = jlm::rvsdg::FunctionType::Create(
243 
244  auto lambda = jlm::rvsdg::LambdaNode::Create(
245  rvsdgModule->Rvsdg().GetRootRegion(),
247 
248  // Load node
249  auto functionArguments = lambda->GetFunctionArguments();
250  auto loadOutput = LoadNonVolatileOperation::Create(
251  functionArguments[0],
252  { functionArguments[1] },
254  32);
255 
256  auto lambdaOutput = lambda->finalize({ loadOutput[1], functionArguments[2] });
257  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "f");
258 
259  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
260 
261  // Act
263  UnusedStateRemoval::CreateAndRun(*rvsdgModule, statisticsCollector);
264 
265  // Assert
266  auto * node = jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::Node>(
267  *rvsdgModule->Rvsdg().GetRootRegion().result(0)->origin());
268  auto lambdaSubregion = jlm::util::assertedCast<jlm::rvsdg::LambdaNode>(node)->subregion();
269  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
270  EXPECT_EQ(lambdaSubregion->narguments(), 2);
271  EXPECT_EQ(lambdaSubregion->nresults(), 1);
272  EXPECT_TRUE(is<MemoryStateType>(lambdaSubregion->result(0)->Type()));
273 }
274 
275 TEST(UnusedStateRemovalTests, TestInvariantMemoryState)
276 {
277  using namespace jlm::llvm;
278  using namespace jlm::hls;
279 
280  auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
281 
282  // Setup the function
283  std::cout << "Function Setup" << std::endl;
284  auto functionType = jlm::rvsdg::FunctionType::Create(
287 
288  auto lambda = jlm::rvsdg::LambdaNode::Create(
289  rvsdgModule->Rvsdg().GetRootRegion(),
291 
292  auto functionArguments = lambda->GetFunctionArguments();
293 
294  // LambdaEntryMemoryStateSplit node
295  auto & memoryStateSplitNode =
296  LambdaEntryMemoryStateSplitOperation::CreateNode(*functionArguments[1], { 0, 1 });
297 
298  // Load node
299  auto loadOutput = LoadNonVolatileOperation::Create(
300  functionArguments[0],
301  { memoryStateSplitNode.output(0) },
303  32);
304 
305  // LambdaExitMemoryStateMerge node
306  std::vector<jlm::rvsdg::Output *> outputs;
307  auto & memoryStateMerge = LambdaExitMemoryStateMergeOperation::CreateNode(
308  *lambda->subregion(),
309  { loadOutput[1], memoryStateSplitNode.output(1) },
310  { 0, 1 });
311 
312  auto lambdaOutput = lambda->finalize({ memoryStateMerge.output(0) });
313  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "f");
314 
315  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
316 
317  // Act
318  // This pass should have no effect on the graph
320  UnusedStateRemoval::CreateAndRun(*rvsdgModule, statisticsCollector);
321 
322  // Assert
323  auto * node = jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::Node>(
324  *rvsdgModule->Rvsdg().GetRootRegion().result(0)->origin());
325  auto lambdaSubregion = jlm::util::assertedCast<jlm::rvsdg::LambdaNode>(node)->subregion();
326  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
327  EXPECT_EQ(lambdaSubregion->narguments(), 2);
328  EXPECT_EQ(lambdaSubregion->nresults(), 1);
329  EXPECT_TRUE(is<MemoryStateType>(lambdaSubregion->result(0)->Type()));
330  EXPECT_TRUE(jlm::rvsdg::Region::ContainsOperation<LambdaEntryMemoryStateSplitOperation>(
331  *lambdaSubregion,
332  true));
333  EXPECT_TRUE(jlm::rvsdg::Region::ContainsOperation<LambdaExitMemoryStateMergeOperation>(
334  *lambdaSubregion,
335  true));
336 }
static jlm::util::StatisticsCollector statisticsCollector
TEST(UnusedStateRemovalTests, TestGamma)
static void CreateAndRun(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector)
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 std::unique_ptr< llvm::ThreeAddressCode > Create(const Variable *address, const Variable *state, std::shared_ptr< const rvsdg::Type > loadedType, size_t alignment)
Definition: Load.hpp:444
static std::shared_ptr< const MemoryStateType > Create()
Definition: types.cpp:379
static std::shared_ptr< const PointerType > Create()
Definition: types.cpp:45
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
static GraphImport & Create(Graph &graph, std::shared_ptr< const rvsdg::Type > type, std::string name)
Definition: graph.cpp:36
Lambda node.
Definition: lambda.hpp:83
static LambdaNode * Create(rvsdg::Region &parent, std::unique_ptr< LambdaOperation > operation)
Definition: lambda.cpp:140
static std::shared_ptr< const TestType > createValueType()
Definition: TestType.cpp:67
Global memory state passed between functions.
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