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 @@ -2799,6 +2799,14 @@ /// DW_OP_LLVM_arg op as its first operand, or if it contains none. bool isSingleLocationExpression() const; + /// Removes all elements from \p Expr that do not apply to an undef debug + /// value, which includes every operator that computes the value/location on + /// the DWARF stack, including any DW_OP_LLVM_arg elements (making the result + /// of this function always a single-location expression) while leaving + /// everything that defines what the computed value applies to, i.e. the + /// fragment information. + static const DIExpression *convertToUndefExpression(const DIExpression *Expr); + /// If \p Expr is a non-variadic expression (i.e. one that does not contain /// DW_OP_LLVM_arg), returns \p Expr converted to variadic form by adding a /// leading [DW_OP_LLVM_arg, 0] to the expression; otherwise returns \p Expr. diff --git a/llvm/lib/CodeGen/MachineFunction.cpp b/llvm/lib/CodeGen/MachineFunction.cpp --- a/llvm/lib/CodeGen/MachineFunction.cpp +++ b/llvm/lib/CodeGen/MachineFunction.cpp @@ -44,7 +44,6 @@ #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constant.h" #include "llvm/IR/DataLayout.h" -#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" @@ -1136,52 +1135,58 @@ auto MakeUndefDbgValue = [&](MachineInstr &MI) { const MCInstrDesc &RefII = TII->get(TargetOpcode::DBG_VALUE_LIST); MI.setDesc(RefII); - MI.getDebugOperand(0).setReg(0); + MI.setDebugValueUndef(); }; DenseMap ArgDbgPHIs; for (auto &MBB : *this) { for (auto &MI : MBB) { - if (!MI.isDebugRef() || !MI.getDebugOperand(0).isReg()) + if (!MI.isDebugRef()) continue; - Register Reg = MI.getDebugOperand(0).getReg(); + bool IsValidRef = true; - // Some vregs can be deleted as redundant in the meantime. Mark those - // as DBG_VALUE $noreg. Additionally, some normal instructions are - // quickly deleted, leaving dangling references to vregs with no def. - if (Reg == 0 || !RegInfo->hasOneDef(Reg)) { - MakeUndefDbgValue(MI); - continue; - } - // Only convert Expr to variadic form when we're sure we're emitting a - // complete instruction reference. - MI.getDebugExpressionOp().setMetadata( - DIExpression::convertToVariadicExpression(MI.getDebugExpression())); - - assert(Reg.isVirtual()); - MachineInstr &DefMI = *RegInfo->def_instr_begin(Reg); - - // If we've found a copy-like instruction, follow it back to the - // instruction that defines the source value, see salvageCopySSA docs - // for why this is important. - if (DefMI.isCopyLike() || TII->isCopyInstr(DefMI)) { - auto Result = salvageCopySSA(DefMI, ArgDbgPHIs); - MI.getDebugOperand(0).ChangeToDbgInstrRef(Result.first, Result.second); - } else { - // Otherwise, identify the operand number that the VReg refers to. - unsigned OperandIdx = 0; - for (const auto &MO : DefMI.operands()) { - if (MO.isReg() && MO.isDef() && MO.getReg() == Reg) - break; - ++OperandIdx; + for (MachineOperand &MO : MI.debug_operands()) { + if (!MO.isReg()) + continue; + + Register Reg = MO.getReg(); + + // Some vregs can be deleted as redundant in the meantime. Mark those + // as DBG_VALUE $noreg. Additionally, some normal instructions are + // quickly deleted, leaving dangling references to vregs with no def. + if (Reg == 0 || !RegInfo->hasOneDef(Reg)) { + IsValidRef = false; + break; } - assert(OperandIdx < DefMI.getNumOperands()); - // Morph this instr ref to point at the given instruction and operand. - unsigned ID = DefMI.getDebugInstrNum(); - MI.getDebugOperand(0).ChangeToDbgInstrRef(ID, OperandIdx); + assert(Reg.isVirtual()); + MachineInstr &DefMI = *RegInfo->def_instr_begin(Reg); + + // If we've found a copy-like instruction, follow it back to the + // instruction that defines the source value, see salvageCopySSA docs + // for why this is important. + if (DefMI.isCopyLike() || TII->isCopyInstr(DefMI)) { + auto Result = salvageCopySSA(DefMI, ArgDbgPHIs); + MO.ChangeToDbgInstrRef(Result.first, Result.second); + } else { + // Otherwise, identify the operand number that the VReg refers to. + unsigned OperandIdx = 0; + for (const auto &DefMO : DefMI.operands()) { + if (DefMO.isReg() && DefMO.isDef() && DefMO.getReg() == Reg) + break; + ++OperandIdx; + } + assert(OperandIdx < DefMI.getNumOperands()); + + // Morph this instr ref to point at the given instruction and operand. + unsigned ID = DefMI.getDebugInstrNum(); + MO.ChangeToDbgInstrRef(ID, OperandIdx); + } } + + if (!IsValidRef) + MakeUndefDbgValue(MI); } } } diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1256,8 +1256,9 @@ // If using instruction referencing, produce this as a DBG_INSTR_REF, // to be later patched up by finalizeDebugInstrRefs. Tack a deref onto // the expression, we don't have an "indirect" flag in DBG_INSTR_REF. - auto *NewExpr = - DIExpression::prepend(DI->getExpression(), DIExpression::DerefBefore); + SmallVector Ops( + {dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_deref}); + auto *NewExpr = DIExpression::prependOpcodes(DI->getExpression(), Ops); BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD.getDL(), TII.get(TargetOpcode::DBG_INSTR_REF), /*IsIndirect*/ false, *Op, DI->getVariable(), NewExpr); @@ -1325,9 +1326,11 @@ /* isKill */ false, /* isDead */ false, /* isUndef */ false, /* isEarlyClobber */ false, /* SubReg */ 0, /* isDebug */ true)}); + SmallVector Ops({dwarf::DW_OP_LLVM_arg, 0}); + auto *NewExpr = DIExpression::prependOpcodes(DI->getExpression(), Ops); BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD.getDL(), TII.get(TargetOpcode::DBG_INSTR_REF), /*IsIndirect*/ false, MOs, - DI->getVariable(), DI->getExpression()); + DI->getVariable(), NewExpr); } } else { // We don't know how to handle other cases, so we drop. diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h @@ -126,6 +126,10 @@ /// Emit a DBG_VALUE $noreg, indicating a variable has no location. MachineInstr *EmitDbgNoLocation(SDDbgValue *SD); + /// Emit a DBG_VALUE_LIST from the operands to SDDbgValue. + MachineInstr *EmitDbgValueList(SDDbgValue *SD, + DenseMap &VRBaseMap); + /// Emit a DBG_VALUE from the operands to SDDbgValue. MachineInstr *EmitDbgValueFromSingleOp(SDDbgValue *SD, DenseMap &VRBaseMap); diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -678,7 +678,6 @@ InstrEmitter::EmitDbgValue(SDDbgValue *SD, DenseMap &VRBaseMap) { MDNode *Var = SD->getVariable(); - MDNode *Expr = SD->getExpression(); DebugLoc DL = SD->getDebugLoc(); assert(cast(Var)->isValidLocationForIntrinsic(DL) && "Expected inlined-at fields to agree"); @@ -691,29 +690,41 @@ if (SD->isInvalidated()) return EmitDbgNoLocation(SD); - // Emit variadic dbg_value nodes as DBG_VALUE_LIST. - if (SD->isVariadic()) { - // DBG_VALUE_LIST := "DBG_VALUE_LIST" var, expression, loc (, loc)* - const MCInstrDesc &DbgValDesc = TII->get(TargetOpcode::DBG_VALUE_LIST); - // Build the DBG_VALUE_LIST instruction base. - auto MIB = BuildMI(*MF, DL, DbgValDesc); - MIB.addMetadata(Var); - MIB.addMetadata(Expr); - AddDbgValueLocationOps(MIB, DbgValDesc, LocationOps, VRBaseMap); - return &*MIB; - } - // Attempt to produce a DBG_INSTR_REF if we've been asked to. - // We currently exclude the possibility of instruction references for - // variadic nodes; if at some point we enable them, this should be moved - // above the variadic block. if (EmitDebugInstrRefs) if (auto *InstrRef = EmitDbgInstrRef(SD, VRBaseMap)) return InstrRef; + // Emit variadic dbg_value nodes as DBG_VALUE_LIST if they have not been + // emitted as instruction references. + if (SD->isVariadic()) + return EmitDbgValueList(SD, VRBaseMap); + + // Emit single-location dbg_value nodes as DBG_VALUE if they have not been + // emitted as instruction references. return EmitDbgValueFromSingleOp(SD, VRBaseMap); } +MachineOperand GetMOForConstDbgOp(const SDDbgOperand &Op) { + const Value *V = Op.getConst(); + if (const ConstantInt *CI = dyn_cast(V)) { + if (CI->getBitWidth() > 64) + return MachineOperand::CreateCImm(CI); + return MachineOperand::CreateImm(CI->getSExtValue()); + } + if (const ConstantFP *CF = dyn_cast(V)) + return MachineOperand::CreateFPImm(CF); + // Note: This assumes that all nullptr constants are zero-valued. + if (isa(V)) + return MachineOperand::CreateImm(0); + // Undef or unhandled value type, so return an undef operand. + return MachineOperand::CreateReg( + /* Reg */ 0U, /* isDef */ false, /* isImp */ false, + /* isKill */ false, /* isDead */ false, + /* isUndef */ false, /* isEarlyClobber */ false, + /* SubReg */ 0, /* isDebug */ true); +} + void InstrEmitter::AddDbgValueLocationOps( MachineInstrBuilder &MIB, const MCInstrDesc &DbgValDesc, ArrayRef LocationOps, @@ -739,24 +750,9 @@ AddOperand(MIB, V, (*MIB).getNumOperands(), &DbgValDesc, VRBaseMap, /*IsDebug=*/true, /*IsClone=*/false, /*IsCloned=*/false); } break; - case SDDbgOperand::CONST: { - const Value *V = Op.getConst(); - if (const ConstantInt *CI = dyn_cast(V)) { - if (CI->getBitWidth() > 64) - MIB.addCImm(CI); - else - MIB.addImm(CI->getSExtValue()); - } else if (const ConstantFP *CF = dyn_cast(V)) { - MIB.addFPImm(CF); - } else if (isa(V)) { - // Note: This assumes that all nullptr constants are zero-valued. - MIB.addImm(0); - } else { - // Could be an Undef. In any case insert an Undef so we can see what we - // dropped. - MIB.addReg(0U); - } - } break; + case SDDbgOperand::CONST: + MIB.add(GetMOForConstDbgOp(Op)); + break; } } } @@ -764,101 +760,130 @@ MachineInstr * InstrEmitter::EmitDbgInstrRef(SDDbgValue *SD, DenseMap &VRBaseMap) { - assert(!SD->isVariadic()); - SDDbgOperand DbgOperand = SD->getLocationOps()[0]; MDNode *Var = SD->getVariable(); const DIExpression *Expr = (DIExpression *)SD->getExpression(); DebugLoc DL = SD->getDebugLoc(); const MCInstrDesc &RefII = TII->get(TargetOpcode::DBG_INSTR_REF); - // Handle variable locations that don't actually depend on the instructions - // in the program: constants and stack locations. - if (DbgOperand.getKind() == SDDbgOperand::FRAMEIX || - DbgOperand.getKind() == SDDbgOperand::CONST) + // Returns true if the given operand is not a legal debug operand for a + // DBG_INSTR_REF. + auto IsInvalidOp = [](SDDbgOperand DbgOp) { + return DbgOp.getKind() == SDDbgOperand::FRAMEIX; + }; + // Returns true if the given operand is not itself an instruction reference + // but is a legal debug operand for a DBG_INSTR_REF. + auto IsNonInstrRefOp = [](SDDbgOperand DbgOp) { + return DbgOp.getKind() == SDDbgOperand::CONST; + }; + + // If this variable location does not depend on any instructions or contains + // any stack locations, produce it as a standard debug value instead. + if (any_of(SD->getLocationOps(), IsInvalidOp) || + all_of(SD->getLocationOps(), IsNonInstrRefOp)) { + if (SD->isVariadic()) + return EmitDbgValueList(SD, VRBaseMap); return EmitDbgValueFromSingleOp(SD, VRBaseMap); + } // Immediately fold any indirectness from the LLVM-IR intrinsic into the // expression: - if (SD->isIndirect()) { - std::vector Elts = {dwarf::DW_OP_deref}; - Expr = DIExpression::append(Expr, Elts); - } + if (SD->isIndirect()) + Expr = DIExpression::append(Expr, dwarf::DW_OP_deref); + // If this is not already a variadic expression, it must be modified to become + // one. + if (!SD->isVariadic()) + Expr = DIExpression::convertToVariadicExpression(Expr); + + SmallVector MOs; // It may not be immediately possible to identify the MachineInstr that // defines a VReg, it can depend for example on the order blocks are // emitted in. When this happens, or when further analysis is needed later, // produce an instruction like this: // - // DBG_INSTR_REF %0:gr64, 0, !123, !456 + // DBG_INSTR_REF !123, !456, %0:gr64 // // i.e., point the instruction at the vreg, and patch it up later in // MachineFunction::finalizeDebugInstrRefs. - auto EmitHalfDoneInstrRef = [&](unsigned VReg) -> MachineInstr * { - SmallVector MOs({MachineOperand::CreateReg( + auto AddVRegOp = [&](unsigned VReg) { + MOs.push_back(MachineOperand::CreateReg( /* Reg */ VReg, /* isDef */ false, /* isImp */ false, /* isKill */ false, /* isDead */ false, /* isUndef */ false, /* isEarlyClobber */ false, - /* SubReg */ 0, /* isDebug */ true)}); - return BuildMI(*MF, DL, RefII, false, MOs, Var, Expr); + /* SubReg */ 0, /* isDebug */ true)); }; + unsigned OpCount = SD->getLocationOps().size(); + for (unsigned OpIdx = 0; OpIdx < OpCount; ++OpIdx) { + SDDbgOperand DbgOperand = SD->getLocationOps()[OpIdx]; - // Try to find both the defined register and the instruction defining it. - MachineInstr *DefMI = nullptr; - unsigned VReg; + // Try to find both the defined register and the instruction defining it. + MachineInstr *DefMI = nullptr; + unsigned VReg; - if (DbgOperand.getKind() == SDDbgOperand::VREG) { - VReg = DbgOperand.getVReg(); + if (DbgOperand.getKind() == SDDbgOperand::VREG) { + VReg = DbgOperand.getVReg(); - // No definition means that block hasn't been emitted yet. Leave a vreg - // reference to be fixed later. - if (!MRI->hasOneDef(VReg)) - return EmitHalfDoneInstrRef(VReg); + // No definition means that block hasn't been emitted yet. Leave a vreg + // reference to be fixed later. + if (!MRI->hasOneDef(VReg)) { + AddVRegOp(VReg); + continue; + } - DefMI = &*MRI->def_instr_begin(VReg); - } else { - assert(DbgOperand.getKind() == SDDbgOperand::SDNODE); - // Look up the corresponding VReg for the given SDNode, if any. - SDNode *Node = DbgOperand.getSDNode(); - SDValue Op = SDValue(Node, DbgOperand.getResNo()); - DenseMap::iterator I = VRBaseMap.find(Op); - // No VReg -> produce a DBG_VALUE $noreg instead. - if (I==VRBaseMap.end()) - return EmitDbgNoLocation(SD); - - // Try to pick out a defining instruction at this point. - VReg = getVR(Op, VRBaseMap); - - // Again, if there's no instruction defining the VReg right now, fix it up - // later. - if (!MRI->hasOneDef(VReg)) - return EmitHalfDoneInstrRef(VReg); - - DefMI = &*MRI->def_instr_begin(VReg); - } + DefMI = &*MRI->def_instr_begin(VReg); + } else if (DbgOperand.getKind() == SDDbgOperand::SDNODE) { + // Look up the corresponding VReg for the given SDNode, if any. + SDNode *Node = DbgOperand.getSDNode(); + SDValue Op = SDValue(Node, DbgOperand.getResNo()); + DenseMap::iterator I = VRBaseMap.find(Op); + // No VReg -> produce a DBG_VALUE $noreg instead. + if (I == VRBaseMap.end()) + break; - // Avoid copy like instructions: they don't define values, only move them. - // Leave a virtual-register reference until it can be fixed up later, to find - // the underlying value definition. - if (DefMI->isCopyLike() || TII->isCopyInstr(*DefMI)) - return EmitHalfDoneInstrRef(VReg); + // Try to pick out a defining instruction at this point. + VReg = getVR(Op, VRBaseMap); - // Find the operand number which defines the specified VReg. - unsigned OperandIdx = 0; - for (const auto &MO : DefMI->operands()) { - if (MO.isReg() && MO.isDef() && MO.getReg() == VReg) - break; - ++OperandIdx; + // Again, if there's no instruction defining the VReg right now, fix it up + // later. + if (!MRI->hasOneDef(VReg)) { + AddVRegOp(VReg); + continue; + } + + DefMI = &*MRI->def_instr_begin(VReg); + } else { + assert(DbgOperand.getKind() == SDDbgOperand::CONST); + MOs.push_back(GetMOForConstDbgOp(DbgOperand)); + continue; + } + + // Avoid copy like instructions: they don't define values, only move them. + // Leave a virtual-register reference until it can be fixed up later, to + // find the underlying value definition. + if (DefMI->isCopyLike() || TII->isCopyInstr(*DefMI)) { + AddVRegOp(VReg); + continue; + } + + // Find the operand number which defines the specified VReg. + unsigned OperandIdx = 0; + for (const auto &MO : DefMI->operands()) { + if (MO.isReg() && MO.isDef() && MO.getReg() == VReg) + break; + ++OperandIdx; + } + assert(OperandIdx < DefMI->getNumOperands()); + + // Make the DBG_INSTR_REF refer to that instruction, and that operand. + unsigned InstrNum = DefMI->getDebugInstrNum(); + MOs.push_back(MachineOperand::CreateDbgInstrRef(InstrNum, OperandIdx)); } - assert(OperandIdx < DefMI->getNumOperands()); - // Only convert Expr to variadic form when we're sure we're emitting a - // complete instruction reference. - if (!SD->isVariadic()) - Expr = DIExpression::convertToVariadicExpression(Expr); - // Make the DBG_INSTR_REF refer to that instruction, and that operand. - unsigned InstrNum = DefMI->getDebugInstrNum(); - SmallVector MOs( - {MachineOperand::CreateDbgInstrRef(InstrNum, OperandIdx)}); + // If we haven't created a valid MachineOperand for every DbgOp, abort and + // produce an undef DBG_VALUE. + if (MOs.size() != OpCount) + return EmitDbgNoLocation(SD); + return BuildMI(*MF, DL, RefII, false, MOs, Var, Expr); } @@ -866,14 +891,27 @@ // An invalidated SDNode must generate an undef DBG_VALUE: although the // original value is no longer computed, earlier DBG_VALUEs live ranges // must not leak into later code. + DIVariable *Var = SD->getVariable(); + const DIExpression *Expr = + DIExpression::convertToUndefExpression(SD->getExpression()); + DebugLoc DL = SD->getDebugLoc(); + const MCInstrDesc &Desc = TII->get(TargetOpcode::DBG_VALUE); + return BuildMI(*MF, DL, Desc, false, 0U, Var, Expr); +} + +MachineInstr * +InstrEmitter::EmitDbgValueList(SDDbgValue *SD, + DenseMap &VRBaseMap) { MDNode *Var = SD->getVariable(); - MDNode *Expr = SD->getExpression(); + DIExpression *Expr = SD->getExpression(); DebugLoc DL = SD->getDebugLoc(); - auto MIB = BuildMI(*MF, DL, TII->get(TargetOpcode::DBG_VALUE)); - MIB.addReg(0U); - MIB.addReg(0U); + // DBG_VALUE_LIST := "DBG_VALUE_LIST" var, expression, loc (, loc)* + const MCInstrDesc &DbgValDesc = TII->get(TargetOpcode::DBG_VALUE_LIST); + // Build the DBG_VALUE_LIST instruction base. + auto MIB = BuildMI(*MF, DL, DbgValDesc); MIB.addMetadata(Var); MIB.addMetadata(Expr); + AddDbgValueLocationOps(MIB, DbgValDesc, SD->getLocationOps(), VRBaseMap); return &*MIB; } 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 @@ -5590,6 +5590,8 @@ // the DIExpression. if (Indirect) NewDIExpr = DIExpression::prepend(FragExpr, DIExpression::DerefBefore); + SmallVector Ops({dwarf::DW_OP_LLVM_arg, 0}); + NewDIExpr = DIExpression::prependOpcodes(NewDIExpr, Ops); return BuildMI(MF, DL, Inst, false, MOs, Variable, NewDIExpr); } else { // Create a completely standard DBG_VALUE. 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 @@ -1462,6 +1462,16 @@ }); } +const DIExpression * +DIExpression::convertToUndefExpression(const DIExpression *Expr) { + SmallVector UndefOps; + if (auto FragmentInfo = Expr->getFragmentInfo()) { + UndefOps.append({dwarf::DW_OP_LLVM_fragment, FragmentInfo->OffsetInBits, + FragmentInfo->SizeInBits}); + } + return DIExpression::get(Expr->getContext(), UndefOps); +} + const DIExpression * DIExpression::convertToVariadicExpression(const DIExpression *Expr) { if (any_of(Expr->expr_ops(), [](auto ExprOp) { diff --git a/llvm/test/CodeGen/X86/pr57673.ll b/llvm/test/CodeGen/X86/pr57673.ll --- a/llvm/test/CodeGen/X86/pr57673.ll +++ b/llvm/test/CodeGen/X86/pr57673.ll @@ -1,4 +1,7 @@ -; RUN: llc -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -stop-after=x86-optimize-LEAs < %s | FileCheck %s +; RUN: llc -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -stop-after=x86-optimize-LEAs -experimental-debug-variable-locations=false < %s \ +; RUN: | FileCheck %s --check-prefix=NORMAL +; RUN: llc -mtriple=x86_64-unknown-unknown -mcpu=x86-64 -stop-after=x86-optimize-LEAs -experimental-debug-variable-locations < %s \ +; RUN: | FileCheck %s --check-prefix=INSTRREF ; The LEA optimization pass used to crash on this testcase. @@ -9,7 +12,8 @@ ; CHECK: LEA64r ; CHECK-NOT: LEA64r -; CHECK: DBG_VALUE_LIST +; NORMAL: DBG_VALUE_LIST +; INSTRREF: DBG_INSTR_REF target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/DebugInfo/X86/arg-dbg-value-list.ll b/llvm/test/DebugInfo/X86/arg-dbg-value-list.ll --- a/llvm/test/DebugInfo/X86/arg-dbg-value-list.ll +++ b/llvm/test/DebugInfo/X86/arg-dbg-value-list.ll @@ -1,50 +1,51 @@ -;; FIXME: We currently don't do any special bookkeeping for unused args used by -;; variadic dbg_values. When/if we support them, the DBG_VALUE_LIST should be -;; updated accordingly. - -; RUN: llc %s -start-after=codegenprepare -stop-before=finalize-isel -o - | FileCheck %s - -;; Check that unused argument values are handled the same way for variadic -;; dbg_values as non-variadics. - -; CHECK: ![[A:[0-9]+]] = !DILocalVariable(name: "a", -; CHECK: ![[B:[0-9]+]] = !DILocalVariable(name: "b", -; CHECK: ![[C:[0-9]+]] = !DILocalVariable(name: "c", - -; CHECK: DBG_VALUE $ecx, $noreg, ![[A]], !DIExpression(), debug-location -; CHECK: DBG_VALUE $edx, $noreg, ![[B]], !DIExpression(), debug-location -; CHECK: DBG_VALUE $noreg, $noreg, ![[C]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), debug-location - -target triple = "x86_64-pc-windows-msvc19.16.27034" -define dso_local i32 @"?foo@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr !dbg !8 { -entry: - call void @llvm.dbg.value(metadata i32 %a, metadata !14, metadata !DIExpression()), !dbg !17 - call void @llvm.dbg.value(metadata i32 %b, metadata !15, metadata !DIExpression()), !dbg !17 - call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b), metadata !16, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !17 - ret i32 0 -} - -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_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) -!1 = !DIFile(filename: "test.cpp", directory: "/") -!2 = !{} -!3 = !{i32 2, !"CodeView", i32 1} -!4 = !{i32 2, !"Debug Info Version", i32 3} -!5 = !{i32 1, !"wchar_size", i32 2} -!6 = !{i32 7, !"PIC Level", i32 2} -!7 = !{!"clang version 11.0.0"} -!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAHHH@Z", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) -!9 = !DIFile(filename: "test.cpp", directory: "/") -!10 = !DISubroutineType(types: !11) -!11 = !{!12, !12, !12} -!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) -!13 = !{!14, !15, !16} -!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !9, line: 1, type: !12) -!15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !9, line: 1, type: !12) -!16 = !DILocalVariable(name: "c", scope: !8, file: !9, line: 2, type: !12) -!17 = !DILocation(line: 0, scope: !8) +;; FIXME: We currently don't do any special bookkeeping for unused args used by +;; variadic dbg_values. When/if we support them, the DBG_VALUE_LIST should be +;; updated accordingly. + +; RUN: llc %s -start-after=codegenprepare --experimental-debug-variable-locations=false -stop-before=finalize-isel -o - | FileCheck %s +; RUN: llc %s -start-after=codegenprepare --experimental-debug-variable-locations -stop-before=finalize-isel -o - | FileCheck %s + +;; Check that unused argument values are handled the same way for variadic +;; dbg_values as non-variadics. + +; CHECK: ![[A:[0-9]+]] = !DILocalVariable(name: "a", +; CHECK: ![[B:[0-9]+]] = !DILocalVariable(name: "b", +; CHECK: ![[C:[0-9]+]] = !DILocalVariable(name: "c", + +; CHECK: DBG_VALUE $ecx, $noreg, ![[A]], !DIExpression(), debug-location +; CHECK: DBG_VALUE $edx, $noreg, ![[B]], !DIExpression(), debug-location +; CHECK: DBG_VALUE $noreg, $noreg, ![[C]], !DIExpression(), debug-location + +target triple = "x86_64-pc-windows-msvc19.16.27034" +define dso_local i32 @"?foo@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr !dbg !8 { +entry: + call void @llvm.dbg.value(metadata i32 %a, metadata !14, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 %b, metadata !15, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b), metadata !16, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !17 + ret i32 0 +} + +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_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAHHH@Z", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) +!9 = !DIFile(filename: "test.cpp", directory: "/") +!10 = !DISubroutineType(types: !11) +!11 = !{!12, !12, !12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !{!14, !15, !16} +!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !9, line: 1, type: !12) +!15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !9, line: 1, type: !12) +!16 = !DILocalVariable(name: "c", scope: !8, file: !9, line: 2, type: !12) +!17 = !DILocation(line: 0, scope: !8) diff --git a/llvm/test/DebugInfo/X86/dbg-val-list-undef.ll b/llvm/test/DebugInfo/X86/dbg-val-list-undef.ll --- a/llvm/test/DebugInfo/X86/dbg-val-list-undef.ll +++ b/llvm/test/DebugInfo/X86/dbg-val-list-undef.ll @@ -1,41 +1,42 @@ -; RUN: llc %s -start-after=codegenprepare -stop-before=finalize-isel -o - | FileCheck %s - -;; %y is unused and cannot (FIXME: currently) be salvaged. Ensure that the -;; variadic dbg_value using %y becomes undef. - -; CHECK: ![[C:[0-9]+]] = !DILocalVariable(name: "c", -; CHECK: DBG_VALUE $noreg, $noreg, ![[C]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), debug-location - -target triple = "x86_64-pc-windows-msvc19.16.27034" -define dso_local i32 @"?foo@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr !dbg !8 { -entry: - %x = add i32 %a, %b - %y = mul i32 %x, %b - call void @llvm.dbg.value(metadata !DIArgList(i32 %x, i32 %y), metadata !16, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !17 - ret i32 %x -} - -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_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) -!1 = !DIFile(filename: "test.cpp", directory: "/") -!2 = !{} -!3 = !{i32 2, !"CodeView", i32 1} -!4 = !{i32 2, !"Debug Info Version", i32 3} -!5 = !{i32 1, !"wchar_size", i32 2} -!6 = !{i32 7, !"PIC Level", i32 2} -!7 = !{!"clang version 11.0.0"} -!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAHHH@Z", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) -!9 = !DIFile(filename: "test.cpp", directory: "/") -!10 = !DISubroutineType(types: !11) -!11 = !{!12, !12, !12} -!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) -!13 = !{!14, !15, !16} -!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !9, line: 1, type: !12) -!15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !9, line: 1, type: !12) -!16 = !DILocalVariable(name: "c", scope: !8, file: !9, line: 2, type: !12) -!17 = !DILocation(line: 0, scope: !8) +; RUN: llc %s -start-after=codegenprepare --experimental-debug-variable-locations=false -stop-before=finalize-isel -o - | FileCheck %s +; RUN: llc %s -start-after=codegenprepare --experimental-debug-variable-locations -stop-before=finalize-isel -o - | FileCheck %s + +;; %y is unused and cannot (FIXME: currently) be salvaged. Ensure that the +;; variadic dbg_value using %y becomes undef. + +; CHECK: ![[C:[0-9]+]] = !DILocalVariable(name: "c", +; CHECK: DBG_VALUE $noreg, $noreg, ![[C]], !DIExpression(), debug-location + +target triple = "x86_64-pc-windows-msvc19.16.27034" +define dso_local i32 @"?foo@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr !dbg !8 { +entry: + %x = add i32 %a, %b + %y = mul i32 %x, %b + call void @llvm.dbg.value(metadata !DIArgList(i32 %x, i32 %y), metadata !16, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !17 + ret i32 %x +} + +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_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAHHH@Z", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) +!9 = !DIFile(filename: "test.cpp", directory: "/") +!10 = !DISubroutineType(types: !11) +!11 = !{!12, !12, !12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !{!14, !15, !16} +!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !9, line: 1, type: !12) +!15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !9, line: 1, type: !12) +!16 = !DILocalVariable(name: "c", scope: !8, file: !9, line: 2, type: !12) +!17 = !DILocation(line: 0, scope: !8) diff --git a/llvm/test/DebugInfo/X86/instr-ref-sdag-empty-vreg.ll b/llvm/test/DebugInfo/X86/instr-ref-sdag-empty-vreg.ll --- a/llvm/test/DebugInfo/X86/instr-ref-sdag-empty-vreg.ll +++ b/llvm/test/DebugInfo/X86/instr-ref-sdag-empty-vreg.ll @@ -7,7 +7,8 @@ ;; vreg that is never defined, which risks a crash. Check that we don't crash, ;; and produce an empty variable location. -; CHECK: DBG_VALUE_LIST {{.+}}, $noreg +; CHECK: DBG_VALUE_LIST {{.+}}, $noreg +; CHECK: DBG_VALUE_LIST {{.+}}, $noreg, 4 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" @@ -20,6 +21,7 @@ _ZN4Vec39normalizeEv.exit: ; preds = %cond.false.i call void @llvm.dbg.value(metadata float %width, metadata !11, metadata !DIExpression()), !dbg !12 + call void @llvm.dbg.value(metadata !DIArgList(float %width, i32 4), metadata !11, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_mul, DW_OP_stack_value)), !dbg !12 %mul.i = fmul float %width, 0.000000e+00, !dbg !12 ret void, !dbg !12 } diff --git a/llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll b/llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll --- a/llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll +++ b/llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll @@ -26,8 +26,10 @@ ; NORMAL: %[[REG0:[0-9]+]]:gr32 = ADD32rr ; NORMAL-NEXT: DBG_VALUE %[[REG0]] +; NORMAL-NEXT: DBG_VALUE_LIST {{.+}}, %[[REG0]], 2 ; NORMAL-NEXT: %[[REG1:[0-9]+]]:gr32 = ADD32rr ; NORMAL-NEXT: DBG_VALUE %[[REG1]] +; NORMAL-NEXT: DBG_VALUE_LIST {{.+}}, %[[REG1]], %[[REG0]] ; Note that I'm baking in an assumption of one-based ordering here. We could ; capture and check for the instruction numbers, we'd rely on machine verifier @@ -38,20 +40,25 @@ ; INSTRREF: ADD32rr ; INSTRREF-SAME: debug-instr-number 1 ; INSTRREF-NEXT: DBG_INSTR_REF {{.+}}, dbg-instr-ref(1, 0) +; INSTRREF-NEXT: DBG_INSTR_REF {{.+}}, dbg-instr-ref(1, 0), 2 ; INSTRREF-NEXT: ADD32rr ; INSTRREF-SAME: debug-instr-number 2 ; INSTRREF-NEXT: DBG_INSTR_REF {{.+}}, dbg-instr-ref(2, 0) +; INSTRREF-NEXT: DBG_INSTR_REF {{.+}}, dbg-instr-ref(2, 0), dbg-instr-ref(1, 0) -; Test that fast-isel will produce DBG_INSTR_REFs too. +; Test that fast-isel will produce DBG_INSTR_REFs too, except for debug values +; using DIArgList, which is not supported in FastIsel. ; FASTISEL-INSTRREF-LABEL: name: foo ; FASTISEL-INSTRREF: ADD32rr ; FASTISEL-INSTRREF-SAME: debug-instr-number 1 ; FASTISEL-INSTRREF-NEXT: DBG_INSTR_REF {{.+}}, dbg-instr-ref(1, 0) +; FASTISEL-INSTRREF-NEXT: DBG_VALUE $noreg, {{.+}} ; FASTISEL-INSTRREF-NEXT: ADD32rr ; FASTISEL-INSTRREF-SAME: debug-instr-number 2 ; FASTISEL-INSTRREF-NEXT: DBG_INSTR_REF {{.+}}, dbg-instr-ref(2, 0) +; FASTISEL-INSTRREF-NEXT: DBG_VALUE $noreg, {{.+}} @glob32 = global i32 0 @glob16 = global i16 0 @@ -64,8 +71,10 @@ entry: %0 = add i32 %bar, %baz, !dbg !14 call void @llvm.dbg.value(metadata i32 %0, metadata !13, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata !DIArgList(i32 %0, i32 2), metadata !13, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !14 %1 = add i32 %0, %qux call void @llvm.dbg.value(metadata i32 %1, metadata !13, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata !DIArgList(i32 %1, i32 %0), metadata !13, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !14 ret i32 %1, !dbg !14 } @@ -208,7 +217,7 @@ ;; Test for dbg.declare of non-stack-slot Values. These turn up with NRVO and ;; other ABI scenarios where something is technically in memory, but we don't ;; refer to it relative to the stack pointer. We refer to these either with an -;; indirect DBG_VAUE, or a DBG_INSTR_REF with DW_OP_deref prepended. +;; indirect DBG_VALUE, or a DBG_INSTR_REF with DW_OP_deref prepended. ;; ;; Test an inlined dbg.declare in a different scope + block, to test behaviours ;; where the debug intrinsic isn't in the first block. The normal-mode DBG_VALUE diff --git a/llvm/test/DebugInfo/X86/invalidated-dbg-value-is-undef.ll b/llvm/test/DebugInfo/X86/invalidated-dbg-value-is-undef.ll --- a/llvm/test/DebugInfo/X86/invalidated-dbg-value-is-undef.ll +++ b/llvm/test/DebugInfo/X86/invalidated-dbg-value-is-undef.ll @@ -5,7 +5,7 @@ ; CHECK-LABEL: body: -; CHECK: DBG_VALUE $noreg, $noreg, ![[VAR:[0-9]+]] +; CHECK: DBG_VALUE $noreg, $noreg, ![[VAR:[0-9]+]], !DIExpression() 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-linux-gnu" diff --git a/llvm/test/DebugInfo/X86/safestack-byval.ll b/llvm/test/DebugInfo/X86/safestack-byval.ll --- a/llvm/test/DebugInfo/X86/safestack-byval.ll +++ b/llvm/test/DebugInfo/X86/safestack-byval.ll @@ -1,7 +1,8 @@ ; Test dwarf codegen for DILocalVariable of a byval function argument that ; points to neither an argument nor an alloca. This kind of IR is generated by ; SafeStack for unsafe byval arguments. -; RUN: llc -mtriple=x86_64-unknown-unknown -stop-after finalize-isel %s -o - | FileCheck %s +; RUN: llc -mtriple=x86_64-unknown-unknown --experimental-debug-variable-locations=false -stop-after finalize-isel %s -o - | FileCheck %s --check-prefixes=CHECK,NORMAL +; RUN: llc -mtriple=x86_64-unknown-unknown --experimental-debug-variable-locations -stop-after finalize-isel %s -o - | FileCheck %s --check-prefixes=CHECK,INSTRREF ; This was built by compiling the following source with SafeStack and ; simplifying the result a little. @@ -13,8 +14,9 @@ ; return zzz.a[len]; ; } -; CHECK: ![[ZZZ:.*]] = !DILocalVariable(name: "zzz", -; CHECK: DBG_VALUE {{.*}} ![[ZZZ]], !DIExpression(DW_OP_deref, DW_OP_constu, 400, DW_OP_minus) +; CHECK: ![[ZZZ:.*]] = !DILocalVariable(name: "zzz", +; NORMAL: DBG_VALUE {{.*}} ![[ZZZ]], !DIExpression(DW_OP_deref, DW_OP_constu, 400, DW_OP_minus) +; INSTRREF: DBG_INSTR_REF ![[ZZZ]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_constu, 400, DW_OP_minus, DW_OP_deref) %struct.S = type { [100 x i32] } diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -3089,6 +3089,66 @@ #undef EXPECT_INVALID_FRAGMENT } +TEST_F(DIExpressionTest, convertToUndefExpression) { +#define EXPECT_UNDEF_OPS_EQUAL(TestExpr, Expected) \ + do { \ + const DIExpression *Undef = \ + DIExpression::convertToUndefExpression(TestExpr); \ + EXPECT_EQ(Undef, Expected); \ + } while (false) +#define GET_EXPR(...) DIExpression::get(Context, {__VA_ARGS__}) + + // Expressions which are single-location and non-complex should be unchanged. + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(), GET_EXPR()); + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_LLVM_fragment, 0, 32), + GET_EXPR(dwarf::DW_OP_LLVM_fragment, 0, 32)); + + // Variadic expressions should become single-location. + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_LLVM_arg, 0), GET_EXPR()); + EXPECT_UNDEF_OPS_EQUAL( + GET_EXPR(dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_LLVM_fragment, 32, 32), + GET_EXPR(dwarf::DW_OP_LLVM_fragment, 32, 32)); + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_LLVM_arg, 0, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul), + GET_EXPR()); + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_LLVM_arg, 0, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul, + dwarf::DW_OP_LLVM_fragment, 64, 32), + GET_EXPR(dwarf::DW_OP_LLVM_fragment, 64, 32)); + + // Any stack-computing ops should be removed. + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_plus_uconst, 8), GET_EXPR()); + EXPECT_UNDEF_OPS_EQUAL( + GET_EXPR(dwarf::DW_OP_plus_uconst, 8, dwarf::DW_OP_LLVM_fragment, 0, 16), + GET_EXPR(dwarf::DW_OP_LLVM_fragment, 0, 16)); + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_constu, 24, dwarf::DW_OP_shra), + GET_EXPR()); + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_constu, 24, dwarf::DW_OP_shra, + dwarf::DW_OP_LLVM_fragment, 8, 16), + GET_EXPR(dwarf::DW_OP_LLVM_fragment, 8, 16)); + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_deref), GET_EXPR()); + EXPECT_UNDEF_OPS_EQUAL( + GET_EXPR(dwarf::DW_OP_deref, dwarf::DW_OP_LLVM_fragment, 16, 16), + GET_EXPR(dwarf::DW_OP_LLVM_fragment, 16, 16)); + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_constu, 4, dwarf::DW_OP_minus), + GET_EXPR()); + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_constu, 4, dwarf::DW_OP_minus, + dwarf::DW_OP_LLVM_fragment, 24, 16), + GET_EXPR(dwarf::DW_OP_LLVM_fragment, 24, 16)); + + // Stack-value operators are also not preserved. + EXPECT_UNDEF_OPS_EQUAL( + GET_EXPR(dwarf::DW_OP_plus_uconst, 8, dwarf::DW_OP_stack_value), + GET_EXPR()); + EXPECT_UNDEF_OPS_EQUAL(GET_EXPR(dwarf::DW_OP_plus_uconst, 8, + dwarf::DW_OP_stack_value, + dwarf::DW_OP_LLVM_fragment, 32, 16), + GET_EXPR(dwarf::DW_OP_LLVM_fragment, 32, 16)); + +#undef EXPECT_UNDEF_OPS_EQUAL +#undef GET_EXPR +} + TEST_F(DIExpressionTest, convertToVariadicExpression) { #define EXPECT_CONVERT_IS_NOOP(TestExpr) \ do { \