Index: lib/IR/DebugInfoMetadata.cpp =================================================================== --- lib/IR/DebugInfoMetadata.cpp +++ lib/IR/DebugInfoMetadata.cpp @@ -80,17 +80,47 @@ if (!dyn_cast_or_null(ForInst)) return nullptr; + // Find closest common scope for LocA and LocB. + DIScope *CommonScope = nullptr; + SmallPtrSet ScopesA; + for (DIScope *S = LocA->getScope(); S; S = S->getScope().resolve()) + ScopesA.insert(S); + for (DIScope *S = LocB->getScope(); S; S = S->getScope().resolve()) + if (ScopesA.count(S)) { + CommonScope = S; + break; + } + + // If no common scope exists, make up a new compiler-generated location. + // At this point the inlinedAt field will be meaningless as well. + if (!CommonScope) + return DILocation::get(LocA->getContext(), + 0, 0, LocA->getInlinedAtScope()); + + // If either LocA or LocB is not inlined, the merged location does not + // need to be inlined either. + if (LocA->getInlinedAt() == nullptr || LocB->getInlinedAt() == nullptr) + return DILocation::get(LocA->getContext(), 0, 0, CommonScope); + + // Otherwise find the closest common inlined-at location for LocA and LocB. + DILocation *CommonInlinedAt = nullptr; SmallPtrSet InlinedLocationsA; for (DILocation *L = LocA->getInlinedAt(); L; L = L->getInlinedAt()) InlinedLocationsA.insert(L); - const DILocation *Result = LocB; - for (DILocation *L = LocB->getInlinedAt(); L; L = L->getInlinedAt()) { - Result = L; - if (InlinedLocationsA.count(L)) + for (DILocation *L = LocB->getInlinedAt(); L; L = L->getInlinedAt()) + if (InlinedLocationsA.count(L)) { + CommonInlinedAt = L; break; - } - return DILocation::get(Result->getContext(), 0, 0, Result->getScope(), - Result->getInlinedAt()); + } + + // If no common inlined-at location exists, mark as inlined at a + // compiler-generated location in the current function. + if (!CommonInlinedAt) + CommonInlinedAt = DILocation::get(LocA->getContext(), + 0, 0, LocA->getInlinedAtScope()); + + return DILocation::get(LocA->getContext(), + 0, 0, CommonScope, CommonInlinedAt); } DINode::DIFlags DINode::getFlag(StringRef Flag) { Index: test/Transforms/SimplifyCFG/hoist-dbgvalue-inlined.ll =================================================================== --- test/Transforms/SimplifyCFG/hoist-dbgvalue-inlined.ll +++ test/Transforms/SimplifyCFG/hoist-dbgvalue-inlined.ll @@ -0,0 +1,47 @@ +; RUN: opt -simplifycfg -S < %s | FileCheck %s +; Verify that we don't crash due an invalid !dbg location on the hoisted llvm.dbg.value + +define i64 @caller(i64* %ptr, i64 %flag) !dbg !10 { +; CHECK: llvm.dbg.value +init: + %v9 = icmp eq i64 %flag, 0 + br i1 %v9, label %a, label %b + +a: ; preds = %init + %vala = load i64, i64* %ptr, align 8 + call void @llvm.dbg.value(metadata i64 %vala, metadata !8, metadata !DIExpression()), !dbg !12 + br label %test.exit + +b: ; preds = %init + %valb = load i64, i64* %ptr, align 8 + call void @llvm.dbg.value(metadata i64 %valb, metadata !8, metadata !DIExpression()), !dbg !13 + %valbmasked = and i64 %valb, 1 + br label %test.exit + +test.exit: ; preds = %a, %b + %retv = phi i64 [ %vala, %a ], [ %valbmasked, %b ] + ret i64 %retv +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.value(metadata, metadata, metadata) #0 + +attributes #0 = { nounwind readnone speculatable } + +!llvm.module.flags = !{!0} +!llvm.dbg.cu = !{!1} + +!0 = !{i32 2, !"Debug Info Version", i32 3} +!1 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !2, isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !3) +!2 = !DIFile(filename: "optbug", directory: "") +!3 = !{} +!4 = distinct !DISubprogram(name: "callee", scope: !2, file: !2, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !1, variables: !7) +!5 = !DISubroutineType(types: !6) +!6 = !{null} +!7 = !{!8} +!8 = !DILocalVariable(name: "var", scope: !4, file: !2, type: !9) +!9 = !DIBasicType(name: "var_t", size: 64, encoding: DW_ATE_unsigned) +!10 = distinct !DISubprogram(name: "caller", scope: !2, file: !2, line: 5, type: !5, isLocal: false, isDefinition: true, scopeLine: 5, isOptimized: false, unit: !1, variables: !3) +!11 = distinct !DILocation(line: 6, scope: !10) +!12 = !DILocation(line: 2, scope: !4, inlinedAt: !11) +!13 = !DILocation(line: 3, scope: !4, inlinedAt: !11) Index: unittests/IR/MetadataTest.cpp =================================================================== --- unittests/IR/MetadataTest.cpp +++ unittests/IR/MetadataTest.cpp @@ -883,6 +883,81 @@ EXPECT_TRUE(L2->isTemporary()); } +TEST_F(DILocationTest, getMergedLocation) { + FunctionType *FT = FunctionType::get(Type::getVoidTy(Context), false); + auto *F = Function::Create(FT, GlobalValue::ExternalLinkage); + const CallInst *Call = CallInst::Create(F, None); + + // Merging two identical locations should result in the same location. + { + DISubprogram *Prg = getSubprogram(); + DILocation *L1 = DILocation::get(Context, 2, 7, Prg); + DILocation *L2 = DILocation::get(Context, 2, 7, Prg); + const DILocation *L = DILocation::getMergedLocation(L1, L2, nullptr); + EXPECT_EQ(2u, L->getLine()); + EXPECT_EQ(7u, L->getColumn()); + EXPECT_EQ(Prg, L->getScope()); + } + // If the two locations are incompatible, there should be a null result. + { + DISubprogram *Prg = getSubprogram(); + DILocation *L1 = DILocation::get(Context, 2, 7, Prg); + DILocation *L2 = DILocation::get(Context, 3, 7, Prg); + const DILocation *L = DILocation::getMergedLocation(L1, L2, nullptr); + EXPECT_EQ(nullptr, L); + } + // Unless we are merging locations for a call instruction. If the + // scopes agree, the result should be a compiler-generated location + // in that same scope. + { + DISubprogram *Prg = getSubprogram(); + DILocation *L1 = DILocation::get(Context, 2, 7, Prg); + DILocation *L2 = DILocation::get(Context, 3, 7, Prg); + const DILocation *L = DILocation::getMergedLocation(L1, L2, Call); + EXPECT_EQ(0u, L->getLine()); + EXPECT_EQ(Prg, L->getScope()); + } + // Likewise if the one scope is a sub-scope of the other. + { + DISubprogram *Prg = getSubprogram(); + DILexicalBlock *B = DILexicalBlock::get(Context, Prg, getFile(), 5, 7); + DILocation *L1 = DILocation::get(Context, 2, 7, Prg); + DILocation *L2 = DILocation::get(Context, 3, 7, B); + const DILocation *L = DILocation::getMergedLocation(L1, L2, Call); + EXPECT_EQ(0u, L->getLine()); + EXPECT_EQ(Prg, L->getScope()); + } + // Even if the calls were inlined (possibly into different places), + // if they originate from the same scope, the result should also be + // a compiler-generated location in that same scope. + { + DISubprogram *IPrg = getSubprogram(); + DILocation *I1 = DILocation::get(Context, 10, 7, IPrg); + DILocation *I2 = DILocation::get(Context, 11, 7, IPrg); + DISubprogram *Prg = getSubprogram(); + DILocation *L1 = DILocation::get(Context, 2, 7, Prg, I1); + DILocation *L2 = DILocation::get(Context, 3, 7, Prg, I2); + const DILocation *L = DILocation::getMergedLocation(L1, L2, Call); + EXPECT_EQ(0u, L->getLine()); + EXPECT_EQ(Prg, L->getScope()); + EXPECT_NE(nullptr, L->getInlinedAt()); + } + // Likewise if the one scope is a sub-scope of the other. + { + DISubprogram *IPrg = getSubprogram(); + DILocation *I1 = DILocation::get(Context, 10, 7, IPrg); + DILocation *I2 = DILocation::get(Context, 11, 7, IPrg); + DISubprogram *Prg = getSubprogram(); + DILexicalBlock *B = DILexicalBlock::get(Context, Prg, getFile(), 5, 7); + DILocation *L1 = DILocation::get(Context, 2, 7, Prg, I1); + DILocation *L2 = DILocation::get(Context, 3, 7, B, I2); + const DILocation *L = DILocation::getMergedLocation(L1, L2, Call); + EXPECT_EQ(0u, L->getLine()); + EXPECT_EQ(Prg, L->getScope()); + EXPECT_NE(nullptr, L->getInlinedAt()); + } +} + typedef MetadataTest GenericDINodeTest; TEST_F(GenericDINodeTest, get) {