Jlm
LlvmPhiConversionTests.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2024 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #include <gtest/gtest.h>
7 
12 #include <jlm/llvm/ir/print.hpp>
14 
15 #include <llvm/IR/BasicBlock.h>
16 #include <llvm/IR/IRBuilder.h>
17 #include <llvm/IRReader/IRReader.h>
18 
19 #include <string>
20 
49 TEST(LlvmPhiConversionTests, TestPhiConversion)
50 {
51  // Arrange
52  llvm::LLVMContext ctx;
53  llvm::Module module("popcount.c", ctx);
54 
55  // Build LLVM module
56  {
57  llvm::IRBuilder builder(ctx);
58 
59  auto i64 = builder.getInt64Ty();
60  auto prototype = llvm::FunctionType::get(i64, { i64 }, false);
61  llvm::Function * function =
62  llvm::Function::Create(prototype, llvm::Function::ExternalLinkage, "popcount", module);
63 
64  auto bb1 = llvm::BasicBlock::Create(ctx, "bb1", function);
65  auto bb2 = llvm::BasicBlock::Create(ctx, "bb2", function);
66  auto bb3 = llvm::BasicBlock::Create(ctx, "bb3", function);
67  auto bb4 = llvm::BasicBlock::Create(ctx, "bb4", function);
68  auto bb5 = llvm::BasicBlock::Create(ctx, "bb5", function);
69  auto bb6 = llvm::BasicBlock::Create(ctx, "bb6", function);
70 
71  builder.SetInsertPoint(bb1); // Entry block
72  builder.CreateBr(bb2);
73 
74  builder.SetInsertPoint(bb2); // Predecessors: bb1, bb4, bb5
75  auto phiX = builder.CreatePHI(i64, 3);
76  auto phiPopcount = builder.CreatePHI(i64, 3);
77 
78  auto xIs0 = builder.CreateICmpEQ(phiX, llvm::ConstantInt::get(i64, 0, false));
79  builder.CreateCondBr(xIs0, bb6, bb3);
80 
81  builder.SetInsertPoint(bb3); // Predecessors: bb2
82  auto rem = builder.CreateURem(phiX, llvm::ConstantInt::get(i64, 2, false));
83  auto remEq1 = builder.CreateICmpEQ(rem, llvm::ConstantInt::get(i64, 1, false));
84  auto halfX = builder.CreateLShr(phiX, llvm::ConstantInt::get(i64, 1, false));
85  builder.CreateCondBr(remEq1, bb4, bb5);
86 
87  builder.SetInsertPoint(bb4); // Predecessor: bb3
88  auto popcountPlus1 = builder.CreateAdd(phiPopcount, llvm::ConstantInt::get(i64, 1, false));
89  builder.CreateBr(bb2);
90 
91  builder.SetInsertPoint(bb5); // Predecessor: bb3
92  builder.CreateBr(bb2);
93 
94  builder.SetInsertPoint(bb6); // Predecessor: bb2
95  builder.CreateRet(phiPopcount);
96 
97  // Finally give the phi nodes their operands
98  phiX->addIncoming(function->getArg(0), bb1);
99  phiX->addIncoming(halfX, bb4);
100  phiX->addIncoming(halfX, bb5);
101 
102  phiPopcount->addIncoming(llvm::ConstantInt::get(i64, 0, false), bb1);
103  phiPopcount->addIncoming(popcountPlus1, bb4);
104  phiPopcount->addIncoming(phiPopcount, bb5);
105  }
106 
107  // jlm::tests::print(module);
108 
109  // Act
110  auto ipgmod = jlm::llvm::ConvertLlvmModule(module);
111 
112  // print(*ipgmod, stdout);
113 
114  // Assert
115  // First traverse from the function's entry node to bb2
116  auto popcount =
117  jlm::util::assertedCast<const jlm::llvm::FunctionNode>(ipgmod->ipgraph().find("popcount"));
118  auto entry_node = popcount->cfg()->entry();
119  EXPECT_TRUE(entry_node->single_successor());
120  auto bb1_node = entry_node->OutEdge(0)->sink();
121  EXPECT_TRUE(bb1_node->single_successor());
122  auto bb2_node = bb1_node->OutEdge(0)->sink();
123  auto bb2 = jlm::util::assertedCast<jlm::llvm::BasicBlock>(bb2_node);
124 
125  // The first two three address codes should be the phi representing x and popcnt respectively
126  auto tacs = bb2->begin();
127  auto & phiX = *tacs;
128  auto & phiPopcnt = *std::next(tacs);
129 
130  // Check that they are both phi operations
131  auto phiXOp = *jlm::util::assertedCast<const jlm::llvm::SsaPhiOperation>(&phiX->operation());
132  auto phiPopcntOp =
133  *jlm::util::assertedCast<const jlm::llvm::SsaPhiOperation>(&phiPopcnt->operation());
134 
135  // Both phi nodes should have 3 operands, representing the loop entry, and the two "continue"s
136  EXPECT_EQ(phiX->noperands(), 3u);
137  // The phi node for x takes its value from the function arg in the first operand
138  EXPECT_EQ(phiX->operand(0), popcount->cfg()->entry()->argument(0));
139  // The last two predecessor basic blocks both use the same value for x
140  EXPECT_EQ(phiX->operand(1), phiX->operand(2));
141 
142  EXPECT_EQ(phiPopcnt->noperands(), 3u);
143  // The first operand of the phi node is the constant integer 0
144  auto constant0variable =
145  jlm::util::assertedCast<const jlm::llvm::ThreeAddressCodeVariable>(phiPopcnt->operand(0));
146  auto constant0op = jlm::util::assertedCast<const jlm::llvm::IntegerConstantOperation>(
147  &constant0variable->tac()->operation());
148  EXPECT_EQ(constant0op->Representation(), 0);
149  // The last operand of the popcnt phi is the result of the phi itself
150  EXPECT_EQ(phiPopcnt->operand(2), phiPopcnt->result(0));
151 }
152 
158 TEST(LlvmPhiConversionTests, TestPhiOperandElision)
159 {
160  // Arrange
161  llvm::LLVMContext ctx;
162  llvm::Module module("phi-elide.c", ctx);
163 
164  // Build LLVM module
165  {
166  llvm::IRBuilder builder(ctx);
167 
168  auto i64 = builder.getInt64Ty();
169  auto prototype = llvm::FunctionType::get(i64, { i64 }, false);
170  llvm::Function * function =
171  llvm::Function::Create(prototype, llvm::Function::ExternalLinkage, "phi_elide", module);
172 
173  auto bb1 = llvm::BasicBlock::Create(ctx, "bb1", function);
174  auto bb2 = llvm::BasicBlock::Create(ctx, "bb2", function);
175  auto bb3 = llvm::BasicBlock::Create(ctx, "bb3", function);
176  auto bb4 = llvm::BasicBlock::Create(ctx, "bb4", function);
177  auto bb5 = llvm::BasicBlock::Create(ctx, "bb5", function);
178 
179  builder.SetInsertPoint(bb1); // entry block
180  auto xIs0 = builder.CreateICmpEQ(function->getArg(0), llvm::ConstantInt::get(i64, 0));
181  builder.CreateCondBr(xIs0, bb4, bb5);
182 
183  builder.SetInsertPoint(bb2); // No predecessors (dead)
184  auto xPlus1 = builder.CreateAdd(function->getArg(0), llvm::ConstantInt::get(i64, 1));
185  auto xIs1 = builder.CreateICmpEQ(function->getArg(0), llvm::ConstantInt::get(i64, 1));
186  builder.CreateCondBr(xIs1, bb3, bb5);
187 
188  builder.SetInsertPoint(bb3); // Predecessors: bb2 (dead)
189  builder.CreateBr(bb5);
190 
191  builder.SetInsertPoint(bb4); // Predecessors: bb1
192  auto xPlus2 = builder.CreateAdd(function->getArg(0), llvm::ConstantInt::get(i64, 2));
193  builder.CreateBr(bb5);
194 
195  builder.SetInsertPoint(bb5); // Predecessors: bb1, bb2 (dead), bb3 (dead), bb4
196  auto bb5phi = builder.CreatePHI(i64, 4);
197  builder.CreateRet(bb5phi);
198 
199  bb5phi->addIncoming(llvm::ConstantInt::get(i64, 0), bb1);
200  bb5phi->addIncoming(xPlus1, bb2); // Dead
201  bb5phi->addIncoming(function->getArg(0), bb3); // Dead
202  bb5phi->addIncoming(xPlus2, bb4);
203  }
204 
205  module.print(llvm::errs(), nullptr);
206 
207  // Act
208  auto ipgmod = jlm::llvm::ConvertLlvmModule(module);
209 
210  print(*ipgmod, stdout);
211 
212  // Assert
213  // Get the CFG of the function
214  auto phi_elide =
215  jlm::util::assertedCast<const jlm::llvm::FunctionNode>(ipgmod->ipgraph().find("phi_elide"));
216 
217  // Traverse the cfg and save every phi node
218  size_t numBasicBlocks = 0;
219  std::vector<jlm::llvm::ThreeAddressCode *> phiTacs;
220  for (auto & bb : *phi_elide->cfg())
221  {
222  numBasicBlocks++;
223  for (auto tac : bb)
224  {
225  if (jlm::rvsdg::is<jlm::llvm::SsaPhiOperation>(tac->operation()))
226  phiTacs.push_back(tac);
227  }
228  }
229 
230  // There should be 3 basic blocks left (bb1, bb4, bb5)
231  EXPECT_EQ(numBasicBlocks, 3u);
232  // There should be exactly one phi three address code
233  EXPECT_EQ(phiTacs.size(), 1u);
234  auto phiTac = phiTacs[0];
235  // The phi should have two operands
236  EXPECT_EQ(phiTac->noperands(), 2u);
237  // The first phi operand should be a constant 0
238  auto constant0variable =
239  jlm::util::assertedCast<const jlm::llvm::ThreeAddressCodeVariable>(phiTac->operand(0));
240  auto constant0op = jlm::util::assertedCast<const jlm::llvm::IntegerConstantOperation>(
241  &constant0variable->tac()->operation());
242  EXPECT_EQ(constant0op->Representation(), 0);
243 }
TEST(LlvmPhiConversionTests, TestPhiConversion)
void print(const AggregationNode &n, const AnnotationMap &dm, FILE *out)
Definition: print.cpp:120
std::unique_ptr< InterProceduralGraphModule > ConvertLlvmModule(::llvm::Module &llvmModule)