Index: llvm/include/llvm/IR/IntrinsicInst.h
===================================================================
--- llvm/include/llvm/IR/IntrinsicInst.h
+++ llvm/include/llvm/IR/IntrinsicInst.h
@@ -90,6 +90,7 @@
     case Intrinsic::assume:
     case Intrinsic::sideeffect:
     case Intrinsic::pseudoprobe:
+    case Intrinsic::dbg_assign:
     case Intrinsic::dbg_declare:
     case Intrinsic::dbg_value:
     case Intrinsic::dbg_label:
@@ -124,6 +125,7 @@
   case Intrinsic::dbg_value:
   case Intrinsic::dbg_addr:
   case Intrinsic::dbg_label:
+  case Intrinsic::dbg_assign:
     return true;
   default:
     return false;
@@ -226,10 +228,12 @@
 
   bool hasArgList() const { return isa<DIArgList>(getRawLocation()); }
 
-  /// Does this describe the address of a local variable. True for dbg.addr
-  /// and dbg.declare, but not dbg.value, which describes its value.
+  /// Does this describe the address of a local variable. True for dbg.addr and
+  /// dbg.declare, but not dbg.value, which describes its value, or dbg.assign,
+  /// which describes a combination of the variable's value and address.
   bool isAddressOfVariable() const {
-    return getIntrinsicID() != Intrinsic::dbg_value;
+    return getIntrinsicID() != Intrinsic::dbg_value &&
+           getIntrinsicID() != Intrinsic::dbg_assign;
   }
 
   void setUndef() {
@@ -281,6 +285,11 @@
   /// is described.
   Optional<uint64_t> getFragmentSizeInBits() const;
 
+  /// Get the FragmentInfo for the variable.
+  Optional<DIExpression::FragmentInfo> getFragment() const {
+    return getExpression()->getFragmentInfo();
+  }
+
   /// \name Casting methods
   /// @{
   static bool classof(const IntrinsicInst *I) {
@@ -288,6 +297,7 @@
     case Intrinsic::dbg_declare:
     case Intrinsic::dbg_value:
     case Intrinsic::dbg_addr:
+    case Intrinsic::dbg_assign:
       return true;
     default:
       return false;
@@ -297,7 +307,7 @@
     return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
   }
   /// @}
-private:
+protected:
   void setArgOperand(unsigned i, Value *v) {
     DbgInfoIntrinsic::setArgOperand(i, v);
   }
@@ -358,7 +368,49 @@
   /// \name Casting methods
   /// @{
   static bool classof(const IntrinsicInst *I) {
-    return I->getIntrinsicID() == Intrinsic::dbg_value;
+    return I->getIntrinsicID() == Intrinsic::dbg_value ||
+           I->getIntrinsicID() == Intrinsic::dbg_assign;
+  }
+  static bool classof(const Value *V) {
+    return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
+  }
+  /// @}
+};
+
+/// This represents the llvm.dbg.assign instruction.
+class DbgAssignIntrinsic : public DbgValueInst {
+  enum Operands {
+    OpValue,
+    OpVar,
+    OpExpr,
+    OpAssignID,
+    OpAddress,
+    OpAddressExpr,
+  };
+
+public:
+  Value *getAddress() const;
+  MetadataAsValue *getAssignIdMetadataAsValue() const {
+    return cast<MetadataAsValue>(getArgOperand(OpAssignID));
+  }
+  Metadata *getAssignId() const {
+    return getAssignIdMetadataAsValue()->getMetadata();
+  }
+  DIExpression *getAddressExpression() const {
+    return cast<DIExpression>(
+        cast<MetadataAsValue>(getArgOperand(OpAddressExpr))->getMetadata());
+  }
+  void setAddressExpression(DIExpression *NewExpr) {
+    setArgOperand(OpAddressExpr,
+                  MetadataAsValue::get(NewExpr->getContext(), NewExpr));
+  }
+  void setAssignId(DIAssignID *New);
+  void setAddress(Value *V);
+  void setValue(Value *V);
+  /// \name Casting methods
+  /// @{
+  static bool classof(const IntrinsicInst *I) {
+    return I->getIntrinsicID() == Intrinsic::dbg_assign;
   }
   static bool classof(const Value *V) {
     return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
Index: llvm/include/llvm/IR/Intrinsics.td
===================================================================
--- llvm/include/llvm/IR/Intrinsics.td
+++ llvm/include/llvm/IR/Intrinsics.td
@@ -958,6 +958,13 @@
                                        [llvm_metadata_ty,
                                         llvm_metadata_ty,
                                         llvm_metadata_ty]>;
+  def int_dbg_assign        : DefaultAttrsIntrinsic<[],
+                                       [llvm_metadata_ty,
+                                        llvm_metadata_ty,
+                                        llvm_metadata_ty,
+                                        llvm_metadata_ty,
+                                        llvm_metadata_ty,
+                                        llvm_metadata_ty]>;
   def int_dbg_label        : DefaultAttrsIntrinsic<[],
                                        [llvm_metadata_ty]>;
 }
Index: llvm/lib/IR/IntrinsicInst.cpp
===================================================================
--- llvm/lib/IR/IntrinsicInst.cpp
+++ llvm/lib/IR/IntrinsicInst.cpp
@@ -79,10 +79,23 @@
 
 void DbgVariableIntrinsic::replaceVariableLocationOp(Value *OldValue,
                                                      Value *NewValue) {
+  // If OldValue is used as the address part of a dbg.assign intrinsic replace
+  // it with NewValue and return true.
+  auto ReplaceDbgAssignAddress = [this, OldValue, NewValue]() -> bool {
+    auto *DAI = dyn_cast<DbgAssignIntrinsic>(this);
+    if (!DAI || OldValue != DAI->getAddress())
+      return false;
+    DAI->setAddress(NewValue);
+    return true;
+  };
+  bool DbgAssignAddrReplaced = ReplaceDbgAssignAddress();
+  (void)DbgAssignAddrReplaced;
+
   assert(NewValue && "Values must be non-null");
   auto Locations = location_ops();
   auto OldIt = find(Locations, OldValue);
-  assert(OldIt != Locations.end() && "OldValue must be a current location");
+  assert((OldIt != Locations.end() || DbgAssignAddrReplaced) &&
+         "OldValue must be a current location");
   if (!hasArgList()) {
     Value *NewOperand = isa<MetadataAsValue>(NewValue)
                             ? NewValue
@@ -139,6 +152,33 @@
   return getVariable()->getSizeInBits();
 }
 
+Value *DbgAssignIntrinsic::getAddress() const {
+  Value *Op = getArgOperand(OpAddress);
+  auto *MD = cast<MetadataAsValue>(Op)->getMetadata();
+  if (auto *V = dyn_cast<ValueAsMetadata>(MD))
+    return V->getValue();
+
+  // When the value goes to null, it gets replaced by an empty MDNode.
+  assert(!cast<MDNode>(MD)->getNumOperands() && "Expected an empty MDNode");
+  return nullptr;
+}
+
+void DbgAssignIntrinsic::setAssignId(DIAssignID *New) {
+  setOperand(OpAssignID, MetadataAsValue::get(getContext(), New));
+}
+
+void DbgAssignIntrinsic::setAddress(Value *V) {
+  assert(V->getType()->isPointerTy() &&
+         "Destination Component must be a pointer type");
+  setOperand(OpAddress,
+             MetadataAsValue::get(getContext(), ValueAsMetadata::get(V)));
+}
+
+void DbgAssignIntrinsic::setValue(Value *V) {
+  setOperand(OpValue,
+             MetadataAsValue::get(getContext(), ValueAsMetadata::get(V)));
+}
+
 int llvm::Intrinsic::lookupLLVMIntrinsicByName(ArrayRef<const char *> NameTable,
                                                StringRef Name) {
   assert(Name.startswith("llvm."));
Index: llvm/lib/IR/Verifier.cpp
===================================================================
--- llvm/lib/IR/Verifier.cpp
+++ llvm/lib/IR/Verifier.cpp
@@ -4424,6 +4424,16 @@
       isa<AllocaInst>(I) || isa<StoreInst>(I) || isa<MemIntrinsic>(I);
   AssertDI(ExpectedInstTy,
            "!DIAssignID attached to unexpected instruction kind", I, MD);
+
+  // Iterate over the MetadataAsValue uses of the DIAssignID - these should
+  // only be found as DbgAssignIntrinsic operands.
+  if (auto *AsValue = MetadataAsValue::getIfExists(Context, MD)) {
+    for (auto *User : AsValue->users()) {
+      AssertDI(isa<DbgAssignIntrinsic>(User),
+               "!DIAssignID should only be used by llvm.dbg.assign intrinsics",
+               MD, User);
+    }
+  }
 }
 
 void Verifier::visitAnnotationMetadata(MDNode *Annotation) {
@@ -4805,6 +4815,9 @@
   case Intrinsic::dbg_value: // llvm.dbg.value
     visitDbgIntrinsic("value", cast<DbgVariableIntrinsic>(Call));
     break;
+  case Intrinsic::dbg_assign: // llvm.dbg.assign
+    visitDbgIntrinsic("assign", cast<DbgVariableIntrinsic>(Call));
+    break;
   case Intrinsic::dbg_label: // llvm.dbg.label
     visitDbgLabelIntrinsic("label", cast<DbgLabelInst>(Call));
     break;
@@ -5646,6 +5659,19 @@
          "invalid llvm.dbg." + Kind + " intrinsic expression", &DII,
          DII.getRawExpression());
 
+  if (auto *DAI = dyn_cast<DbgAssignIntrinsic>(&DII)) {
+    AssertDI(isa<DIAssignID>(DAI->getAssignId()),
+             "invalid llvm.dbg.assign intrinsic ID (4th arg)", &DII,
+             DAI->getAssignId());
+    auto *Addr = cast<MetadataAsValue>(DII.getArgOperand(4))->getMetadata();
+    AssertDI(isa<ValueAsMetadata>(Addr),
+             "invalid llvm.dbg.assign intrinsic address (5th arg)", &DII, Addr);
+    auto *AddrExpr = cast<MetadataAsValue>(DII.getArgOperand(5))->getMetadata();
+    AssertDI(isa<DIExpression>(AddrExpr),
+             "invalid llvm.dbg.assign intrinsic address expression (6th arg)",
+             &DII, AddrExpr);
+  }
+
   // Ignore broken !dbg attachments; they're checked elsewhere.
   if (MDNode *N = DII.getDebugLoc().getAsMDNode())
     if (!isa<DILocation>(N))
Index: llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/roundtrip.ll
===================================================================
--- llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/roundtrip.ll
+++ llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/roundtrip.ll
@@ -2,17 +2,81 @@
 ; RUN: | opt -verify -S -experimental-assignment-tracking \
 ; RUN: | FileCheck %s
 
-;; Roundtrip test (text -> bitcode -> text) for DIAssignID attachments.
-
-; CHECK: %local = alloca i32, align 4, !DIAssignID ![[ID:[0-9]+]]
-; CHECK-DAG: ![[ID]] = distinct !DIAssignID()
+;; Roundtrip test (text -> bitcode -> text) for DIAssignID metadata and
+;; llvm.dbg.assign intrinsics.
 
+;; DIAssignID attachment only.
+; CHECK-LABEL: @fun()
+; CHECK: %local = alloca i32, align 4, !DIAssignID ![[ID1:[0-9]+]]
 define dso_local void @fun() !dbg !7 {
 entry:
   %local = alloca i32, align 4, !DIAssignID !14
   ret void, !dbg !13
 }
 
+;; Unlinked llvm.dbg.assign.
+; CHECK-DAG: @fun2()
+; CHECK: llvm.dbg.assign(metadata i32 undef, metadata ![[VAR2:[0-9]+]], metadata !DIExpression(), metadata ![[ID2:[0-9]+]], metadata i32 undef, metadata !DIExpression()), !dbg ![[DBG2:[0-9]+]]
+define dso_local void @fun2() !dbg !15 {
+entry:
+  %local = alloca i32, align 4
+  call void @llvm.dbg.assign(metadata i32 undef, metadata !16, metadata !DIExpression(), metadata !18, metadata i32 undef, metadata !DIExpression()), !dbg !17
+  ret void, !dbg !17
+}
+
+;; An llvm.dbg.assign linked to an alloca.
+; CHECK-LABEL: @fun3()
+; CHECK: %local = alloca i32, align 4, !DIAssignID ![[ID3:[0-9]+]]
+; CHECK-NEXT: llvm.dbg.assign(metadata i32 undef, metadata ![[VAR3:[0-9]+]], metadata !DIExpression(), metadata ![[ID3]], metadata i32 undef, metadata !DIExpression()), !dbg ![[DBG3:[0-9]+]]
+define dso_local void @fun3() !dbg !19 {
+entry:
+  %local = alloca i32, align 4, !DIAssignID !22
+  call void @llvm.dbg.assign(metadata i32 undef, metadata !20, metadata !DIExpression(), metadata !22, metadata i32 undef, metadata !DIExpression()), !dbg !21
+  ret void, !dbg !21
+}
+
+;; Check that using a DIAssignID as an operand before using it as an attachment
+;; works (the order of the alloca and dbg.assign has been swapped).
+; CHECK-LABEL: @fun4()
+; CHECK: llvm.dbg.assign(metadata i32 undef, metadata ![[VAR4:[0-9]+]], metadata !DIExpression(), metadata ![[ID4:[0-9]+]], metadata i32 undef, metadata !DIExpression()), !dbg ![[DBG4:[0-9]+]]
+; CHECK-NEXT: %local = alloca i32, align 4, !DIAssignID ![[ID4]]
+define dso_local void @fun4() !dbg !23 {
+entry:
+  call void @llvm.dbg.assign(metadata i32 undef, metadata !24, metadata !DIExpression(), metadata !26, metadata i32 undef, metadata !DIExpression()), !dbg !25
+  %local = alloca i32, align 4, !DIAssignID !26
+  ret void, !dbg !25
+}
+
+;; Check that the value and address operands print correctly.
+;; There are currently no plans to support DIArgLists for the address component.
+; CHECK-LABEL: @fun5
+; CHECK: %local = alloca i32, align 4, !DIAssignID ![[ID5:[0-9]+]]
+; CHECK-NEXT: llvm.dbg.assign(metadata i32 %v, metadata ![[VAR5:[0-9]+]], metadata !DIExpression(), metadata ![[ID5]], metadata i32* %local, metadata !DIExpression()), !dbg ![[DBG5:[0-9]+]]
+; CHECK-NEXT: llvm.dbg.assign(metadata !DIArgList(i32 %v, i32 1), metadata ![[VAR5]], metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_minus, DW_OP_stack_value), metadata ![[ID5]], metadata i32* %local, metadata !DIExpression()), !dbg ![[DBG5]]
+define dso_local void @fun5(i32 %v) !dbg !27 {
+entry:
+  %local = alloca i32, align 4, !DIAssignID !30
+  call void @llvm.dbg.assign(metadata i32 %v, metadata !28, metadata !DIExpression(), metadata !30, metadata i32* %local, metadata !DIExpression()), !dbg !29
+  call void @llvm.dbg.assign(metadata !DIArgList(i32 %v, i32 1), metadata !28, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_minus, DW_OP_stack_value), metadata !30, metadata i32* %local, metadata !DIExpression()), !dbg !29
+  ret void
+}
+
+; CHECK-DAG: ![[ID1]] = distinct !DIAssignID()
+; CHECK-DAG: ![[ID2]] = distinct !DIAssignID()
+; CHECK-DAG: ![[VAR2]] = !DILocalVariable(name: "local2",
+; CHECK-DAG: ![[DBG2]] = !DILocation(line: 2
+; CHECK-DAG: ![[ID3]] = distinct !DIAssignID()
+; CHECK-DAG: ![[VAR3]] = !DILocalVariable(name: "local3",
+; CHECK-DAG: ![[DBG3]] = !DILocation(line: 3,
+; CHECK-DAG: ![[ID4]] = distinct !DIAssignID()
+; CHECK-DAG: ![[VAR4]] = !DILocalVariable(name: "local4",
+; CHECK-DAG: ![[DBG4]] = !DILocation(line: 4,
+; CHECK-DAG: ![[ID5]] = distinct !DIAssignID()
+; CHECK-DAG: ![[VAR5]] = !DILocalVariable(name: "local5",
+; CHECK-DAG: ![[DBG5]] = !DILocation(line: 5,
+
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
 !llvm.dbg.cu = !{!0}
 !llvm.module.flags = !{!3, !4, !5}
 !llvm.ident = !{!6}
@@ -29,5 +93,23 @@
 !9 = !{null}
 !10 = !DILocalVariable(name: "local", scope: !7, file: !1, line: 2, type: !11)
 !11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
-!13 = !DILocation(line: 3, column: 1, scope: !7)
+!13 = !DILocation(line: 1, column: 1, scope: !7)
 !14 = distinct !DIAssignID()
+!15 = distinct !DISubprogram(name: "fun2", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!16 = !DILocalVariable(name: "local2", scope: !15, file: !1, line: 2, type: !11)
+!17 = !DILocation(line: 2, column: 1, scope: !15)
+!18 = distinct !DIAssignID()
+!19 = distinct !DISubprogram(name: "fun3", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!20 = !DILocalVariable(name: "local3", scope: !19, file: !1, line: 2, type: !11)
+!21 = !DILocation(line: 3, column: 1, scope: !19)
+!22 = distinct !DIAssignID()
+!23 = distinct !DISubprogram(name: "fun4", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!24 = !DILocalVariable(name: "local4", scope: !23, file: !1, line: 2, type: !11)
+!25 = !DILocation(line: 4, column: 1, scope: !23)
+!26 = distinct !DIAssignID()
+!27 = distinct !DISubprogram(name: "fun5", scope: !1, file: !1, line: 1, type: !31, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!28 = !DILocalVariable(name: "local5", scope: !27, file: !1, line: 2, type: !11)
+!29 = !DILocation(line: 5, column: 1, scope: !27)
+!30 = distinct !DIAssignID()
+!31 = !DISubroutineType(types: !32)
+!32 = !{null, !11}
Index: llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/verify.ll
===================================================================
--- /dev/null
+++ llvm/test/DebugInfo/Generic/assignment-tracking/parse-and-verify/verify.ll
@@ -0,0 +1,52 @@
+; RUN: opt %s -S -verify -experimental-assignment-tracking 2>&1 \
+; RUN: | FileCheck %s
+
+;; Check that badly formed assignment tracking metadata is caught either
+;; while parsing or by the verifier.
+;;
+;; Checks for this one are inline.
+
+define dso_local void @fun() !dbg !7 {
+entry:
+  %a = alloca i32, align 4, !DIAssignID !14
+  ; Here something other than a dbg.assign intrinsic is using a DIAssignID.
+  ; CHECK: !DIAssignID should only be used by llvm.dbg.assign intrinsics
+  call void @llvm.dbg.value(metadata !14, metadata !10, metadata !DIExpression()), !dbg !13
+
+  ; Each following dbg.assign has an argument of the incorrect type.
+  ; CHECK: invalid llvm.dbg.assign intrinsic address/value
+  call void @llvm.dbg.assign(metadata !3, metadata !10, metadata !DIExpression(), metadata !14, metadata i32* undef, metadata !DIExpression()), !dbg !13
+  ; CHECK: invalid llvm.dbg.assign intrinsic variable
+  call void @llvm.dbg.assign(metadata i32 0, metadata !2, metadata !DIExpression(), metadata !14, metadata i32* undef, metadata !DIExpression()), !dbg !13
+  ; CHECK: invalid llvm.dbg.assign intrinsic expression
+  call void @llvm.dbg.assign(metadata !14, metadata !10, metadata !2, metadata !14, metadata i32* undef, metadata !DIExpression()), !dbg !13
+  ; CHECK: invalid llvm.dbg.assign intrinsic ID (4th arg)
+  call void @llvm.dbg.assign(metadata !14, metadata !10, metadata !DIExpression(), metadata !2, metadata i32* undef, metadata !DIExpression()), !dbg !13
+  ; CHECK: invalid llvm.dbg.assign intrinsic address (5th arg)
+  call void @llvm.dbg.assign(metadata !14, metadata !10, metadata !DIExpression(), metadata !14, metadata !3, metadata !DIExpression()), !dbg !13
+  ; CHECK: invalid llvm.dbg.assign intrinsic address expression (6th arg)
+  call void @llvm.dbg.assign(metadata !14, metadata !10, metadata !DIExpression(), metadata !14, metadata i32* undef, metadata !2), !dbg !13
+  ret void
+}
+
+declare void @llvm.dbg.value(metadata, metadata, metadata)
+declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5}
+!llvm.ident = !{!6}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/")
+!2 = !{}
+!3 = !{i32 7, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 14.0.0"}
+!7 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!10 = !DILocalVariable(name: "local", scope: !7, file: !1, line: 2, type: !11)
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!13 = !DILocation(line: 1, column: 1, scope: !7)
+!14 = distinct !DIAssignID()