Jlm
MemoryQueueTests.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2024 Magnus Sjalander <work@sjalander.com>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #include <gtest/gtest.h>
7 
11 #include <jlm/hls/ir/hls.hpp>
17 #include <jlm/rvsdg/lambda.hpp>
18 #include <jlm/rvsdg/theta.hpp>
19 #include <jlm/rvsdg/traverser.hpp>
20 #include <jlm/rvsdg/view.hpp>
21 #include <jlm/util/Statistics.hpp>
22 
23 TEST(MemoryQueueTests, TestSingleLoad)
24 {
25  using namespace jlm::llvm;
26  using namespace jlm::hls;
27 
28  auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
29 
30  // Setup the function
31  std::cout << "Function Setup" << std::endl;
32  auto functionType = jlm::rvsdg::FunctionType::Create(
35 
36  auto lambda = jlm::rvsdg::LambdaNode::Create(
37  rvsdgModule->Rvsdg().GetRootRegion(),
39 
40  // Theta
41  auto theta = jlm::rvsdg::ThetaNode::create(lambda->subregion());
42  auto constant = &jlm::rvsdg::BitConstantOperation::create(*theta->subregion(), { 1, 1 });
43  auto & matchNode = jlm::rvsdg::MatchOperation::CreateNode(*constant, { { 1, 1 } }, 0, 2);
44  theta->set_predicate(matchNode.output(0));
45 
46  // Load node
47  auto functionArguments = lambda->GetFunctionArguments();
48  auto loadAddress = theta->AddLoopVar(functionArguments[0]);
49  auto memoryStateArgument = theta->AddLoopVar(functionArguments[1]);
50  auto loadOutput = LoadNonVolatileOperation::Create(
51  loadAddress.pre,
52  { memoryStateArgument.pre },
54  32);
55  loadAddress.post->divert_to(loadOutput[0]);
56  memoryStateArgument.post->divert_to(loadOutput[1]);
57 
58  auto lambdaOutput = lambda->finalize({ theta->output(0), theta->output(1) });
59  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "f");
60 
61  auto lambdaRegion = lambda->subregion();
62  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
63 
64  // Act
66  MemoryStateSeparation::CreateAndRun(*rvsdgModule, statisticsCollector);
67 
68  // Assert
69  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
70  auto & entryMemoryStateSplitInput = lambdaRegion->argument(1)->SingleUser();
71  auto * entryMemoryStateSplitNode =
72  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(entryMemoryStateSplitInput);
73  jlm::util::assertedCast<const LambdaEntryMemoryStateSplitOperation>(
74  &entryMemoryStateSplitNode->GetOperation());
75  auto exitMemoryStateMergeNode =
76  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*lambdaRegion->result(1)->origin());
77  jlm::util::assertedCast<const LambdaExitMemoryStateMergeOperation>(
78  &exitMemoryStateMergeNode->GetOperation());
79 
80  // Act
81  ThetaNodeConversion::CreateAndRun(*rvsdgModule, statisticsCollector);
82  // Simple assert as ConvertThetaNodes() is tested in separate unit tests
83  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
84  EXPECT_TRUE(jlm::rvsdg::Region::ContainsNodeType<LoopNode>(*lambdaRegion, true));
85 
86  // Act
87  AddressQueueInsertion::CreateAndRun(*rvsdgModule, statisticsCollector);
88 
89  // Assert
90  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
91  EXPECT_TRUE(jlm::rvsdg::Region::ContainsOperation<StateGateOperation>(*lambdaRegion, true));
92  EXPECT_FALSE(jlm::rvsdg::Region::ContainsOperation<AddressQueueOperation>(*lambdaRegion, true));
93 }
94 
95 TEST(MemoryQueueTests, TestLoadStore)
96 {
97  using namespace jlm::llvm;
98  using namespace jlm::hls;
99 
100  auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
101 
102  // Setup the function
103  std::cout << "Function Setup" << std::endl;
104  auto functionType = jlm::rvsdg::FunctionType::Create(
109 
110  auto lambda = jlm::rvsdg::LambdaNode::Create(
111  rvsdgModule->Rvsdg().GetRootRegion(),
113 
114  // Theta
115  auto theta = jlm::rvsdg::ThetaNode::create(lambda->subregion());
116  auto constant = &jlm::rvsdg::BitConstantOperation::create(*theta->subregion(), { 1, 1 });
117  auto & matchNode = jlm::rvsdg::MatchOperation::CreateNode(*constant, { { 1, 1 } }, 0, 2);
118  theta->set_predicate(matchNode.output(0));
119 
120  // Load node
121  auto functionArguments = lambda->GetFunctionArguments();
122  auto loadAddress = theta->AddLoopVar(functionArguments[0]);
123  auto storeAddress = theta->AddLoopVar(functionArguments[1]);
124  auto memoryStateArgument = theta->AddLoopVar(functionArguments[2]);
125  auto loadOutput = LoadNonVolatileOperation::Create(
126  loadAddress.pre,
127  { memoryStateArgument.pre },
129  32);
130  auto storeOutput = StoreNonVolatileOperation::Create(
131  storeAddress.pre,
132  &jlm::rvsdg::BitConstantOperation::create(*theta->subregion(), { 32, 1 }),
133  { loadOutput[1] },
134  32);
135 
136  loadAddress.post->divert_to(loadOutput[0]);
137  memoryStateArgument.post->divert_to(storeOutput[0]);
138 
139  auto lambdaOutput = lambda->finalize({ theta->output(0), theta->output(2) });
140  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "f");
141 
142  auto lambdaRegion = lambda->subregion();
143  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
144 
145  // Act
147  MemoryStateSeparation::CreateAndRun(*rvsdgModule, statisticsCollector);
148 
149  // Assert
150  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
151  auto & entryMemoryStateSplitInput = lambdaRegion->argument(2)->SingleUser();
152  auto * entryMemoryStateSplitNode =
153  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(entryMemoryStateSplitInput);
154  jlm::util::assertedCast<const LambdaEntryMemoryStateSplitOperation>(
155  &entryMemoryStateSplitNode->GetOperation());
156  auto exitMemoryStateMergeNode =
157  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*lambdaRegion->result(1)->origin());
158  jlm::util::assertedCast<const LambdaExitMemoryStateMergeOperation>(
159  &exitMemoryStateMergeNode->GetOperation());
160 
161  // Act
162  ThetaNodeConversion::CreateAndRun(*rvsdgModule, statisticsCollector);
163  // Simple assert as ConvertThetaNodes() is tested in separate unit tests
164  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
165  EXPECT_TRUE(jlm::rvsdg::Region::ContainsNodeType<LoopNode>(*lambdaRegion, true));
166 
167  // Act
168  AddressQueueInsertion::CreateAndRun(*rvsdgModule, statisticsCollector);
169 
170  // Assert
171  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
172  EXPECT_TRUE(jlm::rvsdg::Region::ContainsOperation<StateGateOperation>(*lambdaRegion, true));
173  EXPECT_FALSE(jlm::rvsdg::Region::ContainsOperation<AddressQueueOperation>(*lambdaRegion, true));
174 }
175 
176 TEST(MemoryQueueTests, TestAddrQueue)
177 {
178  using namespace jlm::llvm;
179  using namespace jlm::hls;
180 
181  auto rvsdgModule = LlvmRvsdgModule::Create(jlm::util::FilePath(""), "", "");
182 
183  // Setup the function
184  std::cout << "Function Setup" << std::endl;
185  auto functionType = jlm::rvsdg::FunctionType::Create(
188 
189  auto lambda = jlm::rvsdg::LambdaNode::Create(
190  rvsdgModule->Rvsdg().GetRootRegion(),
192 
193  // Theta
194  auto theta = jlm::rvsdg::ThetaNode::create(lambda->subregion());
195  auto constant = &jlm::rvsdg::BitConstantOperation::create(*theta->subregion(), { 1, 1 });
196  auto & matchNode = jlm::rvsdg::MatchOperation::CreateNode(*constant, { { 1, 1 } }, 0, 2);
197  theta->set_predicate(matchNode.output(0));
198 
199  // Load node
200  auto functionArguments = lambda->GetFunctionArguments();
201  auto address = theta->AddLoopVar(functionArguments[0]);
202  auto memoryStateArgument = theta->AddLoopVar(functionArguments[1]);
203  auto loadOutput = LoadNonVolatileOperation::Create(
204  address.pre,
205  { memoryStateArgument.pre },
207  32);
208  auto storeOutput =
209  StoreNonVolatileOperation::Create(address.pre, loadOutput[0], { loadOutput[1] }, 32);
210 
211  address.post->divert_to(loadOutput[0]);
212  memoryStateArgument.post->divert_to(storeOutput[0]);
213 
214  auto lambdaOutput = lambda->finalize({ theta->output(0), theta->output(1) });
215  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "f");
216 
217  auto lambdaRegion = lambda->subregion();
218  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
219 
220  // Act
222  MemoryStateSeparation::CreateAndRun(*rvsdgModule, statisticsCollector);
223 
224  // Assert
225  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
226  auto & entryMemoryStateSplitInput = lambdaRegion->argument(1)->SingleUser();
227  auto * entryMemoryStateSplitNode =
228  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(entryMemoryStateSplitInput);
229  jlm::util::assertedCast<const LambdaEntryMemoryStateSplitOperation>(
230  &entryMemoryStateSplitNode->GetOperation());
231  auto exitMemoryStateMergeNode =
232  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*lambdaRegion->result(1)->origin());
233  jlm::util::assertedCast<const LambdaExitMemoryStateMergeOperation>(
234  &exitMemoryStateMergeNode->GetOperation());
235 
236  // Act
237  ThetaNodeConversion::CreateAndRun(*rvsdgModule, statisticsCollector);
238  // Simple assert as ConvertThetaNodes() is tested in separate unit tests
239  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
240  EXPECT_TRUE(jlm::rvsdg::Region::ContainsNodeType<LoopNode>(*lambdaRegion, true));
241 
242  // Act
243  AddressQueueInsertion::CreateAndRun(*rvsdgModule, statisticsCollector);
244 
245  // Assert
246  jlm::rvsdg::view(rvsdgModule->Rvsdg(), stdout);
247  EXPECT_TRUE(jlm::rvsdg::Region::ContainsOperation<StateGateOperation>(*lambdaRegion, true));
248  EXPECT_TRUE(jlm::rvsdg::Region::ContainsOperation<AddressQueueOperation>(*lambdaRegion, true));
249 
250  for (auto & node : jlm::rvsdg::TopDownTraverser(lambdaRegion))
251  {
252  if (auto loopNode = dynamic_cast<jlm::hls::LoopNode *>(node))
253  {
254  for (auto & node : jlm::rvsdg::TopDownTraverser(loopNode->subregion()))
255  {
256  if (is<StoreNonVolatileOperation>(node))
257  {
258  auto loadNode =
259  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*node->input(1)->origin());
260  jlm::util::assertedCast<const jlm::llvm::LoadOperation>(&loadNode->GetOperation());
261  auto stateGate =
262  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*loadNode->input(0)->origin());
263  jlm::util::assertedCast<const StateGateOperation>(&stateGate->GetOperation());
264  auto addrQueue =
265  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*stateGate->input(0)->origin());
266  jlm::util::assertedCast<const AddressQueueOperation>(&addrQueue->GetOperation());
267  }
268  }
269  }
270  }
271 }
static jlm::util::StatisticsCollector statisticsCollector
TEST(MemoryQueueTests, TestSingleLoad)
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::unique_ptr< llvm::ThreeAddressCode > Create(const Variable *address, const Variable *value, const Variable *state, size_t alignment)
Definition: Store.hpp:304
static Output & create(Region &region, BitValueRepresentation value)
Definition: constant.hpp:44
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 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 Node & CreateNode(Output &predicate, const std::unordered_map< uint64_t, uint64_t > &mapping, const uint64_t defaultAlternative, const size_t numAlternatives)
Definition: control.hpp:226
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