Jlm
Loading...
Searching...
No Matches
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
49TEST(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(), 0u);
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
158TEST(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(), 0u);
243}
static void print(const jlm::util::DisjointSet< int >::Set &set)
TEST(LlvmPhiConversionTests, TestPhiConversion)
std::unique_ptr< InterProceduralGraphModule > ConvertLlvmModule(::llvm::Module &llvmModule)
NodeType * TryGetOwnerNode(const rvsdg::Input &input) noexcept
Checks if this is an input to a node of specified type.
Definition node.hpp:872