Index: include/llvm/IR/DebugInfoMetadata.h =================================================================== --- include/llvm/IR/DebugInfoMetadata.h +++ include/llvm/IR/DebugInfoMetadata.h @@ -778,6 +778,12 @@ unsigned getEncoding() const { return Encoding; } + enum class Signedness { Signed, Unsigned }; + + /// Return the signedness of this type, or None if this type is neither + /// signed nor unsigned. + Optional getSignedness() const; + static bool classof(const Metadata *MD) { return MD->getMetadataID() == DIBasicTypeKind; } @@ -2206,6 +2212,14 @@ /// Determines the size of the variable's type. Optional getSizeInBits() const; + /// Return the signedness of this variable's type, or None if this type is + /// neither signed nor unsigned. + Optional getSignedness() const { + if (auto *BT = dyn_cast(getType().resolve())) + return BT->getSignedness(); + return None; + } + StringRef getFilename() const { if (auto *F = getFile()) return F->getFilename(); Index: include/llvm/Transforms/Utils/Local.h =================================================================== --- include/llvm/Transforms/Utils/Local.h +++ include/llvm/Transforms/Utils/Local.h @@ -337,17 +337,13 @@ /// Assuming the value \p From is going to be deleted, insert replacement /// dbg.value intrinsics for each debug user of \p From. The newly-inserted /// dbg.values refer to \p To instead of \p From. Each replacement dbg.value -/// has the same location and variable as the debug user it replaces, has a -/// DIExpression determined by the result of \p RewriteExpr applied to an old -/// debug user of \p From, and is placed before \p InsertBefore. If -/// \p RewriteExpr returns nullptr, no replacement for the specified debug -/// user is emitted. -void insertReplacementDbgValues( - Value &From, Value &To, Instruction &InsertBefore, - function_ref RewriteExpr); - -/// An overload of insertReplacementDbgValues() for the common case where -/// the replacement dbg.values have the same DIExpressions as the originals. +/// has the same location and variable as the debug user it replaces and is +/// placed before \p InsertBefore. \p From must be convertible to \p To using +/// truncation, (sign|zero|fp)-extension, or the identity mapping. +/// +/// Note that creating replacement dbg.values may fail. This function guarantees +/// that either a dbg.value replacement will be inserted for every debug user of +/// \p From, or no replacements will be inserted. void insertReplacementDbgValues(Value &From, Value &To, Instruction &InsertBefore); Index: lib/IR/DebugInfoMetadata.cpp =================================================================== --- lib/IR/DebugInfoMetadata.cpp +++ lib/IR/DebugInfoMetadata.cpp @@ -283,6 +283,19 @@ Ops); } +Optional DIBasicType::getSignedness() const { + switch (getEncoding()) { + case dwarf::DW_ATE_signed: + case dwarf::DW_ATE_signed_char: + return Signedness::Signed; + case dwarf::DW_ATE_unsigned: + case dwarf::DW_ATE_unsigned_char: + return Signedness::Unsigned; + default: + return None; + } +} + DIDerivedType *DIDerivedType::getImpl( LLVMContext &Context, unsigned Tag, MDString *Name, Metadata *File, unsigned Line, Metadata *Scope, Metadata *BaseType, uint64_t SizeInBits, Index: lib/Transforms/InstCombine/InstCombineCasts.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCasts.cpp +++ lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -270,9 +270,7 @@ auto *Res = CastInst::Create(NewOpc, CSrc->getOperand(0), Ty); // Replace debug users of the eliminable cast by emitting debug values // which refer to the new cast. - if (Ty->isIntegerTy() || Ty->isPointerTy()) - // TODO: Support floats and vectors (see DW_OP_convert, fragment). - insertReplacementDbgValues(*CSrc, *Res, *std::next(CI.getIterator())); + insertReplacementDbgValues(*CSrc, *Res, *std::next(CI.getIterator())); return Res; } } Index: lib/Transforms/Utils/Local.cpp =================================================================== --- lib/Transforms/Utils/Local.cpp +++ lib/Transforms/Utils/Local.cpp @@ -1704,33 +1704,153 @@ } } -void llvm::insertReplacementDbgValues( - Value &From, Value &To, Instruction &InsertBefore, - function_ref RewriteExpr) { +/// A dbg.value replacement consists of a replacement value operand and a new +/// (possibly null) DIExpression. +using DbgValReplacement = std::pair; + +/// Insert replacement dbg.values for \p From. The value operand and +/// DIExpression for new dbg.values is determined by \p RewriteExpr. A null +/// DIExpression indicates that no dbg.value should be emitted. +/// +/// Returns true if \p From has debug users, and all replacement dbg.values for +/// these users were emitted. +static bool replaceDbgValues( + Value &From, Instruction &InsertBefore, + function_ref RewriteExpr) { // Collect all debug users of From. SmallVector Users; findDbgUsers(Users, &From); if (Users.empty()) - return; + return false; // Insert a replacement debug value for each old debug user. It's assumed // that the old debug users will be erased later. + SmallVector Replacements; DIBuilder DIB(*InsertBefore.getModule()); - for (auto *OldDII : Users) - if (DIExpression *Expr = RewriteExpr(*OldDII)) { - auto *I = DIB.insertDbgValueIntrinsic(&To, OldDII->getVariable(), Expr, - OldDII->getDebugLoc().get(), - &InsertBefore); - (void)I; - LLVM_DEBUG(dbgs() << "REPLACE: " << *I << '\n'); - } + for (auto *OldDII : Users) { + DbgValReplacement Replacement = RewriteExpr(*OldDII); + if (!Replacement.second) + break; + + auto *I = DIB.insertDbgValueIntrinsic( + Replacement.first, OldDII->getVariable(), Replacement.second, + OldDII->getDebugLoc().get(), &InsertBefore); + Replacements.push_back(I); + LLVM_DEBUG(dbgs() << "REPLACE: " << *I << '\n'); + } + + // Replacements for all users were inserted. + if (Replacements.size() == Users.size()) + return true; + + // Some replacements could not be emitted. Erase any that were just inserted. + for (Instruction *I : Replacements) { + LLVM_DEBUG(dbgs() << "ERASING INCOMPLETE REPLACEMENT: " << *I << '\n'); + I->eraseFromParent(); + } + return false; +} + +/// Check if the debug info representation of values of type \p FromTy and \p +/// ToTy are compatible. +/// +/// Note that Type::canLosslesslyBitCastTo is not suitable here because it +/// allows semantically unequivalent bitcasts, such as <2 x i64> -> <4 x i32>, +/// but does not allow lossless pointer <-> integer conversions. +static bool hasCompatibleDebugInfoRepresentation(const DataLayout &DL, + Type *FromTy, Type *ToTy) { + // Trivially compatible types. + if (FromTy == ToTy) + return true; + + // Handle compatible pointer <-> integer conversions. + return (FromTy->isPointerTy() || ToTy->isPointerTy()) && + !DL.isNonIntegralPointerType(FromTy) && + !DL.isNonIntegralPointerType(ToTy); } void llvm::insertReplacementDbgValues(Value &From, Value &To, Instruction &InsertBefore) { - return llvm::insertReplacementDbgValues( - From, To, InsertBefore, - [](DbgInfoIntrinsic &OldDII) { return OldDII.getExpression(); }); + Type *FromTy = From.getType(); + Type *ToTy = To.getType(); + + auto Identity = [&](DbgInfoIntrinsic &OldDII) -> DbgValReplacement { + return {&To, OldDII.getExpression()}; + }; + + // The simple case: no conversion between the values is necessary. + const DataLayout &DL = InsertBefore.getModule()->getDataLayout(); + if (hasCompatibleDebugInfoRepresentation(DL, FromTy, ToTy)) { + replaceDbgValues(From, InsertBefore, Identity); + return; + } + + // Handle integer-to-integer widening and narrowing. + // FIXME: Use DW_OP_convert when it's available everywhere. + if (FromTy->isIntegerTy() && ToTy->isIntegerTy()) { + uint64_t FromBits = FromTy->getPrimitiveSizeInBits(); + uint64_t ToBits = ToTy->getPrimitiveSizeInBits(); + + // When the width of the result grows, assume that a debugger will only + // access the low `FromBits` bits when inspecting the source variable. + if (FromBits < ToBits) { + replaceDbgValues(From, InsertBefore, Identity); + return; + } + + // The width of the result has shrunk. First, do sign/zero extension to + // describe the source variable's high bits. + auto SignOrZeroExt = [&](DbgInfoIntrinsic &OldDII) -> DbgValReplacement { + DILocalVariable *Var = OldDII.getVariable(); + + // If the variable has no signedness or has the wrong size, do not + // attempt to replace any dbg.values. + auto Signedness = Var->getSignedness(); + auto VarSize = Var->getSizeInBits(); + if (!Signedness || (!VarSize || *VarSize != FromBits)) + return {nullptr, nullptr}; + + bool Signed = *Signedness == DIBasicType::Signedness::Signed; + + // Sign extension for a >64-bit integer can't be represented yet. + if (Signed && FromBits > 64) + return {nullptr, nullptr}; + + if (!Signed) { + // In the unsigned case, the high bits are just 0. + return {ConstantInt::get(FromTy, 0), + DIExpression::createFragmentExpression( + OldDII.getExpression(), ToBits, FromBits - ToBits) + .getValueOr(nullptr)}; + } else { + // In the signed case, the high bits are given by sign extension, i.e: + // (To >> (ToBits - 1)) * ((2 ^ FromBits) - 1) + uint64_t Mask = APInt::getAllOnesValue(FromBits).getLimitedValue(); + SmallVector Ops({dwarf::DW_OP_constu, (ToBits - 1), + dwarf::DW_OP_shr, dwarf::DW_OP_constu, + Mask, dwarf::DW_OP_mul}); + auto *Expr = DIExpression::prependOpcodes(OldDII.getExpression(), Ops, + DIExpression::WithStackValue); + return {&To, DIExpression::createFragmentExpression(Expr, ToBits, + FromBits - ToBits) + .getValueOr(nullptr)}; + } + }; + bool ReplacedAllUsers = replaceDbgValues(From, InsertBefore, SignOrZeroExt); + if (!ReplacedAllUsers) + return; + + // Finally, describe the low `ToBits` bits of the source variable. + ReplacedAllUsers = replaceDbgValues( + From, InsertBefore, [&](DbgInfoIntrinsic &OldDII) -> DbgValReplacement { + return {&To, DIExpression::createFragmentExpression( + OldDII.getExpression(), 0, ToBits) + .getValueOr(nullptr)}; + }); + assert(ReplacedAllUsers && "Could not describe low bits of variable"); + } + + // TODO: Floating-point conversions, vectors. } unsigned llvm::removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB) { Index: test/Transforms/InstCombine/cast-set-preserve-signed-dbg-val.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/cast-set-preserve-signed-dbg-val.ll @@ -0,0 +1,53 @@ +; RUN: opt -instcombine -S < %s | FileCheck %s + +; CHECK-LABEL: define {{.*}} @test5 +define i16 @test5(i16 %A) !dbg !34 { + ; CHECK: [[and:%.*]] = and i16 %A, 15 + + %B = sext i16 %A to i32, !dbg !40 + call void @llvm.dbg.value(metadata i32 %B, metadata !36, metadata !DIExpression()), !dbg !40 + + %C = and i32 %B, 15, !dbg !41 + call void @llvm.dbg.value(metadata i32 %C, metadata !37, metadata !DIExpression()), !dbg !41 + + ; Preserve the dbg.value for the DCE'd 32-bit 'and'. + ; + ; The high 16 bits of the 'and' require sign-extending the new 16-bit and: + ; CHECK-NEXT: call void @llvm.dbg.value(metadata i16 [[and]], metadata [[C:![0-9]+]], + ; CHECK-SAME: metadata !DIExpression(DW_OP_constu, 15, DW_OP_shr, DW_OP_constu, 4294967295, DW_OP_mul, DW_OP_stack_value, DW_OP_LLVM_fragment, 16, 16)) + ; + ; The low 16 bits of the 'and' are exactly equal to the result of the 16-bit 'and': + ; CHECK-NEXT: call void @llvm.dbg.value(metadata i16 [[and]], metadata [[C]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 16)) + + %D = trunc i32 %C to i16, !dbg !42 + call void @llvm.dbg.value(metadata i16 %D, metadata !38, metadata !DIExpression()), !dbg !42 + + ; The dbg.value for a truncate should simply point to the result of the 16-bit 'and'. + ; CHECK-NEXT: call void @llvm.dbg.value(metadata i16 [[and]], metadata [[D:![0-9]+]], metadata !DIExpression()) + + ret i16 %D, !dbg !43 + ; CHECK-NEXT: ret i16 [[and]] +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "void", directory: "/") +!2 = !{} +!5 = !{i32 2, !"Debug Info Version", i32 3} +!7 = !DISubroutineType(types: !2) +!10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_signed) +!12 = !DIBasicType(name: "ty8", size: 8, encoding: DW_ATE_signed) +!34 = distinct !DISubprogram(name: "test5", linkageName: "test5", scope: null, file: !1, line: 12, type: !7, isLocal: false, isDefinition: true, scopeLine: 12, isOptimized: true, unit: !0, retainedNodes: !35) +!35 = !{!36, !37, !38} +!36 = !DILocalVariable(name: "B", scope: !34, file: !1, line: 12, type: !10) +!37 = !DILocalVariable(name: "C", scope: !34, file: !1, line: 13, type: !10) +!38 = !DILocalVariable(name: "D", scope: !34, file: !1, line: 14, type: !39) +!39 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_signed) +!40 = !DILocation(line: 12, column: 1, scope: !34) +!41 = !DILocation(line: 13, column: 1, scope: !34) +!42 = !DILocation(line: 14, column: 1, scope: !34) +!43 = !DILocation(line: 15, column: 1, scope: !34) Index: test/Transforms/InstCombine/dce-iterate.ll =================================================================== --- test/Transforms/InstCombine/dce-iterate.ll +++ test/Transforms/InstCombine/dce-iterate.ll @@ -1,9 +1,20 @@ -; RUN: opt < %s -instcombine -S | grep "ret double .sy" +; RUN: opt < %s -debugify -instcombine -S | FileCheck %s +; CHECK-LABEL: define {{.*}} @ScaleObjectAdd define internal double @ScaleObjectAdd(double %sx, double %sy, double %sz) nounwind { entry: %sx34 = bitcast double %sx to i64 ; [#uses=1] %sx3435 = zext i64 %sx34 to i960 ; [#uses=1] + +; The i64 -> i960 zero extension is DCE'd. Check that we preserve debug values +; for the intermediate SSA values. +; +; Bits 64..960 of the zext (just 0): +; CHECK: call void @llvm.dbg.value(metadata i960 0, metadata [[sx3435:![0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 896)) +; +; Bits 1..64 of the zext. This is the bitcasted double %sx. +; CHECK-NEXT: call void @llvm.dbg.value(metadata double %sx, metadata [[sx3435]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64)) + %sy22 = bitcast double %sy to i64 ; [#uses=1] %sy2223 = zext i64 %sy22 to i960 ; [#uses=1] %sy222324 = shl i960 %sy2223, 320 ; [#uses=1] @@ -20,5 +31,8 @@ %e = bitcast i64 %d to double ; [#uses=1] %f = fadd double %b, %e +; CHECK: ret double %sy ret double %e } + +; CHECK: [[sx3435]] = !DILocalVariable(name: "2"