| //===- subzero/src/IceTargetLoweringX86Base.h - x86 lowering ----*- C++ -*-===// | |
| // | |
| // The Subzero Code Generator | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// \brief Declares the TargetLoweringX86 template class, which implements the | |
| /// TargetLowering base interface for the x86 architecture. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #ifndef SUBZERO_SRC_ICETARGETLOWERINGX86BASE_H | |
| #define SUBZERO_SRC_ICETARGETLOWERINGX86BASE_H | |
| #include "IceDefs.h" | |
| #include "IceInst.h" | |
| #include "IceSwitchLowering.h" | |
| #include "IceTargetLowering.h" | |
| #include "IceTargetLoweringX86RegClass.h" | |
| #include "IceUtils.h" | |
| #include <array> | |
| #include <type_traits> | |
| #include <utility> | |
| #ifndef X86NAMESPACE | |
| #error "You must define the X86 Target namespace." | |
| #endif | |
| namespace Ice { | |
| namespace X86NAMESPACE { | |
| using namespace ::Ice::X86; | |
| template <typename Traits> class BoolFolding; | |
| /// TargetX86Base is a template for all X86 Targets, and it relies on the CRT | |
| /// pattern for generating code, delegating to actual backends target-specific | |
| /// lowerings (e.g., call, ret, and intrinsics.) Backends are expected to | |
| /// implement the following methods (which should be accessible from | |
| /// TargetX86Base): | |
| /// | |
| /// Operand *createNaClReadTPSrcOperand() | |
| /// | |
| /// Note: Ideally, we should be able to | |
| /// | |
| /// static_assert(std::is_base_of<TargetX86Base<TraitsType>, | |
| /// Machine>::value); | |
| /// | |
| /// but that does not work: the compiler does not know that Machine inherits | |
| /// from TargetX86Base at this point in translation. | |
| template <typename TraitsType> class TargetX86Base : public TargetLowering { | |
| TargetX86Base() = delete; | |
| TargetX86Base(const TargetX86Base &) = delete; | |
| TargetX86Base &operator=(const TargetX86Base &) = delete; | |
| public: | |
| using Traits = TraitsType; | |
| using ConcreteTarget = typename Traits::ConcreteTarget; | |
| using InstructionSetEnum = typename Traits::InstructionSet; | |
| using BrCond = typename Traits::Cond::BrCond; | |
| using CmppsCond = typename Traits::Cond::CmppsCond; | |
| using X86Address = typename Traits::Address; | |
| using X86Operand = typename Traits::X86Operand; | |
| using X86OperandMem = typename Traits::X86OperandMem; | |
| using SegmentRegisters = typename Traits::X86OperandMem::SegmentRegisters; | |
| using InstX86Br = typename Traits::Insts::Br; | |
| using InstX86FakeRMW = typename Traits::Insts::FakeRMW; | |
| using InstX86Label = typename Traits::Insts::Label; | |
| ~TargetX86Base() override = default; | |
| static void staticInit(GlobalContext *Ctx); | |
| static bool shouldBePooled(const Constant *C); | |
| static ::Ice::Type getPointerType(); | |
| static FixupKind getPcRelFixup() { return PcRelFixup; } | |
| static FixupKind getAbsFixup() { return AbsFixup; } | |
| bool needSandboxing() const { return NeedSandboxing; } | |
| void translateOm1() override; | |
| void translateO2() override; | |
| void doLoadOpt(); | |
| bool doBranchOpt(Inst *I, const CfgNode *NextNode) override; | |
| SizeT getNumRegisters() const override { | |
| return Traits::RegisterSet::Reg_NUM; | |
| } | |
| Inst *createLoweredMove(Variable *Dest, Variable *SrcVar) override { | |
| if (isVectorType(Dest->getType())) { | |
| return Traits::Insts::Movp::create(Func, Dest, SrcVar); | |
| } | |
| return Traits::Insts::Mov::create(Func, Dest, SrcVar); | |
| (void)Dest; | |
| (void)SrcVar; | |
| return nullptr; | |
| } | |
| Variable *getPhysicalRegister(RegNumT RegNum, | |
| Type Ty = IceType_void) override; | |
| const char *getRegName(RegNumT RegNum, Type Ty) const override; | |
| static const char *getRegClassName(RegClass C) { | |
| auto ClassNum = static_cast<RegClassX86>(C); | |
| assert(ClassNum < RCX86_NUM); | |
| switch (ClassNum) { | |
| default: | |
| assert(C < RC_Target); | |
| return regClassString(C); | |
| case RCX86_Is64To8: | |
| return "i64to8"; // 64-bit GPR truncable to i8 | |
| case RCX86_Is32To8: | |
| return "i32to8"; // 32-bit GPR truncable to i8 | |
| case RCX86_Is16To8: | |
| return "i16to8"; // 16-bit GPR truncable to i8 | |
| case RCX86_IsTrunc8Rcvr: | |
| return "i8from"; // 8-bit GPR truncable from wider GPRs | |
| case RCX86_IsAhRcvr: | |
| return "i8fromah"; // 8-bit GPR that ah can be assigned to | |
| } | |
| } | |
| SmallBitVector getRegisterSet(RegSetMask Include, | |
| RegSetMask Exclude) const override; | |
| const SmallBitVector & | |
| getRegistersForVariable(const Variable *Var) const override { | |
| RegClass RC = Var->getRegClass(); | |
| assert(static_cast<RegClassX86>(RC) < RCX86_NUM); | |
| return TypeToRegisterSet[RC]; | |
| } | |
| const SmallBitVector & | |
| getAllRegistersForVariable(const Variable *Var) const override { | |
| RegClass RC = Var->getRegClass(); | |
| assert(static_cast<RegClassX86>(RC) < RCX86_NUM); | |
| return TypeToRegisterSetUnfiltered[RC]; | |
| } | |
| const SmallBitVector &getAliasesForRegister(RegNumT Reg) const override { | |
| Reg.assertIsValid(); | |
| return RegisterAliases[Reg]; | |
| } | |
| bool hasFramePointer() const override { return IsEbpBasedFrame; } | |
| void setHasFramePointer() override { IsEbpBasedFrame = true; } | |
| RegNumT getStackReg() const override { return Traits::StackPtr; } | |
| RegNumT getFrameReg() const override { return Traits::FramePtr; } | |
| RegNumT getFrameOrStackReg() const override { | |
| // If the stack pointer needs to be aligned, then the frame pointer is | |
| // unaligned, so always use the stack pointer. | |
| if (needsStackPointerAlignment()) | |
| return getStackReg(); | |
| return IsEbpBasedFrame ? getFrameReg() : getStackReg(); | |
| } | |
| size_t typeWidthInBytesOnStack(Type Ty) const override { | |
| // Round up to the next multiple of WordType bytes. | |
| const uint32_t WordSizeInBytes = typeWidthInBytes(Traits::WordType); | |
| return Utils::applyAlignment(typeWidthInBytes(Ty), WordSizeInBytes); | |
| } | |
| uint32_t getStackAlignment() const override { | |
| return Traits::X86_STACK_ALIGNMENT_BYTES; | |
| } | |
| bool needsStackPointerAlignment() const override { | |
| // If the ABI's stack alignment is smaller than the vector size (16 bytes), | |
| // use the (realigned) stack pointer for addressing any stack variables. | |
| return Traits::X86_STACK_ALIGNMENT_BYTES < 16; | |
| } | |
| void reserveFixedAllocaArea(size_t Size, size_t Align) override { | |
| FixedAllocaSizeBytes = Size; | |
| assert(llvm::isPowerOf2_32(Align)); | |
| FixedAllocaAlignBytes = Align; | |
| PrologEmitsFixedAllocas = true; | |
| } | |
| /// Returns the (negative) offset from ebp/rbp where the fixed Allocas start. | |
| int32_t getFrameFixedAllocaOffset() const override { | |
| return FixedAllocaSizeBytes - (SpillAreaSizeBytes - maxOutArgsSizeBytes()); | |
| } | |
| virtual uint32_t maxOutArgsSizeBytes() const override { | |
| return MaxOutArgsSizeBytes; | |
| } | |
| virtual void updateMaxOutArgsSizeBytes(uint32_t Size) { | |
| MaxOutArgsSizeBytes = std::max(MaxOutArgsSizeBytes, Size); | |
| } | |
| bool shouldSplitToVariable64On32(Type Ty) const override { | |
| return Traits::Is64Bit ? false : Ty == IceType_i64; | |
| } | |
| ConstantRelocatable *createGetIPForRegister(const Variable *Dest) { | |
| assert(Dest->hasReg()); | |
| const std::string RegName = Traits::getRegName(Dest->getRegNum()); | |
| return llvm::cast<ConstantRelocatable>(Ctx->getConstantExternSym( | |
| Ctx->getGlobalString(H_getIP_prefix + RegName))); | |
| } | |
| SizeT getMinJumpTableSize() const override { return 4; } | |
| void emitVariable(const Variable *Var) const override; | |
| void emit(const ConstantInteger32 *C) const final; | |
| void emit(const ConstantInteger64 *C) const final; | |
| void emit(const ConstantFloat *C) const final; | |
| void emit(const ConstantDouble *C) const final; | |
| void emit(const ConstantUndef *C) const final; | |
| void emit(const ConstantRelocatable *C) const final; | |
| void initNodeForLowering(CfgNode *Node) override; | |
| template <typename T = Traits> | |
| typename std::enable_if<!T::Is64Bit, Operand>::type * | |
| loOperand(Operand *Operand); | |
| template <typename T = Traits> | |
| typename std::enable_if<T::Is64Bit, Operand>::type *loOperand(Operand *) { | |
| llvm::report_fatal_error( | |
| "Hey, yo! This is x86-64. Watcha doin'? (loOperand)"); | |
| } | |
| template <typename T = Traits> | |
| typename std::enable_if<!T::Is64Bit, Operand>::type * | |
| hiOperand(Operand *Operand); | |
| template <typename T = Traits> | |
| typename std::enable_if<T::Is64Bit, Operand>::type *hiOperand(Operand *) { | |
| llvm::report_fatal_error( | |
| "Hey, yo! This is x86-64. Watcha doin'? (hiOperand)"); | |
| } | |
| void addProlog(CfgNode *Node) override; | |
| void finishArgumentLowering(Variable *Arg, Variable *FramePtr, | |
| size_t BasicFrameOffset, size_t StackAdjBytes, | |
| size_t &InArgsSizeBytes); | |
| void addEpilog(CfgNode *Node) override; | |
| X86Address stackVarToAsmOperand(const Variable *Var) const; | |
| InstructionSetEnum getInstructionSet() const { return InstructionSet; } | |
| Operand *legalizeUndef(Operand *From, RegNumT RegNum = RegNumT()); | |
| protected: | |
| const bool NeedSandboxing; | |
| explicit TargetX86Base(Cfg *Func); | |
| void postLower() override; | |
| /// Initializes the RebasePtr member variable -- if so required by | |
| /// SandboxingType for the concrete Target. | |
| void initRebasePtr() { | |
| assert(SandboxingType != ST_None); | |
| dispatchToConcrete(&Traits::ConcreteTarget::initRebasePtr); | |
| } | |
| /// Emit code that initializes the value of the RebasePtr near the start of | |
| /// the function -- if so required by SandboxingType for the concrete type. | |
| void initSandbox() { | |
| assert(SandboxingType != ST_None); | |
| dispatchToConcrete(&Traits::ConcreteTarget::initSandbox); | |
| } | |
| void lowerAlloca(const InstAlloca *Instr) override; | |
| void lowerArguments() override; | |
| void lowerArithmetic(const InstArithmetic *Instr) override; | |
| void lowerAssign(const InstAssign *Instr) override; | |
| void lowerBr(const InstBr *Instr) override; | |
| void lowerBreakpoint(const InstBreakpoint *Instr) override; | |
| void lowerCall(const InstCall *Instr) override; | |
| void lowerCast(const InstCast *Instr) override; | |
| void lowerExtractElement(const InstExtractElement *Instr) override; | |
| void lowerFcmp(const InstFcmp *Instr) override; | |
| void lowerIcmp(const InstIcmp *Instr) override; | |
| void lowerIntrinsic(const InstIntrinsic *Instr) override; | |
| void lowerInsertElement(const InstInsertElement *Instr) override; | |
| void lowerLoad(const InstLoad *Instr) override; | |
| void lowerPhi(const InstPhi *Instr) override; | |
| void lowerRet(const InstRet *Instr) override; | |
| void lowerSelect(const InstSelect *Instr) override; | |
| void lowerShuffleVector(const InstShuffleVector *Instr) override; | |
| void lowerStore(const InstStore *Instr) override; | |
| void lowerSwitch(const InstSwitch *Instr) override; | |
| void lowerUnreachable(const InstUnreachable *Instr) override; | |
| void lowerOther(const Inst *Instr) override; | |
| void lowerRMW(const InstX86FakeRMW *RMW); | |
| void prelowerPhis() override; | |
| uint32_t getCallStackArgumentsSizeBytes(const CfgVector<Type> &ArgTypes, | |
| Type ReturnType); | |
| uint32_t getCallStackArgumentsSizeBytes(const InstCall *Instr) override; | |
| void genTargetHelperCallFor(Inst *Instr) override; | |
| /// OptAddr wraps all the possible operands that an x86 address might have. | |
| struct OptAddr { | |
| Variable *Base = nullptr; | |
| Variable *Index = nullptr; | |
| uint16_t Shift = 0; | |
| int32_t Offset = 0; | |
| ConstantRelocatable *Relocatable = nullptr; | |
| }; | |
| /// Legalizes Addr w.r.t. SandboxingType. The exact type of legalization | |
| /// varies for different <Target, SandboxingType> tuples. | |
| bool legalizeOptAddrForSandbox(OptAddr *Addr) { | |
| return dispatchToConcrete( | |
| &Traits::ConcreteTarget::legalizeOptAddrForSandbox, std::move(Addr)); | |
| } | |
| // Builds information for a canonical address expresion: | |
| // <Relocatable + Offset>(Base, Index, Shift) | |
| X86OperandMem *computeAddressOpt(const Inst *Instr, Type MemType, | |
| Operand *Addr); | |
| void doAddressOptOther() override; | |
| void doAddressOptLoad() override; | |
| void doAddressOptStore() override; | |
| void doAddressOptLoadSubVector() override; | |
| void doAddressOptStoreSubVector() override; | |
| void doMockBoundsCheck(Operand *Opnd) override; | |
| /// Naive lowering of cmpxchg. | |
| void lowerAtomicCmpxchg(Variable *DestPrev, Operand *Ptr, Operand *Expected, | |
| Operand *Desired); | |
| /// Attempt a more optimized lowering of cmpxchg. Returns true if optimized. | |
| bool tryOptimizedCmpxchgCmpBr(Variable *DestPrev, Operand *Ptr, | |
| Operand *Expected, Operand *Desired); | |
| void lowerAtomicRMW(Variable *Dest, uint32_t Operation, Operand *Ptr, | |
| Operand *Val); | |
| void lowerCountZeros(bool Cttz, Type Ty, Variable *Dest, Operand *FirstVal, | |
| Operand *SecondVal); | |
| /// Load from memory for a given type. | |
| void typedLoad(Type Ty, Variable *Dest, Variable *Base, Constant *Offset); | |
| /// Store to memory for a given type. | |
| void typedStore(Type Ty, Variable *Value, Variable *Base, Constant *Offset); | |
| /// Copy memory of given type from Src to Dest using OffsetAmt on both. | |
| void copyMemory(Type Ty, Variable *Dest, Variable *Src, int32_t OffsetAmt); | |
| /// Replace some calls to memcpy with inline instructions. | |
| void lowerMemcpy(Operand *Dest, Operand *Src, Operand *Count); | |
| /// Replace some calls to memmove with inline instructions. | |
| void lowerMemmove(Operand *Dest, Operand *Src, Operand *Count); | |
| /// Replace some calls to memset with inline instructions. | |
| void lowerMemset(Operand *Dest, Operand *Val, Operand *Count); | |
| /// Lower an indirect jump adding sandboxing when needed. | |
| void lowerIndirectJump(Variable *JumpTarget) { | |
| // Without std::move below, the compiler deduces that the argument to | |
| // lowerIndirectJmp is a Variable *&, not a Variable *. | |
| dispatchToConcrete(&Traits::ConcreteTarget::lowerIndirectJump, | |
| std::move(JumpTarget)); | |
| } | |
| /// Check the comparison is in [Min,Max]. The flags register will be modified | |
| /// with: | |
| /// - below equal, if in range | |
| /// - above, set if not in range | |
| /// The index into the range is returned. | |
| Operand *lowerCmpRange(Operand *Comparison, uint64_t Min, uint64_t Max); | |
| /// Lowering of a cluster of switch cases. If the case is not matched control | |
| /// will pass to the default label provided. If the default label is nullptr | |
| /// then control will fall through to the next instruction. DoneCmp should be | |
| /// true if the flags contain the result of a comparison with the Comparison. | |
| void lowerCaseCluster(const CaseCluster &Case, Operand *Src0, bool DoneCmp, | |
| CfgNode *DefaultLabel = nullptr); | |
| using LowerBinOp = void (TargetX86Base::*)(Variable *, Operand *); | |
| void expandAtomicRMWAsCmpxchg(LowerBinOp op_lo, LowerBinOp op_hi, | |
| Variable *Dest, Operand *Ptr, Operand *Val); | |
| void eliminateNextVectorSextInstruction(Variable *SignExtendedResult); | |
| void emitGetIP(CfgNode *Node) { | |
| dispatchToConcrete(&Traits::ConcreteTarget::emitGetIP, std::move(Node)); | |
| } | |
| /// Emit a sandboxed return sequence rather than a return. | |
| void emitSandboxedReturn() { | |
| dispatchToConcrete(&Traits::ConcreteTarget::emitSandboxedReturn); | |
| } | |
| void emitStackProbe(size_t StackSizeBytes) { | |
| dispatchToConcrete(&Traits::ConcreteTarget::emitStackProbe, | |
| std::move(StackSizeBytes)); | |
| } | |
| /// Emit just the call instruction (without argument or return variable | |
| /// processing), sandboxing if needed. | |
| virtual Inst *emitCallToTarget(Operand *CallTarget, Variable *ReturnReg, | |
| size_t NumVariadicFpArgs = 0) = 0; | |
| /// Materialize the moves needed to return a value of the specified type. | |
| virtual Variable *moveReturnValueToRegister(Operand *Value, | |
| Type ReturnType) = 0; | |
| /// Emit a jump table to the constant pool. | |
| void emitJumpTable(const Cfg *Func, | |
| const InstJumpTable *JumpTable) const override; | |
| /// Emit a fake use of esp to make sure esp stays alive for the entire | |
| /// function. Otherwise some esp adjustments get dead-code eliminated. | |
| void keepEspLiveAtExit() { | |
| Variable *esp = | |
| Func->getTarget()->getPhysicalRegister(getStackReg(), Traits::WordType); | |
| Context.insert<InstFakeUse>(esp); | |
| } | |
| /// Operand legalization helpers. To deal with address mode constraints, the | |
| /// helpers will create a new Operand and emit instructions that guarantee | |
| /// that the Operand kind is one of those indicated by the LegalMask (a | |
| /// bitmask of allowed kinds). If the input Operand is known to already meet | |
| /// the constraints, it may be simply returned as the result, without creating | |
| /// any new instructions or operands. | |
| enum OperandLegalization { | |
| Legal_None = 0, | |
| Legal_Reg = 1 << 0, // physical register, not stack location | |
| Legal_Imm = 1 << 1, | |
| Legal_Mem = 1 << 2, // includes [eax+4*ecx] as well as [esp+12] | |
| Legal_Rematerializable = 1 << 3, | |
| Legal_AddrAbs = 1 << 4, // ConstantRelocatable doesn't have to add RebasePtr | |
| Legal_Default = ~(Legal_Rematerializable | Legal_AddrAbs) | |
| // TODO(stichnot): Figure out whether this default works for x86-64. | |
| }; | |
| using LegalMask = uint32_t; | |
| Operand *legalize(Operand *From, LegalMask Allowed = Legal_Default, | |
| RegNumT RegNum = RegNumT()); | |
| Variable *legalizeToReg(Operand *From, RegNumT RegNum = RegNumT()); | |
| /// Legalize the first source operand for use in the cmp instruction. | |
| Operand *legalizeSrc0ForCmp(Operand *Src0, Operand *Src1); | |
| /// Turn a pointer operand into a memory operand that can be used by a real | |
| /// load/store operation. Legalizes the operand as well. This is a nop if the | |
| /// operand is already a legal memory operand. | |
| X86OperandMem *formMemoryOperand(Operand *Ptr, Type Ty, | |
| bool DoLegalize = true); | |
| Variable *makeReg(Type Ty, RegNumT RegNum = RegNumT()); | |
| static Type stackSlotType(); | |
| static constexpr uint32_t NoSizeLimit = 0; | |
| /// Returns the largest type which is equal to or larger than Size bytes. The | |
| /// type is suitable for copying memory i.e. a load and store will be a single | |
| /// instruction (for example x86 will get f64 not i64). | |
| static Type largestTypeInSize(uint32_t Size, uint32_t MaxSize = NoSizeLimit); | |
| /// Returns the smallest type which is equal to or larger than Size bytes. If | |
| /// one doesn't exist then the largest type smaller than Size bytes is | |
| /// returned. The type is suitable for memory copies as described at | |
| /// largestTypeInSize. | |
| static Type firstTypeThatFitsSize(uint32_t Size, | |
| uint32_t MaxSize = NoSizeLimit); | |
| Variable *copyToReg8(Operand *Src, RegNumT RegNum = RegNumT()); | |
| Variable *copyToReg(Operand *Src, RegNumT RegNum = RegNumT()); | |
| /// Returns a register containing all zeros, without affecting the FLAGS | |
| /// register, using the best instruction for the type. | |
| Variable *makeZeroedRegister(Type Ty, RegNumT RegNum = RegNumT()); | |
| /// \name Returns a vector in a register with the given constant entries. | |
| /// @{ | |
| Variable *makeVectorOfZeros(Type Ty, RegNumT RegNum = RegNumT()); | |
| Variable *makeVectorOfOnes(Type Ty, RegNumT RegNum = RegNumT()); | |
| Variable *makeVectorOfMinusOnes(Type Ty, RegNumT RegNum = RegNumT()); | |
| Variable *makeVectorOfHighOrderBits(Type Ty, RegNumT RegNum = RegNumT()); | |
| Variable *makeVectorOfFabsMask(Type Ty, RegNumT RegNum = RegNumT()); | |
| /// @} | |
| /// Return a memory operand corresponding to a stack allocated Variable. | |
| X86OperandMem *getMemoryOperandForStackSlot(Type Ty, Variable *Slot, | |
| uint32_t Offset = 0); | |
| /// AutoMemorySandboxer emits a bundle-lock/bundle-unlock pair if the | |
| /// instruction's operand is a memory reference. This is only needed for | |
| /// x86-64 NaCl sandbox. | |
| template <InstBundleLock::Option BundleLockOpt = InstBundleLock::Opt_None> | |
| class AutoMemorySandboxer { | |
| AutoMemorySandboxer() = delete; | |
| AutoMemorySandboxer(const AutoMemorySandboxer &) = delete; | |
| AutoMemorySandboxer &operator=(const AutoMemorySandboxer &) = delete; | |
| private: | |
| typename Traits::TargetLowering *Target; | |
| template <typename T, typename... Tail> | |
| X86OperandMem **findMemoryReference(T **First, Tail... Others) { | |
| if (llvm::isa<X86OperandMem>(*First)) { | |
| return reinterpret_cast<X86OperandMem **>(First); | |
| } | |
| return findMemoryReference(Others...); | |
| } | |
| X86OperandMem **findMemoryReference() { return nullptr; } | |
| public: | |
| AutoBundle *Bundler = nullptr; | |
| X86OperandMem **const MemOperand; | |
| template <typename... T> | |
| AutoMemorySandboxer(typename Traits::TargetLowering *Target, T... Args) | |
| : Target(Target), MemOperand(Target->SandboxingType == ST_None | |
| ? nullptr | |
| : findMemoryReference(Args...)) { | |
| if (MemOperand != nullptr) { | |
| if (Traits::Is64Bit) { | |
| Bundler = new (Target->Func->template allocate<AutoBundle>()) | |
| AutoBundle(Target, BundleLockOpt); | |
| } | |
| *MemOperand = Target->_sandbox_mem_reference(*MemOperand); | |
| } | |
| } | |
| ~AutoMemorySandboxer() { | |
| if (Bundler != nullptr) { | |
| Bundler->~AutoBundle(); | |
| } | |
| } | |
| }; | |
| /// The following are helpers that insert lowered x86 instructions with | |
| /// minimal syntactic overhead, so that the lowering code can look as close to | |
| /// assembly as practical. | |
| void _adc(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Adc>(Dest, Src0); | |
| } | |
| void _adc_rmw(X86OperandMem *DestSrc0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &DestSrc0, &Src1); | |
| Context.insert<typename Traits::Insts::AdcRMW>(DestSrc0, Src1); | |
| } | |
| void _add(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Add>(Dest, Src0); | |
| } | |
| void _add_rmw(X86OperandMem *DestSrc0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &DestSrc0, &Src1); | |
| Context.insert<typename Traits::Insts::AddRMW>(DestSrc0, Src1); | |
| } | |
| void _addps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Addps>(Dest, Src0); | |
| } | |
| void _addss(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Addss>(Dest, Src0); | |
| } | |
| void _add_sp(Operand *Adjustment) { | |
| dispatchToConcrete(&Traits::ConcreteTarget::_add_sp, std::move(Adjustment)); | |
| } | |
| void _and(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::And>(Dest, Src0); | |
| } | |
| void _andnps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Andnps>(Dest, Src0); | |
| } | |
| void _andps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Andps>(Dest, Src0); | |
| } | |
| void _and_rmw(X86OperandMem *DestSrc0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &DestSrc0, &Src1); | |
| Context.insert<typename Traits::Insts::AndRMW>(DestSrc0, Src1); | |
| } | |
| void _blendvps(Variable *Dest, Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Blendvps>(Dest, Src0, Src1); | |
| } | |
| void _br(BrCond Condition, CfgNode *TargetTrue, CfgNode *TargetFalse) { | |
| Context.insert<InstX86Br>(TargetTrue, TargetFalse, Condition, | |
| InstX86Br::Far); | |
| } | |
| void _br(CfgNode *Target) { | |
| Context.insert<InstX86Br>(Target, InstX86Br::Far); | |
| } | |
| void _br(BrCond Condition, CfgNode *Target) { | |
| Context.insert<InstX86Br>(Target, Condition, InstX86Br::Far); | |
| } | |
| void _br(BrCond Condition, InstX86Label *Label, | |
| typename InstX86Br::Mode Kind = InstX86Br::Near) { | |
| Context.insert<InstX86Br>(Label, Condition, Kind); | |
| } | |
| void _bsf(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Bsf>(Dest, Src0); | |
| } | |
| void _bsr(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Bsr>(Dest, Src0); | |
| } | |
| void _bswap(Variable *SrcDest) { | |
| AutoMemorySandboxer<> _(this, &SrcDest); | |
| Context.insert<typename Traits::Insts::Bswap>(SrcDest); | |
| } | |
| void _cbwdq(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Cbwdq>(Dest, Src0); | |
| } | |
| void _cmov(Variable *Dest, Operand *Src0, BrCond Condition) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Cmov>(Dest, Src0, Condition); | |
| } | |
| void _cmp(Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Icmp>(Src0, Src1); | |
| } | |
| void _cmpps(Variable *Dest, Operand *Src0, CmppsCond Condition) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Cmpps>(Dest, Src0, Condition); | |
| } | |
| void _cmpxchg(Operand *DestOrAddr, Variable *Eax, Variable *Desired, | |
| bool Locked) { | |
| AutoMemorySandboxer<> _(this, &DestOrAddr); | |
| Context.insert<typename Traits::Insts::Cmpxchg>(DestOrAddr, Eax, Desired, | |
| Locked); | |
| // Mark eax as possibly modified by cmpxchg. | |
| Context.insert<InstFakeDef>(Eax, llvm::dyn_cast<Variable>(DestOrAddr)); | |
| _set_dest_redefined(); | |
| Context.insert<InstFakeUse>(Eax); | |
| } | |
| void _cmpxchg8b(X86OperandMem *Addr, Variable *Edx, Variable *Eax, | |
| Variable *Ecx, Variable *Ebx, bool Locked) { | |
| AutoMemorySandboxer<> _(this, &Addr); | |
| Context.insert<typename Traits::Insts::Cmpxchg8b>(Addr, Edx, Eax, Ecx, Ebx, | |
| Locked); | |
| // Mark edx, and eax as possibly modified by cmpxchg8b. | |
| Context.insert<InstFakeDef>(Edx); | |
| _set_dest_redefined(); | |
| Context.insert<InstFakeUse>(Edx); | |
| Context.insert<InstFakeDef>(Eax); | |
| _set_dest_redefined(); | |
| Context.insert<InstFakeUse>(Eax); | |
| } | |
| void _cvt(Variable *Dest, Operand *Src0, | |
| typename Traits::Insts::Cvt::CvtVariant Variant) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Cvt>(Dest, Src0, Variant); | |
| } | |
| void _round(Variable *Dest, Operand *Src0, Operand *Imm) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Round>(Dest, Src0, Imm); | |
| } | |
| void _div(Variable *Dest, Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Div>(Dest, Src0, Src1); | |
| } | |
| void _divps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Divps>(Dest, Src0); | |
| } | |
| void _divss(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Divss>(Dest, Src0); | |
| } | |
| template <typename T = Traits> | |
| typename std::enable_if<T::UsesX87, void>::type _fld(Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Src0); | |
| Context.insert<typename Traits::Insts::template Fld<>>(Src0); | |
| } | |
| // TODO(jpp): when implementing the X8664 calling convention, make sure x8664 | |
| // does not invoke this method, and remove it. | |
| template <typename T = Traits> | |
| typename std::enable_if<!T::UsesX87, void>::type _fld(Operand *) { | |
| llvm::report_fatal_error("fld is not available in x86-64"); | |
| } | |
| template <typename T = Traits> | |
| typename std::enable_if<T::UsesX87, void>::type _fstp(Variable *Dest) { | |
| AutoMemorySandboxer<> _(this, &Dest); | |
| Context.insert<typename Traits::Insts::template Fstp<>>(Dest); | |
| } | |
| // TODO(jpp): when implementing the X8664 calling convention, make sure x8664 | |
| // does not invoke this method, and remove it. | |
| template <typename T = Traits> | |
| typename std::enable_if<!T::UsesX87, void>::type _fstp(Variable *) { | |
| llvm::report_fatal_error("fstp is not available in x86-64"); | |
| } | |
| void _idiv(Variable *Dest, Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Idiv>(Dest, Src0, Src1); | |
| } | |
| void _imul(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Imul>(Dest, Src0); | |
| } | |
| void _imul_imm(Variable *Dest, Operand *Src0, Constant *Imm) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::ImulImm>(Dest, Src0, Imm); | |
| } | |
| void _insertps(Variable *Dest, Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Insertps>(Dest, Src0, Src1); | |
| } | |
| void _int3() { Context.insert<typename Traits::Insts::Int3>(); } | |
| void _jmp(Operand *Target) { | |
| AutoMemorySandboxer<> _(this, &Target); | |
| Context.insert<typename Traits::Insts::Jmp>(Target); | |
| } | |
| void _lea(Variable *Dest, Operand *Src0) { | |
| Context.insert<typename Traits::Insts::Lea>(Dest, Src0); | |
| } | |
| void _link_bp() { dispatchToConcrete(&Traits::ConcreteTarget::_link_bp); } | |
| void _push_reg(RegNumT RegNum) { | |
| dispatchToConcrete(&Traits::ConcreteTarget::_push_reg, std::move(RegNum)); | |
| } | |
| void _pop_reg(RegNumT RegNum) { | |
| dispatchToConcrete(&Traits::ConcreteTarget::_pop_reg, std::move(RegNum)); | |
| } | |
| void _mfence() { Context.insert<typename Traits::Insts::Mfence>(); } | |
| /// Moves can be used to redefine registers, creating "partial kills" for | |
| /// liveness. Mark where moves are used in this way. | |
| void _redefined(Inst *MovInst, bool IsRedefinition = true) { | |
| if (IsRedefinition) | |
| MovInst->setDestRedefined(); | |
| } | |
| /// If Dest=nullptr is passed in, then a new variable is created, marked as | |
| /// infinite register allocation weight, and returned through the in/out Dest | |
| /// argument. | |
| typename Traits::Insts::Mov *_mov(Variable *&Dest, Operand *Src0, | |
| RegNumT RegNum = RegNumT()) { | |
| if (Dest == nullptr) | |
| Dest = makeReg(Src0->getType(), RegNum); | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| return Context.insert<typename Traits::Insts::Mov>(Dest, Src0); | |
| } | |
| void _mov_sp(Operand *NewValue) { | |
| dispatchToConcrete(&Traits::ConcreteTarget::_mov_sp, std::move(NewValue)); | |
| } | |
| typename Traits::Insts::Movp *_movp(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| return Context.insert<typename Traits::Insts::Movp>(Dest, Src0); | |
| } | |
| void _movd(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Movd>(Dest, Src0); | |
| } | |
| void _movq(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Movq>(Dest, Src0); | |
| } | |
| void _movss(Variable *Dest, Variable *Src0) { | |
| Context.insert<typename Traits::Insts::MovssRegs>(Dest, Src0); | |
| } | |
| void _movsx(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Movsx>(Dest, Src0); | |
| } | |
| typename Traits::Insts::Movzx *_movzx(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| return Context.insert<typename Traits::Insts::Movzx>(Dest, Src0); | |
| } | |
| void _maxss(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Maxss>(Dest, Src0); | |
| } | |
| void _minss(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Minss>(Dest, Src0); | |
| } | |
| void _maxps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Maxps>(Dest, Src0); | |
| } | |
| void _minps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Minps>(Dest, Src0); | |
| } | |
| void _mul(Variable *Dest, Variable *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Mul>(Dest, Src0, Src1); | |
| } | |
| void _mulps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Mulps>(Dest, Src0); | |
| } | |
| void _mulss(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Mulss>(Dest, Src0); | |
| } | |
| void _neg(Variable *SrcDest) { | |
| AutoMemorySandboxer<> _(this, &SrcDest); | |
| Context.insert<typename Traits::Insts::Neg>(SrcDest); | |
| } | |
| void _nop(SizeT Variant) { | |
| Context.insert<typename Traits::Insts::Nop>(Variant); | |
| } | |
| void _or(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Or>(Dest, Src0); | |
| } | |
| void _orps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Orps>(Dest, Src0); | |
| } | |
| void _or_rmw(X86OperandMem *DestSrc0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &DestSrc0, &Src1); | |
| Context.insert<typename Traits::Insts::OrRMW>(DestSrc0, Src1); | |
| } | |
| void _padd(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Padd>(Dest, Src0); | |
| } | |
| void _padds(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Padds>(Dest, Src0); | |
| } | |
| void _paddus(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Paddus>(Dest, Src0); | |
| } | |
| void _pand(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pand>(Dest, Src0); | |
| } | |
| void _pandn(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pandn>(Dest, Src0); | |
| } | |
| void _pblendvb(Variable *Dest, Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Pblendvb>(Dest, Src0, Src1); | |
| } | |
| void _pcmpeq(Variable *Dest, Operand *Src0, | |
| Type ArithmeticTypeOverride = IceType_void) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pcmpeq>(Dest, Src0, | |
| ArithmeticTypeOverride); | |
| } | |
| void _pcmpgt(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pcmpgt>(Dest, Src0); | |
| } | |
| void _pextr(Variable *Dest, Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Pextr>(Dest, Src0, Src1); | |
| } | |
| void _pinsr(Variable *Dest, Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Pinsr>(Dest, Src0, Src1); | |
| } | |
| void _pmull(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pmull>(Dest, Src0); | |
| } | |
| void _pmulhw(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pmulhw>(Dest, Src0); | |
| } | |
| void _pmulhuw(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pmulhuw>(Dest, Src0); | |
| } | |
| void _pmaddwd(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pmaddwd>(Dest, Src0); | |
| } | |
| void _pmuludq(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pmuludq>(Dest, Src0); | |
| } | |
| void _pop(Variable *Dest) { | |
| Context.insert<typename Traits::Insts::Pop>(Dest); | |
| } | |
| void _por(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Por>(Dest, Src0); | |
| } | |
| void _punpckl(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Punpckl>(Dest, Src0); | |
| } | |
| void _punpckh(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Punpckh>(Dest, Src0); | |
| } | |
| void _packss(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Packss>(Dest, Src0); | |
| } | |
| void _packus(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Packus>(Dest, Src0); | |
| } | |
| void _pshufb(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pshufb>(Dest, Src0); | |
| } | |
| void _pshufd(Variable *Dest, Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Pshufd>(Dest, Src0, Src1); | |
| } | |
| void _psll(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Psll>(Dest, Src0); | |
| } | |
| void _psra(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Psra>(Dest, Src0); | |
| } | |
| void _psrl(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Psrl>(Dest, Src0); | |
| } | |
| void _psub(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Psub>(Dest, Src0); | |
| } | |
| void _psubs(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Psubs>(Dest, Src0); | |
| } | |
| void _psubus(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Psubus>(Dest, Src0); | |
| } | |
| void _push(Operand *Src0) { | |
| Context.insert<typename Traits::Insts::Push>(Src0); | |
| } | |
| void _pxor(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Pxor>(Dest, Src0); | |
| } | |
| void _ret(Variable *Src0 = nullptr) { | |
| Context.insert<typename Traits::Insts::Ret>(Src0); | |
| } | |
| void _rol(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Rol>(Dest, Src0); | |
| } | |
| void _round(Variable *Dest, Operand *Src, Constant *Imm) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src); | |
| Context.insert<typename Traits::Insts::Round>(Dest, Src, Imm); | |
| } | |
| X86OperandMem *_sandbox_mem_reference(X86OperandMem *Mem) { | |
| return dispatchToConcrete(&Traits::ConcreteTarget::_sandbox_mem_reference, | |
| std::move(Mem)); | |
| } | |
| void _sar(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Sar>(Dest, Src0); | |
| } | |
| void _sbb(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Sbb>(Dest, Src0); | |
| } | |
| void _sbb_rmw(X86OperandMem *DestSrc0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &DestSrc0, &Src1); | |
| Context.insert<typename Traits::Insts::SbbRMW>(DestSrc0, Src1); | |
| } | |
| void _setcc(Variable *Dest, BrCond Condition) { | |
| Context.insert<typename Traits::Insts::Setcc>(Dest, Condition); | |
| } | |
| void _shl(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Shl>(Dest, Src0); | |
| } | |
| void _shld(Variable *Dest, Variable *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Shld>(Dest, Src0, Src1); | |
| } | |
| void _shr(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Shr>(Dest, Src0); | |
| } | |
| void _shrd(Variable *Dest, Variable *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Shrd>(Dest, Src0, Src1); | |
| } | |
| void _shufps(Variable *Dest, Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Shufps>(Dest, Src0, Src1); | |
| } | |
| void _movmsk(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Movmsk>(Dest, Src0); | |
| } | |
| void _sqrt(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Sqrt>(Dest, Src0); | |
| } | |
| void _store(Operand *Value, X86Operand *Mem) { | |
| AutoMemorySandboxer<> _(this, &Value, &Mem); | |
| Context.insert<typename Traits::Insts::Store>(Value, Mem); | |
| } | |
| void _storep(Variable *Value, X86OperandMem *Mem) { | |
| AutoMemorySandboxer<> _(this, &Value, &Mem); | |
| Context.insert<typename Traits::Insts::StoreP>(Value, Mem); | |
| } | |
| void _storeq(Operand *Value, X86OperandMem *Mem) { | |
| AutoMemorySandboxer<> _(this, &Value, &Mem); | |
| Context.insert<typename Traits::Insts::StoreQ>(Value, Mem); | |
| } | |
| void _stored(Operand *Value, X86OperandMem *Mem) { | |
| AutoMemorySandboxer<> _(this, &Value, &Mem); | |
| Context.insert<typename Traits::Insts::StoreD>(Value, Mem); | |
| } | |
| void _sub(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Sub>(Dest, Src0); | |
| } | |
| void _sub_rmw(X86OperandMem *DestSrc0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &DestSrc0, &Src1); | |
| Context.insert<typename Traits::Insts::SubRMW>(DestSrc0, Src1); | |
| } | |
| void _sub_sp(Operand *Adjustment) { | |
| dispatchToConcrete(&Traits::ConcreteTarget::_sub_sp, std::move(Adjustment)); | |
| } | |
| void _subps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Subps>(Dest, Src0); | |
| } | |
| void _subss(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Subss>(Dest, Src0); | |
| } | |
| void _test(Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Test>(Src0, Src1); | |
| } | |
| void _ucomiss(Operand *Src0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &Src0, &Src1); | |
| Context.insert<typename Traits::Insts::Ucomiss>(Src0, Src1); | |
| } | |
| void _ud2() { Context.insert<typename Traits::Insts::UD2>(); } | |
| void _unlink_bp() { dispatchToConcrete(&Traits::ConcreteTarget::_unlink_bp); } | |
| void _xadd(Operand *Dest, Variable *Src, bool Locked) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src); | |
| Context.insert<typename Traits::Insts::Xadd>(Dest, Src, Locked); | |
| // The xadd exchanges Dest and Src (modifying Src). Model that update with | |
| // a FakeDef followed by a FakeUse. | |
| Context.insert<InstFakeDef>(Src, llvm::dyn_cast<Variable>(Dest)); | |
| _set_dest_redefined(); | |
| Context.insert<InstFakeUse>(Src); | |
| } | |
| void _xchg(Operand *Dest, Variable *Src) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src); | |
| Context.insert<typename Traits::Insts::Xchg>(Dest, Src); | |
| // The xchg modifies Dest and Src -- model that update with a | |
| // FakeDef/FakeUse. | |
| Context.insert<InstFakeDef>(Src, llvm::dyn_cast<Variable>(Dest)); | |
| _set_dest_redefined(); | |
| Context.insert<InstFakeUse>(Src); | |
| } | |
| void _xor(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Xor>(Dest, Src0); | |
| } | |
| void _xorps(Variable *Dest, Operand *Src0) { | |
| AutoMemorySandboxer<> _(this, &Dest, &Src0); | |
| Context.insert<typename Traits::Insts::Xorps>(Dest, Src0); | |
| } | |
| void _xor_rmw(X86OperandMem *DestSrc0, Operand *Src1) { | |
| AutoMemorySandboxer<> _(this, &DestSrc0, &Src1); | |
| Context.insert<typename Traits::Insts::XorRMW>(DestSrc0, Src1); | |
| } | |
| void _iaca_start() { | |
| if (!BuildDefs::minimal()) | |
| Context.insert<typename Traits::Insts::IacaStart>(); | |
| } | |
| void _iaca_end() { | |
| if (!BuildDefs::minimal()) | |
| Context.insert<typename Traits::Insts::IacaEnd>(); | |
| } | |
| /// This class helps wrap IACA markers around the code generated by the | |
| /// current scope. It means you don't need to put an end before each return. | |
| class ScopedIacaMark { | |
| ScopedIacaMark(const ScopedIacaMark &) = delete; | |
| ScopedIacaMark &operator=(const ScopedIacaMark &) = delete; | |
| public: | |
| ScopedIacaMark(TargetX86Base *Lowering) : Lowering(Lowering) { | |
| Lowering->_iaca_start(); | |
| } | |
| ~ScopedIacaMark() { end(); } | |
| void end() { | |
| if (!Lowering) | |
| return; | |
| Lowering->_iaca_end(); | |
| Lowering = nullptr; | |
| } | |
| private: | |
| TargetX86Base *Lowering; | |
| }; | |
| bool optimizeScalarMul(Variable *Dest, Operand *Src0, int32_t Src1); | |
| void findRMW(); | |
| InstructionSetEnum InstructionSet = Traits::InstructionSet::Begin; | |
| bool IsEbpBasedFrame = false; | |
| size_t RequiredStackAlignment = sizeof(Traits::WordType); | |
| size_t SpillAreaSizeBytes = 0; | |
| size_t FixedAllocaSizeBytes = 0; | |
| size_t FixedAllocaAlignBytes = 0; | |
| bool PrologEmitsFixedAllocas = false; | |
| uint32_t MaxOutArgsSizeBytes = 0; | |
| static std::array<SmallBitVector, RCX86_NUM> TypeToRegisterSet; | |
| static std::array<SmallBitVector, RCX86_NUM> TypeToRegisterSetUnfiltered; | |
| static std::array<SmallBitVector, Traits::RegisterSet::Reg_NUM> | |
| RegisterAliases; | |
| SmallBitVector RegsUsed; | |
| std::array<VarList, IceType_NUM> PhysicalRegisters; | |
| // RebasePtr is a Variable that holds the Rebasing pointer (if any) for the | |
| // current sandboxing type. | |
| Variable *RebasePtr = nullptr; | |
| private: | |
| /// dispatchToConcrete is the template voodoo that allows TargetX86Base to | |
| /// invoke methods in Machine (which inherits from TargetX86Base) without | |
| /// having to rely on virtual method calls. There are two overloads, one for | |
| /// non-void types, and one for void types. We need this becase, for non-void | |
| /// types, we need to return the method result, where as for void, we don't. | |
| /// While it is true that the code compiles without the void "version", there | |
| /// used to be a time when compilers would reject such code. | |
| /// | |
| /// This machinery is far from perfect. Note that, in particular, the | |
| /// arguments provided to dispatchToConcrete() need to match the arguments for | |
| /// Method **exactly** (i.e., no argument promotion is performed.) | |
| template <typename Ret, typename... Args> | |
| typename std::enable_if<!std::is_void<Ret>::value, Ret>::type | |
| dispatchToConcrete(Ret (ConcreteTarget::*Method)(Args...), Args &&... args) { | |
| return (static_cast<ConcreteTarget *>(this)->*Method)( | |
| std::forward<Args>(args)...); | |
| } | |
| template <typename... Args> | |
| void dispatchToConcrete(void (ConcreteTarget::*Method)(Args...), | |
| Args &&... args) { | |
| (static_cast<ConcreteTarget *>(this)->*Method)(std::forward<Args>(args)...); | |
| } | |
| void lowerShift64(InstArithmetic::OpKind Op, Operand *Src0Lo, Operand *Src0Hi, | |
| Operand *Src1Lo, Variable *DestLo, Variable *DestHi); | |
| /// Emit the code for a combined operation and consumer instruction, or set | |
| /// the destination variable of the operation if Consumer == nullptr. | |
| void lowerIcmpAndConsumer(const InstIcmp *Icmp, const Inst *Consumer); | |
| void lowerFcmpAndConsumer(const InstFcmp *Fcmp, const Inst *Consumer); | |
| void lowerArithAndConsumer(const InstArithmetic *Arith, const Inst *Consumer); | |
| /// Emit a setcc instruction if Consumer == nullptr; otherwise emit a | |
| /// specialized version of Consumer. | |
| void setccOrConsumer(BrCond Condition, Variable *Dest, const Inst *Consumer); | |
| /// Emit a mov [1|0] instruction if Consumer == nullptr; otherwise emit a | |
| /// specialized version of Consumer. | |
| void movOrConsumer(bool IcmpResult, Variable *Dest, const Inst *Consumer); | |
| /// Emit the code for instructions with a vector type. | |
| void lowerIcmpVector(const InstIcmp *Icmp); | |
| void lowerFcmpVector(const InstFcmp *Icmp); | |
| void lowerSelectVector(const InstSelect *Instr); | |
| /// Helpers for select lowering. | |
| void lowerSelectMove(Variable *Dest, BrCond Cond, Operand *SrcT, | |
| Operand *SrcF); | |
| void lowerSelectIntMove(Variable *Dest, BrCond Cond, Operand *SrcT, | |
| Operand *SrcF); | |
| /// Generic helper to move an arbitrary type from Src to Dest. | |
| void lowerMove(Variable *Dest, Operand *Src, bool IsRedefinition); | |
| /// Optimizations for idiom recognition. | |
| bool lowerOptimizeFcmpSelect(const InstFcmp *Fcmp, const InstSelect *Select); | |
| /// Complains loudly if invoked because the cpu can handle 64-bit types | |
| /// natively. | |
| template <typename T = Traits> | |
| typename std::enable_if<T::Is64Bit, void>::type lowerIcmp64(const InstIcmp *, | |
| const Inst *) { | |
| llvm::report_fatal_error( | |
| "Hey, yo! This is x86-64. Watcha doin'? (lowerIcmp64)"); | |
| } | |
| /// x86lowerIcmp64 handles 64-bit icmp lowering. | |
| template <typename T = Traits> | |
| typename std::enable_if<!T::Is64Bit, void>::type | |
| lowerIcmp64(const InstIcmp *Icmp, const Inst *Consumer); | |
| BoolFolding<Traits> FoldingInfo; | |
| /// Helpers for lowering ShuffleVector | |
| /// @{ | |
| Variable *lowerShuffleVector_AllFromSameSrc(Operand *Src, SizeT Index0, | |
| SizeT Index1, SizeT Index2, | |
| SizeT Index3); | |
| static constexpr SizeT IGNORE_INDEX = 0x80000000u; | |
| Variable *lowerShuffleVector_TwoFromSameSrc(Operand *Src0, SizeT Index0, | |
| SizeT Index1, Operand *Src1, | |
| SizeT Index2, SizeT Index3); | |
| static constexpr SizeT UNIFIED_INDEX_0 = 0; | |
| static constexpr SizeT UNIFIED_INDEX_1 = 2; | |
| Variable *lowerShuffleVector_UnifyFromDifferentSrcs(Operand *Src0, | |
| SizeT Index0, | |
| Operand *Src1, | |
| SizeT Index1); | |
| static constexpr SizeT CLEAR_ALL_BITS = 0x80; | |
| SizeT PshufbMaskCount = 0; | |
| GlobalString lowerShuffleVector_NewMaskName(); | |
| ConstantRelocatable *lowerShuffleVector_CreatePshufbMask( | |
| int8_t Idx0, int8_t Idx1, int8_t Idx2, int8_t Idx3, int8_t Idx4, | |
| int8_t Idx5, int8_t Idx6, int8_t Idx7, int8_t Idx8, int8_t Idx9, | |
| int8_t Idx10, int8_t Idx11, int8_t Idx12, int8_t Idx13, int8_t Idx14, | |
| int8_t Idx15); | |
| void lowerShuffleVector_UsingPshufb(Variable *Dest, Operand *Src0, | |
| Operand *Src1, int8_t Idx0, int8_t Idx1, | |
| int8_t Idx2, int8_t Idx3, int8_t Idx4, | |
| int8_t Idx5, int8_t Idx6, int8_t Idx7, | |
| int8_t Idx8, int8_t Idx9, int8_t Idx10, | |
| int8_t Idx11, int8_t Idx12, int8_t Idx13, | |
| int8_t Idx14, int8_t Idx15); | |
| /// @} | |
| static FixupKind PcRelFixup; | |
| static FixupKind AbsFixup; | |
| }; | |
| template <typename TraitsType> | |
| class TargetDataX86 final : public TargetDataLowering { | |
| using Traits = TraitsType; | |
| TargetDataX86() = delete; | |
| TargetDataX86(const TargetDataX86 &) = delete; | |
| TargetDataX86 &operator=(const TargetDataX86 &) = delete; | |
| public: | |
| ~TargetDataX86() override = default; | |
| static std::unique_ptr<TargetDataLowering> create(GlobalContext *Ctx) { | |
| return makeUnique<TargetDataX86>(Ctx); | |
| } | |
| void lowerGlobals(const VariableDeclarationList &Vars, | |
| const std::string &SectionSuffix) override; | |
| void lowerConstants() override; | |
| void lowerJumpTables() override; | |
| private: | |
| ENABLE_MAKE_UNIQUE; | |
| explicit TargetDataX86(GlobalContext *Ctx) : TargetDataLowering(Ctx) {} | |
| template <typename T> static void emitConstantPool(GlobalContext *Ctx); | |
| }; | |
| class TargetHeaderX86 : public TargetHeaderLowering { | |
| TargetHeaderX86() = delete; | |
| TargetHeaderX86(const TargetHeaderX86 &) = delete; | |
| TargetHeaderX86 &operator=(const TargetHeaderX86 &) = delete; | |
| public: | |
| ~TargetHeaderX86() = default; | |
| static std::unique_ptr<TargetHeaderLowering> create(GlobalContext *Ctx) { | |
| return makeUnique<TargetHeaderX86>(Ctx); | |
| } | |
| private: | |
| ENABLE_MAKE_UNIQUE; | |
| explicit TargetHeaderX86(GlobalContext *Ctx) : TargetHeaderLowering(Ctx) {} | |
| }; | |
| } // end of namespace X86NAMESPACE | |
| } // end of namespace Ice | |
| #include "IceTargetLoweringX86BaseImpl.h" | |
| #endif // SUBZERO_SRC_ICETARGETLOWERINGX86BASE_H |