Jlm
IfConversionTests.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 
14 #include <jlm/rvsdg/gamma.hpp>
15 #include <jlm/rvsdg/lambda.hpp>
17 #include <jlm/rvsdg/TestType.hpp>
18 #include <jlm/rvsdg/view.hpp>
19 #include <jlm/util/Statistics.hpp>
20 
21 TEST(IfConversionTests, GammaWithoutMatch)
22 {
23  using namespace jlm::llvm;
24  using namespace jlm::util;
25 
26  // Arrange
27  auto valueType = jlm::rvsdg::TestType::createValueType();
28  auto functionType = jlm::rvsdg::FunctionType::Create(
29  { jlm::rvsdg::ControlType::Create(2), valueType, valueType },
30  { valueType });
31 
32  LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
33 
34  auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
35  rvsdgModule.Rvsdg().GetRootRegion(),
36  LlvmLambdaOperation::Create(functionType, "lambdaOutput", Linkage::externalLinkage));
37  const auto conditionValue = lambdaNode->GetFunctionArguments()[0];
38  const auto trueValue = lambdaNode->GetFunctionArguments()[1];
39  const auto falseValue = lambdaNode->GetFunctionArguments()[2];
40 
41  auto gammaNode = jlm::rvsdg::GammaNode::create(conditionValue, 2);
42  auto gammaInput1 = gammaNode->AddEntryVar(trueValue);
43  auto gammaInput2 = gammaNode->AddEntryVar(falseValue);
44  auto gammaOutput =
45  gammaNode->AddExitVar({ gammaInput1.branchArgument[0], gammaInput2.branchArgument[1] });
46 
47  auto lambdaOutput = lambdaNode->finalize({ gammaOutput.output });
48  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "");
49 
50  jlm::rvsdg::view(rvsdgModule.Rvsdg(), stdout);
51 
52  // Act
54  IfConversion ifConversion;
55  ifConversion.Run(rvsdgModule, statisticsCollector);
56 
57  view(rvsdgModule.Rvsdg(), stdout);
58 
59  // Assert
60 
61  EXPECT_TRUE(gammaNode->IsDead());
62  const auto selectNode =
63  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::Node>(*lambdaNode->subregion()->result(0)->origin());
64  EXPECT_TRUE(selectNode && is<SelectOperation>(selectNode));
65  EXPECT_EQ(selectNode->input(1)->origin(), falseValue);
66  EXPECT_EQ(selectNode->input(2)->origin(), trueValue);
67 
68  const auto controlToBitsNode =
69  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::Node>(*selectNode->input(0)->origin());
70  EXPECT_TRUE(controlToBitsNode && is<ControlToIntOperation>(controlToBitsNode));
71  EXPECT_EQ(controlToBitsNode->input(0)->origin(), conditionValue);
72 }
73 
74 TEST(IfConversionTests, EmptyGammaWithTwoSubregionsAndMatch)
75 {
76  using namespace jlm::llvm;
77  using namespace jlm::util;
78 
79  // Arrange
80  auto valueType = jlm::rvsdg::TestType::createValueType();
81  const auto functionType = jlm::rvsdg::FunctionType::Create(
82  { jlm::rvsdg::BitType::Create(32), valueType, valueType },
83  { valueType });
84 
85  LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
86 
87  const auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
88  rvsdgModule.Rvsdg().GetRootRegion(),
89  LlvmLambdaOperation::Create(functionType, "lambdaOutput", Linkage::externalLinkage));
90  const auto conditionValue = lambdaNode->GetFunctionArguments()[0];
91  const auto trueValue = lambdaNode->GetFunctionArguments()[1];
92  const auto falseValue = lambdaNode->GetFunctionArguments()[2];
93 
94  auto caseValue = 24;
95  auto & matchNode =
96  jlm::rvsdg::MatchOperation::CreateNode(*conditionValue, { { caseValue, 0 } }, 1, 2);
97 
98  const auto gammaNode = jlm::rvsdg::GammaNode::create(matchNode.output(0), 2);
99  auto [inputTrue, branchArgumentTrue] = gammaNode->AddEntryVar(trueValue);
100  auto [inputFalse, branchArgumentFalse] = gammaNode->AddEntryVar(falseValue);
101  auto [_, gammaOutput] = gammaNode->AddExitVar({ branchArgumentTrue[0], branchArgumentFalse[1] });
102 
103  const auto lambdaOutput = lambdaNode->finalize({ gammaOutput });
104  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "");
105 
106  view(rvsdgModule.Rvsdg(), stdout);
107 
108  // Act
110  IfConversion ifConversion;
111  ifConversion.Run(rvsdgModule, statisticsCollector);
112 
113  view(rvsdgModule.Rvsdg(), stdout);
114 
115  // Assert
116 
117  EXPECT_TRUE(gammaNode->IsDead());
118  const auto selectNode =
119  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::Node>(*lambdaNode->subregion()->result(0)->origin());
120  EXPECT_TRUE(selectNode && is<SelectOperation>(selectNode));
121  EXPECT_EQ(selectNode->input(1)->origin(), trueValue);
122  EXPECT_EQ(selectNode->input(2)->origin(), falseValue);
123 
124  const auto eqNode =
125  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*selectNode->input(0)->origin());
126  EXPECT_TRUE(eqNode && is<IntegerEqOperation>(eqNode));
127 
128  auto constantNode =
129  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*eqNode->input(0)->origin());
130  if (constantNode)
131  {
132  EXPECT_EQ(eqNode->input(1)->origin(), conditionValue);
133  auto constantOperation =
134  dynamic_cast<const IntegerConstantOperation *>(&constantNode->GetOperation());
135  EXPECT_NE(constantOperation, nullptr);
136  EXPECT_EQ(constantOperation->Representation(), 24);
137  }
138  else
139  {
140  EXPECT_EQ(eqNode->input(0)->origin(), conditionValue);
141  constantNode = jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::SimpleNode>(*eqNode->input(1)->origin());
142  auto constantOperation =
143  dynamic_cast<const IntegerConstantOperation *>(&constantNode->GetOperation());
144  EXPECT_NE(constantOperation, nullptr);
145  EXPECT_EQ(constantOperation->Representation(), 24);
146  }
147 }
148 
149 TEST(IfConversionTests, EmptyGammaWithTwoSubregions)
150 {
151  using namespace jlm::llvm;
152  using namespace jlm::util;
153 
154  // Arrange
155  auto valueType = jlm::rvsdg::TestType::createValueType();
156  auto functionType = jlm::rvsdg::FunctionType::Create(
157  { jlm::rvsdg::BitType::Create(32), valueType, valueType },
158  { valueType });
159 
160  LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
161 
162  const auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
163  rvsdgModule.Rvsdg().GetRootRegion(),
164  LlvmLambdaOperation::Create(functionType, "lambdaOutput", Linkage::externalLinkage));
165  const auto trueValue = lambdaNode->GetFunctionArguments()[1];
166  const auto falseValue = lambdaNode->GetFunctionArguments()[2];
167 
168  auto & matchNode = jlm::rvsdg::MatchOperation::CreateNode(
169  *lambdaNode->GetFunctionArguments()[0],
170  { { 0, 0 } },
171  1,
172  2);
173 
174  const auto gammaNode0 = jlm::rvsdg::GammaNode::create(matchNode.output(0), 2);
175  const auto & c0 = jlm::rvsdg::CreateOpNode<jlm::rvsdg::ControlConstantOperation>(
176  *gammaNode0->subregion(0),
178  const auto & c1 = jlm::rvsdg::CreateOpNode<jlm::rvsdg::ControlConstantOperation>(
179  *gammaNode0->subregion(1),
181  auto c = gammaNode0->AddExitVar({ c0.output(0), c1.output(0) });
182 
183  const auto gammaNode1 = jlm::rvsdg::GammaNode::create(c.output, 2);
184  auto [inputTrue, branchArgumentTrue] = gammaNode1->AddEntryVar(trueValue);
185  auto [inputFalse, branchArgumentFalse] = gammaNode1->AddEntryVar(falseValue);
186  auto [_, gammaOutput] = gammaNode1->AddExitVar({ branchArgumentFalse[0], branchArgumentTrue[1] });
187 
188  const auto lambdaOutput = lambdaNode->finalize({ gammaOutput });
189  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "");
190 
191  view(rvsdgModule.Rvsdg(), stdout);
192 
193  // Act
195  IfConversion ifConversion;
196  ifConversion.Run(rvsdgModule, statisticsCollector);
197 
198  view(rvsdgModule.Rvsdg(), stdout);
199 
200  // Assert
201  EXPECT_TRUE(gammaNode1->IsDead());
202  const auto selectNode =
203  jlm::rvsdg::TryGetOwnerNode<jlm::rvsdg::Node>(*lambdaNode->subregion()->result(0)->origin());
204  EXPECT_TRUE(selectNode && is<SelectOperation>(selectNode));
205  EXPECT_EQ(selectNode->input(1)->origin(), trueValue);
206  EXPECT_EQ(selectNode->input(2)->origin(), falseValue);
207 }
208 
209 TEST(IfConversionTests, EmptyGammaWithThreeSubregions)
210 {
211  using namespace jlm::llvm;
212  using namespace jlm::util;
213 
214  // Arrange
215  auto valueType = jlm::rvsdg::TestType::createValueType();
216  auto functionType = jlm::rvsdg::FunctionType::Create(
217  { jlm::rvsdg::BitType::Create(32), valueType, valueType },
218  { valueType });
219 
220  LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
221 
222  auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
223  rvsdgModule.Rvsdg().GetRootRegion(),
224  LlvmLambdaOperation::Create(functionType, "lambdaOutput", Linkage::externalLinkage));
225 
226  auto & matchNode = jlm::rvsdg::MatchOperation::CreateNode(
227  *lambdaNode->GetFunctionArguments()[0],
228  { { 0, 0 }, { 1, 1 } },
229  2,
230  3);
231 
232  auto gammaNode = jlm::rvsdg::GammaNode::create(matchNode.output(0), 3);
233  auto gammaInput1 = gammaNode->AddEntryVar(lambdaNode->GetFunctionArguments()[1]);
234  auto gammaInput2 = gammaNode->AddEntryVar(lambdaNode->GetFunctionArguments()[2]);
235  auto gammaOutput = gammaNode->AddExitVar({ gammaInput1.branchArgument[0],
236  gammaInput1.branchArgument[1],
237  gammaInput2.branchArgument[2] });
238 
239  auto lambdaOutput = lambdaNode->finalize({ gammaOutput.output });
240  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "");
241 
242  view(rvsdgModule.Rvsdg(), stdout);
243 
244  // Act
246  IfConversion ifConversion;
247  ifConversion.Run(rvsdgModule, statisticsCollector);
248 
249  view(rvsdgModule.Rvsdg(), stdout);
250 
251  // Assert
252 
253  // Only the gamma and match nodes should be in the lambda region. No select operation
254  // should have been created.
255  EXPECT_EQ(lambdaNode->subregion()->numNodes(), 2u);
256  EXPECT_FALSE(gammaNode->IsDead());
257 }
258 
259 TEST(IfConversionTests, PartialEmptyGamma)
260 {
261  using namespace jlm::llvm;
262  using namespace jlm::rvsdg;
263  using namespace jlm::util;
264 
265  // Arrange
266  auto valueType = jlm::rvsdg::TestType::createValueType();
267  auto functionType = jlm::rvsdg::FunctionType::Create(
268  { jlm::rvsdg::BitType::Create(1), valueType },
269  { valueType });
270 
271  jlm::llvm::LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
272 
273  auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
274  rvsdgModule.Rvsdg().GetRootRegion(),
275  LlvmLambdaOperation::Create(functionType, "lambdaOutput", Linkage::externalLinkage));
276 
277  auto & matchNode =
278  MatchOperation::CreateNode(*lambdaNode->GetFunctionArguments()[0], { { 0, 0 } }, 1, 2);
279  auto gammaNode = GammaNode::create(matchNode.output(0), 2);
280  auto gammaInput = gammaNode->AddEntryVar(lambdaNode->GetFunctionArguments()[1]);
281  auto output = TestOperation::createNode(
282  gammaNode->subregion(1),
283  { gammaInput.branchArgument[1] },
284  { valueType })
285  ->output(0);
286  auto gammaOutput = gammaNode->AddExitVar({ gammaInput.branchArgument[0], output });
287 
288  auto lambdaOutput = lambdaNode->finalize({ gammaOutput.output });
289 
290  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "");
291 
292  view(rvsdgModule.Rvsdg(), stdout);
293 
294  // Act
296  IfConversion ifConversion;
297  ifConversion.Run(rvsdgModule, statisticsCollector);
298 
299  view(rvsdgModule.Rvsdg(), stdout);
300 
301  // Assert
302 
303  // Only the gamma and match nodes should be in the lambda region. No select operation
304  // should have been created.
305  EXPECT_EQ(lambdaNode->subregion()->numNodes(), 2u);
306  EXPECT_FALSE(gammaNode->IsDead());
307 }
308 
309 TEST(IfConversionTests, GammaWithMatchAlternativeMismatch)
310 {
311  using namespace jlm::llvm;
312  using namespace jlm::util;
313 
314  // Arrange
315  auto valueType = jlm::rvsdg::TestType::createValueType();
316  const auto functionType = jlm::rvsdg::FunctionType::Create(
317  { jlm::rvsdg::BitType::Create(32), valueType, valueType },
318  { valueType });
319 
320  LlvmRvsdgModule rvsdgModule(FilePath(""), "", "");
321 
322  const auto lambdaNode = jlm::rvsdg::LambdaNode::Create(
323  rvsdgModule.Rvsdg().GetRootRegion(),
324  LlvmLambdaOperation::Create(functionType, "lambdaOutput", Linkage::externalLinkage));
325  const auto conditionValue = lambdaNode->GetFunctionArguments()[0];
326  const auto trueValue = lambdaNode->GetFunctionArguments()[1];
327  const auto falseValue = lambdaNode->GetFunctionArguments()[2];
328 
329  auto & matchNode = jlm::rvsdg::MatchOperation::CreateNode(
330  *conditionValue,
331  { { 24, 0 }, { 3, 0 }, { 45, 0 } },
332  1,
333  2);
334 
335  const auto gammaNode = jlm::rvsdg::GammaNode::create(matchNode.output(0), 2);
336  auto [inputTrue, branchArgumentTrue] = gammaNode->AddEntryVar(trueValue);
337  auto [inputFalse, branchArgumentFalse] = gammaNode->AddEntryVar(falseValue);
338  auto [_, gammaOutput] = gammaNode->AddExitVar({ branchArgumentTrue[0], branchArgumentFalse[1] });
339 
340  const auto lambdaOutput = lambdaNode->finalize({ gammaOutput });
341  jlm::rvsdg::GraphExport::Create(*lambdaOutput, "");
342 
343  view(rvsdgModule.Rvsdg(), stdout);
344 
345  // Act
347  IfConversion ifConversion;
348  ifConversion.Run(rvsdgModule, statisticsCollector);
349 
350  view(rvsdgModule.Rvsdg(), stdout);
351 
352  // Assert
353 
354  // Only the gamma and match nodes should be in the lambda region. No select operation
355  // should have been created.
356  EXPECT_EQ(lambdaNode->subregion()->numNodes(), 2u);
357  EXPECT_FALSE(gammaNode->IsDead());
358 }
static jlm::util::StatisticsCollector statisticsCollector
TEST(IfConversionTests, GammaWithoutMatch)
If-Conversion Transformation.
void Run(rvsdg::RvsdgModule &module, util::StatisticsCollector &statisticsCollector) override
Perform RVSDG transformation.
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 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 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 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