text: //===- subzero/src/IceInstX8664.cpp - X86-64 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 This file defines X8664 specific data related to X8664 Instructions
  /// and Instruction traits.
  ///
  /// These are declared in the IceTargetLoweringX8664Traits.h header file.
  ///
  /// This file also defines X8664 operand specific methods (dump and emit.)
  ///
  //===----------------------------------------------------------------------===//
  #include "IceInstX8664.h"
  
  #include "IceAssemblerX8664.h"
  #include "IceCfg.h"
  #include "IceCfgNode.h"
  #include "IceConditionCodesX8664.h"
  #include "IceInst.h"
  #include "IceRegistersX8664.h"
  #include "IceTargetLoweringX8664.h"
  #include "IceOperand.h"
  
  namespace Ice {
  
  namespace X8664 {
  
  const TargetX8664Traits::InstBrAttributesType
      TargetX8664Traits::InstBrAttributes[] = {
  #define X(val, encode, opp, dump, emit)                                        \
    { X8664::Traits::Cond::opp, dump, emit }                                     \
    ,
          ICEINSTX8664BR_TABLE
  #undef X
  };
  
  const TargetX8664Traits::InstCmppsAttributesType
      TargetX8664Traits::InstCmppsAttributes[] = {
  #define X(val, emit)                                                           \
    { emit }                                                                     \
    ,
          ICEINSTX8664CMPPS_TABLE
  #undef X
  };
  
  const TargetX8664Traits::TypeAttributesType
      TargetX8664Traits::TypeAttributes[] = {
  #define X(tag, elty, cvt, sdss, pdps, spsd, int_, unpack, pack, width, fld)    \
    { cvt, sdss, pdps, spsd, int_, unpack, pack, width, fld }                    \
    ,
          ICETYPEX8664_TABLE
  #undef X
  };
  
  void TargetX8664Traits::X86Operand::dump(const Cfg *, Ostream &Str) const {
    if (BuildDefs::dump())
      Str << "<OperandX8664>";
  }
  
  TargetX8664Traits::X86OperandMem::X86OperandMem(Cfg *Func, Type Ty,
                                                  Variable *Base,
                                                  Constant *Offset,
                                                  Variable *Index, uint16_t Shift,
                                                  bool IsRebased)
      : X86Operand(kMem, Ty), Base(Base), Offset(Offset), Index(Index),
        Shift(Shift), IsRebased(IsRebased) {
    assert(Shift <= 3);
    Vars = nullptr;
    NumVars = 0;
    if (Base)
      ++NumVars;
    if (Index)
      ++NumVars;
    if (NumVars) {
      Vars = Func->allocateArrayOf<Variable *>(NumVars);
      SizeT I = 0;
      if (Base)
        Vars[I++] = Base;
      if (Index)
        Vars[I++] = Index;
      assert(I == NumVars);
    }
  }
  
  namespace {
  int32_t getRematerializableOffset(Variable *Var,
                                    const ::Ice::X8664::TargetX8664 *Target) {
    int32_t Disp = Var->getStackOffset();
    const auto RegNum = Var->getRegNum();
    if (RegNum == Target->getFrameReg()) {
      Disp += Target->getFrameFixedAllocaOffset();
    } else if (RegNum != Target->getStackReg()) {
      llvm::report_fatal_error("Unexpected rematerializable register type");
    }
    return Disp;
  }
  } // end of anonymous namespace
  
  void TargetX8664Traits::X86OperandMem::emit(const Cfg *Func) const {
    if (!BuildDefs::dump())
      return;
    const auto *Target =
        static_cast<const ::Ice::X8664::TargetX8664 *>(Func->getTarget());
    // If the base is rematerializable, we need to replace it with the correct
    // physical register (stack or base pointer), and update the Offset.
    const bool NeedSandboxing = Target->needSandboxing();
    int32_t Disp = 0;
    if (getBase() && getBase()->isRematerializable()) {
      Disp += getRematerializableOffset(getBase(), Target);
    }
    // The index should never be rematerializable.  But if we ever allow it, then
    // we should make sure the rematerialization offset is shifted by the Shift
    // value.
    if (getIndex())
      assert(!getIndex()->isRematerializable());
    Ostream &Str = Func->getContext()->getStrEmit();
    // Emit as Offset(Base,Index,1<<Shift). Offset is emitted without the leading
    // '$'. Omit the (Base,Index,1<<Shift) part if Base==nullptr.
    if (getOffset() == nullptr && Disp == 0) {
      // No offset, emit nothing.
    } else if (getOffset() == nullptr && Disp != 0) {
      Str << Disp;
    } else if (const auto *CI = llvm::dyn_cast<ConstantInteger32>(Offset)) {
      if (Base == nullptr || CI->getValue() || Disp != 0)
        // Emit a non-zero offset without a leading '$'.
        Str << CI->getValue() + Disp;
    } else if (const auto *CR = llvm::dyn_cast<ConstantRelocatable>(Offset)) {
      // TODO(sehr): ConstantRelocatable still needs updating for
      // rematerializable base/index and Disp.
      assert(Disp == 0);
      const bool UseNonsfi = getFlags().getUseNonsfi();
      CR->emitWithoutPrefix(Target, UseNonsfi ? "@GOTOFF" : "");
      assert(!UseNonsfi);
      if (Base == nullptr && Index == nullptr) {
        // rip-relative addressing.
        if (NeedSandboxing) {
          Str << "(%rip)";
        } else {
          Str << "(%eip)";
        }
      }
    } else {
      llvm_unreachable("Invalid offset type for x86 mem operand");
    }
  
    if (Base == nullptr && Index == nullptr) {
      return;
    }
  
    Str << "(";
    if (Base != nullptr) {
      const Variable *B = Base;
      if (!NeedSandboxing) {
        // TODO(jpp): stop abusing the operand's type to identify LEAs.
        const Type MemType = getType();
        if (Base->getType() != IceType_i32 && MemType != IceType_void) {
          // X86-64 is ILP32, but %rsp and %rbp are accessed as 64-bit registers.
          // For filetype=asm, they need to be emitted as their 32-bit siblings.
          assert(Base->getType() == IceType_i64);
          assert(getEncodedGPR(Base->getRegNum()) == RegX8664::Encoded_Reg_rsp ||
                 getEncodedGPR(Base->getRegNum()) == RegX8664::Encoded_Reg_rbp ||
                 getType() == IceType_void);
          B = B->asType(Func, IceType_i32, X8664::Traits::getGprForType(
                                               IceType_i32, Base->getRegNum()));
        }
      }
  
      B->emit(Func);
    }
  
    if (Index != nullptr) {
      Variable *I = Index;
      Str << ",";
      I->emit(Func);
      if (Shift)
        Str << "," << (1u << Shift);
    }
  
    Str << ")";
  }
  
  void TargetX8664Traits::X86OperandMem::dump(const Cfg *Func,
                                              Ostream &Str) const {
    if (!BuildDefs::dump())
      return;
    bool Dumped = false;
    Str << "[";
    int32_t Disp = 0;
    const auto *Target =
        static_cast<const ::Ice::X8664::TargetX8664 *>(Func->getTarget());
    if (getBase() && getBase()->isRematerializable()) {
      Disp += getRematerializableOffset(getBase(), Target);
    }
    if (Base) {
      if (Func)
        Base->dump(Func);
      else
        Base->dump(Str);
      Dumped = true;
    }
    if (Index) {
      if (Base)
        Str << "+";
      if (Shift > 0)
        Str << (1u << Shift) << "*";
      if (Func)
        Index->dump(Func);
      else
        Index->dump(Str);
      Dumped = true;
    }
    if (Disp) {
      if (Disp > 0)
        Str << "+";
      Str << Disp;
      Dumped = true;
    }
    // Pretty-print the Offset.
    bool OffsetIsZero = false;
    bool OffsetIsNegative = false;
    if (!Offset) {
      OffsetIsZero = true;
    } else if (const auto *CI = llvm::dyn_cast<ConstantInteger32>(Offset)) {
      OffsetIsZero = (CI->getValue() == 0);
      OffsetIsNegative = (static_cast<int32_t>(CI->getValue()) < 0);
    } else {
      assert(llvm::isa<ConstantRelocatable>(Offset));
    }
    if (Dumped) {
      if (!OffsetIsZero) {     // Suppress if Offset is known to be 0
        if (!OffsetIsNegative) // Suppress if Offset is known to be negative
          Str << "+";
        Offset->dump(Func, Str);
      }
    } else {
      // There is only the offset.
      Offset->dump(Func, Str);
    }
    Str << "]";
  }
  
  TargetX8664Traits::Address TargetX8664Traits::X86OperandMem::toAsmAddress(
      TargetX8664Traits::Assembler *Asm,
      const Ice::TargetLowering *TargetLowering, bool IsLeaAddr) const {
    (void)IsLeaAddr;
    const auto *Target =
        static_cast<const ::Ice::X8664::TargetX8664 *>(TargetLowering);
    int32_t Disp = 0;
    if (getBase() && getBase()->isRematerializable()) {
      Disp += getRematerializableOffset(getBase(), Target);
    }
    if (getIndex() != nullptr) {
      assert(!getIndex()->isRematerializable());
    }
  
    AssemblerFixup *Fixup = nullptr;
    // Determine the offset (is it relocatable?)
    if (getOffset() != nullptr) {
      if (const auto *CI = llvm::dyn_cast<ConstantInteger32>(getOffset())) {
        Disp += static_cast<int32_t>(CI->getValue());
      } else if (const auto *CR =
                     llvm::dyn_cast<ConstantRelocatable>(getOffset())) {
        const auto FixupKind =
            (getBase() != nullptr || getIndex() != nullptr) ? FK_Abs : FK_PcRel;
        const RelocOffsetT DispAdjustment = FixupKind == FK_PcRel ? 4 : 0;
        Fixup = Asm->createFixup(FixupKind, CR);
        Fixup->set_addend(-DispAdjustment);
        Disp = CR->getOffset();
      } else {
        llvm_unreachable("Unexpected offset type");
      }
    }
  
    // Now convert to the various possible forms.
    if (getBase() && getIndex()) {
      const bool NeedSandboxing = Target->needSandboxing();
      (void)NeedSandboxing;
      assert(!NeedSandboxing || IsLeaAddr ||
             (getBase()->getRegNum() == Traits::RegisterSet::Reg_r15) ||
             (getBase()->getRegNum() == Traits::RegisterSet::Reg_rsp) ||
             (getBase()->getRegNum() == Traits::RegisterSet::Reg_rbp));
      return X8664::Traits::Address(getEncodedGPR(getBase()->getRegNum()),
                                    getEncodedGPR(getIndex()->getRegNum()),
                                    X8664::Traits::ScaleFactor(getShift()), Disp,
                                    Fixup);
    }
  
    if (getBase()) {
      return X8664::Traits::Address(getEncodedGPR(getBase()->getRegNum()), Disp,
                                    Fixup);
    }
  
    if (getIndex()) {
      return X8664::Traits::Address(getEncodedGPR(getIndex()->getRegNum()),
                                    X8664::Traits::ScaleFactor(getShift()), Disp,
                                    Fixup);
    }
  
    if (Fixup == nullptr) {
      // Absolute addresses are not allowed in Nexes -- they must be rebased
      // w.r.t. %r15.
      // Exception: LEAs are fine because they do not touch memory.
      assert(!Target->needSandboxing() || IsLeaAddr);
      return X8664::Traits::Address::Absolute(Disp);
    }
  
    return X8664::Traits::Address::RipRelative(Disp, Fixup);
  }
  
  TargetX8664Traits::Address
  TargetX8664Traits::VariableSplit::toAsmAddress(const Cfg *Func) const {
    assert(!Var->hasReg());
    const ::Ice::TargetLowering *Target = Func->getTarget();
    int32_t Offset = Var->getStackOffset() + getOffset();
    return X8664::Traits::Address(getEncodedGPR(Target->getFrameOrStackReg()),
                                  Offset, AssemblerFixup::NoFixup);
  }
  
  void TargetX8664Traits::VariableSplit::emit(const Cfg *Func) const {
    if (!BuildDefs::dump())
      return;
    Ostream &Str = Func->getContext()->getStrEmit();
    assert(!Var->hasReg());
    // The following is copied/adapted from TargetX8664::emitVariable().
    const ::Ice::TargetLowering *Target = Func->getTarget();
    constexpr Type Ty = IceType_i32;
    int32_t Offset = Var->getStackOffset() + getOffset();
    if (Offset)
      Str << Offset;
    Str << "(%" << Target->getRegName(Target->getFrameOrStackReg(), Ty) << ")";
  }
  
  void TargetX8664Traits::VariableSplit::dump(const Cfg *Func,
                                              Ostream &Str) const {
    if (!BuildDefs::dump())
      return;
    switch (Part) {
    case Low:
      Str << "low";
      break;
    case High:
      Str << "high";
      break;
    }
    Str << "(";
    if (Func)
      Var->dump(Func);
    else
      Var->dump(Str);
    Str << ")";
  }
  
  } // namespace X8664
  } // end of namespace Ice
  
  X86INSTS_DEFINE_STATIC_DATA(X8664, X8664::Traits)
  