//===- subzero/src/IceOperand.cpp - High-level operand 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 Operand class and its target-independent subclasses,
/// primarily for the methods of the Variable class.
///
//===----------------------------------------------------------------------===//

#include "IceOperand.h"

#include "IceCfg.h"
#include "IceCfgNode.h"
#include "IceInst.h"
#include "IceInstVarIter.h"
#include "IceTargetLowering.h" // dumping stack/frame pointer register

namespace Ice {

bool operator==(const RelocatableTuple &A, const RelocatableTuple &B) {
  return A.Offset == B.Offset && A.Name == B.Name;
}

bool operator<(const RegWeight &A, const RegWeight &B) {
  return A.getWeight() < B.getWeight();
}
bool operator<=(const RegWeight &A, const RegWeight &B) { return !(B < A); }
bool operator==(const RegWeight &A, const RegWeight &B) {
  return !(B < A) && !(A < B);
}

void LiveRange::addSegment(InstNumberT Start, InstNumberT End) {
  if (!Range.empty()) {
    // Check for merge opportunity.
    InstNumberT CurrentEnd = Range.back().second;
    assert(Start >= CurrentEnd);
    if (Start == CurrentEnd) {
      Range.back().second = End;
      return;
    }
  }
  Range.push_back(RangeElementType(Start, End));
}

// Returns true if this live range ends before Other's live range starts. This
// means that the highest instruction number in this live range is less than or
// equal to the lowest instruction number of the Other live range.
bool LiveRange::endsBefore(const LiveRange &Other) const {
  // Neither range should be empty, but let's be graceful.
  if (Range.empty() || Other.Range.empty())
    return true;
  InstNumberT MyEnd = (*Range.rbegin()).second;
  InstNumberT OtherStart = (*Other.Range.begin()).first;
  return MyEnd <= OtherStart;
}

// Returns true if there is any overlap between the two live ranges.
bool LiveRange::overlaps(const LiveRange &Other, bool UseTrimmed) const {
  // Do a two-finger walk through the two sorted lists of segments.
  auto I1 = (UseTrimmed ? TrimmedBegin : Range.begin()),
       I2 = (UseTrimmed ? Other.TrimmedBegin : Other.Range.begin());
  auto E1 = Range.end(), E2 = Other.Range.end();
  while (I1 != E1 && I2 != E2) {
    if (I1->second <= I2->first) {
      ++I1;
      continue;
    }
    if (I2->second <= I1->first) {
      ++I2;
      continue;
    }
    return true;
  }
  return false;
}

bool LiveRange::overlapsInst(InstNumberT OtherBegin, bool UseTrimmed) const {
  bool Result = false;
  for (auto I = (UseTrimmed ? TrimmedBegin : Range.begin()), E = Range.end();
       I != E; ++I) {
    if (OtherBegin < I->first) {
      Result = false;
      break;
    }
    if (OtherBegin < I->second) {
      Result = true;
      break;
    }
  }
  // This is an equivalent but less inefficient implementation. It's expensive
  // enough that we wouldn't want to run it under any build, but it could be
  // enabled if e.g. the LiveRange implementation changes and extra testing is
  // needed.
  if (BuildDefs::extraValidation()) {
    LiveRange Temp;
    Temp.addSegment(OtherBegin, OtherBegin + 1);
    bool Validation = overlaps(Temp);
    (void)Validation;
    assert(Result == Validation);
  }
  return Result;
}

// Returns true if the live range contains the given instruction number. This
// is only used for validating the live range calculation. The IsDest argument
// indicates whether the Variable being tested is used in the Dest position (as
// opposed to a Src position).
bool LiveRange::containsValue(InstNumberT Value, bool IsDest) const {
  for (const RangeElementType &I : Range) {
    if (I.first <= Value &&
        (Value < I.second || (!IsDest && Value == I.second)))
      return true;
  }
  return false;
}

void LiveRange::trim(InstNumberT Lower) {
  while (TrimmedBegin != Range.end() && TrimmedBegin->second <= Lower)
    ++TrimmedBegin;
}

IceString Variable::getName(const Cfg *Func) const {
  if (Func && NameIndex >= 0)
    return Func->getIdentifierName(NameIndex);
  return "__" + std::to_string(getIndex());
}

const Variable *Variable::asType(Type Ty, int32_t NewRegNum) const {
  // Note: This returns a Variable, even if the "this" object is a subclass of
  // Variable.
  if (!BuildDefs::dump() || getType() == Ty)
    return this;
  Variable *V = new (getCurrentCfgAllocator()->Allocate<Variable>())
      Variable(kVariable, Ty, Number);
  V->NameIndex = NameIndex;
  V->RegNum = NewRegNum == NoRegister ? RegNum : NewRegNum;
  V->StackOffset = StackOffset;
  return V;
}

RegWeight Variable::getWeight(const Cfg *Func) const {
  VariablesMetadata *VMetadata = Func->getVMetadata();
  return mustHaveReg() ? RegWeight(RegWeight::Inf)
                       : mustNotHaveReg() ? RegWeight(RegWeight::Zero)
                                          : VMetadata->getUseWeight(this);
}

void VariableTracking::markUse(MetadataKind TrackingKind, const Inst *Instr,
                               CfgNode *Node, bool IsImplicit) {
  (void)TrackingKind;

  // Increment the use weight depending on the loop nest depth. The weight is
  // exponential in the nest depth as inner loops are expected to be executed
  // an exponentially greater number of times.
  constexpr uint32_t LogLoopTripCountEstimate = 2; // 2^2 = 4
  constexpr SizeT MaxShift = sizeof(uint32_t) * CHAR_BIT - 1;
  constexpr SizeT MaxLoopNestDepth = MaxShift / LogLoopTripCountEstimate;
  const uint32_t LoopNestDepth =
      std::min(Node->getLoopNestDepth(), MaxLoopNestDepth);
  const uint32_t ThisUseWeight = uint32_t(1)
                                 << LoopNestDepth * LogLoopTripCountEstimate;
  UseWeight.addWeight(ThisUseWeight);

  if (MultiBlock == MBS_MultiBlock)
    return;
  // TODO(stichnot): If the use occurs as a source operand in the first
  // instruction of the block, and its definition is in this block's only
  // predecessor, we might consider not marking this as a separate use. This
  // may also apply if it's the first instruction of the block that actually
  // uses a Variable.
  assert(Node);
  bool MakeMulti = false;
  if (IsImplicit)
    MakeMulti = true;
  // A phi source variable conservatively needs to be marked as multi-block,
  // even if its definition is in the same block. This is because there can be
  // additional control flow before branching back to this node, and the
  // variable is live throughout those nodes.
  if (Instr && llvm::isa<InstPhi>(Instr))
    MakeMulti = true;

  if (!MakeMulti) {
    switch (MultiBlock) {
    case MBS_Unknown:
      MultiBlock = MBS_SingleBlock;
      SingleUseNode = Node;
      break;
    case MBS_SingleBlock:
      if (SingleUseNode != Node)
        MakeMulti = true;
      break;
    case MBS_MultiBlock:
      break;
    }
  }

  if (MakeMulti) {
    MultiBlock = MBS_MultiBlock;
    SingleUseNode = nullptr;
  }
}

void VariableTracking::markDef(MetadataKind TrackingKind, const Inst *Instr,
                               CfgNode *Node) {
  // TODO(stichnot): If the definition occurs in the last instruction of the
  // block, consider not marking this as a separate use. But be careful not to
  // omit all uses of the variable if markDef() and markUse() both use this
  // optimization.
  assert(Node);
  // Verify that instructions are added in increasing order.
  if (BuildDefs::asserts()) {
    if (TrackingKind == VMK_All) {
      const Inst *LastInstruction =
          Definitions.empty() ? FirstOrSingleDefinition : Definitions.back();
      (void)LastInstruction;
      assert(LastInstruction == nullptr ||
             Instr->getNumber() >= LastInstruction->getNumber());
    }
  }
  constexpr bool IsImplicit = false;
  markUse(TrackingKind, Instr, Node, IsImplicit);
  if (TrackingKind == VMK_Uses)
    return;
  if (FirstOrSingleDefinition == nullptr)
    FirstOrSingleDefinition = Instr;
  else if (TrackingKind == VMK_All)
    Definitions.push_back(Instr);
  switch (MultiDef) {
  case MDS_Unknown:
    assert(SingleDefNode == nullptr);
    MultiDef = MDS_SingleDef;
    SingleDefNode = Node;
    break;
  case MDS_SingleDef:
    assert(SingleDefNode);
    if (Node == SingleDefNode) {
      MultiDef = MDS_MultiDefSingleBlock;
    } else {
      MultiDef = MDS_MultiDefMultiBlock;
      SingleDefNode = nullptr;
    }
    break;
  case MDS_MultiDefSingleBlock:
    assert(SingleDefNode);
    if (Node != SingleDefNode) {
      MultiDef = MDS_MultiDefMultiBlock;
      SingleDefNode = nullptr;
    }
    break;
  case MDS_MultiDefMultiBlock:
    assert(SingleDefNode == nullptr);
    break;
  }
}

const Inst *VariableTracking::getFirstDefinitionSingleBlock() const {
  switch (MultiDef) {
  case MDS_Unknown:
  case MDS_MultiDefMultiBlock:
    return nullptr;
  case MDS_SingleDef:
  case MDS_MultiDefSingleBlock:
    assert(FirstOrSingleDefinition);
    return FirstOrSingleDefinition;
  }
  return nullptr;
}

const Inst *VariableTracking::getSingleDefinition() const {
  switch (MultiDef) {
  case MDS_Unknown:
  case MDS_MultiDefMultiBlock:
  case MDS_MultiDefSingleBlock:
    return nullptr;
  case MDS_SingleDef:
    assert(FirstOrSingleDefinition);
    return FirstOrSingleDefinition;
  }
  return nullptr;
}

const Inst *VariableTracking::getFirstDefinition() const {
  switch (MultiDef) {
  case MDS_Unknown:
    return nullptr;
  case MDS_MultiDefMultiBlock:
  case MDS_SingleDef:
  case MDS_MultiDefSingleBlock:
    assert(FirstOrSingleDefinition);
    return FirstOrSingleDefinition;
  }
  return nullptr;
}

void VariablesMetadata::init(MetadataKind TrackingKind) {
  TimerMarker T(TimerStack::TT_vmetadata, Func);
  Kind = TrackingKind;
  Metadata.clear();
  Metadata.resize(Func->getNumVariables());

  // Mark implicit args as being used in the entry node.
  for (Variable *Var : Func->getImplicitArgs()) {
    constexpr Inst *NoInst = nullptr;
    CfgNode *EntryNode = Func->getEntryNode();
    constexpr bool IsImplicit = true;
    Metadata[Var->getIndex()].markUse(Kind, NoInst, EntryNode, IsImplicit);
  }

  for (CfgNode *Node : Func->getNodes())
    addNode(Node);
}

void VariablesMetadata::addNode(CfgNode *Node) {
  if (Func->getNumVariables() > Metadata.size())
    Metadata.resize(Func->getNumVariables());

  for (Inst &I : Node->getPhis()) {
    if (I.isDeleted())
      continue;
    if (Variable *Dest = I.getDest()) {
      SizeT DestNum = Dest->getIndex();
      assert(DestNum < Metadata.size());
      Metadata[DestNum].markDef(Kind, &I, Node);
    }
    for (SizeT SrcNum = 0; SrcNum < I.getSrcSize(); ++SrcNum) {
      if (const Variable *Var = llvm::dyn_cast<Variable>(I.getSrc(SrcNum))) {
        SizeT VarNum = Var->getIndex();
        assert(VarNum < Metadata.size());
        constexpr bool IsImplicit = false;
        Metadata[VarNum].markUse(Kind, &I, Node, IsImplicit);
      }
    }
  }

  for (Inst &I : Node->getInsts()) {
    if (I.isDeleted())
      continue;
    // Note: The implicit definitions (and uses) from InstFakeKill are
    // deliberately ignored.
    if (Variable *Dest = I.getDest()) {
      SizeT DestNum = Dest->getIndex();
      assert(DestNum < Metadata.size());
      Metadata[DestNum].markDef(Kind, &I, Node);
    }
    FOREACH_VAR_IN_INST(Var, I) {
      SizeT VarNum = Var->getIndex();
      assert(VarNum < Metadata.size());
      constexpr bool IsImplicit = false;
      Metadata[VarNum].markUse(Kind, &I, Node, IsImplicit);
    }
  }
}

bool VariablesMetadata::isMultiDef(const Variable *Var) const {
  assert(Kind != VMK_Uses);
  if (Var->getIsArg())
    return false;
  if (!isTracked(Var))
    return true; // conservative answer
  SizeT VarNum = Var->getIndex();
  // Conservatively return true if the state is unknown.
  return Metadata[VarNum].getMultiDef() != VariableTracking::MDS_SingleDef;
}

bool VariablesMetadata::isMultiBlock(const Variable *Var) const {
  if (Var->getIsArg())
    return true;
  if (!isTracked(Var))
    return true; // conservative answer
  SizeT VarNum = Var->getIndex();
  // Conservatively return true if the state is unknown.
  return Metadata[VarNum].getMultiBlock() != VariableTracking::MBS_SingleBlock;
}

const Inst *
VariablesMetadata::getFirstDefinitionSingleBlock(const Variable *Var) const {
  assert(Kind != VMK_Uses);
  if (!isTracked(Var))
    return nullptr; // conservative answer
  SizeT VarNum = Var->getIndex();
  return Metadata[VarNum].getFirstDefinitionSingleBlock();
}

const Inst *VariablesMetadata::getSingleDefinition(const Variable *Var) const {
  assert(Kind != VMK_Uses);
  if (!isTracked(Var))
    return nullptr; // conservative answer
  SizeT VarNum = Var->getIndex();
  return Metadata[VarNum].getSingleDefinition();
}

const Inst *VariablesMetadata::getFirstDefinition(const Variable *Var) const {
  assert(Kind != VMK_Uses);
  if (!isTracked(Var))
    return nullptr; // conservative answer
  SizeT VarNum = Var->getIndex();
  return Metadata[VarNum].getFirstDefinition();
}

const InstDefList &
VariablesMetadata::getLatterDefinitions(const Variable *Var) const {
  assert(Kind == VMK_All);
  if (!isTracked(Var))
    return NoDefinitions;
  SizeT VarNum = Var->getIndex();
  return Metadata[VarNum].getLatterDefinitions();
}

CfgNode *VariablesMetadata::getLocalUseNode(const Variable *Var) const {
  if (!isTracked(Var))
    return nullptr; // conservative answer
  SizeT VarNum = Var->getIndex();
  return Metadata[VarNum].getNode();
}

RegWeight VariablesMetadata::getUseWeight(const Variable *Var) const {
  if (!isTracked(Var))
    return RegWeight(1); // conservative answer
  SizeT VarNum = Var->getIndex();
  return Metadata[VarNum].getUseWeight();
}

const InstDefList VariablesMetadata::NoDefinitions;

// ======================== dump routines ======================== //

void Variable::emit(const Cfg *Func) const {
  if (BuildDefs::dump())
    Func->getTarget()->emitVariable(this);
}

void Variable::dump(const Cfg *Func, Ostream &Str) const {
  if (!BuildDefs::dump())
    return;
  if (Func == nullptr) {
    Str << "%" << getName(Func);
    return;
  }
  if (Func->isVerbose(IceV_RegOrigins) ||
      (!hasReg() && !Func->getTarget()->hasComputedFrame()))
    Str << "%" << getName(Func);
  if (hasReg()) {
    if (Func->isVerbose(IceV_RegOrigins))
      Str << ":";
    Str << Func->getTarget()->getRegName(RegNum, getType());
  } else if (Func->getTarget()->hasComputedFrame()) {
    if (Func->isVerbose(IceV_RegOrigins))
      Str << ":";
    int32_t BaseRegisterNumber = getBaseRegNum();
    if (BaseRegisterNumber == NoRegister)
      BaseRegisterNumber = Func->getTarget()->getFrameOrStackReg();
    Str << "["
        << Func->getTarget()->getRegName(BaseRegisterNumber, IceType_i32);
    int32_t Offset = getStackOffset();
    if (Offset) {
      if (Offset > 0)
        Str << "+";
      Str << Offset;
    }
    Str << "]";
  }
}

template <> void ConstantInteger32::emit(TargetLowering *Target) const {
  Target->emit(this);
}

template <> void ConstantInteger64::emit(TargetLowering *Target) const {
  Target->emit(this);
}

template <> void ConstantFloat::emit(TargetLowering *Target) const {
  Target->emit(this);
}

template <> void ConstantDouble::emit(TargetLowering *Target) const {
  Target->emit(this);
}

void ConstantRelocatable::emit(TargetLowering *Target) const {
  Target->emit(this);
}

void ConstantRelocatable::emitWithoutPrefix(
    const TargetLowering *Target) const {
  Target->emitWithoutPrefix(this);
}

void ConstantRelocatable::dump(const Cfg *Func, Ostream &Str) const {
  if (!BuildDefs::dump())
    return;
  Str << "@";
  if (Func && !SuppressMangling) {
    Str << Func->getContext()->mangleName(Name);
  } else {
    Str << Name;
  }
  if (Offset)
    Str << "+" << Offset;
}

void ConstantUndef::emit(TargetLowering *Target) const { Target->emit(this); }

void LiveRange::dump(Ostream &Str) const {
  if (!BuildDefs::dump())
    return;
  bool First = true;
  for (const RangeElementType &I : Range) {
    if (!First)
      Str << ", ";
    First = false;
    Str << "[" << I.first << ":" << I.second << ")";
  }
}

Ostream &operator<<(Ostream &Str, const LiveRange &L) {
  if (!BuildDefs::dump())
    return Str;
  L.dump(Str);
  return Str;
}

Ostream &operator<<(Ostream &Str, const RegWeight &W) {
  if (!BuildDefs::dump())
    return Str;
  if (W.getWeight() == RegWeight::Inf)
    Str << "Inf";
  else
    Str << W.getWeight();
  return Str;
}

// =========== Immediate Randomization and Pooling routines ==============
// Specialization of the template member function for ConstantInteger32
// TODO(stichnot): try to move this specialization into a target-specific file.
template <>
bool ConstantInteger32::shouldBeRandomizedOrPooled(const GlobalContext *Ctx) {
  uint32_t Threshold = Ctx->getFlags().getRandomizeAndPoolImmediatesThreshold();
  if (Ctx->getFlags().getRandomizeAndPoolImmediatesOption() == RPI_None)
    return false;
  if (getType() != IceType_i32 && getType() != IceType_i16 &&
      getType() != IceType_i8)
    return false;
  // The Following checks if the signed representation of Value is between
  // -Threshold/2 and +Threshold/2
  bool largerThanThreshold = Threshold / 2 + Value >= Threshold;
  return largerThanThreshold;
}

} // end of namespace Ice
