| //===- 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) |