diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -2552,6 +2552,11 @@ return 0; } + using ExtOps = std::array; + + /// Returns the ops for a zero- or sign-extension in a DIExpression. + static ExtOps getExtOps(unsigned FromSize, unsigned ToSize, bool Signed); + /// Append a zero- or sign-extension to \p Expr. Converts the expression to a /// stack value if it isn't one already. static DIExpression *appendExt(const DIExpression *Expr, unsigned FromSize, diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5549,8 +5549,26 @@ = [&](ArrayRef> SplitRegs) { unsigned Offset = 0; for (auto RegAndSize : SplitRegs) { + // If the expression is already a fragment, the current register + // offset+size might extend beyond the fragment. In this case, only + // the register bits that are inside the fragment are relevant. + int RegFragmentSizeInBits = RegAndSize.second; + if (auto ExprFragmentInfo = Expr->getFragmentInfo()) { + uint64_t ExprFragmentSizeInBits = ExprFragmentInfo->SizeInBits; + // The register is entirely outside the expression fragment, + // so is irrelevant for debug info. + if (Offset >= ExprFragmentSizeInBits) + break; + // The register is partially outside the expression fragment, only + // the low bits within the fragment are relevant for debug info. + if (Offset + RegFragmentSizeInBits > ExprFragmentSizeInBits) { + RegFragmentSizeInBits = ExprFragmentSizeInBits - Offset; + } + } + auto FragmentExpr = DIExpression::createFragmentExpression( - Expr, Offset, RegAndSize.second); + Expr, Offset, RegFragmentSizeInBits); + Offset += RegAndSize.second; // If a valid fragment expression cannot be created, the variable's // correct value cannot be determined and so it is set as Undef. if (!FragmentExpr) { @@ -5563,7 +5581,6 @@ FuncInfo.ArgDbgValues.push_back( BuildMI(MF, DL, TII->get(TargetOpcode::DBG_VALUE), false, RegAndSize.first, Variable, *FragmentExpr)); - Offset += RegAndSize.second; } }; diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -1196,13 +1196,18 @@ return true; } +DIExpression::ExtOps DIExpression::getExtOps(unsigned FromSize, unsigned ToSize, + bool Signed) { + dwarf::TypeKind TK = Signed ? dwarf::DW_ATE_signed : dwarf::DW_ATE_unsigned; + DIExpression::ExtOps Ops({dwarf::DW_OP_LLVM_convert, FromSize, TK, + dwarf::DW_OP_LLVM_convert, ToSize, TK}); + return Ops; +} + DIExpression *DIExpression::appendExt(const DIExpression *Expr, unsigned FromSize, unsigned ToSize, bool Signed) { - dwarf::TypeKind TK = Signed ? dwarf::DW_ATE_signed : dwarf::DW_ATE_unsigned; - uint64_t Ops[] = {dwarf::DW_OP_LLVM_convert, FromSize, TK, - dwarf::DW_OP_LLVM_convert, ToSize, TK}; - return appendToStack(Expr, Ops); + return appendToStack(Expr, getExtOps(FromSize, ToSize, Signed)); } DIGlobalVariableExpression * diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -1676,9 +1676,8 @@ }; // initializer-list helper for applying operators to the source DIExpression. - auto applyOps = - [&](std::initializer_list Opcodes) -> DIExpression * { - SmallVector Ops(Opcodes); + auto applyOps = [&](ArrayRef Opcodes) -> DIExpression * { + SmallVector Ops(Opcodes.begin(), Opcodes.end()); return doSalvage(Ops); }; @@ -1686,8 +1685,21 @@ // No-op casts and zexts are irrelevant for debug info. if (CI->isNoopCast(DL) || isa(&I)) return SrcDIExpr; - return nullptr; - } else if (auto *GEP = dyn_cast(&I)) { + + Type *Type = CI->getType(); + // Casts other than Trunc or SExt to scalar types cannot be salvaged. + if (Type->isVectorTy() || (!isa(&I) && !isa(&I))) + return nullptr; + + Value *FromValue = CI->getOperand(0); + unsigned FromTypeBitSize = FromValue->getType()->getScalarSizeInBits(); + unsigned ToTypeBitSize = Type->getScalarSizeInBits(); + + return applyOps(DIExpression::getExtOps(FromTypeBitSize, ToTypeBitSize, + isa(&I))); + } + + if (auto *GEP = dyn_cast(&I)) { unsigned BitWidth = M.getDataLayout().getIndexSizeInBits(GEP->getPointerAddressSpace()); // Rewrite a constant GEP into a DIExpression. diff --git a/llvm/test/CodeGen/ARM/fragmented-args-multiple-regs.ll b/llvm/test/CodeGen/ARM/fragmented-args-multiple-regs.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/fragmented-args-multiple-regs.ll @@ -0,0 +1,72 @@ +; RUN: llc < %s -mtriple=armv7-linux-gnueabihf -O1 -stop-after=finalize-isel | FileCheck %s + +define dso_local i32 @h(i64 %j) local_unnamed_addr !dbg !8 { +entry: + call void @llvm.dbg.value(metadata i64 %j, metadata !14, metadata !DIExpression()), !dbg !29 + call void @llvm.dbg.value(metadata i64 %j, metadata !15, metadata !DIExpression(DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32)), !dbg !29 + call void @llvm.dbg.value(metadata i64 %j, metadata !15, metadata !DIExpression(DW_OP_constu, 32, DW_OP_shr, DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value, DW_OP_LLVM_fragment, 32, 32)), !dbg !29 + %tobool = icmp ult i64 %j, 4294967296, !dbg !30 + br i1 %tobool, label %cleanup, label %if.then, !dbg !31 + +if.then: ; preds = %entry + call void @llvm.dbg.value(metadata i64 %j, metadata !15, metadata !DIExpression(DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 32)), !dbg !29 + %conv = sitofp i64 %j to double, !dbg !32 + %add = fadd double %conv, 0x43F0000000000000, !dbg !33 + call void @llvm.dbg.value(metadata double %add, metadata !25, metadata !DIExpression()), !dbg !34 + %conv2 = fptosi double %add to i32, !dbg !35 + br label %cleanup + +cleanup: ; preds = %entry, %if.then + %retval.0 = phi i32 [ %conv2, %if.then ], [ undef, %entry ] + ret i32 %retval.0, !dbg !36 +} + +; CHECK-LABEL: bb.0.entry: +; CHECK: DBG_VALUE [[REG1:%[0-9]+]], $noreg, !14, !DIExpression(DW_OP_LLVM_fragment, 32, 32 +; CHECK: DBG_VALUE [[REG2:%[0-9]+]], $noreg, !14, !DIExpression(DW_OP_LLVM_fragment, 0, 32 +; CHECK: DBG_VALUE [[REG2]], $noreg, !15, !DIExpression({{.+}}DW_OP_LLVM_fragment, 0, 32 +; CHECK: DBG_VALUE $noreg, $noreg, !15, !DIExpression({{.+}}DW_OP_LLVM_fragment, 32, 32 + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "tif_aux.c", directory: "/tmp") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 1, !"min_enum_size", i32 4} +!7 = !{!"clang version 10.0.0 "} +!8 = distinct !DISubprogram(name: "h", scope: !1, file: !1, line: 10, type: !9, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) +!9 = !DISubroutineType(types: !10) +!10 = !{!11, !12} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed) +!13 = !{!14, !15, !25} +!14 = !DILocalVariable(name: "j", arg: 1, scope: !8, file: !1, line: 10, type: !12) +!15 = !DILocalVariable(name: "i", scope: !8, file: !1, line: 11, type: !16) +!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "g", file: !1, line: 8, baseType: !17) +!17 = distinct !DICompositeType(tag: DW_TAG_union_type, file: !1, line: 5, size: 64, elements: !18) +!18 = !{!19, !24} +!19 = !DIDerivedType(tag: DW_TAG_member, name: "e", scope: !17, file: !1, line: 6, baseType: !20, size: 64) +!20 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "b", file: !1, line: 1, size: 64, elements: !21) +!21 = !{!22, !23} +!22 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !20, file: !1, line: 2, baseType: !11, size: 32) +!23 = !DIDerivedType(tag: DW_TAG_member, name: "d", scope: !20, file: !1, line: 2, baseType: !11, size: 32, offset: 32) +!24 = !DIDerivedType(tag: DW_TAG_member, name: "f", scope: !17, file: !1, line: 7, baseType: !12, size: 64) +!25 = !DILocalVariable(name: "a", scope: !26, file: !1, line: 14, type: !28) +!26 = distinct !DILexicalBlock(scope: !27, file: !1, line: 13, column: 14) +!27 = distinct !DILexicalBlock(scope: !8, file: !1, line: 13, column: 7) +!28 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!29 = !DILocation(line: 0, scope: !8) +!30 = !DILocation(line: 13, column: 7, scope: !27) +!31 = !DILocation(line: 13, column: 7, scope: !8) +!32 = !DILocation(line: 14, column: 16, scope: !26) +!33 = !DILocation(line: 14, column: 20, scope: !26) +!34 = !DILocation(line: 0, scope: !26) +!35 = !DILocation(line: 15, column: 12, scope: !26) +!36 = !DILocation(line: 17, column: 1, scope: !8) diff --git a/llvm/test/DebugInfo/X86/dbg-value-dropped-instcombine.ll b/llvm/test/DebugInfo/X86/dbg-value-dropped-instcombine.ll --- a/llvm/test/DebugInfo/X86/dbg-value-dropped-instcombine.ll +++ b/llvm/test/DebugInfo/X86/dbg-value-dropped-instcombine.ll @@ -21,8 +21,8 @@ ; } ; CHECK: define dso_local i64 @foo -; CHECK: @llvm.dbg.value({{.*}}, metadata ![[BEE:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), -; CHECK: @llvm.dbg.value(metadata i32 undef, metadata ![[BEE]], metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32)), +; CHECK: @llvm.dbg.value(metadata i64 {{.*}}, metadata ![[BEE:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), +; CHECK: @llvm.dbg.value(metadata i64 {{.*}}, metadata ![[BEE]], metadata !DIExpression({{.*}}, DW_OP_LLVM_fragment, 32, 32)), target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-unknown" diff --git a/llvm/test/DebugInfo/salvage-cast-debug-info.ll b/llvm/test/DebugInfo/salvage-cast-debug-info.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/salvage-cast-debug-info.ll @@ -0,0 +1,25 @@ +; RUN: opt %s -debugify -early-cse -S | FileCheck %s +define i32 @foo(i64 %nose, i32 %more) { +; CHECK-LABEL: @foo( +; CHECK: call void @llvm.dbg.value(metadata i64 %nose, metadata [[V1:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned +; CHECK: call void @llvm.dbg.value(metadata i64 %nose.shift, metadata [[V2:![0-9]+]] +; CHECK: call void @llvm.dbg.value(metadata i64 %nose.shift, metadata [[V3:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_convert, 64, DW_ATE_unsigned, DW_OP_LLVM_convert, 32, DW_ATE_unsigned + +entry: + %nose.trunc = trunc i64 %nose to i32 + %nose.shift = lshr i64 %nose, 32 + %nose.trunc.2 = trunc i64 %nose.shift to i32 + %add = add nsw i32 %more, 1 + ret i32 %add +} + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 2} +!1 = !{i32 7, !"PIC Level", i32 2} +!2 = !{!"clang version 10.0.0 "} + +; CHECK: [[V1]] = !DILocalVariable( +; CHECK: [[V2]] = !DILocalVariable( +; CHECK: [[V3]] = !DILocalVariable( diff --git a/llvm/test/Transforms/EarlyCSE/debug-info-undef.ll b/llvm/test/Transforms/EarlyCSE/debug-info-undef.ll --- a/llvm/test/Transforms/EarlyCSE/debug-info-undef.ll +++ b/llvm/test/Transforms/EarlyCSE/debug-info-undef.ll @@ -10,7 +10,7 @@ %0 = load i8, i8* @a, align 1, !dbg !19, !tbaa !20 %conv = sext i8 %0 to i16, !dbg !19 -; CHECK: call void @llvm.dbg.value(metadata i16 undef, metadata !17, metadata !DIExpression()), !dbg !18 +; CHECK: call void @llvm.dbg.value(metadata i8 %0, metadata !17, metadata !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 16, DW_ATE_signed, DW_OP_stack_value)), !dbg !18 ; CHECK-NEXT: call i32 (...) @optimize_me_not() call void @llvm.dbg.value(metadata i16 %conv, metadata !17, metadata !DIExpression()), !dbg !18 diff --git a/llvm/test/Transforms/InstCombine/cast-mul-select.ll b/llvm/test/Transforms/InstCombine/cast-mul-select.ll --- a/llvm/test/Transforms/InstCombine/cast-mul-select.ll +++ b/llvm/test/Transforms/InstCombine/cast-mul-select.ll @@ -13,8 +13,8 @@ ; we preserve the debug information in the resulting ; instruction. ; DBGINFO-LABEL: @mul( -; DBGINFO-NEXT: call void @llvm.dbg.value(metadata i8 undef -; DBGINFO-NEXT: call void @llvm.dbg.value(metadata i8 undef +; DBGINFO-NEXT: call void @llvm.dbg.value(metadata i32 %x +; DBGINFO-NEXT: call void @llvm.dbg.value(metadata i32 %y ; DBGINFO-NEXT: [[C:%.*]] = mul i32 {{.*}} ; DBGINFO-NEXT: [[D:%.*]] = and i32 {{.*}} ; DBGINFO-NEXT: call void @llvm.dbg.value(metadata i32 [[C]] diff --git a/llvm/test/Transforms/InstCombine/pr43893.ll b/llvm/test/Transforms/InstCombine/pr43893.ll --- a/llvm/test/Transforms/InstCombine/pr43893.ll +++ b/llvm/test/Transforms/InstCombine/pr43893.ll @@ -13,9 +13,10 @@ ;CHECK: call void @llvm.dbg.value(metadata i32 -8 ;CHECK: call void @llvm.dbg.value(metadata i32 undef %conv = sext i8 %dec to i32, !dbg !17 - call void @llvm.dbg.value(metadata i32 %conv, metadata !18, metadata !DIExpression()), !dbg !19 + %udiv = udiv i32 %conv, 4, !dbg !17 + call void @llvm.dbg.value(metadata i32 %udiv, metadata !18, metadata !DIExpression()), !dbg !19 call void @llvm.dbg.value(metadata i32 -8, metadata !20, metadata !DIExpression()), !dbg !19 - call void @llvm.dbg.value(metadata i32 %conv, metadata !20, metadata !DIExpression()), !dbg !19 + call void @llvm.dbg.value(metadata i32 %udiv, metadata !20, metadata !DIExpression()), !dbg !19 store i8 0, i8* @b, align 1, !dbg !21 %cmp = icmp sgt i32 %conv, 0, !dbg !22 %conv1 = zext i1 %cmp to i32, !dbg !22 diff --git a/llvm/test/Transforms/InstCombine/unavailable-debug.ll b/llvm/test/Transforms/InstCombine/unavailable-debug.ll --- a/llvm/test/Transforms/InstCombine/unavailable-debug.ll +++ b/llvm/test/Transforms/InstCombine/unavailable-debug.ll @@ -2,7 +2,7 @@ ; Make sure to update the debug value after dead code elimination. ; CHECK: %call = call signext i8 @b(i32 6), !dbg !39 -; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 undef, metadata !30, metadata !DIExpression()), !dbg !38 +; CHECK-NEXT: call void @llvm.dbg.value(metadata i8 %call, metadata !30, metadata !DIExpression(DW_OP_LLVM_convert, 8, DW_ATE_signed, DW_OP_LLVM_convert, 32, DW_ATE_signed, DW_OP_stack_value)), !dbg !38 @e = common local_unnamed_addr global i8 0, align 1, !dbg !0 @c = common local_unnamed_addr global i32 0, align 4, !dbg !6