[llvm] da0faa0 - [DebugInfo] Produce variadic DBG_INSTR_REFs from ISel

Stephen Tozer via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 9 00:58:59 PST 2023


Author: Stephen Tozer
Date: 2023-01-09T08:58:33Z
New Revision: da0faa0594b9df6d401d1e5686ff32766148b075

URL: https://github.com/llvm/llvm-project/commit/da0faa0594b9df6d401d1e5686ff32766148b075
DIFF: https://github.com/llvm/llvm-project/commit/da0faa0594b9df6d401d1e5686ff32766148b075.diff

LOG: [DebugInfo] Produce variadic DBG_INSTR_REFs from ISel

This patch modifies SelectionDAG and FastISel to produce DBG_INSTR_REFs with
variadic expressions, and produce DBG_INSTR_REFs for debug values with variadic
location expressions. The former essentially means just prepending
DW_OP_LLVM_arg, 0 to the existing expression. The latter is achieved in
MachineFunction::finalizeDebugInstrRefs and InstrEmitter::EmitDbgInstrRef.

Reviewed By: jmorse, Orlando

Differential Revision: https://reviews.llvm.org/D133929

Added: 
    

Modified: 
    llvm/include/llvm/IR/DebugInfoMetadata.h
    llvm/lib/CodeGen/MachineFunction.cpp
    llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
    llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
    llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h
    llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
    llvm/lib/IR/DebugInfoMetadata.cpp
    llvm/test/CodeGen/X86/pr57673.ll
    llvm/test/DebugInfo/X86/arg-dbg-value-list.ll
    llvm/test/DebugInfo/X86/dbg-val-list-undef.ll
    llvm/test/DebugInfo/X86/instr-ref-sdag-empty-vreg.ll
    llvm/test/DebugInfo/X86/instr-ref-selectiondag.ll
    llvm/test/DebugInfo/X86/invalidated-dbg-value-is-undef.ll
    llvm/test/DebugInfo/X86/safestack-byval.ll
    llvm/unittests/IR/MetadataTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index f3602b62a3a71..def3ce2b56eab 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2799,6 +2799,14 @@ class DIExpression : public MDNode {
   /// 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
index 9625099cfe2cf..ba73abd9f3d96 100644
--- 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 @@ void MachineFunction::finalizeDebugInstrRefs() {
   auto MakeUndefDbgValue = [&](MachineInstr &MI) {
     const MCInstrDesc &RefII = TII->get(TargetOpcode::DBG_VALUE_LIST);
     MI.setDesc(RefII);
-    MI.getDebugOperand(0).setReg(0);
+    MI.setDebugValueUndef();
   };
 
   DenseMap<Register, DebugInstrOperandPair> 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
index af51223554728..bc48d168d7171 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1256,8 +1256,9 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
         // 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<uint64_t, 3> 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 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) {
             /* isKill */ false, /* isDead */ false,
             /* isUndef */ false, /* isEarlyClobber */ false,
             /* SubReg */ 0, /* isDebug */ true)});
+        SmallVector<uint64_t, 2> 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.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
index 2ee4a047a1deb..dbf0f8a421768 100644
--- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
@@ -678,7 +678,6 @@ MachineInstr *
 InstrEmitter::EmitDbgValue(SDDbgValue *SD,
                            DenseMap<SDValue, Register> &VRBaseMap) {
   MDNode *Var = SD->getVariable();
-  MDNode *Expr = SD->getExpression();
   DebugLoc DL = SD->getDebugLoc();
   assert(cast<DILocalVariable>(Var)->isValidLocationForIntrinsic(DL) &&
          "Expected inlined-at fields to agree");
@@ -691,29 +690,41 @@ InstrEmitter::EmitDbgValue(SDDbgValue *SD,
   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<ConstantInt>(V)) {
+    if (CI->getBitWidth() > 64)
+      return MachineOperand::CreateCImm(CI);
+    return MachineOperand::CreateImm(CI->getSExtValue());
+  }
+  if (const ConstantFP *CF = dyn_cast<ConstantFP>(V))
+    return MachineOperand::CreateFPImm(CF);
+  // Note: This assumes that all nullptr constants are zero-valued.
+  if (isa<ConstantPointerNull>(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<SDDbgOperand> LocationOps,
@@ -739,24 +750,9 @@ void InstrEmitter::AddDbgValueLocationOps(
         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<ConstantInt>(V)) {
-        if (CI->getBitWidth() > 64)
-          MIB.addCImm(CI);
-        else
-          MIB.addImm(CI->getSExtValue());
-      } else if (const ConstantFP *CF = dyn_cast<ConstantFP>(V)) {
-        MIB.addFPImm(CF);
-      } else if (isa<ConstantPointerNull>(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 @@ void InstrEmitter::AddDbgValueLocationOps(
 MachineInstr *
 InstrEmitter::EmitDbgInstrRef(SDDbgValue *SD,
                               DenseMap<SDValue, Register> &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<uint64_t> 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<MachineOperand> 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<MachineOperand, 1> 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<SDValue, Register>::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<SDValue, Register>::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<MachineOperand, 1> 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 @@ MachineInstr *InstrEmitter::EmitDbgNoLocation(SDDbgValue *SD) {
   // 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<SDValue, Register> &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/InstrEmitter.h b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h
index 0da4b962450cb..909d1a95560d9 100644
--- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h
+++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h
@@ -126,6 +126,10 @@ class LLVM_LIBRARY_VISIBILITY InstrEmitter {
   /// 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<SDValue, Register> &VRBaseMap);
+
   /// Emit a DBG_VALUE from the operands to SDDbgValue.
   MachineInstr *EmitDbgValueFromSingleOp(SDDbgValue *SD,
                                     DenseMap<SDValue, Register> &VRBaseMap);

diff  --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 954628d779f30..1fb7ab1cc3606 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -5590,6 +5590,8 @@ bool SelectionDAGBuilder::EmitFuncArgumentDbgValue(
       // the DIExpression.
       if (Indirect)
         NewDIExpr = DIExpression::prepend(FragExpr, DIExpression::DerefBefore);
+      SmallVector<uint64_t, 2> 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
index 905a1884e2692..fb9a7b8822204 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1462,6 +1462,16 @@ bool DIExpression::isSingleLocationExpression() const {
   });
 }
 
+const DIExpression *
+DIExpression::convertToUndefExpression(const DIExpression *Expr) {
+  SmallVector<uint64_t, 3> 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
index 5e7102059c0d2..d20704d58eda5 100644
--- 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
index a817f9aef8c66..828cebfd0728d 100644
--- 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 at 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 at 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 at 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 at 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
index 8aa641d0c0fc8..4b58d631dcba3 100644
--- 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 at 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 at 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 at 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 at 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
index fc53edbbba12a..279a7b79503ac 100644
--- 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 @@ cond.false.i:
 
 _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
index 003f5f57b36c1..dbbef2b39587d 100644
--- 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 @@ define i32 @foo(i32 %bar, i32 %baz, i32 %qux) !dbg !7 {
 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 @@ shoes:
 ;; 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 
diff erent 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
index b69021637c05a..4c6921519bb2d 100644
--- 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
index e0e26dddba98a..acaa8031537ca 100644
--- 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
index f1d86daf54616..3b60a719780c7 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -3089,6 +3089,66 @@ TEST_F(DIExpressionTest, createFragmentExpression) {
 #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 {                                                                         \


        


More information about the llvm-commits mailing list