| //===- subzero/src/IceInstX8632.cpp - X86-32 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 Defines X8632 specific data related to X8632 Instructions and | |
| /// Instruction traits. | |
| /// | |
| /// These are declared in the IceTargetLoweringX8632Traits.h header file. This | |
| /// file also defines X8632 operand specific methods (dump and emit.) | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "IceInstX8632.h" | |
| #include "IceAssemblerX8632.h" | |
| #include "IceCfg.h" | |
| #include "IceCfgNode.h" | |
| #include "IceConditionCodesX8632.h" | |
| #include "IceInst.h" | |
| #include "IceRegistersX8632.h" | |
| #include "IceTargetLoweringX8632.h" | |
| #include "IceOperand.h" | |
| namespace Ice { | |
| namespace X8632 { | |
| const TargetX8632Traits::InstBrAttributesType | |
| TargetX8632Traits::InstBrAttributes[] = { | |
| #define X(val, encode, opp, dump, emit) \ | |
| { X8632::Traits::Cond::opp, dump, emit } \ | |
| , | |
| ICEINSTX8632BR_TABLE | |
| #undef X | |
| }; | |
| const TargetX8632Traits::InstCmppsAttributesType | |
| TargetX8632Traits::InstCmppsAttributes[] = { | |
| #define X(val, emit) \ | |
| { emit } \ | |
| , | |
| ICEINSTX8632CMPPS_TABLE | |
| #undef X | |
| }; | |
| const TargetX8632Traits::TypeAttributesType | |
| TargetX8632Traits::TypeAttributes[] = { | |
| #define X(tag, elty, cvt, sdss, pdps, spsd, int_, unpack, pack, width, fld) \ | |
| { cvt, sdss, pdps, spsd, int_, unpack, pack, width, fld } \ | |
| , | |
| ICETYPEX8632_TABLE | |
| #undef X | |
| }; | |
| const char *TargetX8632Traits::InstSegmentRegNames[] = { | |
| #define X(val, name, prefix) name, | |
| SEG_REGX8632_TABLE | |
| #undef X | |
| }; | |
| uint8_t TargetX8632Traits::InstSegmentPrefixes[] = { | |
| #define X(val, name, prefix) prefix, | |
| SEG_REGX8632_TABLE | |
| #undef X | |
| }; | |
| void TargetX8632Traits::X86Operand::dump(const Cfg *, Ostream &Str) const { | |
| if (BuildDefs::dump()) | |
| Str << "<OperandX8632>"; | |
| } | |
| TargetX8632Traits::X86OperandMem::X86OperandMem( | |
| Cfg *Func, Type Ty, Variable *Base, Constant *Offset, Variable *Index, | |
| uint16_t Shift, SegmentRegisters SegmentReg, bool IsRebased) | |
| : X86Operand(kMem, Ty), Base(Base), Offset(Offset), Index(Index), | |
| Shift(Shift), SegmentReg(SegmentReg), 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::X8632::TargetX8632 *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; | |
| } | |
| void validateMemOperandPIC(const TargetX8632Traits::X86OperandMem *Mem, | |
| bool UseNonsfi) { | |
| if (!BuildDefs::asserts()) | |
| return; | |
| const bool HasCR = | |
| Mem->getOffset() && llvm::isa<ConstantRelocatable>(Mem->getOffset()); | |
| (void)HasCR; | |
| const bool IsRebased = Mem->getIsRebased(); | |
| (void)IsRebased; | |
| if (UseNonsfi) | |
| assert(HasCR == IsRebased); | |
| else | |
| assert(!IsRebased); | |
| } | |
| } // end of anonymous namespace | |
| void TargetX8632Traits::X86OperandMem::emit(const Cfg *Func) const { | |
| if (!BuildDefs::dump()) | |
| return; | |
| const bool UseNonsfi = getFlags().getUseNonsfi(); | |
| validateMemOperandPIC(this, UseNonsfi); | |
| const auto *Target = | |
| static_cast<const ::Ice::X8632::TargetX8632 *>(Func->getTarget()); | |
| // If the base is rematerializable, we need to replace it with the correct | |
| // physical register (esp or ebp), and update the Offset. | |
| 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(); | |
| if (SegmentReg != DefaultSegment) { | |
| assert(SegmentReg >= 0 && SegmentReg < SegReg_NUM); | |
| Str << "%" << X8632::Traits::InstSegmentRegNames[SegmentReg] << ":"; | |
| } | |
| // 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>(getOffset())) { | |
| if (getBase() == 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>(getOffset())) { | |
| // TODO(sehr): ConstantRelocatable still needs updating for | |
| // rematerializable base/index and Disp. | |
| assert(Disp == 0); | |
| CR->emitWithoutPrefix(Target, UseNonsfi ? "@GOTOFF" : ""); | |
| } else { | |
| llvm_unreachable("Invalid offset type for x86 mem operand"); | |
| } | |
| if (getBase() || getIndex()) { | |
| Str << "("; | |
| if (getBase()) | |
| getBase()->emit(Func); | |
| if (getIndex()) { | |
| Str << ","; | |
| getIndex()->emit(Func); | |
| if (getShift()) | |
| Str << "," << (1u << getShift()); | |
| } | |
| Str << ")"; | |
| } | |
| } | |
| void TargetX8632Traits::X86OperandMem::dump(const Cfg *Func, | |
| Ostream &Str) const { | |
| if (!BuildDefs::dump()) | |
| return; | |
| if (SegmentReg != DefaultSegment) { | |
| assert(SegmentReg >= 0 && SegmentReg < SegReg_NUM); | |
| Str << X8632::Traits::InstSegmentRegNames[SegmentReg] << ":"; | |
| } | |
| bool Dumped = false; | |
| Str << "["; | |
| int32_t Disp = 0; | |
| const auto *Target = | |
| static_cast<const ::Ice::X8632::TargetX8632 *>(Func->getTarget()); | |
| if (getBase() && getBase()->isRematerializable()) { | |
| Disp += getRematerializableOffset(getBase(), Target); | |
| } | |
| if (getBase()) { | |
| if (Func) | |
| getBase()->dump(Func); | |
| else | |
| getBase()->dump(Str); | |
| Dumped = true; | |
| } | |
| if (getIndex()) { | |
| assert(!getIndex()->isRematerializable()); | |
| if (getBase()) | |
| Str << "+"; | |
| if (getShift() > 0) | |
| Str << (1u << getShift()) << "*"; | |
| if (Func) | |
| getIndex()->dump(Func); | |
| else | |
| getIndex()->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 (getOffset() == nullptr) { | |
| OffsetIsZero = true; | |
| } else if (const auto *CI = llvm::dyn_cast<ConstantInteger32>(getOffset())) { | |
| OffsetIsZero = (CI->getValue() == 0); | |
| OffsetIsNegative = (static_cast<int32_t>(CI->getValue()) < 0); | |
| } else { | |
| assert(llvm::isa<ConstantRelocatable>(getOffset())); | |
| } | |
| if (Dumped) { | |
| if (!OffsetIsZero) { // Suppress if Offset is known to be 0 | |
| if (!OffsetIsNegative) // Suppress if Offset is known to be negative | |
| Str << "+"; | |
| getOffset()->dump(Func, Str); | |
| } | |
| } else { | |
| // There is only the offset. | |
| getOffset()->dump(Func, Str); | |
| } | |
| Str << "]"; | |
| } | |
| void TargetX8632Traits::X86OperandMem::emitSegmentOverride( | |
| TargetX8632Traits::Assembler *Asm) const { | |
| if (SegmentReg != DefaultSegment) { | |
| assert(SegmentReg >= 0 && SegmentReg < SegReg_NUM); | |
| Asm->emitSegmentOverride(X8632::Traits::InstSegmentPrefixes[SegmentReg]); | |
| } | |
| } | |
| TargetX8632Traits::Address TargetX8632Traits::X86OperandMem::toAsmAddress( | |
| TargetX8632Traits::Assembler *Asm, | |
| const Ice::TargetLowering *TargetLowering, bool /*IsLeaAddr*/) const { | |
| const auto *Target = | |
| static_cast<const ::Ice::X8632::TargetX8632 *>(TargetLowering); | |
| const bool UseNonsfi = getFlags().getUseNonsfi(); | |
| validateMemOperandPIC(this, UseNonsfi); | |
| 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()); | |
| AssemblerFixup *Fixup = nullptr; | |
| // Determine the offset (is it relocatable?) | |
| if (getOffset()) { | |
| 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())) { | |
| Disp += CR->getOffset(); | |
| Fixup = Asm->createFixup(Target->getAbsFixup(), CR); | |
| } else { | |
| llvm_unreachable("Unexpected offset type"); | |
| } | |
| } | |
| // Now convert to the various possible forms. | |
| if (getBase() && getIndex()) { | |
| return X8632::Traits::Address(getEncodedGPR(getBase()->getRegNum()), | |
| getEncodedGPR(getIndex()->getRegNum()), | |
| X8632::Traits::ScaleFactor(getShift()), Disp, | |
| Fixup); | |
| } else if (getBase()) { | |
| return X8632::Traits::Address(getEncodedGPR(getBase()->getRegNum()), Disp, | |
| Fixup); | |
| } else if (getIndex()) { | |
| return X8632::Traits::Address(getEncodedGPR(getIndex()->getRegNum()), | |
| X8632::Traits::ScaleFactor(getShift()), Disp, | |
| Fixup); | |
| } else { | |
| return X8632::Traits::Address(Disp, Fixup); | |
| } | |
| } | |
| TargetX8632Traits::Address | |
| TargetX8632Traits::VariableSplit::toAsmAddress(const Cfg *Func) const { | |
| assert(!Var->hasReg()); | |
| const ::Ice::TargetLowering *Target = Func->getTarget(); | |
| int32_t Offset = Var->getStackOffset() + getOffset(); | |
| return X8632::Traits::Address(getEncodedGPR(Target->getFrameOrStackReg()), | |
| Offset, AssemblerFixup::NoFixup); | |
| } | |
| void TargetX8632Traits::VariableSplit::emit(const Cfg *Func) const { | |
| if (!BuildDefs::dump()) | |
| return; | |
| Ostream &Str = Func->getContext()->getStrEmit(); | |
| assert(!Var->hasReg()); | |
| // The following is copied/adapted from TargetX8632::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 TargetX8632Traits::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 X8632 | |
| } // end of namespace Ice | |
| X86INSTS_DEFINE_STATIC_DATA(X8632, X8632::Traits) |