|
15 | 15 | #include "ListReducer.h"
|
16 | 16 | #include "ToolRunner.h"
|
17 | 17 | #include "llvm/ADT/SmallPtrSet.h"
|
| 18 | +#include "llvm/ADT/StringSet.h" |
18 | 19 | #include "llvm/IR/CFG.h"
|
19 | 20 | #include "llvm/IR/Constants.h"
|
20 | 21 | #include "llvm/IR/DerivedTypes.h"
|
@@ -49,6 +50,10 @@ namespace {
|
49 | 50 | DontReducePassList("disable-pass-list-reduction",
|
50 | 51 | cl::desc("Skip pass list reduction steps"),
|
51 | 52 | cl::init(false));
|
| 53 | + |
| 54 | + cl::opt<bool> NoNamedMDRM("disable-namedmd-remove", |
| 55 | + cl::desc("Do not remove global named metadata"), |
| 56 | + cl::init(false)); |
52 | 57 | }
|
53 | 58 |
|
54 | 59 | namespace llvm {
|
@@ -497,6 +502,149 @@ bool ReduceCrashingInstructions::TestInsts(std::vector<const Instruction*>
|
497 | 502 | return false;
|
498 | 503 | }
|
499 | 504 |
|
| 505 | +namespace { |
| 506 | +// Reduce the list of Named Metadata nodes. We keep this as a list of |
| 507 | +// names to avoid having to convert back and forth every time. |
| 508 | +class ReduceCrashingNamedMD : public ListReducer<std::string> { |
| 509 | + BugDriver &BD; |
| 510 | + bool (*TestFn)(const BugDriver &, Module *); |
| 511 | + |
| 512 | +public: |
| 513 | + ReduceCrashingNamedMD(BugDriver &bd, |
| 514 | + bool (*testFn)(const BugDriver &, Module *)) |
| 515 | + : BD(bd), TestFn(testFn) {} |
| 516 | + |
| 517 | + TestResult doTest(std::vector<std::string> &Prefix, |
| 518 | + std::vector<std::string> &Kept, |
| 519 | + std::string &Error) override { |
| 520 | + if (!Kept.empty() && TestNamedMDs(Kept)) |
| 521 | + return KeepSuffix; |
| 522 | + if (!Prefix.empty() && TestNamedMDs(Prefix)) |
| 523 | + return KeepPrefix; |
| 524 | + return NoFailure; |
| 525 | + } |
| 526 | + |
| 527 | + bool TestNamedMDs(std::vector<std::string> &NamedMDs); |
| 528 | +}; |
| 529 | +} |
| 530 | + |
| 531 | +bool ReduceCrashingNamedMD::TestNamedMDs(std::vector<std::string> &NamedMDs) { |
| 532 | + |
| 533 | + ValueToValueMapTy VMap; |
| 534 | + Module *M = CloneModule(BD.getProgram(), VMap); |
| 535 | + |
| 536 | + outs() << "Checking for crash with only these named metadata nodes:"; |
| 537 | + unsigned NumPrint = std::min<size_t>(NamedMDs.size(), 10); |
| 538 | + for (unsigned i = 0, e = NumPrint; i != e; ++i) |
| 539 | + outs() << " " << NamedMDs[i]; |
| 540 | + if (NumPrint < NamedMDs.size()) |
| 541 | + outs() << "... <" << NamedMDs.size() << " total>"; |
| 542 | + outs() << ": "; |
| 543 | + |
| 544 | + // Make a StringMap for faster lookup |
| 545 | + StringSet<> Names; |
| 546 | + for (const std::string &Name : NamedMDs) |
| 547 | + Names.insert(Name); |
| 548 | + |
| 549 | + // First collect all the metadata to delete in a vector, then |
| 550 | + // delete them all at once to avoid invalidating the iterator |
| 551 | + std::vector<NamedMDNode *> ToDelete; |
| 552 | + ToDelete.reserve(M->named_metadata_size() - Names.size()); |
| 553 | + for (auto &NamedMD : M->named_metadata()) |
| 554 | + if (!Names.count(NamedMD.getName())) |
| 555 | + ToDelete.push_back(&NamedMD); |
| 556 | + |
| 557 | + for (auto *NamedMD : ToDelete) |
| 558 | + NamedMD->eraseFromParent(); |
| 559 | + |
| 560 | + // Verify that this is still valid. |
| 561 | + legacy::PassManager Passes; |
| 562 | + Passes.add(createVerifierPass()); |
| 563 | + Passes.run(*M); |
| 564 | + |
| 565 | + // Try running on the hacked up program... |
| 566 | + if (TestFn(BD, M)) { |
| 567 | + BD.setNewProgram(M); // It crashed, keep the trimmed version... |
| 568 | + return true; |
| 569 | + } |
| 570 | + delete M; // It didn't crash, try something else. |
| 571 | + return false; |
| 572 | +} |
| 573 | + |
| 574 | +namespace { |
| 575 | +// Reduce the list of operands to named metadata nodes |
| 576 | +class ReduceCrashingNamedMDOps : public ListReducer<const MDNode *> { |
| 577 | + BugDriver &BD; |
| 578 | + bool (*TestFn)(const BugDriver &, Module *); |
| 579 | + |
| 580 | +public: |
| 581 | + ReduceCrashingNamedMDOps(BugDriver &bd, |
| 582 | + bool (*testFn)(const BugDriver &, Module *)) |
| 583 | + : BD(bd), TestFn(testFn) {} |
| 584 | + |
| 585 | + TestResult doTest(std::vector<const MDNode *> &Prefix, |
| 586 | + std::vector<const MDNode *> &Kept, |
| 587 | + std::string &Error) override { |
| 588 | + if (!Kept.empty() && TestNamedMDOps(Kept)) |
| 589 | + return KeepSuffix; |
| 590 | + if (!Prefix.empty() && TestNamedMDOps(Prefix)) |
| 591 | + return KeepPrefix; |
| 592 | + return NoFailure; |
| 593 | + } |
| 594 | + |
| 595 | + bool TestNamedMDOps(std::vector<const MDNode *> &NamedMDOps); |
| 596 | +}; |
| 597 | +} |
| 598 | + |
| 599 | +bool ReduceCrashingNamedMDOps::TestNamedMDOps( |
| 600 | + std::vector<const MDNode *> &NamedMDOps) { |
| 601 | + // Convert list to set for fast lookup... |
| 602 | + SmallPtrSet<const MDNode *, 64> OldMDNodeOps; |
| 603 | + for (unsigned i = 0, e = NamedMDOps.size(); i != e; ++i) { |
| 604 | + OldMDNodeOps.insert(NamedMDOps[i]); |
| 605 | + } |
| 606 | + |
| 607 | + outs() << "Checking for crash with only " << OldMDNodeOps.size(); |
| 608 | + if (OldMDNodeOps.size() == 1) |
| 609 | + outs() << " named metadata operand: "; |
| 610 | + else |
| 611 | + outs() << " named metadata operands: "; |
| 612 | + |
| 613 | + ValueToValueMapTy VMap; |
| 614 | + Module *M = CloneModule(BD.getProgram(), VMap); |
| 615 | + |
| 616 | + // This is a little wasteful. In the future it might be good if we could have |
| 617 | + // these dropped during cloning. |
| 618 | + for (auto &NamedMD : BD.getProgram()->named_metadata()) { |
| 619 | + // Drop the old one and create a new one |
| 620 | + M->eraseNamedMetadata(M->getNamedMetadata(NamedMD.getName())); |
| 621 | + NamedMDNode *NewNamedMDNode = |
| 622 | + M->getOrInsertNamedMetadata(NamedMD.getName()); |
| 623 | + for (MDNode *op : NamedMD.operands()) |
| 624 | + if (OldMDNodeOps.count(op)) |
| 625 | + NewNamedMDNode->addOperand(cast<MDNode>(MapMetadata(op, VMap))); |
| 626 | + } |
| 627 | + |
| 628 | + // Verify that this is still valid. |
| 629 | + legacy::PassManager Passes; |
| 630 | + Passes.add(createVerifierPass()); |
| 631 | + Passes.run(*M); |
| 632 | + |
| 633 | + // Try running on the hacked up program... |
| 634 | + if (TestFn(BD, M)) { |
| 635 | + // Make sure to use instruction pointers that point into the now-current |
| 636 | + // module, and that they don't include any deleted blocks. |
| 637 | + NamedMDOps.clear(); |
| 638 | + for (const MDNode *Node : OldMDNodeOps) |
| 639 | + NamedMDOps.push_back(cast<MDNode>(VMap.MD()[Node].get())); |
| 640 | + |
| 641 | + BD.setNewProgram(M); // It crashed, keep the trimmed version... |
| 642 | + return true; |
| 643 | + } |
| 644 | + delete M; // It didn't crash, try something else. |
| 645 | + return false; |
| 646 | +} |
| 647 | + |
500 | 648 | /// DebugACrash - Given a predicate that determines whether a component crashes
|
501 | 649 | /// on a program, try to destructively reduce the program while still keeping
|
502 | 650 | /// the predicate true.
|
@@ -661,6 +809,31 @@ static bool DebugACrash(BugDriver &BD,
|
661 | 809 | }
|
662 | 810 |
|
663 | 811 | } while (Simplification);
|
| 812 | + |
| 813 | + if (!NoNamedMDRM) { |
| 814 | + BD.EmitProgressBitcode(BD.getProgram(), "reduced-instructions"); |
| 815 | + |
| 816 | + if (!BugpointIsInterrupted) { |
| 817 | + // Try to reduce the amount of global metadata (particularly debug info), |
| 818 | + // by dropping global named metadata that anchors them |
| 819 | + outs() << "\n*** Attempting to remove named metadata: "; |
| 820 | + std::vector<std::string> NamedMDNames; |
| 821 | + for (auto &NamedMD : BD.getProgram()->named_metadata()) |
| 822 | + NamedMDNames.push_back(NamedMD.getName().str()); |
| 823 | + ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames, Error); |
| 824 | + } |
| 825 | + |
| 826 | + if (!BugpointIsInterrupted) { |
| 827 | + // Now that we quickly dropped all the named metadata that doesn't |
| 828 | + // contribute to the crash, bisect the operands of the remaining ones |
| 829 | + std::vector<const MDNode *> NamedMDOps; |
| 830 | + for (auto &NamedMD : BD.getProgram()->named_metadata()) |
| 831 | + NamedMDOps.insert(NamedMDOps.end(), NamedMD.op_begin(), |
| 832 | + NamedMD.op_end()); |
| 833 | + ReduceCrashingNamedMDOps(BD, TestFn).reduceList(NamedMDOps, Error); |
| 834 | + } |
| 835 | + } |
| 836 | + |
664 | 837 | ExitLoops:
|
665 | 838 |
|
666 | 839 | // Try to clean up the testcase by running funcresolve and globaldce...
|
|
0 commit comments