diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -1537,7 +1537,8 @@ static void HandleByValArgumentInit(Type *ByValType, Value *Dst, Value *Src, Module *M, BasicBlock *InsertBlock, - InlineFunctionInfo &IFI) { + InlineFunctionInfo &IFI, + Function *CalledFunc) { IRBuilder<> Builder(InsertBlock, InsertBlock->begin()); Value *Size = @@ -1546,8 +1547,15 @@ // Always generate a memcpy of alignment 1 here because we don't know // the alignment of the src pointer. Other optimizations can infer // better alignment. - Builder.CreateMemCpy(Dst, /*DstAlign*/ Align(1), Src, - /*SrcAlign*/ Align(1), Size); + CallInst *CI = Builder.CreateMemCpy(Dst, /*DstAlign*/ Align(1), Src, + /*SrcAlign*/ Align(1), Size); + + // The verifier requires that all calls of debug-info-bearing functions + // from debug-info-bearing functions have a debug location (for inlining + // purposes). Assign a dummy location to satisfy the constraint. + if (!CI->getDebugLoc() && InsertBlock->getParent()->getSubprogram()) + if (DISubprogram *SP = CalledFunc->getSubprogram()) + CI->setDebugLoc(DILocation::get(SP->getContext(), 0, 0, SP)); } /// When inlining a call site that has a byval argument, @@ -2242,7 +2250,7 @@ // Inject byval arguments initialization. for (ByValInit &Init : ByValInits) HandleByValArgumentInit(Init.Ty, Init.Dst, Init.Src, Caller->getParent(), - &*FirstNewBlock, IFI); + &*FirstNewBlock, IFI, CalledFunc); std::optional ParentDeopt = CB.getOperandBundle(LLVMContext::OB_deopt); diff --git a/llvm/test/Transforms/Inline/byval-dbg.ll b/llvm/test/Transforms/Inline/byval-dbg.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Inline/byval-dbg.ll @@ -0,0 +1,49 @@ +; RUN: opt < %s -passes=inline -S | FileCheck %s + +; Check that the memcpy generated by byval lowering is decorated by debuginfo. + +target datalayout = "p:32:32-p1:64:64-p2:16:16-n16:32:64" + +%struct.ss = type { i32, i64 } + +define internal void @f(ptr byval(%struct.ss) %b) nounwind !dbg !4 { +entry: + %tmp = getelementptr %struct.ss, ptr %b, i32 0, i32 0 ; [#uses=2] + %tmp1 = load i32, ptr %tmp, align 4 ; [#uses=1] + %tmp2 = add i32 %tmp1, 1 ; [#uses=1] + store i32 %tmp2, ptr %tmp, align 4 + ret void +} + +define i32 @test1() nounwind !dbg !10 { +entry: + %S = alloca %struct.ss ; [#uses=4] + %tmp1 = getelementptr %struct.ss, ptr %S, i32 0, i32 0 ; [#uses=1] + store i32 1, ptr %tmp1, align 8 + %tmp4 = getelementptr %struct.ss, ptr %S, i32 0, i32 1 ; [#uses=1] + store i64 2, ptr %tmp4, align 4 + call void @f(ptr byval(%struct.ss) %S) nounwind, !dbg !14 + ret i32 0 +; CHECK: @test1() +; CHECK: call void @llvm.memcpy{{.*}} !dbg +; CHECK: ret i32 0 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "/app/example.c", directory: "/app") +!2 = !{i32 7, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = distinct !DISubprogram(name: "f", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !9) +!5 = !DIFile(filename: "example.c", directory: "/app") +!6 = !DISubroutineType(types: !7) +!7 = !{null, !8} +!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!9 = !{} +!10 = distinct !DISubprogram(name: "test1", scope: !5, file: !5, line: 4, type: !11, scopeLine: 4, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !9) +!11 = !DISubroutineType(types: !12) +!12 = !{!13} +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !DILocation(line: 6, column: 1, scope: !10)