|
28 | 28 | #include "llvm/Support/CommandLine.h"
|
29 | 29 | #include "llvm/Support/FileUtilities.h"
|
30 | 30 | #include "llvm/Transforms/Scalar.h"
|
| 31 | +#include "llvm/Transforms/Utils/BasicBlockUtils.h" |
31 | 32 | #include "llvm/Transforms/Utils/Cloning.h"
|
| 33 | +#include "llvm/Transforms/Utils/Local.h" |
32 | 34 | #include <set>
|
33 | 35 | using namespace llvm;
|
34 | 36 |
|
@@ -430,6 +432,158 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock*> &BBs) {
|
430 | 432 | return false;
|
431 | 433 | }
|
432 | 434 |
|
| 435 | +namespace { |
| 436 | +/// Simplify the CFG without completely destroying it. |
| 437 | +/// This is not well defined, but basically comes down to "try to eliminate |
| 438 | +/// unreachable blocks and constant fold terminators without deciding that |
| 439 | +/// certain undefined behavior cuts off the program at the legs". |
| 440 | +void simpleSimplifyCfg(Function &F, SmallVectorImpl<BasicBlock *> &BBs) { |
| 441 | + if (F.empty()) |
| 442 | + return; |
| 443 | + |
| 444 | + for (auto *BB : BBs) { |
| 445 | + ConstantFoldTerminator(BB); |
| 446 | + MergeBlockIntoPredecessor(BB); |
| 447 | + } |
| 448 | + |
| 449 | + // Remove unreachable blocks |
| 450 | + // removeUnreachableBlocks can't be used here, it will turn various |
| 451 | + // undefined behavior into unreachables, but bugpoint was the thing that |
| 452 | + // generated the undefined behavior, and we don't want it to kill the entire |
| 453 | + // program. |
| 454 | + SmallPtrSet<BasicBlock *, 16> Visited; |
| 455 | + for (auto *BB : depth_first(&F.getEntryBlock())) |
| 456 | + Visited.insert(BB); |
| 457 | + |
| 458 | + SmallVector<BasicBlock *, 16> Unreachable; |
| 459 | + for (auto &BB : F) |
| 460 | + if (!Visited.count(&BB)) |
| 461 | + Unreachable.push_back(&BB); |
| 462 | + |
| 463 | + // The dead BB's may be in a dead cycle or otherwise have references to each |
| 464 | + // other. Because of this, we have to drop all references first, then delete |
| 465 | + // them all at once. |
| 466 | + for (auto *BB : Unreachable) { |
| 467 | + for (BasicBlock *Successor : successors(&*BB)) |
| 468 | + if (Visited.count(Successor)) |
| 469 | + Successor->removePredecessor(&*BB); |
| 470 | + BB->dropAllReferences(); |
| 471 | + } |
| 472 | + for (auto *BB : Unreachable) |
| 473 | + BB->eraseFromParent(); |
| 474 | +} |
| 475 | + |
| 476 | +/// ReduceCrashingConditionals reducer - This works by changing |
| 477 | +/// conditional branches to unconditional ones, then simplifying the CFG |
| 478 | +/// This has the effect of chopping up the CFG really fast which can reduce |
| 479 | +/// large functions quickly. |
| 480 | +/// |
| 481 | +class ReduceCrashingConditionals : public ListReducer<const BasicBlock *> { |
| 482 | + BugDriver &BD; |
| 483 | + bool (*TestFn)(const BugDriver &, Module *); |
| 484 | + bool Direction; |
| 485 | + |
| 486 | +public: |
| 487 | + ReduceCrashingConditionals(BugDriver &bd, |
| 488 | + bool (*testFn)(const BugDriver &, Module *), |
| 489 | + bool Direction) |
| 490 | + : BD(bd), TestFn(testFn), Direction(Direction) {} |
| 491 | + |
| 492 | + TestResult doTest(std::vector<const BasicBlock *> &Prefix, |
| 493 | + std::vector<const BasicBlock *> &Kept, |
| 494 | + std::string &Error) override { |
| 495 | + if (!Kept.empty() && TestBlocks(Kept)) |
| 496 | + return KeepSuffix; |
| 497 | + if (!Prefix.empty() && TestBlocks(Prefix)) |
| 498 | + return KeepPrefix; |
| 499 | + return NoFailure; |
| 500 | + } |
| 501 | + |
| 502 | + bool TestBlocks(std::vector<const BasicBlock *> &Prefix); |
| 503 | +}; |
| 504 | +} |
| 505 | + |
| 506 | +bool ReduceCrashingConditionals::TestBlocks( |
| 507 | + std::vector<const BasicBlock *> &BBs) { |
| 508 | + // Clone the program to try hacking it apart... |
| 509 | + ValueToValueMapTy VMap; |
| 510 | + Module *M = CloneModule(BD.getProgram(), VMap).release(); |
| 511 | + |
| 512 | + // Convert list to set for fast lookup... |
| 513 | + SmallPtrSet<const BasicBlock *, 8> Blocks; |
| 514 | + for (const auto *BB: BBs) |
| 515 | + Blocks.insert(cast<BasicBlock>(VMap[BB])); |
| 516 | + |
| 517 | + outs() << "Checking for crash with changing conditionals to always jump to " |
| 518 | + << (Direction ? "true" : "false") << ":"; |
| 519 | + unsigned NumPrint = Blocks.size(); |
| 520 | + if (NumPrint > 10) |
| 521 | + NumPrint = 10; |
| 522 | + for (unsigned i = 0, e = NumPrint; i != e; ++i) |
| 523 | + outs() << " " << BBs[i]->getName(); |
| 524 | + if (NumPrint < Blocks.size()) |
| 525 | + outs() << "... <" << Blocks.size() << " total>"; |
| 526 | + outs() << ": "; |
| 527 | + |
| 528 | + // Loop over and delete any hack up any blocks that are not listed... |
| 529 | + for (auto &F: *M) |
| 530 | + for (auto &BB : F) |
| 531 | + if (!Blocks.count(&BB)) { |
| 532 | + auto *BR = dyn_cast<BranchInst>(BB.getTerminator()); |
| 533 | + if (!BR || !BR->isConditional()) |
| 534 | + continue; |
| 535 | + if (Direction) |
| 536 | + BR->setCondition(ConstantInt::getTrue(BR->getContext())); |
| 537 | + else |
| 538 | + BR->setCondition(ConstantInt::getFalse(BR->getContext())); |
| 539 | + } |
| 540 | + |
| 541 | + // The following may destroy some blocks, so we save them first |
| 542 | + std::vector<std::pair<std::string, std::string>> BlockInfo; |
| 543 | + |
| 544 | + for (const BasicBlock *BB : Blocks) |
| 545 | + BlockInfo.emplace_back(BB->getParent()->getName(), BB->getName()); |
| 546 | + |
| 547 | + SmallVector<BasicBlock *, 16> ToProcess; |
| 548 | + for (auto &F :*M) { |
| 549 | + for (auto &BB : F) |
| 550 | + if (!Blocks.count(&BB)) |
| 551 | + ToProcess.push_back(&BB); |
| 552 | + simpleSimplifyCfg(F, ToProcess); |
| 553 | + ToProcess.clear(); |
| 554 | + } |
| 555 | + // Verify we didn't break anything |
| 556 | + std::vector<std::string> Passes; |
| 557 | + Passes.push_back("verify"); |
| 558 | + std::unique_ptr<Module> New = BD.runPassesOn(M, Passes); |
| 559 | + delete M; |
| 560 | + if (!New) { |
| 561 | + errs() << "verify failed!\n"; |
| 562 | + exit(1); |
| 563 | + } |
| 564 | + M = New.release(); |
| 565 | + |
| 566 | + // Try running on the hacked up program... |
| 567 | + if (TestFn(BD, M)) { |
| 568 | + BD.setNewProgram(M); // It crashed, keep the trimmed version... |
| 569 | + |
| 570 | + // Make sure to use basic block pointers that point into the now-current |
| 571 | + // module, and that they don't include any deleted blocks. |
| 572 | + BBs.clear(); |
| 573 | + const ValueSymbolTable &GST = M->getValueSymbolTable(); |
| 574 | + for (auto &BI : BlockInfo) { |
| 575 | + auto *F = cast<Function>(GST.lookup(BI.first)); |
| 576 | + ValueSymbolTable &ST = F->getValueSymbolTable(); |
| 577 | + Value *V = ST.lookup(BI.second); |
| 578 | + if (V && V->getType() == Type::getLabelTy(V->getContext())) |
| 579 | + BBs.push_back(cast<BasicBlock>(V)); |
| 580 | + } |
| 581 | + return true; |
| 582 | + } |
| 583 | + delete M; // It didn't crash, try something else. |
| 584 | + return false; |
| 585 | +} |
| 586 | + |
433 | 587 | namespace {
|
434 | 588 | /// ReduceCrashingInstructions reducer - This works by removing the specified
|
435 | 589 | /// non-terminator instructions and replacing them with undef.
|
@@ -813,6 +967,20 @@ static bool DebugACrash(BugDriver &BD,
|
813 | 967 | BD.EmitProgressBitcode(BD.getProgram(), "reduced-function");
|
814 | 968 | }
|
815 | 969 |
|
| 970 | + // Attempt to change conditional branches into unconditional branches to |
| 971 | + // eliminate blocks. |
| 972 | + if (!DisableSimplifyCFG && !BugpointIsInterrupted) { |
| 973 | + std::vector<const BasicBlock*> Blocks; |
| 974 | + for (Function &F : *BD.getProgram()) |
| 975 | + for (BasicBlock &BB : F) |
| 976 | + Blocks.push_back(&BB); |
| 977 | + unsigned OldSize = Blocks.size(); |
| 978 | + ReduceCrashingConditionals(BD, TestFn, true).reduceList(Blocks, Error); |
| 979 | + ReduceCrashingConditionals(BD, TestFn, false).reduceList(Blocks, Error); |
| 980 | + if (Blocks.size() < OldSize) |
| 981 | + BD.EmitProgressBitcode(BD.getProgram(), "reduced-conditionals"); |
| 982 | + } |
| 983 | + |
816 | 984 | // Attempt to delete entire basic blocks at a time to speed up
|
817 | 985 | // convergence... this actually works by setting the terminator of the blocks
|
818 | 986 | // to a return instruction then running simplifycfg, which can potentially
|
|
0 commit comments