/// \brief Track the brokenness of the module while recursively visiting.
bool Broken;
+ bool EverBroken;
explicit VerifierSupport(raw_ostream &OS)
- : OS(OS), M(nullptr), Broken(false) {}
+ : OS(OS), M(nullptr), Broken(false), EverBroken(false) {}
private:
void Write(const Value *V) {
OS << '\n';
}
+ void Write(const NamedMDNode *NMD) {
+ if (!NMD)
+ return;
+ NMD->print(OS);
+ OS << '\n';
+ }
+
void Write(Type *T) {
if (!T)
return;
/// something is not correct.
void CheckFailed(const Twine &Message) {
OS << Message << '\n';
- Broken = true;
+ EverBroken = Broken = true;
}
/// \brief A check failed (with values to print).
visitModuleFlags(M);
visitModuleIdents(M);
+ // Verify debug info last.
+ verifyDebugInfo();
+
return !Broken;
}
#define HANDLE_SPECIALIZED_MDNODE_LEAF(CLASS) void visit##CLASS(const CLASS &N);
#include "llvm/IR/Metadata.def"
+ void visitMDScope(const MDScope &N);
+ void visitMDDerivedTypeBase(const MDDerivedTypeBase &N);
+ void visitMDVariable(const MDVariable &N);
// InstVisitor overrides...
using InstVisitor<Verifier>::visit;
void VerifyConstantExprBitcastType(const ConstantExpr *CE);
void VerifyStatepoint(ImmutableCallSite CS);
void verifyFrameRecoverIndices();
-};
-class DebugInfoVerifier : public VerifierSupport {
-public:
- explicit DebugInfoVerifier(raw_ostream &OS = dbgs()) : VerifierSupport(OS) {}
-
- bool verify(const Module &M) {
- this->M = &M;
- verifyDebugInfo();
- return !Broken;
- }
-private:
+ // Module-level debug info verification...
void verifyDebugInfo();
void processInstructions(DebugInfoFinder &Finder);
void processCallInst(DebugInfoFinder &Finder, const CallInst &CI);
if (!MD)
continue;
+ if (NMD.getName() == "llvm.dbg.cu") {
+ Assert(isa<MDCompileUnit>(MD), "invalid compile unit", &NMD, MD);
+ }
+
visitMDNode(*MD);
}
}
visitValueAsMetadata(*V, F);
}
+/// \brief Check if a value can be a reference to a type.
+static bool isTypeRef(const Metadata *MD) {
+ if (!MD)
+ return true;
+ if (auto *S = dyn_cast<MDString>(MD))
+ return !S->getString().empty();
+ return isa<MDType>(MD);
+}
+
+/// \brief Check if a value can be a ScopeRef.
+static bool isScopeRef(const Metadata *MD) {
+ if (!MD)
+ return true;
+ if (auto *S = dyn_cast<MDString>(MD))
+ return !S->getString().empty();
+ return isa<MDScope>(MD);
+}
+
void Verifier::visitMDLocation(const MDLocation &N) {
- Assert(N.getScope(), "location requires a valid scope", &N);
- if (auto *IA = N.getInlinedAt())
+ Assert(N.getRawScope() && isa<MDLocalScope>(N.getRawScope()),
+ "location requires a valid scope", &N, N.getRawScope());
+ if (auto *IA = N.getRawInlinedAt())
Assert(isa<MDLocation>(IA), "inlined-at should be a location", &N, IA);
}
Assert(N.getTag(), "invalid tag", &N);
}
+void Verifier::visitMDScope(const MDScope &N) {
+ if (auto *F = N.getRawFile())
+ Assert(isa<MDFile>(F), "invalid file", &N, F);
+}
+
void Verifier::visitMDSubrange(const MDSubrange &N) {
Assert(N.getTag() == dwarf::DW_TAG_subrange_type, "invalid tag", &N);
+ Assert(N.getCount() >= -1, "invalid subrange count", &N);
}
void Verifier::visitMDEnumerator(const MDEnumerator &N) {
"invalid tag", &N);
}
+void Verifier::visitMDDerivedTypeBase(const MDDerivedTypeBase &N) {
+ // Common scope checks.
+ visitMDScope(N);
+
+ Assert(isScopeRef(N.getScope()), "invalid scope", &N, N.getScope());
+ Assert(isTypeRef(N.getBaseType()), "invalid base type", &N, N.getBaseType());
+}
+
void Verifier::visitMDDerivedType(const MDDerivedType &N) {
+ // Common derived type checks.
+ visitMDDerivedTypeBase(N);
+
Assert(N.getTag() == dwarf::DW_TAG_typedef ||
N.getTag() == dwarf::DW_TAG_pointer_type ||
N.getTag() == dwarf::DW_TAG_ptr_to_member_type ||
}
void Verifier::visitMDCompositeType(const MDCompositeType &N) {
+ // Common derived type checks.
+ visitMDDerivedTypeBase(N);
+
Assert(N.getTag() == dwarf::DW_TAG_array_type ||
N.getTag() == dwarf::DW_TAG_structure_type ||
N.getTag() == dwarf::DW_TAG_union_type ||
N.getTag() == dwarf::DW_TAG_subroutine_type ||
N.getTag() == dwarf::DW_TAG_class_type,
"invalid tag", &N);
+
+ Assert(!N.getRawElements() || isa<MDTuple>(N.getRawElements()),
+ "invalid composite elements", &N, N.getRawElements());
+ Assert(isTypeRef(N.getRawVTableHolder()), "invalid vtable holder", &N,
+ N.getRawVTableHolder());
+ Assert(!N.getRawElements() || isa<MDTuple>(N.getRawElements()),
+ "invalid composite elements", &N, N.getRawElements());
}
void Verifier::visitMDSubroutineType(const MDSubroutineType &N) {
Assert(N.getTag() == dwarf::DW_TAG_subroutine_type, "invalid tag", &N);
+ if (auto *Types = N.getRawTypeArray()) {
+ Assert(isa<MDTuple>(Types), "invalid composite elements", &N, Types);
+ for (Metadata *Ty : N.getTypeArray()->operands()) {
+ Assert(isTypeRef(Ty), "invalid subroutine type ref", &N, Types, Ty);
+ }
+ }
}
void Verifier::visitMDFile(const MDFile &N) {
void Verifier::visitMDCompileUnit(const MDCompileUnit &N) {
Assert(N.getTag() == dwarf::DW_TAG_compile_unit, "invalid tag", &N);
+
+ if (auto *Array = N.getRawEnumTypes()) {
+ Assert(isa<MDTuple>(Array), "invalid enum list", &N, Array);
+ for (Metadata *Op : N.getEnumTypes()->operands()) {
+ auto *Enum = dyn_cast_or_null<MDCompositeType>(Op);
+ Assert(Enum && Enum->getTag() == dwarf::DW_TAG_enumeration_type,
+ "invalid enum type", &N, N.getEnumTypes(), Op);
+ }
+ }
+ if (auto *Array = N.getRawRetainedTypes()) {
+ Assert(isa<MDTuple>(Array), "invalid retained type list", &N, Array);
+ for (Metadata *Op : N.getRetainedTypes()->operands()) {
+ Assert(Op && isa<MDType>(Op), "invalid retained type", &N, Op);
+ }
+ }
+ if (auto *Array = N.getRawSubprograms()) {
+ Assert(isa<MDTuple>(Array), "invalid subprogram list", &N, Array);
+ for (Metadata *Op : N.getSubprograms()->operands()) {
+ Assert(Op && isa<MDSubprogram>(Op), "invalid subprogram ref", &N, Op);
+ }
+ }
+ if (auto *Array = N.getRawGlobalVariables()) {
+ Assert(isa<MDTuple>(Array), "invalid global variable list", &N, Array);
+ for (Metadata *Op : N.getGlobalVariables()->operands()) {
+ Assert(Op && isa<MDGlobalVariable>(Op), "invalid global variable ref", &N,
+ Op);
+ }
+ }
+ if (auto *Array = N.getRawImportedEntities()) {
+ Assert(isa<MDTuple>(Array), "invalid imported entity list", &N, Array);
+ for (Metadata *Op : N.getImportedEntities()->operands()) {
+ Assert(Op && isa<MDImportedEntity>(Op), "invalid imported entity ref", &N,
+ Op);
+ }
+ }
}
void Verifier::visitMDSubprogram(const MDSubprogram &N) {
"invalid tag", &N);
}
+void Verifier::visitMDVariable(const MDVariable &N) {
+ if (auto *S = N.getRawScope())
+ Assert(isa<MDScope>(S), "invalid scope", &N, S);
+ Assert(isTypeRef(N.getRawType()), "invalid type ref", &N, N.getRawType());
+ if (auto *F = N.getRawFile())
+ Assert(isa<MDFile>(F), "invalid file", &N, F);
+}
+
void Verifier::visitMDGlobalVariable(const MDGlobalVariable &N) {
+ // Checks common to all variables.
+ visitMDVariable(N);
+
Assert(N.getTag() == dwarf::DW_TAG_variable, "invalid tag", &N);
+ if (auto *V = N.getRawVariable()) {
+ Assert(isa<ConstantAsMetadata>(V) &&
+ !isa<Function>(cast<ConstantAsMetadata>(V)->getValue()),
+ "invalid global varaible ref", &N, V);
+ }
+ if (auto *Member = N.getRawStaticDataMemberDeclaration()) {
+ Assert(isa<MDDerivedType>(Member), "invalid static data member declaration",
+ &N, Member);
+ }
}
void Verifier::visitMDLocalVariable(const MDLocalVariable &N) {
+ // Checks common to all variables.
+ visitMDVariable(N);
+
Assert(N.getTag() == dwarf::DW_TAG_auto_variable ||
N.getTag() == dwarf::DW_TAG_arg_variable,
"invalid tag", &N);
+ Assert(N.getRawScope() && isa<MDLocalScope>(N.getRawScope()),
+ "local variable requires a valid scope", &N, N.getRawScope());
+ if (auto *IA = N.getRawInlinedAt())
+ Assert(isa<MDLocation>(IA), "local variable requires a valid scope", &N,
+ IA);
}
void Verifier::visitMDExpression(const MDExpression &N) {
&I);
}
+ if (MDNode *N = I.getDebugLoc().getAsMDNode()) {
+ Assert(isa<MDLocation>(N), "invalid !dbg metadata attachment", &I, N);
+ visitMDNode(*N);
+ }
+
InstsInThisBlock.insert(&I);
}
break;
}
+ case Intrinsic::eh_parentframe: {
+ auto *AI = dyn_cast<AllocaInst>(CI.getArgOperand(0)->stripPointerCasts());
+ Assert(AI && AI->isStaticAlloca(),
+ "llvm.eh.parentframe requires a static alloca", &CI);
+ break;
+ }
+
+ case Intrinsic::eh_unwindhelp: {
+ auto *AI = dyn_cast<AllocaInst>(CI.getArgOperand(0)->stripPointerCasts());
+ Assert(AI && AI->isStaticAlloca(),
+ "llvm.eh.unwindhelp requires a static alloca", &CI);
+ break;
+ }
+
case Intrinsic::experimental_gc_statepoint:
Assert(!CI.isInlineAsm(),
"gc.statepoint support for inline assembly unimplemented", &CI);
+ Assert(CI.getParent()->getParent()->hasGC(),
+ "Enclosing function does not use GC.", &CI);
VerifyStatepoint(ImmutableCallSite(&CI));
break;
case Intrinsic::experimental_gc_result_float:
case Intrinsic::experimental_gc_result_ptr:
case Intrinsic::experimental_gc_result: {
+ Assert(CI.getParent()->getParent()->hasGC(),
+ "Enclosing function does not use GC.", &CI);
// Are we tied to a statepoint properly?
CallSite StatepointCS(CI.getArgOperand(0));
const Function *StatepointFn =
DII.getRawExpression());
}
-void DebugInfoVerifier::verifyDebugInfo() {
- if (!VerifyDebugInfo)
+void Verifier::verifyDebugInfo() {
+ // Run the debug info verifier only if the regular verifier succeeds, since
+ // sometimes checks that have already failed will cause crashes here.
+ if (EverBroken || !VerifyDebugInfo)
return;
DebugInfoFinder Finder;
}
}
-void DebugInfoVerifier::processInstructions(DebugInfoFinder &Finder) {
+void Verifier::processInstructions(DebugInfoFinder &Finder) {
for (const Function &F : *M)
for (auto I = inst_begin(&F), E = inst_end(&F); I != E; ++I) {
if (MDNode *MD = I->getMetadata(LLVMContext::MD_dbg))
}
}
-void DebugInfoVerifier::processCallInst(DebugInfoFinder &Finder,
- const CallInst &CI) {
+void Verifier::processCallInst(DebugInfoFinder &Finder, const CallInst &CI) {
if (Function *F = CI.getCalledFunction())
if (Intrinsic::ID ID = (Intrinsic::ID)F->getIntrinsicID())
switch (ID) {
// Note that this function's return value is inverted from what you would
// expect of a function called "verify".
- if (!V.verify(M) || Broken)
- return true;
-
- // Run the debug info verifier only if the regular verifier succeeds, since
- // sometimes checks that have already failed will cause crashes here.
- DebugInfoVerifier DIV(OS ? *OS : NullStr);
- return !DIV.verify(M);
+ return !V.verify(M) || Broken;
}
namespace {
AU.setPreservesAll();
}
};
-struct DebugInfoVerifierLegacyPass : public ModulePass {
- static char ID;
-
- DebugInfoVerifier V;
- bool FatalErrors;
-
- DebugInfoVerifierLegacyPass() : ModulePass(ID), FatalErrors(true) {
- initializeDebugInfoVerifierLegacyPassPass(*PassRegistry::getPassRegistry());
- }
- explicit DebugInfoVerifierLegacyPass(bool FatalErrors)
- : ModulePass(ID), V(dbgs()), FatalErrors(FatalErrors) {
- initializeDebugInfoVerifierLegacyPassPass(*PassRegistry::getPassRegistry());
- }
-
- bool runOnModule(Module &M) override {
- if (!V.verify(M) && FatalErrors)
- report_fatal_error("Broken debug info found, compilation aborted!");
-
- return false;
- }
-
- void getAnalysisUsage(AnalysisUsage &AU) const override {
- AU.setPreservesAll();
- }
-};
}
char VerifierLegacyPass::ID = 0;
INITIALIZE_PASS(VerifierLegacyPass, "verify", "Module Verifier", false, false)
-char DebugInfoVerifierLegacyPass::ID = 0;
-INITIALIZE_PASS(DebugInfoVerifierLegacyPass, "verify-di", "Debug Info Verifier",
- false, false)
-
FunctionPass *llvm::createVerifierPass(bool FatalErrors) {
return new VerifierLegacyPass(FatalErrors);
}
-ModulePass *llvm::createDebugInfoVerifierPass(bool FatalErrors) {
- return new DebugInfoVerifierLegacyPass(FatalErrors);
-}
-
PreservedAnalyses VerifierPass::run(Module &M) {
if (verifyModule(M, &dbgs()) && FatalErrors)
report_fatal_error("Broken module found, compilation aborted!");