//=====-- DebugProgramInstruction.cpp - Implement DbgRecords/DbgMarkers --====//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/DebugProgramInstruction.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/Support/Compiler.h"

namespace llvm {

template <typename T>
DbgRecordParamRef<T>::DbgRecordParamRef(const T *Param)
    : Ref(const_cast<T *>(Param)) {}
template <typename T>
DbgRecordParamRef<T>::DbgRecordParamRef(const MDNode *Param)
    : Ref(const_cast<MDNode *>(Param)) {}

template <typename T> T *DbgRecordParamRef<T>::get() const {
  return cast<T>(Ref);
}

template class LLVM_EXPORT_TEMPLATE DbgRecordParamRef<DIExpression>;
template class LLVM_EXPORT_TEMPLATE DbgRecordParamRef<DILabel>;
template class LLVM_EXPORT_TEMPLATE DbgRecordParamRef<DILocalVariable>;

DbgVariableRecord::DbgVariableRecord(const DbgVariableIntrinsic *DVI)
    : DbgRecord(ValueKind, DVI->getDebugLoc()),
      DebugValueUser({DVI->getRawLocation(), nullptr, nullptr}),
      Variable(DVI->getVariable()), Expression(DVI->getExpression()),
      AddressExpression() {
  switch (DVI->getIntrinsicID()) {
  case Intrinsic::dbg_value:
    Type = LocationType::Value;
    break;
  case Intrinsic::dbg_declare:
    Type = LocationType::Declare;
    break;
  case Intrinsic::dbg_assign: {
    Type = LocationType::Assign;
    const DbgAssignIntrinsic *Assign =
        static_cast<const DbgAssignIntrinsic *>(DVI);
    resetDebugValue(1, Assign->getRawAddress());
    AddressExpression = Assign->getAddressExpression();
    setAssignId(Assign->getAssignID());
    break;
  }
  default:
    llvm_unreachable(
        "Trying to create a DbgVariableRecord with an invalid intrinsic type!");
  }
}

DbgVariableRecord::DbgVariableRecord(const DbgVariableRecord &DVR)
    : DbgRecord(ValueKind, DVR.getDebugLoc()), DebugValueUser(DVR.DebugValues),
      Type(DVR.getType()), Variable(DVR.getVariable()),
      Expression(DVR.getExpression()),
      AddressExpression(DVR.AddressExpression) {}

DbgVariableRecord::DbgVariableRecord(Metadata *Location, DILocalVariable *DV,
                                     DIExpression *Expr, const DILocation *DI,
                                     LocationType Type)
    : DbgRecord(ValueKind, DI), DebugValueUser({Location, nullptr, nullptr}),
      Type(Type), Variable(DV), Expression(Expr) {}

DbgVariableRecord::DbgVariableRecord(Metadata *Value, DILocalVariable *Variable,
                                     DIExpression *Expression,
                                     DIAssignID *AssignID, Metadata *Address,
                                     DIExpression *AddressExpression,
                                     const DILocation *DI)
    : DbgRecord(ValueKind, DI), DebugValueUser({Value, Address, AssignID}),
      Type(LocationType::Assign), Variable(Variable), Expression(Expression),
      AddressExpression(AddressExpression) {}

void DbgRecord::deleteRecord() {
  switch (RecordKind) {
  case ValueKind:
    delete cast<DbgVariableRecord>(this);
    return;
  case LabelKind:
    delete cast<DbgLabelRecord>(this);
    return;
  }
  llvm_unreachable("unsupported DbgRecord kind");
}

void DbgRecord::print(raw_ostream &O, bool IsForDebug) const {
  switch (RecordKind) {
  case ValueKind:
    cast<DbgVariableRecord>(this)->print(O, IsForDebug);
    return;
  case LabelKind:
    cast<DbgLabelRecord>(this)->print(O, IsForDebug);
    return;
  };
  llvm_unreachable("unsupported DbgRecord kind");
}

void DbgRecord::print(raw_ostream &O, ModuleSlotTracker &MST,
                      bool IsForDebug) const {
  switch (RecordKind) {
  case ValueKind:
    cast<DbgVariableRecord>(this)->print(O, MST, IsForDebug);
    return;
  case LabelKind:
    cast<DbgLabelRecord>(this)->print(O, MST, IsForDebug);
    return;
  };
  llvm_unreachable("unsupported DbgRecord kind");
}

bool DbgRecord::isIdenticalToWhenDefined(const DbgRecord &R) const {
  if (RecordKind != R.RecordKind)
    return false;
  switch (RecordKind) {
  case ValueKind:
    return cast<DbgVariableRecord>(this)->isIdenticalToWhenDefined(
        *cast<DbgVariableRecord>(&R));
  case LabelKind:
    return cast<DbgLabelRecord>(this)->getLabel() ==
           cast<DbgLabelRecord>(R).getLabel();
  };
  llvm_unreachable("unsupported DbgRecord kind");
}

bool DbgRecord::isEquivalentTo(const DbgRecord &R) const {
  return getDebugLoc() == R.getDebugLoc() && isIdenticalToWhenDefined(R);
}

DbgInfoIntrinsic *
DbgRecord::createDebugIntrinsic(Module *M, Instruction *InsertBefore) const {
  switch (RecordKind) {
  case ValueKind:
    return cast<DbgVariableRecord>(this)->createDebugIntrinsic(M, InsertBefore);
  case LabelKind:
    return cast<DbgLabelRecord>(this)->createDebugIntrinsic(M, InsertBefore);
  };
  llvm_unreachable("unsupported DbgRecord kind");
}

DbgLabelRecord::DbgLabelRecord(MDNode *Label, MDNode *DL)
    : DbgRecord(LabelKind, DebugLoc(DL)), Label(Label) {
  assert(Label && "Unexpected nullptr");
  assert((isa<DILabel>(Label) || Label->isTemporary()) &&
         "Label type must be or resolve to a DILabel");
}
DbgLabelRecord::DbgLabelRecord(DILabel *Label, DebugLoc DL)
    : DbgRecord(LabelKind, DL), Label(Label) {
  assert(Label && "Unexpected nullptr");
}

DbgLabelRecord *DbgLabelRecord::createUnresolvedDbgLabelRecord(MDNode *Label,
                                                               MDNode *DL) {
  return new DbgLabelRecord(Label, DL);
}

DbgVariableRecord::DbgVariableRecord(DbgVariableRecord::LocationType Type,
                                     Metadata *Val, MDNode *Variable,
                                     MDNode *Expression, MDNode *AssignID,
                                     Metadata *Address,
                                     MDNode *AddressExpression, MDNode *DI)
    : DbgRecord(ValueKind, DebugLoc(DI)),
      DebugValueUser({Val, Address, AssignID}), Type(Type), Variable(Variable),
      Expression(Expression), AddressExpression(AddressExpression) {}

DbgVariableRecord *DbgVariableRecord::createUnresolvedDbgVariableRecord(
    DbgVariableRecord::LocationType Type, Metadata *Val, MDNode *Variable,
    MDNode *Expression, MDNode *AssignID, Metadata *Address,
    MDNode *AddressExpression, MDNode *DI) {
  return new DbgVariableRecord(Type, Val, Variable, Expression, AssignID,
                               Address, AddressExpression, DI);
}

DbgVariableRecord *
DbgVariableRecord::createDbgVariableRecord(Value *Location, DILocalVariable *DV,
                                           DIExpression *Expr,
                                           const DILocation *DI) {
  return new DbgVariableRecord(ValueAsMetadata::get(Location), DV, Expr, DI,
                               LocationType::Value);
}

DbgVariableRecord *DbgVariableRecord::createDbgVariableRecord(
    Value *Location, DILocalVariable *DV, DIExpression *Expr,
    const DILocation *DI, DbgVariableRecord &InsertBefore) {
  auto *NewDbgVariableRecord = createDbgVariableRecord(Location, DV, Expr, DI);
  NewDbgVariableRecord->insertBefore(&InsertBefore);
  return NewDbgVariableRecord;
}

DbgVariableRecord *DbgVariableRecord::createDVRDeclare(Value *Address,
                                                       DILocalVariable *DV,
                                                       DIExpression *Expr,
                                                       const DILocation *DI) {
  return new DbgVariableRecord(ValueAsMetadata::get(Address), DV, Expr, DI,
                               LocationType::Declare);
}

DbgVariableRecord *
DbgVariableRecord::createDVRDeclare(Value *Address, DILocalVariable *DV,
                                    DIExpression *Expr, const DILocation *DI,
                                    DbgVariableRecord &InsertBefore) {
  auto *NewDVRDeclare = createDVRDeclare(Address, DV, Expr, DI);
  NewDVRDeclare->insertBefore(&InsertBefore);
  return NewDVRDeclare;
}

DbgVariableRecord *DbgVariableRecord::createDVRAssign(
    Value *Val, DILocalVariable *Variable, DIExpression *Expression,
    DIAssignID *AssignID, Value *Address, DIExpression *AddressExpression,
    const DILocation *DI) {
  return new DbgVariableRecord(ValueAsMetadata::get(Val), Variable, Expression,
                               AssignID, ValueAsMetadata::get(Address),
                               AddressExpression, DI);
}

DbgVariableRecord *DbgVariableRecord::createLinkedDVRAssign(
    Instruction *LinkedInstr, Value *Val, DILocalVariable *Variable,
    DIExpression *Expression, Value *Address, DIExpression *AddressExpression,
    const DILocation *DI) {
  auto *Link = LinkedInstr->getMetadata(LLVMContext::MD_DIAssignID);
  assert(Link && "Linked instruction must have DIAssign metadata attached");
  auto *NewDVRAssign = DbgVariableRecord::createDVRAssign(
      Val, Variable, Expression, cast<DIAssignID>(Link), Address,
      AddressExpression, DI);
  LinkedInstr->getParent()->insertDbgRecordAfter(NewDVRAssign, LinkedInstr);
  return NewDVRAssign;
}

iterator_range<DbgVariableRecord::location_op_iterator>
DbgVariableRecord::location_ops() const {
  auto *MD = getRawLocation();
  // If a Value has been deleted, the "location" for this DbgVariableRecord will
  // be replaced by nullptr. Return an empty range.
  if (!MD)
    return {location_op_iterator(static_cast<ValueAsMetadata *>(nullptr)),
            location_op_iterator(static_cast<ValueAsMetadata *>(nullptr))};

  // If operand is ValueAsMetadata, return a range over just that operand.
  if (auto *VAM = dyn_cast<ValueAsMetadata>(MD))
    return {location_op_iterator(VAM), location_op_iterator(VAM + 1)};

  // If operand is DIArgList, return a range over its args.
  if (auto *AL = dyn_cast<DIArgList>(MD))
    return {location_op_iterator(AL->args_begin()),
            location_op_iterator(AL->args_end())};

  // Operand is an empty metadata tuple, so return empty iterator.
  assert(cast<MDNode>(MD)->getNumOperands() == 0);
  return {location_op_iterator(static_cast<ValueAsMetadata *>(nullptr)),
          location_op_iterator(static_cast<ValueAsMetadata *>(nullptr))};
}

unsigned DbgVariableRecord::getNumVariableLocationOps() const {
  if (hasArgList())
    return cast<DIArgList>(getRawLocation())->getArgs().size();
  return 1;
}

Value *DbgVariableRecord::getVariableLocationOp(unsigned OpIdx) const {
  auto *MD = getRawLocation();
  if (!MD)
    return nullptr;

  if (auto *AL = dyn_cast<DIArgList>(MD))
    return AL->getArgs()[OpIdx]->getValue();
  if (isa<MDNode>(MD))
    return nullptr;
  assert(isa<ValueAsMetadata>(MD) &&
         "Attempted to get location operand from DbgVariableRecord with none.");
  auto *V = cast<ValueAsMetadata>(MD);
  assert(OpIdx == 0 && "Operand Index must be 0 for a debug intrinsic with a "
                       "single location operand.");
  return V->getValue();
}

static ValueAsMetadata *getAsMetadata(Value *V) {
  return isa<MetadataAsValue>(V) ? dyn_cast<ValueAsMetadata>(
                                       cast<MetadataAsValue>(V)->getMetadata())
                                 : ValueAsMetadata::get(V);
}

void DbgVariableRecord::replaceVariableLocationOp(Value *OldValue,
                                                  Value *NewValue,
                                                  bool AllowEmpty) {
  assert(NewValue && "Values must be non-null");

  bool DbgAssignAddrReplaced = isDbgAssign() && OldValue == getAddress();
  if (DbgAssignAddrReplaced)
    setAddress(NewValue);

  auto Locations = location_ops();
  auto OldIt = find(Locations, OldValue);
  if (OldIt == Locations.end()) {
    if (AllowEmpty || DbgAssignAddrReplaced)
      return;
    llvm_unreachable("OldValue must be a current location");
  }

  if (!hasArgList()) {
    // Set our location to be the MAV wrapping the new Value.
    setRawLocation(isa<MetadataAsValue>(NewValue)
                       ? cast<MetadataAsValue>(NewValue)->getMetadata()
                       : ValueAsMetadata::get(NewValue));
    return;
  }

  // We must be referring to a DIArgList, produce a new operands vector with the
  // old value replaced, generate a new DIArgList and set it as our location.
  SmallVector<ValueAsMetadata *, 4> MDs;
  ValueAsMetadata *NewOperand = getAsMetadata(NewValue);
  for (auto *VMD : Locations)
    MDs.push_back(VMD == *OldIt ? NewOperand : getAsMetadata(VMD));
  setRawLocation(DIArgList::get(getVariableLocationOp(0)->getContext(), MDs));
}

void DbgVariableRecord::replaceVariableLocationOp(unsigned OpIdx,
                                                  Value *NewValue) {
  assert(OpIdx < getNumVariableLocationOps() && "Invalid Operand Index");

  if (!hasArgList()) {
    setRawLocation(isa<MetadataAsValue>(NewValue)
                       ? cast<MetadataAsValue>(NewValue)->getMetadata()
                       : ValueAsMetadata::get(NewValue));
    return;
  }

  SmallVector<ValueAsMetadata *, 4> MDs;
  ValueAsMetadata *NewOperand = getAsMetadata(NewValue);
  for (unsigned Idx = 0; Idx < getNumVariableLocationOps(); ++Idx)
    MDs.push_back(Idx == OpIdx ? NewOperand
                               : getAsMetadata(getVariableLocationOp(Idx)));

  setRawLocation(DIArgList::get(getVariableLocationOp(0)->getContext(), MDs));
}

void DbgVariableRecord::addVariableLocationOps(ArrayRef<Value *> NewValues,
                                               DIExpression *NewExpr) {
  assert(NewExpr->hasAllLocationOps(getNumVariableLocationOps() +
                                    NewValues.size()) &&
         "NewExpr for debug variable intrinsic does not reference every "
         "location operand.");
  assert(!is_contained(NewValues, nullptr) && "New values must be non-null");
  setExpression(NewExpr);
  SmallVector<ValueAsMetadata *, 4> MDs;
  for (auto *VMD : location_ops())
    MDs.push_back(getAsMetadata(VMD));
  for (auto *VMD : NewValues)
    MDs.push_back(getAsMetadata(VMD));
  setRawLocation(DIArgList::get(getVariableLocationOp(0)->getContext(), MDs));
}

void DbgVariableRecord::setKillLocation() {
  // TODO: When/if we remove duplicate values from DIArgLists, we don't need
  // this set anymore.
  SmallPtrSet<Value *, 4> RemovedValues;
  for (Value *OldValue : location_ops()) {
    if (!RemovedValues.insert(OldValue).second)
      continue;
    Value *Poison = PoisonValue::get(OldValue->getType());
    replaceVariableLocationOp(OldValue, Poison);
  }
}

bool DbgVariableRecord::isKillLocation() const {
  return (!hasArgList() && isa<MDNode>(getRawLocation())) ||
         (getNumVariableLocationOps() == 0 && !getExpression()->isComplex()) ||
         any_of(location_ops(), [](Value *V) { return isa<UndefValue>(V); });
}

std::optional<DbgVariableFragmentInfo> DbgVariableRecord::getFragment() const {
  return getExpression()->getFragmentInfo();
}

std::optional<uint64_t> DbgVariableRecord::getFragmentSizeInBits() const {
  if (auto Fragment = getExpression()->getFragmentInfo())
    return Fragment->SizeInBits;
  return getVariable()->getSizeInBits();
}

DbgRecord *DbgRecord::clone() const {
  switch (RecordKind) {
  case ValueKind:
    return cast<DbgVariableRecord>(this)->clone();
  case LabelKind:
    return cast<DbgLabelRecord>(this)->clone();
  };
  llvm_unreachable("unsupported DbgRecord kind");
}

DbgVariableRecord *DbgVariableRecord::clone() const {
  return new DbgVariableRecord(*this);
}

DbgLabelRecord *DbgLabelRecord::clone() const {
  return new DbgLabelRecord(getLabel(), getDebugLoc());
}

DbgVariableIntrinsic *
DbgVariableRecord::createDebugIntrinsic(Module *M,
                                        Instruction *InsertBefore) const {
  [[maybe_unused]] DICompileUnit *Unit =
      getDebugLoc()->getScope()->getSubprogram()->getUnit();
  assert(M && Unit &&
         "Cannot clone from BasicBlock that is not part of a Module or "
         "DICompileUnit!");
  LLVMContext &Context = getDebugLoc()->getContext();
  Function *IntrinsicFn;

  // Work out what sort of intrinsic we're going to produce.
  switch (getType()) {
  case DbgVariableRecord::LocationType::Declare:
    IntrinsicFn = Intrinsic::getOrInsertDeclaration(M, Intrinsic::dbg_declare);
    break;
  case DbgVariableRecord::LocationType::Value:
    IntrinsicFn = Intrinsic::getOrInsertDeclaration(M, Intrinsic::dbg_value);
    break;
  case DbgVariableRecord::LocationType::Assign:
    IntrinsicFn = Intrinsic::getOrInsertDeclaration(M, Intrinsic::dbg_assign);
    break;
  case DbgVariableRecord::LocationType::End:
  case DbgVariableRecord::LocationType::Any:
    llvm_unreachable("Invalid LocationType");
  }

  // Create the intrinsic from this DbgVariableRecord's information, optionally
  // insert into the target location.
  DbgVariableIntrinsic *DVI;
  assert(getRawLocation() &&
         "DbgVariableRecord's RawLocation should be non-null.");
  if (isDbgAssign()) {
    Value *AssignArgs[] = {
        MetadataAsValue::get(Context, getRawLocation()),
        MetadataAsValue::get(Context, getVariable()),
        MetadataAsValue::get(Context, getExpression()),
        MetadataAsValue::get(Context, getAssignID()),
        MetadataAsValue::get(Context, getRawAddress()),
        MetadataAsValue::get(Context, getAddressExpression())};
    DVI = cast<DbgVariableIntrinsic>(CallInst::Create(
        IntrinsicFn->getFunctionType(), IntrinsicFn, AssignArgs));
  } else {
    Value *Args[] = {MetadataAsValue::get(Context, getRawLocation()),
                     MetadataAsValue::get(Context, getVariable()),
                     MetadataAsValue::get(Context, getExpression())};
    DVI = cast<DbgVariableIntrinsic>(
        CallInst::Create(IntrinsicFn->getFunctionType(), IntrinsicFn, Args));
  }
  DVI->setTailCall();
  DVI->setDebugLoc(getDebugLoc());
  if (InsertBefore)
    DVI->insertBefore(InsertBefore->getIterator());

  return DVI;
}

DbgLabelInst *
DbgLabelRecord::createDebugIntrinsic(Module *M,
                                     Instruction *InsertBefore) const {
  auto *LabelFn = Intrinsic::getOrInsertDeclaration(M, Intrinsic::dbg_label);
  Value *Args[] = {
      MetadataAsValue::get(getDebugLoc()->getContext(), getLabel())};
  DbgLabelInst *DbgLabel = cast<DbgLabelInst>(
      CallInst::Create(LabelFn->getFunctionType(), LabelFn, Args));
  DbgLabel->setTailCall();
  DbgLabel->setDebugLoc(getDebugLoc());
  if (InsertBefore)
    DbgLabel->insertBefore(InsertBefore->getIterator());
  return DbgLabel;
}

Value *DbgVariableRecord::getAddress() const {
  auto *MD = getRawAddress();
  if (auto *V = dyn_cast_or_null<ValueAsMetadata>(MD))
    return V->getValue();

  // When the value goes to null, it gets replaced by an empty MDNode.
  assert((!MD || !cast<MDNode>(MD)->getNumOperands()) &&
         "Expected an empty MDNode");
  return nullptr;
}

DIAssignID *DbgVariableRecord::getAssignID() const {
  return cast<DIAssignID>(DebugValues[2]);
}

void DbgVariableRecord::setAssignId(DIAssignID *New) {
  resetDebugValue(2, New);
}

void DbgVariableRecord::setKillAddress() {
  resetDebugValue(
      1, ValueAsMetadata::get(PoisonValue::get(getAddress()->getType())));
}

bool DbgVariableRecord::isKillAddress() const {
  Value *Addr = getAddress();
  return !Addr || isa<UndefValue>(Addr);
}

const Instruction *DbgRecord::getInstruction() const {
  return Marker->MarkedInstr;
}

const BasicBlock *DbgRecord::getParent() const {
  return Marker->MarkedInstr->getParent();
}

BasicBlock *DbgRecord::getParent() { return Marker->MarkedInstr->getParent(); }

BasicBlock *DbgRecord::getBlock() { return Marker->getParent(); }

const BasicBlock *DbgRecord::getBlock() const { return Marker->getParent(); }

Function *DbgRecord::getFunction() { return getBlock()->getParent(); }

const Function *DbgRecord::getFunction() const {
  return getBlock()->getParent();
}

Module *DbgRecord::getModule() { return getFunction()->getParent(); }

const Module *DbgRecord::getModule() const {
  return getFunction()->getParent();
}

LLVMContext &DbgRecord::getContext() { return getBlock()->getContext(); }

const LLVMContext &DbgRecord::getContext() const {
  return getBlock()->getContext();
}

void DbgRecord::insertBefore(DbgRecord *InsertBefore) {
  assert(!getMarker() &&
         "Cannot insert a DbgRecord that is already has a DbgMarker!");
  assert(InsertBefore->getMarker() &&
         "Cannot insert a DbgRecord before a DbgRecord that does not have a "
         "DbgMarker!");
  InsertBefore->getMarker()->insertDbgRecord(this, InsertBefore);
}
void DbgRecord::insertAfter(DbgRecord *InsertAfter) {
  assert(!getMarker() &&
         "Cannot insert a DbgRecord that is already has a DbgMarker!");
  assert(InsertAfter->getMarker() &&
         "Cannot insert a DbgRecord after a DbgRecord that does not have a "
         "DbgMarker!");
  InsertAfter->getMarker()->insertDbgRecordAfter(this, InsertAfter);
}

void DbgRecord::insertBefore(self_iterator InsertBefore) {
  assert(!getMarker() &&
         "Cannot insert a DbgRecord that is already has a DbgMarker!");
  assert(InsertBefore->getMarker() &&
         "Cannot insert a DbgRecord before a DbgRecord that does not have a "
         "DbgMarker!");
  InsertBefore->getMarker()->insertDbgRecord(this, &*InsertBefore);
}
void DbgRecord::insertAfter(self_iterator InsertAfter) {
  assert(!getMarker() &&
         "Cannot insert a DbgRecord that is already has a DbgMarker!");
  assert(InsertAfter->getMarker() &&
         "Cannot insert a DbgRecord after a DbgRecord that does not have a "
         "DbgMarker!");
  InsertAfter->getMarker()->insertDbgRecordAfter(this, &*InsertAfter);
}

void DbgRecord::moveBefore(DbgRecord *MoveBefore) {
  assert(getMarker() &&
         "Canot move a DbgRecord that does not currently have a DbgMarker!");
  removeFromParent();
  insertBefore(MoveBefore);
}
void DbgRecord::moveAfter(DbgRecord *MoveAfter) {
  assert(getMarker() &&
         "Canot move a DbgRecord that does not currently have a DbgMarker!");
  removeFromParent();
  insertAfter(MoveAfter);
}

void DbgRecord::moveBefore(self_iterator MoveBefore) {
  assert(getMarker() &&
         "Canot move a DbgRecord that does not currently have a DbgMarker!");
  removeFromParent();
  insertBefore(MoveBefore);
}
void DbgRecord::moveAfter(self_iterator MoveAfter) {
  assert(getMarker() &&
         "Canot move a DbgRecord that does not currently have a DbgMarker!");
  removeFromParent();
  insertAfter(MoveAfter);
}

///////////////////////////////////////////////////////////////////////////////

// An empty, global, DbgMarker for the purpose of describing empty ranges of
// DbgRecords.
DbgMarker DbgMarker::EmptyDbgMarker;

void DbgMarker::dropDbgRecords() {
  while (!StoredDbgRecords.empty()) {
    auto It = StoredDbgRecords.begin();
    DbgRecord *DR = &*It;
    StoredDbgRecords.erase(It);
    DR->deleteRecord();
  }
}

void DbgMarker::dropOneDbgRecord(DbgRecord *DR) {
  assert(DR->getMarker() == this);
  StoredDbgRecords.erase(DR->getIterator());
  DR->deleteRecord();
}

const BasicBlock *DbgMarker::getParent() const {
  return MarkedInstr->getParent();
}

BasicBlock *DbgMarker::getParent() { return MarkedInstr->getParent(); }

void DbgMarker::removeMarker() {
  // Are there any DbgRecords in this DbgMarker? If not, nothing to preserve.
  Instruction *Owner = MarkedInstr;
  if (StoredDbgRecords.empty()) {
    eraseFromParent();
    Owner->DebugMarker = nullptr;
    return;
  }

  // The attached DbgRecords need to be preserved; attach them to the next
  // instruction. If there isn't a next instruction, put them on the
  // "trailing" list.
  DbgMarker *NextMarker = Owner->getParent()->getNextMarker(Owner);
  if (NextMarker) {
    NextMarker->absorbDebugValues(*this, true);
    eraseFromParent();
  } else {
    // We can avoid a deallocation -- just store this marker onto the next
    // instruction. Unless we're at the end of the block, in which case this
    // marker becomes the trailing marker of a degenerate block.
    BasicBlock::iterator NextIt = std::next(Owner->getIterator());
    if (NextIt == getParent()->end()) {
      getParent()->setTrailingDbgRecords(this);
      MarkedInstr = nullptr;
    } else {
      NextIt->DebugMarker = this;
      MarkedInstr = &*NextIt;
    }
  }
  Owner->DebugMarker = nullptr;
}

void DbgMarker::removeFromParent() {
  MarkedInstr->DebugMarker = nullptr;
  MarkedInstr = nullptr;
}

void DbgMarker::eraseFromParent() {
  if (MarkedInstr)
    removeFromParent();
  dropDbgRecords();
  delete this;
}

iterator_range<DbgRecord::self_iterator> DbgMarker::getDbgRecordRange() {
  return make_range(StoredDbgRecords.begin(), StoredDbgRecords.end());
}
iterator_range<DbgRecord::const_self_iterator>
DbgMarker::getDbgRecordRange() const {
  return make_range(StoredDbgRecords.begin(), StoredDbgRecords.end());
}

void DbgRecord::removeFromParent() {
  getMarker()->StoredDbgRecords.erase(getIterator());
  Marker = nullptr;
}

void DbgRecord::eraseFromParent() {
  removeFromParent();
  deleteRecord();
}

void DbgMarker::insertDbgRecord(DbgRecord *New, bool InsertAtHead) {
  auto It = InsertAtHead ? StoredDbgRecords.begin() : StoredDbgRecords.end();
  StoredDbgRecords.insert(It, *New);
  New->setMarker(this);
}
void DbgMarker::insertDbgRecord(DbgRecord *New, DbgRecord *InsertBefore) {
  assert(InsertBefore->getMarker() == this &&
         "DbgRecord 'InsertBefore' must be contained in this DbgMarker!");
  StoredDbgRecords.insert(InsertBefore->getIterator(), *New);
  New->setMarker(this);
}
void DbgMarker::insertDbgRecordAfter(DbgRecord *New, DbgRecord *InsertAfter) {
  assert(InsertAfter->getMarker() == this &&
         "DbgRecord 'InsertAfter' must be contained in this DbgMarker!");
  StoredDbgRecords.insert(++(InsertAfter->getIterator()), *New);
  New->setMarker(this);
}

void DbgMarker::absorbDebugValues(DbgMarker &Src, bool InsertAtHead) {
  auto It = InsertAtHead ? StoredDbgRecords.begin() : StoredDbgRecords.end();
  for (DbgRecord &DVR : Src.StoredDbgRecords)
    DVR.setMarker(this);

  StoredDbgRecords.splice(It, Src.StoredDbgRecords);
}

void DbgMarker::absorbDebugValues(
    iterator_range<DbgRecord::self_iterator> Range, DbgMarker &Src,
    bool InsertAtHead) {
  for (DbgRecord &DR : Range)
    DR.setMarker(this);

  auto InsertPos =
      (InsertAtHead) ? StoredDbgRecords.begin() : StoredDbgRecords.end();

  StoredDbgRecords.splice(InsertPos, Src.StoredDbgRecords, Range.begin(),
                          Range.end());
}

iterator_range<simple_ilist<DbgRecord>::iterator> DbgMarker::cloneDebugInfoFrom(
    DbgMarker *From, std::optional<simple_ilist<DbgRecord>::iterator> from_here,
    bool InsertAtHead) {
  DbgRecord *First = nullptr;
  // Work out what range of DbgRecords to clone: normally all the contents of
  // the "From" marker, optionally we can start from the from_here position down
  // to end().
  auto Range =
      make_range(From->StoredDbgRecords.begin(), From->StoredDbgRecords.end());
  if (from_here.has_value())
    Range = make_range(*from_here, From->StoredDbgRecords.end());

  // Clone each DbgVariableRecord and insert into StoreDbgVariableRecords;
  // optionally place them at the start or the end of the list.
  auto Pos = (InsertAtHead) ? StoredDbgRecords.begin() : StoredDbgRecords.end();
  for (DbgRecord &DR : Range) {
    DbgRecord *New = DR.clone();
    New->setMarker(this);
    StoredDbgRecords.insert(Pos, *New);
    if (!First)
      First = New;
  }

  if (!First)
    return {StoredDbgRecords.end(), StoredDbgRecords.end()};

  if (InsertAtHead)
    // If InsertAtHead is set, we cloned a range onto the front of of the
    // StoredDbgRecords collection, return that range.
    return {StoredDbgRecords.begin(), Pos};
  else
    // We inserted a block at the end, return that range.
    return {First->getIterator(), StoredDbgRecords.end()};
}

} // end namespace llvm
