Matthew Simpson | 0b0eddf | 2017-10-16 17:44:17 +0000 | [diff] [blame] | 1 | //===- SparsePropagation.cpp - Unit tests for the generic solver ----------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | |
| 10 | #include "llvm/Analysis/SparsePropagation.h" |
| 11 | #include "llvm/ADT/PointerIntPair.h" |
| 12 | #include "llvm/IR/CallSite.h" |
| 13 | #include "llvm/IR/IRBuilder.h" |
| 14 | #include "gtest/gtest.h" |
| 15 | using namespace llvm; |
| 16 | |
| 17 | namespace { |
| 18 | /// To enable interprocedural analysis, we assign LLVM values to the following |
| 19 | /// groups. The register group represents SSA registers, the return group |
| 20 | /// represents the return values of functions, and the memory group represents |
| 21 | /// in-memory values. An LLVM Value can technically be in more than one group. |
| 22 | /// It's necessary to distinguish these groups so we can, for example, track a |
| 23 | /// global variable separately from the value stored at its location. |
| 24 | enum class IPOGrouping { Register, Return, Memory }; |
| 25 | |
| 26 | /// Our LatticeKeys are PointerIntPairs composed of LLVM values and groupings. |
| 27 | /// The PointerIntPair header provides a DenseMapInfo specialization, so using |
| 28 | /// these as LatticeKeys is fine. |
| 29 | using TestLatticeKey = PointerIntPair<Value *, 2, IPOGrouping>; |
| 30 | } // namespace |
| 31 | |
| 32 | namespace llvm { |
| 33 | /// A specialization of LatticeKeyInfo for TestLatticeKeys. The generic solver |
| 34 | /// must translate between LatticeKeys and LLVM Values when adding Values to |
| 35 | /// its work list and inspecting the state of control-flow related values. |
| 36 | template <> struct LatticeKeyInfo<TestLatticeKey> { |
| 37 | static inline Value *getValueFromLatticeKey(TestLatticeKey Key) { |
| 38 | return Key.getPointer(); |
| 39 | } |
| 40 | static inline TestLatticeKey getLatticeKeyFromValue(Value *V) { |
| 41 | return TestLatticeKey(V, IPOGrouping::Register); |
| 42 | } |
| 43 | }; |
| 44 | } // namespace llvm |
| 45 | |
| 46 | namespace { |
| 47 | /// This class defines a simple test lattice value that could be used for |
| 48 | /// solving problems similar to constant propagation. The value is maintained |
| 49 | /// as a PointerIntPair. |
| 50 | class TestLatticeVal { |
| 51 | public: |
| 52 | /// The states of the lattices value. Only the ConstantVal state is |
| 53 | /// interesting; the rest are special states used by the generic solver. The |
| 54 | /// UntrackedVal state differs from the other three in that the generic |
| 55 | /// solver uses it to avoid doing unnecessary work. In particular, when a |
| 56 | /// value moves to the UntrackedVal state, it's users are not notified. |
| 57 | enum TestLatticeStateTy { |
| 58 | UndefinedVal, |
| 59 | ConstantVal, |
| 60 | OverdefinedVal, |
| 61 | UntrackedVal |
| 62 | }; |
| 63 | |
| 64 | TestLatticeVal() : LatticeVal(nullptr, UndefinedVal) {} |
| 65 | TestLatticeVal(Constant *C, TestLatticeStateTy State) |
| 66 | : LatticeVal(C, State) {} |
| 67 | |
| 68 | /// Return true if this lattice value is in the Constant state. This is used |
| 69 | /// for checking the solver results. |
| 70 | bool isConstant() const { return LatticeVal.getInt() == ConstantVal; } |
| 71 | |
| 72 | /// Return true if this lattice value is in the Overdefined state. This is |
| 73 | /// used for checking the solver results. |
| 74 | bool isOverdefined() const { return LatticeVal.getInt() == OverdefinedVal; } |
| 75 | |
| 76 | bool operator==(const TestLatticeVal &RHS) const { |
| 77 | return LatticeVal == RHS.LatticeVal; |
| 78 | } |
| 79 | |
| 80 | bool operator!=(const TestLatticeVal &RHS) const { |
| 81 | return LatticeVal != RHS.LatticeVal; |
| 82 | } |
| 83 | |
| 84 | private: |
| 85 | /// A simple lattice value type for problems similar to constant propagation. |
| 86 | /// It holds the constant value and the lattice state. |
| 87 | PointerIntPair<const Constant *, 2, TestLatticeStateTy> LatticeVal; |
| 88 | }; |
| 89 | |
| 90 | /// This class defines a simple test lattice function that could be used for |
| 91 | /// solving problems similar to constant propagation. The test lattice differs |
| 92 | /// from a "real" lattice in a few ways. First, it initializes all return |
| 93 | /// values, values stored in global variables, and arguments in the undefined |
| 94 | /// state. This means that there are no limitations on what we can track |
| 95 | /// interprocedurally. For simplicity, all global values in the tests will be |
| 96 | /// given internal linkage, since this is not something this lattice function |
| 97 | /// tracks. Second, it only handles the few instructions necessary for the |
| 98 | /// tests. |
| 99 | class TestLatticeFunc |
| 100 | : public AbstractLatticeFunction<TestLatticeKey, TestLatticeVal> { |
| 101 | public: |
| 102 | /// Construct a new test lattice function with special values for the |
| 103 | /// Undefined, Overdefined, and Untracked states. |
| 104 | TestLatticeFunc() |
| 105 | : AbstractLatticeFunction( |
| 106 | TestLatticeVal(nullptr, TestLatticeVal::UndefinedVal), |
| 107 | TestLatticeVal(nullptr, TestLatticeVal::OverdefinedVal), |
| 108 | TestLatticeVal(nullptr, TestLatticeVal::UntrackedVal)) {} |
| 109 | |
| 110 | /// Compute and return a TestLatticeVal for the given TestLatticeKey. For the |
| 111 | /// test analysis, a LatticeKey will begin in the undefined state, unless it |
| 112 | /// represents an LLVM Constant in the register grouping. |
| 113 | TestLatticeVal ComputeLatticeVal(TestLatticeKey Key) override { |
| 114 | if (Key.getInt() == IPOGrouping::Register) |
| 115 | if (auto *C = dyn_cast<Constant>(Key.getPointer())) |
| 116 | return TestLatticeVal(C, TestLatticeVal::ConstantVal); |
| 117 | return getUndefVal(); |
| 118 | } |
| 119 | |
| 120 | /// Merge the two given lattice values. This merge should be equivalent to |
| 121 | /// what is done for constant propagation. That is, the resulting lattice |
| 122 | /// value is constant only if the two given lattice values are constant and |
| 123 | /// hold the same value. |
| 124 | TestLatticeVal MergeValues(TestLatticeVal X, TestLatticeVal Y) override { |
| 125 | if (X == getUntrackedVal() || Y == getUntrackedVal()) |
| 126 | return getUntrackedVal(); |
| 127 | if (X == getOverdefinedVal() || Y == getOverdefinedVal()) |
| 128 | return getOverdefinedVal(); |
| 129 | if (X == getUndefVal() && Y == getUndefVal()) |
| 130 | return getUndefVal(); |
| 131 | if (X == getUndefVal()) |
| 132 | return Y; |
| 133 | if (Y == getUndefVal()) |
| 134 | return X; |
| 135 | if (X == Y) |
| 136 | return X; |
| 137 | return getOverdefinedVal(); |
| 138 | } |
| 139 | |
| 140 | /// Compute the lattice values that change as a result of executing the given |
| 141 | /// instruction. We only handle the few instructions needed for the tests. |
| 142 | void ComputeInstructionState( |
| 143 | Instruction &I, DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues, |
| 144 | SparseSolver<TestLatticeKey, TestLatticeVal> &SS) override { |
| 145 | switch (I.getOpcode()) { |
| 146 | case Instruction::Call: |
| 147 | return visitCallSite(cast<CallInst>(&I), ChangedValues, SS); |
| 148 | case Instruction::Ret: |
| 149 | return visitReturn(*cast<ReturnInst>(&I), ChangedValues, SS); |
| 150 | case Instruction::Store: |
| 151 | return visitStore(*cast<StoreInst>(&I), ChangedValues, SS); |
| 152 | default: |
| 153 | return visitInst(I, ChangedValues, SS); |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | private: |
| 158 | /// Handle call sites. The state of a called function's argument is the merge |
| 159 | /// of the current formal argument state with the call site's corresponding |
| 160 | /// actual argument state. The call site state is the merge of the call site |
| 161 | /// state with the returned value state of the called function. |
| 162 | void visitCallSite(CallSite CS, |
| 163 | DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues, |
| 164 | SparseSolver<TestLatticeKey, TestLatticeVal> &SS) { |
| 165 | Function *F = CS.getCalledFunction(); |
| 166 | Instruction *I = CS.getInstruction(); |
| 167 | auto RegI = TestLatticeKey(I, IPOGrouping::Register); |
| 168 | if (!F) { |
| 169 | ChangedValues[RegI] = getOverdefinedVal(); |
| 170 | return; |
| 171 | } |
| 172 | SS.MarkBlockExecutable(&F->front()); |
| 173 | for (Argument &A : F->args()) { |
| 174 | auto RegFormal = TestLatticeKey(&A, IPOGrouping::Register); |
| 175 | auto RegActual = |
| 176 | TestLatticeKey(CS.getArgument(A.getArgNo()), IPOGrouping::Register); |
| 177 | ChangedValues[RegFormal] = |
| 178 | MergeValues(SS.getValueState(RegFormal), SS.getValueState(RegActual)); |
| 179 | } |
| 180 | auto RetF = TestLatticeKey(F, IPOGrouping::Return); |
| 181 | ChangedValues[RegI] = |
| 182 | MergeValues(SS.getValueState(RegI), SS.getValueState(RetF)); |
| 183 | } |
| 184 | |
| 185 | /// Handle return instructions. The function's return state is the merge of |
| 186 | /// the returned value state and the function's current return state. |
| 187 | void visitReturn(ReturnInst &I, |
| 188 | DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues, |
| 189 | SparseSolver<TestLatticeKey, TestLatticeVal> &SS) { |
| 190 | Function *F = I.getParent()->getParent(); |
| 191 | if (F->getReturnType()->isVoidTy()) |
| 192 | return; |
| 193 | auto RegR = TestLatticeKey(I.getReturnValue(), IPOGrouping::Register); |
| 194 | auto RetF = TestLatticeKey(F, IPOGrouping::Return); |
| 195 | ChangedValues[RetF] = |
| 196 | MergeValues(SS.getValueState(RegR), SS.getValueState(RetF)); |
| 197 | } |
| 198 | |
| 199 | /// Handle store instructions. If the pointer operand of the store is a |
| 200 | /// global variable, we attempt to track the value. The global variable state |
| 201 | /// is the merge of the stored value state with the current global variable |
| 202 | /// state. |
| 203 | void visitStore(StoreInst &I, |
| 204 | DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues, |
| 205 | SparseSolver<TestLatticeKey, TestLatticeVal> &SS) { |
| 206 | auto *GV = dyn_cast<GlobalVariable>(I.getPointerOperand()); |
| 207 | if (!GV) |
| 208 | return; |
| 209 | auto RegVal = TestLatticeKey(I.getValueOperand(), IPOGrouping::Register); |
| 210 | auto MemPtr = TestLatticeKey(GV, IPOGrouping::Memory); |
| 211 | ChangedValues[MemPtr] = |
| 212 | MergeValues(SS.getValueState(RegVal), SS.getValueState(MemPtr)); |
| 213 | } |
| 214 | |
| 215 | /// Handle all other instructions. All other instructions are marked |
| 216 | /// overdefined. |
| 217 | void visitInst(Instruction &I, |
| 218 | DenseMap<TestLatticeKey, TestLatticeVal> &ChangedValues, |
| 219 | SparseSolver<TestLatticeKey, TestLatticeVal> &SS) { |
| 220 | auto RegI = TestLatticeKey(&I, IPOGrouping::Register); |
| 221 | ChangedValues[RegI] = getOverdefinedVal(); |
| 222 | } |
| 223 | }; |
| 224 | |
| 225 | /// This class defines the common data used for all of the tests. The tests |
| 226 | /// should add code to the module and then run the solver. |
| 227 | class SparsePropagationTest : public testing::Test { |
| 228 | protected: |
| 229 | LLVMContext Context; |
| 230 | Module M; |
| 231 | IRBuilder<> Builder; |
| 232 | TestLatticeFunc Lattice; |
| 233 | SparseSolver<TestLatticeKey, TestLatticeVal> Solver; |
| 234 | |
| 235 | public: |
| 236 | SparsePropagationTest() |
| 237 | : M("", Context), Builder(Context), Solver(&Lattice) {} |
| 238 | }; |
| 239 | } // namespace |
| 240 | |
| 241 | /// Test that we mark discovered functions executable. |
| 242 | /// |
| 243 | /// define internal void @f() { |
| 244 | /// call void @g() |
| 245 | /// ret void |
| 246 | /// } |
| 247 | /// |
| 248 | /// define internal void @g() { |
| 249 | /// call void @f() |
| 250 | /// ret void |
| 251 | /// } |
| 252 | /// |
| 253 | /// For this test, we initially mark "f" executable, and the solver discovers |
| 254 | /// "g" because of the call in "f". The mutually recursive call in "g" also |
| 255 | /// tests that we don't add a block to the basic block work list if it is |
| 256 | /// already executable. Doing so would put the solver into an infinite loop. |
| 257 | TEST_F(SparsePropagationTest, MarkBlockExecutable) { |
| 258 | Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 259 | GlobalValue::InternalLinkage, "f", &M); |
| 260 | Function *G = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 261 | GlobalValue::InternalLinkage, "g", &M); |
| 262 | BasicBlock *FEntry = BasicBlock::Create(Context, "", F); |
| 263 | BasicBlock *GEntry = BasicBlock::Create(Context, "", G); |
| 264 | Builder.SetInsertPoint(FEntry); |
| 265 | Builder.CreateCall(G); |
| 266 | Builder.CreateRetVoid(); |
| 267 | Builder.SetInsertPoint(GEntry); |
| 268 | Builder.CreateCall(F); |
| 269 | Builder.CreateRetVoid(); |
| 270 | |
| 271 | Solver.MarkBlockExecutable(FEntry); |
| 272 | Solver.Solve(); |
| 273 | |
| 274 | EXPECT_TRUE(Solver.isBlockExecutable(GEntry)); |
| 275 | } |
| 276 | |
| 277 | /// Test that we propagate information through global variables. |
| 278 | /// |
| 279 | /// @gv = internal global i64 |
| 280 | /// |
| 281 | /// define internal void @f() { |
| 282 | /// store i64 1, i64* @gv |
| 283 | /// ret void |
| 284 | /// } |
| 285 | /// |
| 286 | /// define internal void @g() { |
| 287 | /// store i64 1, i64* @gv |
| 288 | /// ret void |
| 289 | /// } |
| 290 | /// |
| 291 | /// For this test, we initially mark both "f" and "g" executable, and the |
| 292 | /// solver computes the lattice state of the global variable as constant. |
| 293 | TEST_F(SparsePropagationTest, GlobalVariableConstant) { |
| 294 | Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 295 | GlobalValue::InternalLinkage, "f", &M); |
| 296 | Function *G = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 297 | GlobalValue::InternalLinkage, "g", &M); |
| 298 | GlobalVariable *GV = |
| 299 | new GlobalVariable(M, Builder.getInt64Ty(), false, |
| 300 | GlobalValue::InternalLinkage, nullptr, "gv"); |
| 301 | BasicBlock *FEntry = BasicBlock::Create(Context, "", F); |
| 302 | BasicBlock *GEntry = BasicBlock::Create(Context, "", G); |
| 303 | Builder.SetInsertPoint(FEntry); |
| 304 | Builder.CreateStore(Builder.getInt64(1), GV); |
| 305 | Builder.CreateRetVoid(); |
| 306 | Builder.SetInsertPoint(GEntry); |
| 307 | Builder.CreateStore(Builder.getInt64(1), GV); |
| 308 | Builder.CreateRetVoid(); |
| 309 | |
| 310 | Solver.MarkBlockExecutable(FEntry); |
| 311 | Solver.MarkBlockExecutable(GEntry); |
| 312 | Solver.Solve(); |
| 313 | |
| 314 | auto MemGV = TestLatticeKey(GV, IPOGrouping::Memory); |
| 315 | EXPECT_TRUE(Solver.getExistingValueState(MemGV).isConstant()); |
| 316 | } |
| 317 | |
| 318 | /// Test that we propagate information through global variables. |
| 319 | /// |
| 320 | /// @gv = internal global i64 |
| 321 | /// |
| 322 | /// define internal void @f() { |
| 323 | /// store i64 0, i64* @gv |
| 324 | /// ret void |
| 325 | /// } |
| 326 | /// |
| 327 | /// define internal void @g() { |
| 328 | /// store i64 1, i64* @gv |
| 329 | /// ret void |
| 330 | /// } |
| 331 | /// |
| 332 | /// For this test, we initially mark both "f" and "g" executable, and the |
| 333 | /// solver computes the lattice state of the global variable as overdefined. |
| 334 | TEST_F(SparsePropagationTest, GlobalVariableOverDefined) { |
| 335 | Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 336 | GlobalValue::InternalLinkage, "f", &M); |
| 337 | Function *G = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 338 | GlobalValue::InternalLinkage, "g", &M); |
| 339 | GlobalVariable *GV = |
| 340 | new GlobalVariable(M, Builder.getInt64Ty(), false, |
| 341 | GlobalValue::InternalLinkage, nullptr, "gv"); |
| 342 | BasicBlock *FEntry = BasicBlock::Create(Context, "", F); |
| 343 | BasicBlock *GEntry = BasicBlock::Create(Context, "", G); |
| 344 | Builder.SetInsertPoint(FEntry); |
| 345 | Builder.CreateStore(Builder.getInt64(0), GV); |
| 346 | Builder.CreateRetVoid(); |
| 347 | Builder.SetInsertPoint(GEntry); |
| 348 | Builder.CreateStore(Builder.getInt64(1), GV); |
| 349 | Builder.CreateRetVoid(); |
| 350 | |
| 351 | Solver.MarkBlockExecutable(FEntry); |
| 352 | Solver.MarkBlockExecutable(GEntry); |
| 353 | Solver.Solve(); |
| 354 | |
| 355 | auto MemGV = TestLatticeKey(GV, IPOGrouping::Memory); |
| 356 | EXPECT_TRUE(Solver.getExistingValueState(MemGV).isOverdefined()); |
| 357 | } |
| 358 | |
| 359 | /// Test that we propagate information through function returns. |
| 360 | /// |
| 361 | /// define internal i64 @f(i1* %cond) { |
| 362 | /// if: |
| 363 | /// %0 = load i1, i1* %cond |
| 364 | /// br i1 %0, label %then, label %else |
| 365 | /// |
| 366 | /// then: |
| 367 | /// ret i64 1 |
| 368 | /// |
| 369 | /// else: |
| 370 | /// ret i64 1 |
| 371 | /// } |
| 372 | /// |
| 373 | /// For this test, we initially mark "f" executable, and the solver computes |
| 374 | /// the return value of the function as constant. |
| 375 | TEST_F(SparsePropagationTest, FunctionDefined) { |
| 376 | Function *F = |
| 377 | Function::Create(FunctionType::get(Builder.getInt64Ty(), |
| 378 | {Type::getInt1PtrTy(Context)}, false), |
| 379 | GlobalValue::InternalLinkage, "f", &M); |
| 380 | BasicBlock *If = BasicBlock::Create(Context, "if", F); |
| 381 | BasicBlock *Then = BasicBlock::Create(Context, "then", F); |
| 382 | BasicBlock *Else = BasicBlock::Create(Context, "else", F); |
| 383 | F->arg_begin()->setName("cond"); |
| 384 | Builder.SetInsertPoint(If); |
| 385 | LoadInst *Cond = Builder.CreateLoad(F->arg_begin()); |
| 386 | Builder.CreateCondBr(Cond, Then, Else); |
| 387 | Builder.SetInsertPoint(Then); |
| 388 | Builder.CreateRet(Builder.getInt64(1)); |
| 389 | Builder.SetInsertPoint(Else); |
| 390 | Builder.CreateRet(Builder.getInt64(1)); |
| 391 | |
| 392 | Solver.MarkBlockExecutable(If); |
| 393 | Solver.Solve(); |
| 394 | |
| 395 | auto RetF = TestLatticeKey(F, IPOGrouping::Return); |
| 396 | EXPECT_TRUE(Solver.getExistingValueState(RetF).isConstant()); |
| 397 | } |
| 398 | |
| 399 | /// Test that we propagate information through function returns. |
| 400 | /// |
| 401 | /// define internal i64 @f(i1* %cond) { |
| 402 | /// if: |
| 403 | /// %0 = load i1, i1* %cond |
| 404 | /// br i1 %0, label %then, label %else |
| 405 | /// |
| 406 | /// then: |
| 407 | /// ret i64 0 |
| 408 | /// |
| 409 | /// else: |
| 410 | /// ret i64 1 |
| 411 | /// } |
| 412 | /// |
| 413 | /// For this test, we initially mark "f" executable, and the solver computes |
| 414 | /// the return value of the function as overdefined. |
| 415 | TEST_F(SparsePropagationTest, FunctionOverDefined) { |
| 416 | Function *F = |
| 417 | Function::Create(FunctionType::get(Builder.getInt64Ty(), |
| 418 | {Type::getInt1PtrTy(Context)}, false), |
| 419 | GlobalValue::InternalLinkage, "f", &M); |
| 420 | BasicBlock *If = BasicBlock::Create(Context, "if", F); |
| 421 | BasicBlock *Then = BasicBlock::Create(Context, "then", F); |
| 422 | BasicBlock *Else = BasicBlock::Create(Context, "else", F); |
| 423 | F->arg_begin()->setName("cond"); |
| 424 | Builder.SetInsertPoint(If); |
| 425 | LoadInst *Cond = Builder.CreateLoad(F->arg_begin()); |
| 426 | Builder.CreateCondBr(Cond, Then, Else); |
| 427 | Builder.SetInsertPoint(Then); |
| 428 | Builder.CreateRet(Builder.getInt64(0)); |
| 429 | Builder.SetInsertPoint(Else); |
| 430 | Builder.CreateRet(Builder.getInt64(1)); |
| 431 | |
| 432 | Solver.MarkBlockExecutable(If); |
| 433 | Solver.Solve(); |
| 434 | |
| 435 | auto RetF = TestLatticeKey(F, IPOGrouping::Return); |
| 436 | EXPECT_TRUE(Solver.getExistingValueState(RetF).isOverdefined()); |
| 437 | } |
| 438 | |
| 439 | /// Test that we propagate information through arguments. |
| 440 | /// |
| 441 | /// define internal void @f() { |
| 442 | /// call void @g(i64 0, i64 1) |
| 443 | /// call void @g(i64 1, i64 1) |
| 444 | /// ret void |
| 445 | /// } |
| 446 | /// |
| 447 | /// define internal void @g(i64 %a, i64 %b) { |
| 448 | /// ret void |
| 449 | /// } |
| 450 | /// |
| 451 | /// For this test, we initially mark "f" executable, and the solver discovers |
| 452 | /// "g" because of the calls in "f". The solver computes the state of argument |
| 453 | /// "a" as overdefined and the state of "b" as constant. |
| 454 | /// |
| 455 | /// In addition, this test demonstrates that ComputeInstructionState can alter |
| 456 | /// the state of multiple lattice values, in addition to the one associated |
| 457 | /// with the instruction definition. Each call instruction in this test updates |
| 458 | /// the state of arguments "a" and "b". |
| 459 | TEST_F(SparsePropagationTest, ComputeInstructionState) { |
| 460 | Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 461 | GlobalValue::InternalLinkage, "f", &M); |
| 462 | Function *G = Function::Create( |
| 463 | FunctionType::get(Builder.getVoidTy(), |
| 464 | {Builder.getInt64Ty(), Builder.getInt64Ty()}, false), |
| 465 | GlobalValue::InternalLinkage, "g", &M); |
| 466 | Argument *A = G->arg_begin(); |
| 467 | Argument *B = std::next(G->arg_begin()); |
| 468 | A->setName("a"); |
| 469 | B->setName("b"); |
| 470 | BasicBlock *FEntry = BasicBlock::Create(Context, "", F); |
| 471 | BasicBlock *GEntry = BasicBlock::Create(Context, "", G); |
| 472 | Builder.SetInsertPoint(FEntry); |
| 473 | Builder.CreateCall(G, {Builder.getInt64(0), Builder.getInt64(1)}); |
| 474 | Builder.CreateCall(G, {Builder.getInt64(1), Builder.getInt64(1)}); |
| 475 | Builder.CreateRetVoid(); |
| 476 | Builder.SetInsertPoint(GEntry); |
| 477 | Builder.CreateRetVoid(); |
| 478 | |
| 479 | Solver.MarkBlockExecutable(FEntry); |
| 480 | Solver.Solve(); |
| 481 | |
| 482 | auto RegA = TestLatticeKey(A, IPOGrouping::Register); |
| 483 | auto RegB = TestLatticeKey(B, IPOGrouping::Register); |
| 484 | EXPECT_TRUE(Solver.getExistingValueState(RegA).isOverdefined()); |
| 485 | EXPECT_TRUE(Solver.getExistingValueState(RegB).isConstant()); |
| 486 | } |
| 487 | |
| 488 | /// Test that we can handle exceptional terminator instructions. |
| 489 | /// |
| 490 | /// declare internal void @p() |
| 491 | /// |
| 492 | /// declare internal void @g() |
| 493 | /// |
| 494 | /// define internal void @f() personality i8* bitcast (void ()* @p to i8*) { |
| 495 | /// entry: |
| 496 | /// invoke void @g() |
| 497 | /// to label %exit unwind label %catch.pad |
| 498 | /// |
| 499 | /// catch.pad: |
| 500 | /// %0 = catchswitch within none [label %catch.body] unwind to caller |
| 501 | /// |
| 502 | /// catch.body: |
| 503 | /// %1 = catchpad within %0 [] |
| 504 | /// catchret from %1 to label %exit |
| 505 | /// |
| 506 | /// exit: |
| 507 | /// ret void |
| 508 | /// } |
| 509 | /// |
| 510 | /// For this test, we initially mark the entry block executable. The solver |
| 511 | /// then discovers the rest of the blocks in the function are executable. |
| 512 | TEST_F(SparsePropagationTest, ExceptionalTerminatorInsts) { |
| 513 | Function *P = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 514 | GlobalValue::InternalLinkage, "p", &M); |
| 515 | Function *G = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 516 | GlobalValue::InternalLinkage, "g", &M); |
| 517 | Function *F = Function::Create(FunctionType::get(Builder.getVoidTy(), false), |
| 518 | GlobalValue::InternalLinkage, "f", &M); |
| 519 | Constant *C = |
| 520 | ConstantExpr::getCast(Instruction::BitCast, P, Builder.getInt8PtrTy()); |
| 521 | F->setPersonalityFn(C); |
| 522 | BasicBlock *Entry = BasicBlock::Create(Context, "entry", F); |
| 523 | BasicBlock *Pad = BasicBlock::Create(Context, "catch.pad", F); |
| 524 | BasicBlock *Body = BasicBlock::Create(Context, "catch.body", F); |
| 525 | BasicBlock *Exit = BasicBlock::Create(Context, "exit", F); |
| 526 | Builder.SetInsertPoint(Entry); |
| 527 | Builder.CreateInvoke(G, Exit, Pad); |
| 528 | Builder.SetInsertPoint(Pad); |
| 529 | CatchSwitchInst *CatchSwitch = |
| 530 | Builder.CreateCatchSwitch(ConstantTokenNone::get(Context), nullptr, 1); |
| 531 | CatchSwitch->addHandler(Body); |
| 532 | Builder.SetInsertPoint(Body); |
| 533 | CatchPadInst *CatchPad = Builder.CreateCatchPad(CatchSwitch, {}); |
| 534 | Builder.CreateCatchRet(CatchPad, Exit); |
| 535 | Builder.SetInsertPoint(Exit); |
| 536 | Builder.CreateRetVoid(); |
| 537 | |
| 538 | Solver.MarkBlockExecutable(Entry); |
| 539 | Solver.Solve(); |
| 540 | |
| 541 | EXPECT_TRUE(Solver.isBlockExecutable(Pad)); |
| 542 | EXPECT_TRUE(Solver.isBlockExecutable(Body)); |
| 543 | EXPECT_TRUE(Solver.isBlockExecutable(Exit)); |
| 544 | } |