Jlm
Loading...
Searching...
No Matches
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>
17#include <jlm/rvsdg/theta.hpp>
18#include <jlm/rvsdg/view.hpp>
19
20TEST(UnusedStateRemovalTests, TestGamma)
21{
22 using namespace jlm::llvm;
23
24 // Arrange
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(), 7u); // gammaInput1 was removed
67 EXPECT_EQ(gammaNode->noutputs(), 4u); // gammaOutput1 was removed
68 EXPECT_EQ(gammaInput2.input->index(), 1u);
69 EXPECT_EQ(gammaOutput2.output->index(), 0u);
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
76TEST(UnusedStateRemovalTests, TestTheta)
77{
78 using namespace jlm::llvm;
79 using namespace jlm::rvsdg;
80
81 // Arrange
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(), 3u);
117 EXPECT_EQ(thetaNode->noutputs(), 3u);
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
125TEST(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(),
143 LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
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
150 { argument1 },
151 std::vector<std::shared_ptr<const Type>>{ valueType },
152 std::vector<std::shared_ptr<const Type>>{ valueType })
153 .output(0);
154
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(), 1u);
171 auto & newLambdaNode =
172 dynamic_cast<const jlm::rvsdg::LambdaNode &>(*rvsdg.GetRootRegion().Nodes().begin());
173 EXPECT_EQ(newLambdaNode.ninputs(), 2u);
174 EXPECT_EQ(newLambdaNode.subregion()->narguments(), 3u);
175 EXPECT_EQ(newLambdaNode.subregion()->nresults(), 2u);
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
188TEST(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(
198 { jlm::llvm::PointerType::Create(), MemoryStateType::Create() },
199 { MemoryStateType::Create() });
200
201 auto lambda = jlm::rvsdg::LambdaNode::Create(
202 rvsdgModule->Rvsdg().GetRootRegion(),
203 LlvmLambdaOperation::Create(functionType, "test", Linkage::externalLinkage));
204
205 // Load node
206 auto functionArguments = lambda->GetFunctionArguments();
207 auto loadOutput = LoadNonVolatileOperation::Create(
208 functionArguments[0],
209 { functionArguments[1] },
210 PointerType::Create(),
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);
225 *rvsdgModule->Rvsdg().GetRootRegion().result(0)->origin());
226 auto lambdaSubregion = jlm::util::assertedCast<jlm::rvsdg::LambdaNode>(node)->subregion();
227 EXPECT_EQ(lambdaSubregion->nresults(), 1u);
228 EXPECT_TRUE(is<MemoryStateType>(lambdaSubregion->result(0)->Type()));
229}
230
231TEST(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(
241 { jlm::llvm::PointerType::Create(), MemoryStateType::Create(), MemoryStateType::Create() },
242 { MemoryStateType::Create(), MemoryStateType::Create() });
243
244 auto lambda = jlm::rvsdg::LambdaNode::Create(
245 rvsdgModule->Rvsdg().GetRootRegion(),
246 LlvmLambdaOperation::Create(functionType, "test", Linkage::externalLinkage));
247
248 // Load node
249 auto functionArguments = lambda->GetFunctionArguments();
250 auto loadOutput = LoadNonVolatileOperation::Create(
251 functionArguments[0],
252 { functionArguments[1] },
253 PointerType::Create(),
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
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(), 2u);
271 EXPECT_EQ(lambdaSubregion->nresults(), 1u);
272 EXPECT_TRUE(is<MemoryStateType>(lambdaSubregion->result(0)->Type()));
273}
274
275TEST(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(
285 { jlm::llvm::PointerType::Create(), MemoryStateType::Create() },
286 { MemoryStateType::Create() });
287
288 auto lambda = jlm::rvsdg::LambdaNode::Create(
289 rvsdgModule->Rvsdg().GetRootRegion(),
290 LlvmLambdaOperation::Create(functionType, "test", Linkage::externalLinkage));
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) },
302 PointerType::Create(),
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
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(), 2u);
328 EXPECT_EQ(lambdaSubregion->nresults(), 1u);
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 std::unique_ptr< LlvmRvsdgModule > Create(const util::FilePath &sourceFileName, const std::string &targetTriple, const std::string &dataLayout)
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
static LambdaNode * Create(rvsdg::Region &parent, std::unique_ptr< LambdaOperation > operation)
Definition lambda.cpp:141
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
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872