Jlm
AggregateAllocaSplittingTests.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2026 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #include <gtest/gtest.h>
7 
16 #include <jlm/rvsdg/gamma.hpp>
17 #include <jlm/rvsdg/lambda.hpp>
18 #include <jlm/rvsdg/theta.hpp>
19 #include <jlm/rvsdg/Trace.hpp>
20 
21 static void
23 {
24  using namespace jlm::llvm;
25  using namespace jlm::rvsdg;
26 
27  auto [allocaNode, allocaOperation] =
28  jlm::rvsdg::TryGetSimpleNodeAndOptionalOp<AllocaOperation>(output);
29  EXPECT_TRUE(allocaNode && allocaOperation);
30  EXPECT_EQ(*allocaOperation->allocatedType(), type);
31 }
32 
33 TEST(AggregateAllocaSplittingTests, getElementPtrTest)
34 {
35  using namespace jlm::llvm;
36  using namespace jlm::rvsdg;
37  using namespace jlm::util;
38 
39  // Arrange
40  auto bit32Type = BitType::Create(32);
41  auto bit64Type = BitType::Create(64);
42  const auto pointerType = PointerType::Create();
43  const auto memoryStateType = MemoryStateType::Create();
44  const auto structType = StructType::CreateIdentified({ bit32Type, bit64Type }, false);
45  const auto functionType = FunctionType::Create({}, { memoryStateType });
46 
47  LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
48  auto & rvsdg = rvsdgModule.Rvsdg();
49 
50  auto lambdaNode = LambdaNode::Create(
51  rvsdg.GetRootRegion(),
53 
54  auto & zero32Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 0);
55  auto & zero64Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 64, 0);
56  auto & one32Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 1);
57  auto & allocaNode = AllocaOperation::createNode(structType, *one32Node.output(0), 4);
58 
59  auto & gepXNode = GetElementPtrOperation::createNode(
60  *allocaNode.output(0),
61  { zero32Node.output(0), zero32Node.output(0) },
62  bit32Type);
63  auto & storeGepXNode = StoreNonVolatileOperation::CreateNode(
64  *gepXNode.output(0),
65  *zero32Node.output(0),
66  { &AllocaOperation::getMemoryStateOutput(allocaNode) },
67  4);
68 
69  auto & gepYNode = GetElementPtrOperation::createNode(
70  *allocaNode.output(0),
71  { zero32Node.output(0), one32Node.output(0) },
72  bit64Type);
73  auto & storeGepYNode = StoreNonVolatileOperation::CreateNode(
74  *gepYNode.output(0),
75  *zero64Node.output(0),
76  { storeGepXNode.output(0) },
77  4);
78 
79  auto lambdaOutput = lambdaNode->finalize({ storeGepYNode.output(0) });
80  GraphExport::Create(*lambdaOutput, "f");
81 
82  // Act
84  AggregateAllocaSplitting aggregateAllocaSplitting;
85  aggregateAllocaSplitting.Run(rvsdgModule, statisticsCollector);
86 
87  // Assert
88  // We expect two AllocaOperation, a MemoryStateMergeOperation, and a IntegerConstantOperation
89  // node
90  EXPECT_EQ(lambdaNode->subregion()->numNodes(), 8u);
91 
92  assertAllocaWithType(*StoreOperation::AddressInput(storeGepXNode).origin(), *bit32Type);
93  assertAllocaWithType(*StoreOperation::AddressInput(storeGepYNode).origin(), *bit64Type);
94 
95  // Check memstate
96  {
97  auto [memoryMergeNode, memoryMergeOperation] =
98  TryGetSimpleNodeAndOptionalOp<MemoryStateMergeOperation>(
99  *StoreOperation::getMemoryStateInputs(storeGepXNode).begin()->origin());
100  EXPECT_TRUE(memoryMergeNode && memoryMergeOperation);
101 
102  EXPECT_EQ(memoryMergeNode->ninputs(), 2u);
103  assertAllocaWithType(*memoryMergeNode->input(0)->origin(), *bit32Type);
104  assertAllocaWithType(*memoryMergeNode->input(1)->origin(), *bit64Type);
105  }
106 }
107 
108 TEST(AggregateAllocaSplittingTests, gammaTest)
109 {
110  using namespace jlm::llvm;
111  using namespace jlm::rvsdg;
112  using namespace jlm::util;
113 
114  // Arrange
115  auto bit16Type = BitType::Create(16);
116  auto bit32Type = BitType::Create(32);
117  auto bit64Type = BitType::Create(64);
118  const auto controlType = ControlType::Create(2);
119  const auto pointerType = PointerType::Create();
120  const auto memoryStateType = MemoryStateType::Create();
121  const auto structType = StructType::CreateIdentified({ bit16Type, bit32Type, bit64Type }, false);
122  const auto functionType = FunctionType::Create({ controlType }, { memoryStateType });
123 
124  LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
125  auto & rvsdg = rvsdgModule.Rvsdg();
126 
127  auto lambdaNode = LambdaNode::Create(
128  rvsdg.GetRootRegion(),
130  auto controlArgument = lambdaNode->GetFunctionArguments()[0];
131 
132  auto & zero = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 0);
133  auto & one = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 1);
134  auto & two = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 2);
135  auto & allocaNode = AllocaOperation::createNode(structType, *one.output(0), 4);
136 
137  auto gammaNode = GammaNode::create(controlArgument, 2);
138  auto baseAddressEntryVar = gammaNode->AddEntryVar(&AllocaOperation::getPointerOutput(allocaNode));
139  auto memoryStateEntryVar =
140  gammaNode->AddEntryVar(&AllocaOperation::getMemoryStateOutput(allocaNode));
141 
142  Node *storeGep0Node = nullptr, *storeGep1Node = nullptr;
143  // Subregion 0
144  {
145  auto & zero16Node = IntegerConstantOperation::Create(*gammaNode->subregion(0), 16, 0);
146  auto & zero32Node = IntegerConstantOperation::Create(*gammaNode->subregion(0), 32, 0);
147 
148  auto & gep0Node = GetElementPtrOperation::createNode(
149  *baseAddressEntryVar.branchArgument[0],
150  { zero32Node.output(0), zero32Node.output(0) },
151  bit16Type);
152  storeGep0Node = &StoreNonVolatileOperation::CreateNode(
153  *gep0Node.output(0),
154  *zero16Node.output(0),
155  { memoryStateEntryVar.branchArgument[0] },
156  4);
157  }
158 
159  // Subregion 1
160  {
161  auto & zero32Node = IntegerConstantOperation::Create(*gammaNode->subregion(1), 32, 0);
162  auto & one32Node = IntegerConstantOperation::Create(*gammaNode->subregion(1), 32, 1);
163 
164  auto & gep1Node = GetElementPtrOperation::createNode(
165  *baseAddressEntryVar.branchArgument[1],
166  { zero32Node.output(0), one32Node.output(0) },
167  bit32Type);
168  storeGep1Node = &StoreNonVolatileOperation::CreateNode(
169  *gep1Node.output(0),
170  *zero32Node.output(0),
171  { memoryStateEntryVar.branchArgument[1] },
172  4);
173  }
174  auto baseAddressExitVar = gammaNode->AddExitVar(baseAddressEntryVar.branchArgument);
175  auto memoryStateExitVar =
176  gammaNode->AddExitVar({ storeGep0Node->output(0), storeGep1Node->output(0) });
177 
178  auto & gep2Node = GetElementPtrOperation::createNode(
179  *baseAddressExitVar.output,
180  { zero.output(0), two.output(0) },
181  bit64Type);
182 
183  auto & zero64Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 64, 0);
184  auto & storeGep2Node = StoreNonVolatileOperation::CreateNode(
185  *gep2Node.output(0),
186  *zero64Node.output(0),
187  { memoryStateExitVar.output },
188  4);
189 
190  auto lambdaOutput = lambdaNode->finalize({ storeGep2Node.output(0) });
191  GraphExport::Create(*lambdaOutput, "f");
192 
193  // Act
195  AggregateAllocaSplitting aggregateAllocaSplitting;
196  aggregateAllocaSplitting.Run(rvsdgModule, statisticsCollector);
197 
198  // Assert
199  // Check gep0
200  {
201  auto & gammaInput =
202  gammaNode->mapBranchArgumentToInput(*StoreOperation::AddressInput(*storeGep0Node).origin());
203  assertAllocaWithType(*gammaInput.origin(), *bit16Type);
204  }
205 
206  // Check gep1
207  {
208  auto & gammaInput =
209  gammaNode->mapBranchArgumentToInput(*StoreOperation::AddressInput(*storeGep1Node).origin());
210  assertAllocaWithType(*gammaInput.origin(), *bit32Type);
211  }
212 
213  // Check gep2
214  {
215  assertAllocaWithType(*StoreOperation::AddressInput(storeGep2Node).origin(), *bit64Type);
216  }
217 
218  // Check memState
219  {
220  auto [memoryMergeNode, memoryMergeOperation] =
221  TryGetSimpleNodeAndOptionalOp<MemoryStateMergeOperation>(
222  *memoryStateEntryVar.input->origin());
223  EXPECT_TRUE(memoryMergeNode && memoryMergeOperation);
224 
225  EXPECT_EQ(memoryMergeNode->ninputs(), 3u);
226  assertAllocaWithType(*memoryMergeNode->input(0)->origin(), *bit16Type);
227  assertAllocaWithType(*memoryMergeNode->input(1)->origin(), *bit32Type);
228  assertAllocaWithType(*memoryMergeNode->input(2)->origin(), *bit64Type);
229  }
230 }
231 
232 TEST(AggregateAllocaSplittingTests, thetaTest)
233 {
234  using namespace jlm::llvm;
235  using namespace jlm::rvsdg;
236  using namespace jlm::util;
237 
238  // Arrange
239  auto bit16Type = BitType::Create(16);
240  auto bit32Type = BitType::Create(32);
241  const auto pointerType = PointerType::Create();
242  const auto memoryStateType = MemoryStateType::Create();
243  const auto structType = StructType::CreateIdentified({ bit16Type, bit32Type }, false);
244  const auto functionType = FunctionType::Create({}, { memoryStateType });
245 
246  LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
247  auto & rvsdg = rvsdgModule.Rvsdg();
248 
249  auto lambdaNode = LambdaNode::Create(
250  rvsdg.GetRootRegion(),
252 
253  auto & one = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 1);
254  auto & allocaNode = AllocaOperation::createNode(structType, *one.output(0), 4);
255 
256  Node * storeGep0Node = nullptr;
257  auto thetaNode = ThetaNode::create(lambdaNode->subregion());
258  auto addressLoopVar = thetaNode->AddLoopVar(&AllocaOperation::getPointerOutput(allocaNode));
259  auto memoryStateLoopVar =
260  thetaNode->AddLoopVar(&AllocaOperation::getMemoryStateOutput(allocaNode));
261  {
262  auto & zero16Node = IntegerConstantOperation::Create(*thetaNode->subregion(), 16, 0);
263  auto & zero32Node = IntegerConstantOperation::Create(*thetaNode->subregion(), 32, 0);
264  auto & gep0Node = GetElementPtrOperation::createNode(
265  *addressLoopVar.pre,
266  { zero32Node.output(0), zero32Node.output(0) },
267  bit16Type);
268 
269  storeGep0Node = &StoreNonVolatileOperation::CreateNode(
270  *gep0Node.output(0),
271  *zero16Node.output(0),
272  { memoryStateLoopVar.pre },
273  4);
274 
275  memoryStateLoopVar.post->divert_to(storeGep0Node->output(0));
276  }
277 
278  auto & zero32Node = IntegerConstantOperation::Create(*lambdaNode->subregion(), 32, 0);
279  auto & gep1Node = GetElementPtrOperation::createNode(
280  *addressLoopVar.output,
281  { zero32Node.output(0), one.output(0) },
282  bit32Type);
283  auto & storeGep1Node = StoreNonVolatileOperation::CreateNode(
284  *gep1Node.output(0),
285  *zero32Node.output(0),
286  { memoryStateLoopVar.output },
287  4);
288 
289  auto lambdaOutput = lambdaNode->finalize({ storeGep1Node.output(0) });
290  GraphExport::Create(*lambdaOutput, "f");
291 
292  // Act
294  AggregateAllocaSplitting aggregateAllocaSplitting;
295  aggregateAllocaSplitting.Run(rvsdgModule, statisticsCollector);
296 
297  // Assert
298  // Check gep0
299  {
300  const auto & tracedOutput = traceOutput(*StoreOperation::AddressInput(*storeGep0Node).origin());
301  assertAllocaWithType(tracedOutput, *bit16Type);
302  }
303 
304  // Check gep1
305  {
306  const auto & tracedOutput = traceOutput(*StoreOperation::AddressInput(storeGep1Node).origin());
307  assertAllocaWithType(tracedOutput, *bit32Type);
308  }
309 
310  // Check memstate
311  {
312  auto [memoryMergeNode, memoryMergeOperation] =
313  TryGetSimpleNodeAndOptionalOp<MemoryStateMergeOperation>(
314  *memoryStateLoopVar.input->origin());
315  EXPECT_TRUE(memoryMergeNode && memoryMergeOperation);
316 
317  EXPECT_EQ(memoryMergeNode->ninputs(), 2u);
318  assertAllocaWithType(*memoryMergeNode->input(0)->origin(), *bit16Type);
319  assertAllocaWithType(*memoryMergeNode->input(1)->origin(), *bit32Type);
320  }
321 }
TEST(AggregateAllocaSplittingTests, getElementPtrTest)
static void assertAllocaWithType(const jlm::rvsdg::Output &output, const jlm::rvsdg::Type &type)
static jlm::util::StatisticsCollector statisticsCollector
Aggregate Alloca Splitting Transformation.
void Run(rvsdg::RvsdgModule &module, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
static rvsdg::SimpleNode & createNode(std::shared_ptr< const rvsdg::Type > allocatedType, rvsdg::Output &count, const size_t alignment)
Definition: alloca.hpp:109
static rvsdg::Output & getMemoryStateOutput(rvsdg::Node &node)
Definition: alloca.hpp:81
static rvsdg::Output & getPointerOutput(rvsdg::Node &node)
Definition: alloca.hpp:74
static rvsdg::SimpleNode & createNode(rvsdg::Output &baseAddress, const std::vector< rvsdg::Output * > &indices, std::shared_ptr< const rvsdg::Type > pointeeType)
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 MemoryStateType > Create()
Definition: types.cpp:379
static std::shared_ptr< const PointerType > Create()
Definition: types.cpp:45
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, rvsdg::Output &value, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition: Store.hpp:323
static rvsdg::Node::InputIteratorRange getMemoryStateInputs(const rvsdg::Node &node) noexcept
Definition: Store.hpp:116
static rvsdg::Input & AddressInput(const rvsdg::Node &node) noexcept
Definition: Store.hpp:75
static std::shared_ptr< const StructType > CreateIdentified(const std::string &name, std::vector< std::shared_ptr< const Type >> types, bool isPacked)
Definition: types.hpp:307
Output * origin() const noexcept
Definition: node.hpp:58
NodeOutput * output(size_t index) const noexcept
Definition: node.hpp:650
Global memory state passed between functions.
rvsdg::Output & traceOutput(rvsdg::Output &output, const rvsdg::Region *withinRegion)
Definition: Trace.cpp:54