Jlm
DistributeConstantsTests.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2025 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #include <gtest/gtest.h>
7 
12 #include <jlm/rvsdg/gamma.hpp>
14 #include <jlm/rvsdg/theta.hpp>
15 #include <jlm/rvsdg/view.hpp>
16 #include <jlm/util/Statistics.hpp>
17 
18 TEST(DistributeConstantsTests, GammaSubregionUsage)
19 {
20  using namespace jlm::hls;
21  using namespace jlm::llvm;
22  using namespace jlm::rvsdg;
23  using namespace jlm::util;
24 
25  // Arrange
26  auto controlType = ControlType::Create(3);
27  auto bit32Type = BitType::Create(32);
28  auto functionType = FunctionType::Create({ controlType }, { bit32Type });
29 
30  jlm::llvm::LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
31  auto & rvsdg = rvsdgModule.Rvsdg();
32 
33  auto lambdaNode = LambdaNode::Create(
34  rvsdg.GetRootRegion(),
35  LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
36  auto controlArgument = lambdaNode->GetFunctionArguments()[0];
37 
38  auto & constantNode = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 5);
39 
40  auto gammaNode = GammaNode::create(controlArgument, 3);
41  auto entryVariable = gammaNode->AddEntryVar(constantNode.output(0));
42 
43  auto testNode0 = TestOperation::createNode(
44  gammaNode->subregion(0),
45  { entryVariable.branchArgument[0] },
46  { bit32Type });
47 
48  auto testNode1 = TestOperation::createNode(
49  gammaNode->subregion(1),
50  { entryVariable.branchArgument[1] },
51  { bit32Type });
52 
53  auto exitVariable = gammaNode->AddExitVar(
54  { testNode0->output(0), testNode1->output(0), entryVariable.branchArgument[2] });
55 
56  auto lambdaOutput = lambdaNode->finalize({ exitVariable.output });
57 
58  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "");
59 
60  view(rvsdg, stdout);
61 
62  // Act
65  view(rvsdg, stdout);
66 
67  // Assert
68  EXPECT_EQ(lambdaNode->subregion()->numNodes(), 2);
69 
70  {
71  // check subregion 0 - we expect the constantNode to be distributed into this subregion
72  EXPECT_EQ(gammaNode->subregion(0)->numNodes(), 2);
73  EXPECT_TRUE(IsOwnerNodeOperation<IntegerConstantOperation>(*testNode0->input(0)->origin()));
74  }
75 
76  {
77  // check subregion 1 - we expect the constantNode to be distributed into this subregion
78  EXPECT_EQ(gammaNode->subregion(1)->numNodes(), 2);
79  EXPECT_TRUE(IsOwnerNodeOperation<IntegerConstantOperation>(*testNode1->input(0)->origin()));
80  }
81 
82  {
83  // check subregion 2 - we expect the constantNode to be distributed into this subregion
84  EXPECT_EQ(gammaNode->subregion(2)->numNodes(), 1);
85  EXPECT_TRUE(
86  IsOwnerNodeOperation<IntegerConstantOperation>(*exitVariable.branchResult[2]->origin()));
87  }
88 }
89 
90 TEST(DistributeConstantsTests, NestedGammas)
91 {
92  using namespace jlm::hls;
93  using namespace jlm::llvm;
94  using namespace jlm::rvsdg;
95  using namespace jlm::util;
96 
97  // Arrange
98  auto controlType = ControlType::Create(2);
99  auto bit32Type = BitType::Create(32);
100  auto functionType = FunctionType::Create({ controlType }, { bit32Type });
101 
102  jlm::llvm::LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
103  auto & rvsdg = rvsdgModule.Rvsdg();
104 
105  auto lambdaNode = LambdaNode::Create(
106  rvsdg.GetRootRegion(),
107  LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
108  auto controlArgument = lambdaNode->GetFunctionArguments()[0];
109 
110  auto & constantNode = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 5);
111 
112  auto gammaNodeOuter = GammaNode::create(controlArgument, 2);
113  auto entryVarConstant = gammaNodeOuter->AddEntryVar(constantNode.output(0));
114 
115  // gammaNodeOuter subregion 0
116  auto testNode0 = TestOperation::createNode(
117  gammaNodeOuter->subregion(0),
118  { entryVarConstant.branchArgument[0] },
119  { bit32Type });
120 
121  // gammaNodeOuter subregion 1
122  auto controlConstant = &ControlConstantOperation::create(*gammaNodeOuter->subregion(1), 2, 0);
123  auto gammaNodeInner = GammaNode::create(controlConstant, 2);
124  auto entryVariable = gammaNodeInner->AddEntryVar(entryVarConstant.branchArgument[1]);
125  auto exitVariableInner = gammaNodeInner->AddExitVar(
126  { entryVariable.branchArgument[0], entryVariable.branchArgument[1] });
127 
128  auto exitVariableOuter =
129  gammaNodeOuter->AddExitVar({ testNode0->output(0), exitVariableInner.output });
130 
131  auto testNode1 = TestOperation::createNode(
132  lambdaNode->subregion(),
133  { exitVariableOuter.output },
134  { bit32Type });
135 
136  auto lambdaOutput = lambdaNode->finalize({ testNode1->output(0) });
137 
138  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "");
139 
140  view(rvsdg, stdout);
141 
142  // Act
145  view(rvsdg, stdout);
146 
147  // Assert
148  EXPECT_EQ(lambdaNode->subregion()->numNodes(), 3);
149 
150  {
151  // check gammaNodeOuter subregion 0
152  EXPECT_EQ(gammaNodeOuter->subregion(0)->numNodes(), 2);
153  EXPECT_TRUE(IsOwnerNodeOperation<IntegerConstantOperation>(*testNode0->input(0)->origin()));
154  }
155 
156  {
157  // check gammaNodeOuter subregion 1
158  // The constantNode was copied into this region (even though it does not have a user), so we
159  // expect one more node than before the transformation.
160  EXPECT_EQ(gammaNodeOuter->subregion(1)->numNodes(), 3);
161 
162  {
163  // check gammaNodeInner subregion 0
164  EXPECT_EQ(gammaNodeInner->subregion(0)->numNodes(), 0);
165  }
166 
167  {
168  // check gammaNodeInner subregion 1
169  EXPECT_EQ(gammaNodeInner->subregion(1)->numNodes(), 0);
170  }
171  }
172 
173  EXPECT_TRUE(TryGetOwnerNode<GammaNode>(*testNode1->input(0)->origin()));
174 }
175 
176 TEST(DistributeConstantsTests, Theta)
177 {
178  using namespace jlm::hls;
179  using namespace jlm::llvm;
180  using namespace jlm::rvsdg;
181  using namespace jlm::util;
182 
183  // Arrange
184  auto controlType = ControlType::Create(3);
185  auto bit32Type = BitType::Create(32);
186  auto functionType = FunctionType::Create({}, { bit32Type, bit32Type, bit32Type });
187 
188  jlm::llvm::LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
189  auto & rvsdg = rvsdgModule.Rvsdg();
190 
191  auto lambdaNode = LambdaNode::Create(
192  rvsdg.GetRootRegion(),
193  LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
194 
195  auto & constantNode0 = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 0);
196  auto & constantNode2 = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 2);
197 
198  auto thetaNode = ThetaNode::create(lambdaNode->subregion());
199 
200  auto loopVar0 = thetaNode->AddLoopVar(constantNode0.output(0));
201  auto loopVar1 = thetaNode->AddLoopVar(constantNode0.output(0));
202  auto loopVar2 = thetaNode->AddLoopVar(constantNode2.output(0));
203 
204  auto testNode0 =
205  TestOperation::createNode(thetaNode->subregion(), { loopVar0.pre }, { bit32Type });
206  auto & constantNode1 = IntegerConstantOperation::Create(*thetaNode->subregion(), 32, 1);
207  auto testNode2 =
208  TestOperation::createNode(thetaNode->subregion(), { loopVar2.pre }, { bit32Type });
209 
210  loopVar0.post->divert_to(testNode0->output(0));
211  loopVar1.post->divert_to(constantNode1.output(0));
212 
213  auto testNode1 =
214  TestOperation::createNode(thetaNode->subregion(), { loopVar0.output }, { bit32Type });
215 
216  auto lambdaOutput =
217  lambdaNode->finalize({ testNode1->output(0), loopVar1.output, loopVar2.output });
218 
219  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "");
220 
221  view(rvsdg, stdout);
222 
223  // Act
226  view(rvsdg, stdout);
227 
228  // Arrange
229  // We expect constantNode1 to be distributed from the theta subregion to the lambda subregion
230  EXPECT_EQ(lambdaNode->subregion()->numNodes(), 5);
231 
232  // We expect constantNode2 to be distributed from the lambda subregion to the theta subregion
233  EXPECT_EQ(thetaNode->subregion()->numNodes(), 5);
234 
235  {
236  // We expect no changes with loopVar0
237  auto loopVar = thetaNode->MapOutputLoopVar(*thetaNode->output(0));
238  EXPECT_EQ(lambdaNode->subregion()->result(0)->origin(), testNode1->output(0));
239  EXPECT_EQ(loopVar.output, testNode1->input(0)->origin());
240  EXPECT_EQ(loopVar.post->origin(), testNode0->output(0));
241  EXPECT_EQ(testNode0->input(0)->origin(), loopVar.pre);
242  EXPECT_EQ(loopVar.input->origin(), constantNode0.output(0));
243  }
244 
245  {
246  // We expect constantNode1 to be distributed from the theta subregion to the lambda subregion,
247  // rendering loopVar1 to be dead
248  auto loopVar = thetaNode->MapOutputLoopVar(*thetaNode->output(1));
249  EXPECT_TRUE(loopVar.output->IsDead());
250  EXPECT_TRUE(loopVar.pre->IsDead());
251 
252  auto [constantNode, constantOperation] =
253  TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
254  *lambdaNode->subregion()->result(1)->origin());
255  EXPECT_TRUE(constantNode && constantOperation);
256  EXPECT_EQ(constantOperation->Representation(), 1);
257  }
258 
259  {
260  // LoopVar2 was a passthrough so we expect it to be redirected to constantNode2
261  auto [constantNode, constantOperation] =
262  TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(
263  *lambdaNode->subregion()->result(2)->origin());
264  EXPECT_TRUE(constantNode && constantOperation);
265  EXPECT_EQ(constantNode, &constantNode2);
266  }
267 
268  {
269  // We expect constantNode2 to be distributed t o the theta subregion for testNode2
270  auto [constantNode, constantOperation] =
271  TryGetSimpleNodeAndOptionalOp<IntegerConstantOperation>(*testNode2->input(0)->origin());
272  EXPECT_TRUE(constantNode && constantOperation);
273  EXPECT_EQ(constantOperation->Representation(), 2);
274  }
275 }
276 
277 TEST(DistributeConstantsTests, Lambda)
278 {
279  using namespace jlm::hls;
280  using namespace jlm::llvm;
281  using namespace jlm::rvsdg;
282  using namespace jlm::util;
283 
284  // Arrange
285  auto bit32Type = BitType::Create(32);
286  auto functionType = FunctionType::Create({}, { bit32Type });
287 
288  jlm::llvm::LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
289  auto & rvsdg = rvsdgModule.Rvsdg();
290 
291  auto lambdaNode = LambdaNode::Create(
292  rvsdg.GetRootRegion(),
293  LlvmLambdaOperation::Create(functionType, "f", Linkage::externalLinkage));
294 
295  auto & constantNode0 = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 0);
296 
297  auto lambdaOutput = lambdaNode->finalize({ constantNode0.output(0) });
298 
299  GraphExport::Create(*lambdaOutput, "");
300 
301  view(rvsdg, stdout);
302 
303  // Act
306  view(rvsdg, stdout);
307 
308  // Arrange
309  // We expect no change at all in the graph
310  EXPECT_EQ(lambdaNode->subregion()->numNodes(), 1);
311 }
static jlm::util::StatisticsCollector statisticsCollector
TEST(DistributeConstantsTests, GammaSubregionUsage)
static void CreateAndRun(rvsdg::RvsdgModule &rvsdgModule, util::StatisticsCollector &statisticsCollector)
static rvsdg::Node & Create(rvsdg::Region &region, IntegerValueRepresentation representation)
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::shared_ptr< const BitType > Create(std::size_t nbits)
Creates bit type of specified width.
Definition: type.cpp:45
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
static LambdaNode * Create(rvsdg::Region &parent, std::unique_ptr< LambdaOperation > operation)
Definition: lambda.cpp:140
static SimpleNode * createNode(Region *region, const std::vector< Output * > &operands, std::vector< std::shared_ptr< const Type >> resultTypes)
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