//===- subzero/src/IceInst.cpp - High-level instruction implementation ----===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Implements the Inst class, primarily the various subclass
/// constructors and dump routines.
///
//===----------------------------------------------------------------------===//

#include "IceInst.h"

#include "IceCfg.h"
#include "IceCfgNode.h"
#include "IceInstVarIter.h"
#include "IceLiveness.h"
#include "IceOperand.h"
#include "IceTargetLowering.h"

#include "llvm/Support/Format.h"

namespace Ice {

namespace {

// Using non-anonymous struct so that array_lengthof works.
const struct InstArithmeticAttributes_ {
  const char *DisplayString;
  bool IsCommutative;
} InstArithmeticAttributes[] = {
#define X(tag, str, commutative)                                               \
  { str, commutative }                                                         \
  ,
    ICEINSTARITHMETIC_TABLE
#undef X
};

// Using non-anonymous struct so that array_lengthof works.
const struct InstCastAttributes_ {
  const char *DisplayString;
} InstCastAttributes[] = {
#define X(tag, str)                                                            \
  { str }                                                                      \
  ,
    ICEINSTCAST_TABLE
#undef X
};

// Using non-anonymous struct so that array_lengthof works.
const struct InstFcmpAttributes_ {
  const char *DisplayString;
} InstFcmpAttributes[] = {
#define X(tag, str)                                                            \
  { str }                                                                      \
  ,
    ICEINSTFCMP_TABLE
#undef X
};

// Using non-anonymous struct so that array_lengthof works.
const struct InstIcmpAttributes_ {
  const char *DisplayString;
  InstIcmp::ICond Reverse;
} InstIcmpAttributes[] = {
#define X(tag, reverse, str)                                                   \
  { str, InstIcmp::ICond::reverse }                                            \
  ,
    ICEINSTICMP_TABLE
#undef X
};

} // end of anonymous namespace

Inst::Inst(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest)
    : Kind(Kind), Number(Func->newInstNumber()), Dest(Dest), MaxSrcs(MaxSrcs),
      LiveRangesEnded(0) {
  Srcs.reserve(MaxSrcs);
}

const char *Inst::getInstName() const {
  if (!BuildDefs::dump())
    return "???";

  switch (Kind) {
#define X(InstrKind, name)                                                     \
  case InstrKind:                                                              \
    return name
    X(Unreachable, "unreachable");
    X(Alloca, "alloca");
    X(Arithmetic, "arithmetic");
    X(Br, "br");
    X(Call, "call");
    X(Cast, "cast");
    X(ExtractElement, "extractelement");
    X(Fcmp, "fcmp");
    X(Icmp, "icmp");
    X(IntrinsicCall, "intrinsiccall");
    X(InsertElement, "insertelement");
    X(Load, "load");
    X(Phi, "phi");
    X(Ret, "ret");
    X(Select, "select");
    X(Store, "store");
    X(Switch, "switch");
    X(Assign, "assign");
    X(Breakpoint, "break");
    X(BundleLock, "bundlelock");
    X(BundleUnlock, "bundleunlock");
    X(FakeDef, "fakedef");
    X(FakeUse, "fakeuse");
    X(FakeKill, "fakekill");
    X(JumpTable, "jumptable");
    X(ShuffleVector, "shufflevector");
#undef X
  default:
    assert(Kind >= Target);
    return "target";
  }
}

// Assign the instruction a new number.
void Inst::renumber(Cfg *Func) {
  Number = isDeleted() ? NumberDeleted : Func->newInstNumber();
}

// Delete the instruction if its tentative Dead flag is still set after
// liveness analysis.
void Inst::deleteIfDead() {
  if (Dead)
    setDeleted();
}

// If Src is a Variable, it returns true if this instruction ends Src's live
// range. Otherwise, returns false.
bool Inst::isLastUse(const Operand *TestSrc) const {
  if (LiveRangesEnded == 0)
    return false; // early-exit optimization
  if (auto *TestVar = llvm::dyn_cast<const Variable>(TestSrc)) {
    LREndedBits Mask = LiveRangesEnded;
    FOREACH_VAR_IN_INST(Var, *this) {
      if (Var == TestVar) {
        // We've found where the variable is used in the instruction.
        return Mask & 1;
      }
      Mask >>= 1;
      if (Mask == 0)
        return false; // another early-exit optimization
    }
  }
  return false;
}

// Given an instruction like:
//   a = b + c + [x,y] + e
// which was created from OrigInst:
//   a = b + c + d + e
// with SpliceAssn spliced in:
//   d = [x,y]
//
// Reconstruct the LiveRangesEnded bitmask in this instruction by combining the
// LiveRangesEnded values of OrigInst and SpliceAssn. If operands d and [x,y]
// contain a different number of variables, then the bitmask position for e may
// be different in OrigInst and the current instruction, requiring extra shifts
// and masks in the computation. In the example above, OrigInst has variable e
// in bit position 3, whereas the current instruction has e in bit position 4
// because [x,y] consumes 2 bitmask slots while d only consumed 1.
//
// Additionally, set HasSideEffects if either OrigInst or SpliceAssn have
// HasSideEffects set.
void Inst::spliceLivenessInfo(Inst *OrigInst, Inst *SpliceAssn) {
  HasSideEffects |= OrigInst->HasSideEffects;
  HasSideEffects |= SpliceAssn->HasSideEffects;
  // Find the bitmask index of SpliceAssn's dest within OrigInst.
  Variable *SpliceDest = SpliceAssn->getDest();
  SizeT Index = 0;
  for (SizeT I = 0; I < OrigInst->getSrcSize(); ++I) {
    Operand *Src = OrigInst->getSrc(I);
    if (Src == SpliceDest) {
      LREndedBits LeftMask = OrigInst->LiveRangesEnded & ((1 << Index) - 1);
      LREndedBits RightMask = OrigInst->LiveRangesEnded >> (Index + 1);
      LiveRangesEnded = LeftMask | (SpliceAssn->LiveRangesEnded << Index) |
                        (RightMask << (Index + getSrc(I)->getNumVars()));
      return;
    }
    Index += getSrc(I)->getNumVars();
  }
  llvm::report_fatal_error("Failed to find splice operand");
}

bool Inst::isMemoryWrite() const {
  llvm::report_fatal_error("Attempt to call base Inst::isMemoryWrite() method");
}

void Inst::livenessLightweight(Cfg *Func, LivenessBV &Live) {
  assert(!isDeleted());
  resetLastUses();
  VariablesMetadata *VMetadata = Func->getVMetadata();
  FOREACH_VAR_IN_INST(Var, *this) {
    if (VMetadata->isMultiBlock(Var))
      continue;
    SizeT Index = Var->getIndex();
    if (Live[Index])
      continue;
    Live[Index] = true;
    setLastUse(IndexOfVarInInst(Var));
  }
}

bool Inst::liveness(InstNumberT InstNumber, LivenessBV &Live,
                    Liveness *Liveness, LiveBeginEndMap *LiveBegin,
                    LiveBeginEndMap *LiveEnd) {
  assert(!isDeleted());

  Dead = false;
  if (Dest && !Dest->isRematerializable()) {
    SizeT VarNum = Liveness->getLiveIndex(Dest->getIndex());
    if (Live[VarNum]) {
      if (!isDestRedefined()) {
        Live[VarNum] = false;
        if (LiveBegin && Liveness->getRangeMask(Dest->getIndex())) {
          LiveBegin->push_back(std::make_pair(VarNum, InstNumber));
        }
      }
    } else {
      if (!hasSideEffects())
        Dead = true;
    }
  }
  if (Dead)
    return false;
  // Phi arguments only get added to Live in the predecessor node, but we still
  // need to update LiveRangesEnded.
  bool IsPhi = llvm::isa<InstPhi>(this);
  resetLastUses();
  FOREACH_VAR_IN_INST(Var, *this) {
    if (Var->isRematerializable())
      continue;
    SizeT VarNum = Liveness->getLiveIndex(Var->getIndex());
    if (!Live[VarNum]) {
      setLastUse(IndexOfVarInInst(Var));
      if (!IsPhi) {
        Live[VarNum] = true;
        // For a variable in SSA form, its live range can end at most once in a
        // basic block. However, after lowering to two-address instructions, we
        // end up with sequences like "t=b;t+=c;a=t" where t's live range
        // begins and ends twice. ICE only allows a variable to have a single
        // liveness interval in a basic block (except for blocks where a
        // variable is live-in and live-out but there is a gap in the middle).
        // Therefore, this lowered sequence needs to represent a single
        // conservative live range for t. Since the instructions are being
        // traversed backwards, we make sure LiveEnd is only set once by
        // setting it only when LiveEnd[VarNum]==0 (sentinel value). Note that
        // it's OK to set LiveBegin multiple times because of the backwards
        // traversal.
        if (LiveEnd && Liveness->getRangeMask(Var->getIndex())) {
          // Ideally, we would verify that VarNum wasn't already added in this
          // block, but this can't be done very efficiently with LiveEnd as a
          // vector. Instead, livenessPostprocess() verifies this after the
          // vector has been sorted.
          LiveEnd->push_back(std::make_pair(VarNum, InstNumber));
        }
      }
    }
  }
  return true;
}

InstAlloca::InstAlloca(Cfg *Func, Variable *Dest, Operand *ByteCount,
                       uint32_t AlignInBytes)
    : InstHighLevel(Func, Inst::Alloca, 1, Dest), AlignInBytes(AlignInBytes) {
  // Verify AlignInBytes is 0 or a power of 2.
  assert(AlignInBytes == 0 || llvm::isPowerOf2_32(AlignInBytes));
  addSource(ByteCount);
}

InstArithmetic::InstArithmetic(Cfg *Func, OpKind Op, Variable *Dest,
                               Operand *Source1, Operand *Source2)
    : InstHighLevel(Func, Inst::Arithmetic, 2, Dest), Op(Op) {
  addSource(Source1);
  addSource(Source2);
}

const char *InstArithmetic::getInstName() const {
  if (!BuildDefs::dump())
    return "???";

  return InstArithmeticAttributes[getOp()].DisplayString;
}

const char *InstArithmetic::getOpName(OpKind Op) {
  return Op < InstArithmetic::_num ? InstArithmeticAttributes[Op].DisplayString
                                   : "???";
}

bool InstArithmetic::isCommutative() const {
  return InstArithmeticAttributes[getOp()].IsCommutative;
}

InstAssign::InstAssign(Cfg *Func, Variable *Dest, Operand *Source)
    : InstHighLevel(Func, Inst::Assign, 1, Dest) {
  addSource(Source);
}

bool InstAssign::isVarAssign() const { return llvm::isa<Variable>(getSrc(0)); }

// If TargetTrue==TargetFalse, we turn it into an unconditional branch. This
// ensures that, along with the 'switch' instruction semantics, there is at
// most one edge from one node to another.
InstBr::InstBr(Cfg *Func, Operand *Source, CfgNode *TargetTrue_,
               CfgNode *TargetFalse_)
    : InstHighLevel(Func, Inst::Br, 1, nullptr), TargetFalse(TargetFalse_),
      TargetTrue(TargetTrue_) {
  if (auto *Constant = llvm::dyn_cast<ConstantInteger32>(Source)) {
    int32_t C32 = Constant->getValue();
    if (C32 != 0) {
      TargetFalse = TargetTrue;
    }
    TargetTrue = nullptr; // turn into unconditional version
  } else if (TargetTrue == TargetFalse) {
    TargetTrue = nullptr; // turn into unconditional version
  } else {
    addSource(Source);
  }
}

InstBr::InstBr(Cfg *Func, CfgNode *Target)
    : InstHighLevel(Func, Inst::Br, 0, nullptr), TargetFalse(Target),
      TargetTrue(nullptr) {}

NodeList InstBr::getTerminatorEdges() const {
  NodeList OutEdges;
  OutEdges.reserve(TargetTrue ? 2 : 1);
  OutEdges.push_back(TargetFalse);
  if (TargetTrue)
    OutEdges.push_back(TargetTrue);
  return OutEdges;
}

bool InstBr::repointEdges(CfgNode *OldNode, CfgNode *NewNode) {
  bool Found = false;
  if (TargetFalse == OldNode) {
    TargetFalse = NewNode;
    Found = true;
  }
  if (TargetTrue == OldNode) {
    TargetTrue = NewNode;
    Found = true;
  }
  return Found;
}

InstCast::InstCast(Cfg *Func, OpKind CastKind, Variable *Dest, Operand *Source)
    : InstHighLevel(Func, Inst::Cast, 1, Dest), CastKind(CastKind) {
  addSource(Source);
}

InstExtractElement::InstExtractElement(Cfg *Func, Variable *Dest,
                                       Operand *Source1, Operand *Source2)
    : InstHighLevel(Func, Inst::ExtractElement, 2, Dest) {
  addSource(Source1);
  addSource(Source2);
}

InstFcmp::InstFcmp(Cfg *Func, FCond Condition, Variable *Dest, Operand *Source1,
                   Operand *Source2)
    : InstHighLevel(Func, Inst::Fcmp, 2, Dest), Condition(Condition) {
  addSource(Source1);
  addSource(Source2);
}

InstIcmp::InstIcmp(Cfg *Func, ICond Condition, Variable *Dest, Operand *Source1,
                   Operand *Source2)
    : InstHighLevel(Func, Inst::Icmp, 2, Dest), Condition(Condition) {
  addSource(Source1);
  addSource(Source2);
}

InstInsertElement::InstInsertElement(Cfg *Func, Variable *Dest,
                                     Operand *Source1, Operand *Source2,
                                     Operand *Source3)
    : InstHighLevel(Func, Inst::InsertElement, 3, Dest) {
  addSource(Source1);
  addSource(Source2);
  addSource(Source3);
}

InstLoad::InstLoad(Cfg *Func, Variable *Dest, Operand *SourceAddr)
    : InstHighLevel(Func, Inst::Load, 1, Dest) {
  addSource(SourceAddr);
}

InstPhi::InstPhi(Cfg *Func, SizeT MaxSrcs, Variable *Dest)
    : InstHighLevel(Func, Phi, MaxSrcs, Dest) {
  Labels.reserve(MaxSrcs);
}

// TODO: A Switch instruction (and maybe others) can add duplicate edges. We
// may want to de-dup Phis and validate consistency (i.e., the source operands
// are the same for duplicate edges), though it seems the current lowering code
// is OK with this situation.
void InstPhi::addArgument(Operand *Source, CfgNode *Label) {
  assert(Label);
  Labels.push_back(Label);
  addSource(Source);
}

// Find the source operand corresponding to the incoming edge for the given
// node.
Operand *InstPhi::getOperandForTarget(CfgNode *Target) const {
  for (SizeT I = 0; I < getSrcSize(); ++I) {
    if (Labels[I] == Target)
      return getSrc(I);
  }
  llvm_unreachable("Phi target not found");
  return nullptr;
}

// Replace the source operand corresponding to the incoming edge for the given
// node by a zero of the appropriate type.
void InstPhi::clearOperandForTarget(CfgNode *Target) {
  for (SizeT I = 0; I < getSrcSize(); ++I) {
    if (getLabel(I) == Target) {
      Type Ty = Dest->getType();
      Srcs[I] = Target->getCfg()->getContext()->getConstantZero(Ty);
      return;
    }
  }
  llvm_unreachable("Phi target not found");
}

// Updates liveness for a particular operand based on the given predecessor
// edge. Doesn't mark the operand as live if the Phi instruction is dead or
// deleted.
void InstPhi::livenessPhiOperand(LivenessBV &Live, CfgNode *Target,
                                 Liveness *Liveness) {
  if (isDeleted() || Dead)
    return;
  for (SizeT I = 0; I < getSrcSize(); ++I) {
    if (Labels[I] == Target) {
      if (auto *Var = llvm::dyn_cast<Variable>(getSrc(I))) {
        if (!Var->isRematerializable()) {
          SizeT SrcIndex = Liveness->getLiveIndex(Var->getIndex());
          if (!Live[SrcIndex]) {
            setLastUse(I);
            Live[SrcIndex] = true;
          }
        }
      }
      return;
    }
  }
  llvm_unreachable("Phi operand not found for specified target node");
}

// Change "a=phi(...)" to "a_phi=phi(...)" and return a new instruction
// "a=a_phi".
Inst *InstPhi::lower(Cfg *Func) {
  Variable *Dest = getDest();
  assert(Dest);
  Variable *NewSrc = Func->makeVariable(Dest->getType());
  if (BuildDefs::dump())
    NewSrc->setName(Func, Dest->getName() + "_phi");
  if (auto *NewSrc64On32 = llvm::dyn_cast<Variable64On32>(NewSrc))
    NewSrc64On32->initHiLo(Func);
  this->Dest = NewSrc;
  return InstAssign::create(Func, Dest, NewSrc);
}

InstRet::InstRet(Cfg *Func, Operand *RetValue)
    : InstHighLevel(Func, Ret, RetValue ? 1 : 0, nullptr) {
  if (RetValue)
    addSource(RetValue);
}

InstSelect::InstSelect(Cfg *Func, Variable *Dest, Operand *Condition,
                       Operand *SourceTrue, Operand *SourceFalse)
    : InstHighLevel(Func, Inst::Select, 3, Dest) {
  assert(typeElementType(Condition->getType()) == IceType_i1);
  addSource(Condition);
  addSource(SourceTrue);
  addSource(SourceFalse);
}

InstStore::InstStore(Cfg *Func, Operand *Data, Operand *Addr)
    : InstHighLevel(Func, Inst::Store, 3, nullptr) {
  addSource(Data);
  addSource(Addr);
  // The 3rd operand is a dummy placeholder for the RMW beacon.
  addSource(Data);
}

Variable *InstStore::getRmwBeacon() const {
  return llvm::dyn_cast<Variable>(getSrc(2));
}

void InstStore::setRmwBeacon(Variable *Beacon) {
  Dest = llvm::dyn_cast<Variable>(getData());
  Srcs[2] = Beacon;
}

InstSwitch::InstSwitch(Cfg *Func, SizeT NumCases, Operand *Source,
                       CfgNode *LabelDefault)
    : InstHighLevel(Func, Inst::Switch, 1, nullptr), LabelDefault(LabelDefault),
      NumCases(NumCases) {
  addSource(Source);
  Values = Func->allocateArrayOf<uint64_t>(NumCases);
  Labels = Func->allocateArrayOf<CfgNode *>(NumCases);
  // Initialize in case buggy code doesn't set all entries
  for (SizeT I = 0; I < NumCases; ++I) {
    Values[I] = 0;
    Labels[I] = nullptr;
  }
}

void InstSwitch::addBranch(SizeT CaseIndex, uint64_t Value, CfgNode *Label) {
  assert(CaseIndex < NumCases);
  Values[CaseIndex] = Value;
  Labels[CaseIndex] = Label;
}

NodeList InstSwitch::getTerminatorEdges() const {
  NodeList OutEdges;
  OutEdges.reserve(NumCases + 1);
  assert(LabelDefault);
  OutEdges.push_back(LabelDefault);
  for (SizeT I = 0; I < NumCases; ++I) {
    assert(Labels[I]);
    OutEdges.push_back(Labels[I]);
  }
  std::sort(OutEdges.begin(), OutEdges.end(),
            [](const CfgNode *x, const CfgNode *y) {
              return x->getIndex() < y->getIndex();
            });
  auto Last = std::unique(OutEdges.begin(), OutEdges.end());
  OutEdges.erase(Last, OutEdges.end());
  return OutEdges;
}

bool InstSwitch::repointEdges(CfgNode *OldNode, CfgNode *NewNode) {
  bool Found = false;
  if (LabelDefault == OldNode) {
    LabelDefault = NewNode;
    Found = true;
  }
  for (SizeT I = 0; I < NumCases; ++I) {
    if (Labels[I] == OldNode) {
      Labels[I] = NewNode;
      Found = true;
    }
  }
  return Found;
}

InstUnreachable::InstUnreachable(Cfg *Func)
    : InstHighLevel(Func, Inst::Unreachable, 0, nullptr) {}

InstBundleLock::InstBundleLock(Cfg *Func, InstBundleLock::Option BundleOption)
    : InstHighLevel(Func, Inst::BundleLock, 0, nullptr),
      BundleOption(BundleOption) {}

InstBundleUnlock::InstBundleUnlock(Cfg *Func)
    : InstHighLevel(Func, Inst::BundleUnlock, 0, nullptr) {}

InstFakeDef::InstFakeDef(Cfg *Func, Variable *Dest, Variable *Src)
    : InstHighLevel(Func, Inst::FakeDef, Src ? 1 : 0, Dest) {
  assert(Dest);
  if (Src)
    addSource(Src);
}

InstFakeUse::InstFakeUse(Cfg *Func, Variable *Src, uint32_t Weight)
    : InstHighLevel(Func, Inst::FakeUse, Weight, nullptr) {
  assert(Src);
  for (uint32_t i = 0; i < Weight; ++i)
    addSource(Src);
}

InstFakeKill::InstFakeKill(Cfg *Func, const Inst *Linked)
    : InstHighLevel(Func, Inst::FakeKill, 0, nullptr), Linked(Linked) {}

InstShuffleVector::InstShuffleVector(Cfg *Func, Variable *Dest, Operand *Src0,
                                     Operand *Src1)
    : InstHighLevel(Func, Inst::ShuffleVector, 2, Dest),
      NumIndexes(typeNumElements(Dest->getType())) {
  addSource(Src0);
  addSource(Src1);
  Indexes = Func->allocateArrayOf<ConstantInteger32 *>(NumIndexes);
}

namespace {
GlobalString makeName(Cfg *Func, const SizeT Id) {
  const auto FuncName = Func->getFunctionName();
  auto *Ctx = Func->getContext();
  if (FuncName.hasStdString())
    return GlobalString::createWithString(
        Ctx, ".L" + FuncName.toString() + "$jumptable$__" + std::to_string(Id));
  return GlobalString::createWithString(
      Ctx, "$J" + std::to_string(FuncName.getID()) + "_" + std::to_string(Id));
}
} // end of anonymous namespace

InstJumpTable::InstJumpTable(Cfg *Func, SizeT NumTargets, CfgNode *Default)
    : InstHighLevel(Func, Inst::JumpTable, 1, nullptr),
      Id(Func->getTarget()->makeNextJumpTableNumber()), NumTargets(NumTargets),
      Name(makeName(Func, Id)), FuncName(Func->getFunctionName()) {
  Targets = Func->allocateArrayOf<CfgNode *>(NumTargets);
  for (SizeT I = 0; I < NumTargets; ++I) {
    Targets[I] = Default;
  }
}

bool InstJumpTable::repointEdges(CfgNode *OldNode, CfgNode *NewNode) {
  bool Found = false;
  for (SizeT I = 0; I < NumTargets; ++I) {
    if (Targets[I] == OldNode) {
      Targets[I] = NewNode;
      Found = true;
    }
  }
  return Found;
}

JumpTableData InstJumpTable::toJumpTableData(Assembler *Asm) const {
  JumpTableData::TargetList TargetList(NumTargets);
  for (SizeT i = 0; i < NumTargets; ++i) {
    const SizeT Index = Targets[i]->getIndex();
    TargetList[i] = Asm->getCfgNodeLabel(Index)->getPosition();
  }
  return JumpTableData(Name, FuncName, Id, TargetList);
}

Type InstCall::getReturnType() const {
  if (Dest == nullptr)
    return IceType_void;
  return Dest->getType();
}

// ======================== Dump routines ======================== //

void Inst::dumpDecorated(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  if (!Func->isVerbose(IceV_Deleted) && (isDeleted() || isRedundantAssign()))
    return;
  if (Func->isVerbose(IceV_InstNumbers)) {
    InstNumberT Number = getNumber();
    if (Number == NumberDeleted)
      Str << "[XXX]";
    else
      Str << llvm::format("[%3d]", Number);
  }
  Str << "  ";
  if (isDeleted())
    Str << "  //";
  dump(Func);
  dumpExtras(Func);
  Str << "\n";
}

void Inst::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " =~ " << getInstName() << " ";
  dumpSources(Func);
}

void Inst::dumpExtras(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  bool First = true;
  // Print "LIVEEND={a,b,c}" for all source operands whose live ranges are
  // known to end at this instruction.
  if (Func->isVerbose(IceV_Liveness)) {
    FOREACH_VAR_IN_INST(Var, *this) {
      if (isLastUse(Var)) {
        if (First)
          Str << " // LIVEEND={";
        else
          Str << ",";
        Var->dump(Func);
        First = false;
      }
    }
    if (!First)
      Str << "}";
  }
}

void Inst::dumpSources(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  for (SizeT I = 0; I < getSrcSize(); ++I) {
    if (I > 0)
      Str << ", ";
    getSrc(I)->dump(Func);
  }
}

void Inst::emitSources(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrEmit();
  for (SizeT I = 0; I < getSrcSize(); ++I) {
    if (I > 0)
      Str << ", ";
    getSrc(I)->emit(Func);
  }
}

void Inst::dumpDest(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  if (getDest())
    getDest()->dump(Func);
}

void InstAlloca::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = alloca i8, i32 ";
  getSizeInBytes()->dump(Func);
  if (getAlignInBytes())
    Str << ", align " << getAlignInBytes();
}

void InstArithmetic::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = " << getInstName() << " " << getDest()->getType() << " ";
  dumpSources(Func);
}

void InstAssign::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = " << getDest()->getType() << " ";
  dumpSources(Func);
}

void InstBr::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << "br ";
  if (!isUnconditional()) {
    Str << "i1 ";
    getCondition()->dump(Func);
    Str << ", label %" << getTargetTrue()->getName() << ", ";
  }
  Str << "label %" << getTargetFalse()->getName();
}

void InstCall::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  if (getDest()) {
    dumpDest(Func);
    Str << " = ";
  }
  Str << "call ";
  if (getDest())
    Str << getDest()->getType();
  else
    Str << "void";
  Str << " ";
  getCallTarget()->dump(Func);
  Str << "(";
  for (SizeT I = 0; I < getNumArgs(); ++I) {
    if (I > 0)
      Str << ", ";
    Str << getArg(I)->getType() << " ";
    getArg(I)->dump(Func);
  }
  Str << ")";
}

const char *InstCast::getCastName(InstCast::OpKind Kind) {
  if (Kind < InstCast::OpKind::_num)
    return InstCastAttributes[Kind].DisplayString;
  llvm_unreachable("Invalid InstCast::OpKind");
  return "???";
}

void InstCast::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = " << getCastName(getCastKind()) << " " << getSrc(0)->getType()
      << " ";
  dumpSources(Func);
  Str << " to " << getDest()->getType();
}

void InstIcmp::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = icmp " << InstIcmpAttributes[getCondition()].DisplayString << " "
      << getSrc(0)->getType() << " ";
  dumpSources(Func);
}

void InstExtractElement::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = extractelement ";
  Str << getSrc(0)->getType() << " ";
  getSrc(0)->dump(Func);
  Str << ", ";
  Str << getSrc(1)->getType() << " ";
  getSrc(1)->dump(Func);
}

void InstInsertElement::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = insertelement ";
  Str << getSrc(0)->getType() << " ";
  getSrc(0)->dump(Func);
  Str << ", ";
  Str << getSrc(1)->getType() << " ";
  getSrc(1)->dump(Func);
  Str << ", ";
  Str << getSrc(2)->getType() << " ";
  getSrc(2)->dump(Func);
}

void InstFcmp::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = fcmp " << InstFcmpAttributes[getCondition()].DisplayString << " "
      << getSrc(0)->getType() << " ";
  dumpSources(Func);
}

void InstLoad::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Type Ty = getDest()->getType();
  Str << " = load " << Ty << ", " << Ty << "* ";
  dumpSources(Func);
  Str << ", align " << typeAlignInBytes(Ty);
}

void InstStore::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Type Ty = getData()->getType();
  dumpDest(Func);
  if (Dest)
    Str << " = ";
  Str << "store " << Ty << " ";
  getData()->dump(Func);
  Str << ", " << Ty << "* ";
  getAddr()->dump(Func);
  Str << ", align " << typeAlignInBytes(Ty);
  if (getRmwBeacon()) {
    Str << ", beacon ";
    getRmwBeacon()->dump(Func);
  }
}

void InstSwitch::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Type Ty = getComparison()->getType();
  Str << "switch " << Ty << " ";
  getSrc(0)->dump(Func);
  Str << ", label %" << getLabelDefault()->getName() << " [\n";
  for (SizeT I = 0; I < getNumCases(); ++I) {
    Str << "    " << Ty << " " << static_cast<int64_t>(getValue(I))
        << ", label %" << getLabel(I)->getName() << "\n";
  }
  Str << "  ]";
}

void InstPhi::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = phi " << getDest()->getType() << " ";
  for (SizeT I = 0; I < getSrcSize(); ++I) {
    if (I > 0)
      Str << ", ";
    Str << "[ ";
    getSrc(I)->dump(Func);
    Str << ", %" << Labels[I]->getName() << " ]";
  }
}

void InstRet::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Type Ty = hasRetValue() ? getRetValue()->getType() : IceType_void;
  Str << "ret " << Ty;
  if (hasRetValue()) {
    Str << " ";
    dumpSources(Func);
  }
}

void InstSelect::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Operand *Condition = getCondition();
  Operand *TrueOp = getTrueOperand();
  Operand *FalseOp = getFalseOperand();
  Str << " = select " << Condition->getType() << " ";
  Condition->dump(Func);
  Str << ", " << TrueOp->getType() << " ";
  TrueOp->dump(Func);
  Str << ", " << FalseOp->getType() << " ";
  FalseOp->dump(Func);
}

void InstUnreachable::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Str << "unreachable";
}

void InstBundleLock::emit(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrEmit();
  Str << "\t.bundle_lock";
  switch (BundleOption) {
  case Opt_None:
    break;
  case Opt_AlignToEnd:
    Str << "\t"
           "align_to_end";
    break;
  case Opt_PadToEnd:
    Str << "\t"
           "align_to_end /* pad_to_end */";
    break;
  }
  Str << "\n";
}

void InstBundleLock::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Str << "bundle_lock";
  switch (BundleOption) {
  case Opt_None:
    break;
  case Opt_AlignToEnd:
    Str << " align_to_end";
    break;
  case Opt_PadToEnd:
    Str << " pad_to_end";
    break;
  }
}

void InstBundleUnlock::emit(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrEmit();
  Str << "\t.bundle_unlock";
  Str << "\n";
}

void InstBundleUnlock::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Str << "bundle_unlock";
}

void InstFakeDef::emit(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  // Go ahead and "emit" these for now, since they are relatively rare.
  Ostream &Str = Func->getContext()->getStrEmit();
  Str << "\t# ";
  getDest()->emit(Func);
  Str << " = def.pseudo";
  if (getSrcSize() > 0)
    Str << " ";
  emitSources(Func);
  Str << "\n";
}

void InstFakeDef::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  dumpDest(Func);
  Str << " = def.pseudo ";
  dumpSources(Func);
}

void InstFakeUse::emit(const Cfg *Func) const { (void)Func; }

void InstFakeUse::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Str << "use.pseudo ";
  dumpSources(Func);
}

void InstFakeKill::emit(const Cfg *Func) const { (void)Func; }

void InstFakeKill::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  if (Linked->isDeleted())
    Str << "// ";
  Str << "kill.pseudo scratch_regs";
}

void InstShuffleVector::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Str << "shufflevector ";
  dumpDest(Func);
  Str << " = ";
  dumpSources(Func);
  for (SizeT I = 0; I < NumIndexes; ++I) {
    Str << ", ";
    Indexes[I]->dump(Func);
  }
  Str << "\n";
}

void InstJumpTable::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Str << "jump table [";
  for (SizeT I = 0; I < NumTargets; ++I)
    Str << "\n    " << Targets[I]->getName();
  Str << "\n  ]";
}

void InstTarget::dump(const Cfg *Func) const {
  if (!BuildDefs::dump())
    return;
  Ostream &Str = Func->getContext()->getStrDump();
  Str << "[TARGET] ";
  Inst::dump(Func);
}

InstBreakpoint::InstBreakpoint(Cfg *Func)
    : InstHighLevel(Func, Inst::Breakpoint, 0, nullptr) {}

void InstIcmp::reverseConditionAndOperands() {
  Condition = InstIcmpAttributes[Condition].Reverse;
  std::swap(Srcs[0], Srcs[1]);
}

bool checkForRedundantAssign(const Variable *Dest, const Operand *Source) {
  const auto *SrcVar = llvm::dyn_cast<const Variable>(Source);
  if (SrcVar == nullptr)
    return false;
  if (Dest->hasReg() && Dest->getRegNum() == SrcVar->getRegNum()) {
    // TODO: On x86-64, instructions like "mov eax, eax" are used to clear the
    // upper 32 bits of rax. We need to recognize and preserve these.
    return true;
  }
  if (!Dest->hasReg() && !SrcVar->hasReg()) {
    if (!Dest->hasStackOffset() || !SrcVar->hasStackOffset()) {
      // If called before stack slots have been assigned (i.e. as part of the
      // dump() routine), conservatively return false.
      return false;
    }
    if (Dest->getStackOffset() != SrcVar->getStackOffset()) {
      return false;
    }
    return true;
  }
  // For a "v=t" assignment where t has a register, v has a stack slot, and v
  // has a LinkedTo stack root, and v and t share the same LinkedTo root, return
  // true.  This is because this assignment is effectively reassigning the same
  // value to the original LinkedTo stack root.
  if (SrcVar->hasReg() && Dest->hasStackOffset() &&
      Dest->getLinkedToStackRoot() != nullptr &&
      Dest->getLinkedToRoot() == SrcVar->getLinkedToRoot()) {
    return true;
  }
  return false;
}

} // end of namespace Ice