|  | //===- subzero/src/IceTargetLoweringARM32.h - ARM32 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 TargetLoweringARM32 class, which implements the | 
|  | /// TargetLowering interface for the ARM 32-bit architecture. | 
|  | /// | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #ifndef SUBZERO_SRC_ICETARGETLOWERINGARM32_H | 
|  | #define SUBZERO_SRC_ICETARGETLOWERINGARM32_H | 
|  |  | 
|  | #include "IceAssemblerARM32.h" | 
|  | #include "IceDefs.h" | 
|  | #include "IceInstARM32.h" | 
|  | #include "IceRegistersARM32.h" | 
|  | #include "IceTargetLowering.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | namespace Ice { | 
|  | namespace ARM32 { | 
|  |  | 
|  | // Class encapsulating ARM cpu features / instruction set. | 
|  | class TargetARM32Features { | 
|  | TargetARM32Features() = delete; | 
|  | TargetARM32Features(const TargetARM32Features &) = delete; | 
|  | TargetARM32Features &operator=(const TargetARM32Features &) = delete; | 
|  |  | 
|  | public: | 
|  | explicit TargetARM32Features(const ClFlags &Flags); | 
|  |  | 
|  | enum ARM32InstructionSet { | 
|  | Begin, | 
|  | // Neon is the PNaCl baseline instruction set. | 
|  | Neon = Begin, | 
|  | HWDivArm, // HW divide in ARM mode (not just Thumb mode). | 
|  | End | 
|  | }; | 
|  |  | 
|  | bool hasFeature(ARM32InstructionSet I) const { return I <= InstructionSet; } | 
|  |  | 
|  | private: | 
|  | ARM32InstructionSet InstructionSet = ARM32InstructionSet::Begin; | 
|  | }; | 
|  |  | 
|  | // The target lowering logic for ARM32. | 
|  | class TargetARM32 : public TargetLowering { | 
|  | TargetARM32() = delete; | 
|  | TargetARM32(const TargetARM32 &) = delete; | 
|  | TargetARM32 &operator=(const TargetARM32 &) = delete; | 
|  |  | 
|  | public: | 
|  | static void staticInit(GlobalContext *Ctx); | 
|  |  | 
|  | static bool shouldBePooled(const Constant *C) { | 
|  | if (auto *ConstDouble = llvm::dyn_cast<ConstantDouble>(C)) { | 
|  | return !Utils::isPositiveZero(ConstDouble->getValue()); | 
|  | } | 
|  | if (llvm::isa<ConstantFloat>(C)) | 
|  | return true; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static ::Ice::Type getPointerType() { return ::Ice::IceType_i32; } | 
|  |  | 
|  | // TODO(jvoung): return a unique_ptr. | 
|  | static std::unique_ptr<::Ice::TargetLowering> create(Cfg *Func) { | 
|  | return makeUnique<TargetARM32>(Func); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<::Ice::Assembler> createAssembler() const override { | 
|  | const bool IsNonsfi = SandboxingType == ST_Nonsfi; | 
|  | return makeUnique<ARM32::AssemblerARM32>(IsNonsfi); | 
|  | } | 
|  |  | 
|  | void initNodeForLowering(CfgNode *Node) override { | 
|  | Computations.forgetProducers(); | 
|  | Computations.recordProducers(Node); | 
|  | Computations.dump(Func); | 
|  | } | 
|  |  | 
|  | void translateOm1() override; | 
|  | void translateO2() override; | 
|  | bool doBranchOpt(Inst *I, const CfgNode *NextNode) override; | 
|  |  | 
|  | SizeT getNumRegisters() const override { return RegARM32::Reg_NUM; } | 
|  | Variable *getPhysicalRegister(RegNumT RegNum, | 
|  | Type Ty = IceType_void) override; | 
|  | const char *getRegName(RegNumT RegNum, Type Ty) const override; | 
|  | SmallBitVector getRegisterSet(RegSetMask Include, | 
|  | RegSetMask Exclude) const override; | 
|  | const SmallBitVector & | 
|  | getRegistersForVariable(const Variable *Var) const override { | 
|  | RegClass RC = Var->getRegClass(); | 
|  | switch (RC) { | 
|  | default: | 
|  | assert(RC < RC_Target); | 
|  | return TypeToRegisterSet[RC]; | 
|  | case RegARM32::RCARM32_QtoS: | 
|  | return TypeToRegisterSet[RC]; | 
|  | } | 
|  | } | 
|  | const SmallBitVector & | 
|  | getAllRegistersForVariable(const Variable *Var) const override { | 
|  | RegClass RC = Var->getRegClass(); | 
|  | assert((RegARM32::RegClassARM32)RC < RegARM32::RCARM32_NUM); | 
|  | return TypeToRegisterSetUnfiltered[RC]; | 
|  | } | 
|  | const SmallBitVector &getAliasesForRegister(RegNumT Reg) const override { | 
|  | return RegisterAliases[Reg]; | 
|  | } | 
|  | bool hasFramePointer() const override { return UsesFramePointer; } | 
|  | void setHasFramePointer() override { UsesFramePointer = true; } | 
|  | RegNumT getStackReg() const override { return RegARM32::Reg_sp; } | 
|  | RegNumT getFrameReg() const override { return RegARM32::Reg_fp; } | 
|  | RegNumT getFrameOrStackReg() const override { | 
|  | return UsesFramePointer ? getFrameReg() : getStackReg(); | 
|  | } | 
|  | RegNumT getReservedTmpReg() const { return RegARM32::Reg_ip; } | 
|  |  | 
|  | size_t typeWidthInBytesOnStack(Type Ty) const override { | 
|  | // Round up to the next multiple of 4 bytes. In particular, i1, i8, and i16 | 
|  | // are rounded up to 4 bytes. | 
|  | return (typeWidthInBytes(Ty) + 3) & ~3; | 
|  | } | 
|  | uint32_t getStackAlignment() const override; | 
|  | void reserveFixedAllocaArea(size_t Size, size_t Align) override { | 
|  | FixedAllocaSizeBytes = Size; | 
|  | assert(llvm::isPowerOf2_32(Align)); | 
|  | FixedAllocaAlignBytes = Align; | 
|  | PrologEmitsFixedAllocas = true; | 
|  | } | 
|  | int32_t getFrameFixedAllocaOffset() const override { | 
|  | return FixedAllocaSizeBytes - (SpillAreaSizeBytes - MaxOutArgsSizeBytes); | 
|  | } | 
|  | uint32_t maxOutArgsSizeBytes() const override { return MaxOutArgsSizeBytes; } | 
|  |  | 
|  | bool shouldSplitToVariable64On32(Type Ty) const override { | 
|  | return Ty == IceType_i64; | 
|  | } | 
|  |  | 
|  | // TODO(ascull): what size is best for ARM? | 
|  | SizeT getMinJumpTableSize() const override { return 3; } | 
|  | void emitJumpTable(const Cfg *Func, | 
|  | const InstJumpTable *JumpTable) const override; | 
|  |  | 
|  | void emitVariable(const Variable *Var) const override; | 
|  |  | 
|  | void emit(const ConstantUndef *C) const final; | 
|  | 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 ConstantRelocatable *C) const final; | 
|  |  | 
|  | void lowerArguments() override; | 
|  | void addProlog(CfgNode *Node) override; | 
|  | void addEpilog(CfgNode *Node) override; | 
|  |  | 
|  | Operand *loOperand(Operand *Operand); | 
|  | Operand *hiOperand(Operand *Operand); | 
|  | void finishArgumentLowering(Variable *Arg, Variable *FramePtr, | 
|  | size_t BasicFrameOffset, size_t *InArgsSizeBytes); | 
|  |  | 
|  | bool hasCPUFeature(TargetARM32Features::ARM32InstructionSet I) const { | 
|  | return CPUFeatures.hasFeature(I); | 
|  | } | 
|  |  | 
|  | enum OperandLegalization { | 
|  | Legal_Reg = 1 << 0,  /// physical register, not stack location | 
|  | Legal_Flex = 1 << 1, /// A flexible operand2, which can hold rotated small | 
|  | /// immediates, shifted registers, or modified fp imm. | 
|  | Legal_Mem = 1 << 2,  /// includes [r0, r1 lsl #2] as well as [sp, #12] | 
|  | Legal_Rematerializable = 1 << 3, | 
|  | Legal_Default = ~Legal_Rematerializable, | 
|  | }; | 
|  |  | 
|  | using LegalMask = uint32_t; | 
|  | Operand *legalizeUndef(Operand *From, RegNumT RegNum = RegNumT()); | 
|  | Operand *legalize(Operand *From, LegalMask Allowed = Legal_Default, | 
|  | RegNumT RegNum = RegNumT()); | 
|  | Variable *legalizeToReg(Operand *From, RegNumT RegNum = RegNumT()); | 
|  |  | 
|  | OperandARM32ShAmtImm *shAmtImm(uint32_t ShAmtImm) const { | 
|  | assert(ShAmtImm < 32); | 
|  | return OperandARM32ShAmtImm::create( | 
|  | Func, | 
|  | llvm::cast<ConstantInteger32>(Ctx->getConstantInt32(ShAmtImm & 0x1F))); | 
|  | } | 
|  |  | 
|  | GlobalContext *getCtx() const { return Ctx; } | 
|  |  | 
|  | protected: | 
|  | explicit TargetARM32(Cfg *Func); | 
|  |  | 
|  | void postLower() override; | 
|  |  | 
|  | enum SafeBoolChain { | 
|  | SBC_No, | 
|  | SBC_Yes, | 
|  | }; | 
|  |  | 
|  | void lowerAlloca(const InstAlloca *Instr) override; | 
|  | SafeBoolChain lowerInt1Arithmetic(const InstArithmetic *Instr); | 
|  | void lowerInt64Arithmetic(InstArithmetic::OpKind Op, Variable *Dest, | 
|  | Operand *Src0, Operand *Src1); | 
|  | void lowerArithmetic(const InstArithmetic *Instr) override; | 
|  | void lowerAssign(const InstAssign *Instr) override; | 
|  | void lowerBr(const InstBr *Instr) override; | 
|  | void lowerCall(const InstCall *Instr) override; | 
|  | void lowerCast(const InstCast *Instr) override; | 
|  | void lowerExtractElement(const InstExtractElement *Instr) override; | 
|  |  | 
|  | /// CondWhenTrue is a helper type returned by every method in the lowering | 
|  | /// that emits code to set the condition codes. | 
|  | class CondWhenTrue { | 
|  | public: | 
|  | explicit CondWhenTrue(CondARM32::Cond T0, | 
|  | CondARM32::Cond T1 = CondARM32::kNone) | 
|  | : WhenTrue0(T0), WhenTrue1(T1) { | 
|  | assert(T1 == CondARM32::kNone || T0 != CondARM32::kNone); | 
|  | assert(T1 != T0 || T0 == CondARM32::kNone); | 
|  | } | 
|  | CondARM32::Cond WhenTrue0; | 
|  | CondARM32::Cond WhenTrue1; | 
|  |  | 
|  | /// invert returns a new object with WhenTrue0 and WhenTrue1 inverted. | 
|  | CondWhenTrue invert() const { | 
|  | switch (WhenTrue0) { | 
|  | default: | 
|  | if (WhenTrue1 == CondARM32::kNone) | 
|  | return CondWhenTrue(InstARM32::getOppositeCondition(WhenTrue0)); | 
|  | return CondWhenTrue(InstARM32::getOppositeCondition(WhenTrue0), | 
|  | InstARM32::getOppositeCondition(WhenTrue1)); | 
|  | case CondARM32::AL: | 
|  | return CondWhenTrue(CondARM32::kNone); | 
|  | case CondARM32::kNone: | 
|  | return CondWhenTrue(CondARM32::AL); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | CondWhenTrue lowerFcmpCond(const InstFcmp *Instr); | 
|  | void lowerFcmp(const InstFcmp *Instr) override; | 
|  | CondWhenTrue lowerInt8AndInt16IcmpCond(InstIcmp::ICond Condition, | 
|  | Operand *Src0, Operand *Src1); | 
|  | CondWhenTrue lowerInt32IcmpCond(InstIcmp::ICond Condition, Operand *Src0, | 
|  | Operand *Src1); | 
|  | CondWhenTrue lowerInt64IcmpCond(InstIcmp::ICond Condition, Operand *Src0, | 
|  | Operand *Src1); | 
|  | CondWhenTrue lowerIcmpCond(InstIcmp::ICond Condition, Operand *Src0, | 
|  | Operand *Src1); | 
|  | CondWhenTrue lowerIcmpCond(const InstIcmp *Instr); | 
|  | void lowerIcmp(const InstIcmp *Instr) override; | 
|  | /// Emits the basic sequence for lower-linked/store-exclusive loops: | 
|  | /// | 
|  | /// retry: | 
|  | ///        ldrex tmp, [Addr] | 
|  | ///        StoreValue = Operation(tmp) | 
|  | ///        strexCond success, StoreValue, [Addr] | 
|  | ///        cmpCond success, #0 | 
|  | ///        bne retry | 
|  | /// | 
|  | /// Operation needs to return which value to strex in Addr, it must not change | 
|  | /// the flags if Cond is not AL, and must not emit any instructions that could | 
|  | /// end up writing to memory. Operation also needs to handle fake-defing for | 
|  | /// i64 handling. | 
|  | void | 
|  | lowerLoadLinkedStoreExclusive(Type Ty, Operand *Addr, | 
|  | std::function<Variable *(Variable *)> Operation, | 
|  | CondARM32::Cond Cond = CondARM32::AL); | 
|  | void lowerInt64AtomicRMW(Variable *Dest, uint32_t Operation, Operand *Ptr, | 
|  | Operand *Val); | 
|  | void lowerAtomicRMW(Variable *Dest, uint32_t Operation, Operand *Ptr, | 
|  | Operand *Val); | 
|  | void lowerBreakpoint(const InstBreakpoint *Instr) override; | 
|  | void lowerIntrinsicCall(const InstIntrinsicCall *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 prelowerPhis() override; | 
|  | uint32_t getCallStackArgumentsSizeBytes(const InstCall *Instr) override; | 
|  | void genTargetHelperCallFor(Inst *Instr) override; | 
|  | void doAddressOptLoad() override; | 
|  | void doAddressOptStore() override; | 
|  | void randomlyInsertNop(float Probability, | 
|  | RandomNumberGenerator &RNG) override; | 
|  |  | 
|  | OperandARM32Mem *formMemoryOperand(Operand *Ptr, Type Ty); | 
|  |  | 
|  | Variable64On32 *makeI64RegPair(); | 
|  | Variable *makeReg(Type Ty, RegNumT RegNum = RegNumT()); | 
|  | static Type stackSlotType(); | 
|  | Variable *copyToReg(Operand *Src, RegNumT RegNum = RegNumT()); | 
|  | void alignRegisterPow2(Variable *Reg, uint32_t Align, | 
|  | RegNumT TmpRegNum = RegNumT()); | 
|  |  | 
|  | /// Returns a vector in a register with the given constant entries. | 
|  | Variable *makeVectorOfZeros(Type Ty, RegNumT RegNum = RegNumT()); | 
|  |  | 
|  | void | 
|  | makeRandomRegisterPermutation(llvm::SmallVectorImpl<RegNumT> &Permutation, | 
|  | const SmallBitVector &ExcludeRegisters, | 
|  | uint64_t Salt) const override; | 
|  |  | 
|  | // If a divide-by-zero check is needed, inserts a: test; branch .LSKIP; trap; | 
|  | // .LSKIP: <continuation>. If no check is needed nothing is inserted. | 
|  | void div0Check(Type Ty, Operand *SrcLo, Operand *SrcHi); | 
|  | using ExtInstr = void (TargetARM32::*)(Variable *, Variable *, | 
|  | CondARM32::Cond); | 
|  | using DivInstr = void (TargetARM32::*)(Variable *, Variable *, Variable *, | 
|  | CondARM32::Cond); | 
|  | void lowerIDivRem(Variable *Dest, Variable *T, Variable *Src0R, Operand *Src1, | 
|  | ExtInstr ExtFunc, DivInstr DivFunc, bool IsRemainder); | 
|  |  | 
|  | void lowerCLZ(Variable *Dest, Variable *ValLo, Variable *ValHi); | 
|  |  | 
|  | // The following are helpers that insert lowered ARM32 instructions with | 
|  | // minimal syntactic overhead, so that the lowering code can look as close to | 
|  | // assembly as practical. | 
|  | void _add(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Add>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _adds(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | constexpr bool SetFlags = true; | 
|  | Context.insert<InstARM32Add>(Dest, Src0, Src1, Pred, SetFlags); | 
|  | if (SetFlags) { | 
|  | Context.insert<InstFakeUse>(Dest); | 
|  | } | 
|  | } | 
|  | void _adc(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Adc>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _and(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32And>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _asr(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Asr>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _bic(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Bic>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _br(CfgNode *TargetTrue, CfgNode *TargetFalse, | 
|  | CondARM32::Cond Condition) { | 
|  | Context.insert<InstARM32Br>(TargetTrue, TargetFalse, Condition); | 
|  | } | 
|  | void _br(CfgNode *Target) { Context.insert<InstARM32Br>(Target); } | 
|  | void _br(CfgNode *Target, CondARM32::Cond Condition) { | 
|  | Context.insert<InstARM32Br>(Target, Condition); | 
|  | } | 
|  | void _br(InstARM32Label *Label, CondARM32::Cond Condition) { | 
|  | Context.insert<InstARM32Br>(Label, Condition); | 
|  | } | 
|  | void _cmn(Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Cmn>(Src0, Src1, Pred); | 
|  | } | 
|  | void _cmp(Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Cmp>(Src0, Src1, Pred); | 
|  | } | 
|  | void _clz(Variable *Dest, Variable *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Clz>(Dest, Src0, Pred); | 
|  | } | 
|  | void _dmb() { Context.insert<InstARM32Dmb>(); } | 
|  | void _eor(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Eor>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | /// _ldr, for all your memory to Variable data moves. It handles all types | 
|  | /// (integer, floating point, and vectors.) Addr needs to be valid for Dest's | 
|  | /// type (e.g., no immediates for vector loads, and no index registers for fp | 
|  | /// loads.) | 
|  | void _ldr(Variable *Dest, OperandARM32Mem *Addr, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Ldr>(Dest, Addr, Pred); | 
|  | } | 
|  | InstARM32Ldrex *_ldrex(Variable *Dest, OperandARM32Mem *Addr, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | auto *Ldrex = Context.insert<InstARM32Ldrex>(Dest, Addr, Pred); | 
|  | if (auto *Dest64 = llvm::dyn_cast<Variable64On32>(Dest)) { | 
|  | Context.insert<InstFakeDef>(Dest64->getLo(), Dest); | 
|  | Context.insert<InstFakeDef>(Dest64->getHi(), Dest); | 
|  | } | 
|  | return Ldrex; | 
|  | } | 
|  | void _lsl(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Lsl>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _lsls(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | constexpr bool SetFlags = true; | 
|  | Context.insert<InstARM32Lsl>(Dest, Src0, Src1, Pred, SetFlags); | 
|  | if (SetFlags) { | 
|  | Context.insert<InstFakeUse>(Dest); | 
|  | } | 
|  | } | 
|  | void _lsr(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Lsr>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _mla(Variable *Dest, Variable *Src0, Variable *Src1, Variable *Acc, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Mla>(Dest, Src0, Src1, Acc, Pred); | 
|  | } | 
|  | void _mls(Variable *Dest, Variable *Src0, Variable *Src1, Variable *Acc, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Mls>(Dest, Src0, Src1, Acc, Pred); | 
|  | } | 
|  | /// _mov, for all your Variable to Variable data movement needs. It handles | 
|  | /// all types (integer, floating point, and vectors), as well as moves between | 
|  | /// Core and VFP registers. This is not a panacea: you must obey the (weird, | 
|  | /// confusing, non-uniform) rules for data moves in ARM. | 
|  | void _mov(Variable *Dest, Operand *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | // _mov used to be unique in the sense that it would create a temporary | 
|  | // automagically if Dest was nullptr. It won't do that anymore, so we keep | 
|  | // an assert around just in case there is some untested code path where Dest | 
|  | // is nullptr. | 
|  | assert(Dest != nullptr); | 
|  | assert(!llvm::isa<OperandARM32Mem>(Src0)); | 
|  | auto *Instr = Context.insert<InstARM32Mov>(Dest, Src0, Pred); | 
|  |  | 
|  | if (Instr->isMultiDest()) { | 
|  | // If Instr is multi-dest, then Dest must be a Variable64On32. We add a | 
|  | // fake-def for Instr.DestHi here. | 
|  | assert(llvm::isa<Variable64On32>(Dest)); | 
|  | Context.insert<InstFakeDef>(Instr->getDestHi()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _mov_redefined(Variable *Dest, Operand *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | auto *Instr = Context.insert<InstARM32Mov>(Dest, Src0, Pred); | 
|  | Instr->setDestRedefined(); | 
|  | if (Instr->isMultiDest()) { | 
|  | // If Instr is multi-dest, then Dest must be a Variable64On32. We add a | 
|  | // fake-def for Instr.DestHi here. | 
|  | assert(llvm::isa<Variable64On32>(Dest)); | 
|  | Context.insert<InstFakeDef>(Instr->getDestHi()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void _nop() { Context.insert<InstARM32Nop>(); } | 
|  |  | 
|  | // Generates a vmov instruction to extract the given index from a vector | 
|  | // register. | 
|  | void _extractelement(Variable *Dest, Variable *Src0, uint32_t Index, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Extract>(Dest, Src0, Index, Pred); | 
|  | } | 
|  |  | 
|  | // Generates a vmov instruction to insert a value into the given index of a | 
|  | // vector register. | 
|  | void _insertelement(Variable *Dest, Variable *Src0, uint32_t Index, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Insert>(Dest, Src0, Index, Pred); | 
|  | } | 
|  |  | 
|  | // -------------------------------------------------------------------------- | 
|  | // Begin bool folding machinery. | 
|  | // | 
|  | // There are three types of boolean lowerings handled by this target: | 
|  | // | 
|  | // 1) Boolean expressions leading to a boolean Variable definition | 
|  | // --------------------------------------------------------------- | 
|  | // | 
|  | // Whenever a i1 Variable is live out (i.e., its live range extends beyond | 
|  | // the defining basic block) we do not fold the operation. We instead | 
|  | // materialize (i.e., compute) the variable normally, so that it can be used | 
|  | // when needed. We also materialize i1 values that are not single use to | 
|  | // avoid code duplication. These expressions are not short circuited. | 
|  | // | 
|  | // 2) Boolean expressions leading to a select | 
|  | // ------------------------------------------ | 
|  | // | 
|  | // These include boolean chains leading to a select instruction, as well as | 
|  | // i1 Sexts. These boolean expressions are lowered to: | 
|  | // | 
|  | // mov T, <false value> | 
|  | // CC <- eval(Boolean Expression) | 
|  | // movCC T, <true value> | 
|  | // | 
|  | // For Sexts, <false value> is 0, and <true value> is -1. | 
|  | // | 
|  | // 3) Boolean expressions leading to a br i1 | 
|  | // ----------------------------------------- | 
|  | // | 
|  | // These are the boolean chains leading to a branch. These chains are | 
|  | // short-circuited, i.e.: | 
|  | // | 
|  | //   A = or i1 B, C | 
|  | //   br i1 A, label %T, label %F | 
|  | // | 
|  | // becomes | 
|  | // | 
|  | //   tst B | 
|  | //   jne %T | 
|  | //   tst B | 
|  | //   jne %T | 
|  | //   j %F | 
|  | // | 
|  | // and | 
|  | // | 
|  | //   A = and i1 B, C | 
|  | //   br i1 A, label %T, label %F | 
|  | // | 
|  | // becomes | 
|  | // | 
|  | //   tst B | 
|  | //   jeq %F | 
|  | //   tst B | 
|  | //   jeq %F | 
|  | //   j %T | 
|  | // | 
|  | // Arbitrarily long chains are short circuited, e.g | 
|  | // | 
|  | //   A = or  i1 B, C | 
|  | //   D = and i1 A, E | 
|  | //   F = and i1 G, H | 
|  | //   I = or i1 D, F | 
|  | //   br i1 I, label %True, label %False | 
|  | // | 
|  | // becomes | 
|  | // | 
|  | // Label[A]: | 
|  | //   tst B, 1 | 
|  | //   bne Label[D] | 
|  | //   tst C, 1 | 
|  | //   beq Label[I] | 
|  | // Label[D]: | 
|  | //   tst E, 1 | 
|  | //   bne %True | 
|  | // Label[I] | 
|  | //   tst G, 1 | 
|  | //   beq %False | 
|  | //   tst H, 1 | 
|  | //   beq %False (bne %True) | 
|  |  | 
|  | /// lowerInt1 materializes Boolean to a Variable. | 
|  | SafeBoolChain lowerInt1(Variable *Dest, Operand *Boolean); | 
|  |  | 
|  | /// lowerInt1ForSelect generates the following instruction sequence: | 
|  | /// | 
|  | ///   mov T, FalseValue | 
|  | ///   CC <- eval(Boolean) | 
|  | ///   movCC T, TrueValue | 
|  | ///   mov Dest, T | 
|  | /// | 
|  | /// It is used for lowering select i1, as well as i1 Sext. | 
|  | void lowerInt1ForSelect(Variable *Dest, Operand *Boolean, Operand *TrueValue, | 
|  | Operand *FalseValue); | 
|  |  | 
|  | /// LowerInt1BranchTarget is used by lowerIntForBranch. It wraps a CfgNode, or | 
|  | /// an InstARM32Label (but never both) so that, during br i1 lowering, we can | 
|  | /// create auxiliary labels for short circuiting the condition evaluation. | 
|  | class LowerInt1BranchTarget { | 
|  | public: | 
|  | explicit LowerInt1BranchTarget(CfgNode *const Target) | 
|  | : NodeTarget(Target) {} | 
|  | explicit LowerInt1BranchTarget(InstARM32Label *const Target) | 
|  | : LabelTarget(Target) {} | 
|  |  | 
|  | /// createForLabelOrDuplicate will return a new LowerInt1BranchTarget that | 
|  | /// is the exact copy of this if Label is nullptr; otherwise, the returned | 
|  | /// object will wrap Label instead. | 
|  | LowerInt1BranchTarget | 
|  | createForLabelOrDuplicate(InstARM32Label *Label) const { | 
|  | if (Label != nullptr) | 
|  | return LowerInt1BranchTarget(Label); | 
|  | if (NodeTarget) | 
|  | return LowerInt1BranchTarget(NodeTarget); | 
|  | return LowerInt1BranchTarget(LabelTarget); | 
|  | } | 
|  |  | 
|  | CfgNode *const NodeTarget = nullptr; | 
|  | InstARM32Label *const LabelTarget = nullptr; | 
|  | }; | 
|  |  | 
|  | /// LowerInt1AllowShortCircuit is a helper type used by lowerInt1ForBranch for | 
|  | /// determining which type arithmetic is allowed to be short circuited. This | 
|  | /// is useful for lowering | 
|  | /// | 
|  | ///   t1 = and i1 A, B | 
|  | ///   t2 = and i1 t1, C | 
|  | ///   br i1 t2, label %False, label %True | 
|  | /// | 
|  | /// to | 
|  | /// | 
|  | ///   tst A, 1 | 
|  | ///   beq %False | 
|  | ///   tst B, 1 | 
|  | ///   beq %False | 
|  | ///   tst C, 1 | 
|  | ///   bne %True | 
|  | ///   b %False | 
|  | /// | 
|  | /// Without this information, short circuiting would only allow to short | 
|  | /// circuit a single high level instruction. For example: | 
|  | /// | 
|  | ///   t1 = or i1 A, B | 
|  | ///   t2 = and i1 t1, C | 
|  | ///   br i1 t2, label %False, label %True | 
|  | /// | 
|  | /// cannot be lowered to | 
|  | /// | 
|  | ///   tst A, 1 | 
|  | ///   bne %True | 
|  | ///   tst B, 1 | 
|  | ///   bne %True | 
|  | ///   tst C, 1 | 
|  | ///   beq %True | 
|  | ///   b %False | 
|  | /// | 
|  | /// It needs to be lowered to | 
|  | /// | 
|  | ///   tst A, 1 | 
|  | ///   bne Aux | 
|  | ///   tst B, 1 | 
|  | ///   beq %False | 
|  | /// Aux: | 
|  | ///   tst C, 1 | 
|  | ///   bne %True | 
|  | ///   b %False | 
|  | /// | 
|  | /// TODO(jpp): evaluate if this kind of short circuiting hurts performance (it | 
|  | /// might.) | 
|  | enum LowerInt1AllowShortCircuit { | 
|  | SC_And = 1, | 
|  | SC_Or = 2, | 
|  | SC_All = SC_And | SC_Or, | 
|  | }; | 
|  |  | 
|  | /// ShortCircuitCondAndLabel wraps the condition codes that should be used | 
|  | /// after a lowerInt1ForBranch returns to branch to the | 
|  | /// TrueTarget/FalseTarget. If ShortCircuitLabel is not nullptr, then the | 
|  | /// called lowerInt1forBranch created an internal (i.e., short-circuit) label | 
|  | /// used for short circuiting. | 
|  | class ShortCircuitCondAndLabel { | 
|  | public: | 
|  | explicit ShortCircuitCondAndLabel(CondWhenTrue &&C, | 
|  | InstARM32Label *L = nullptr) | 
|  | : Cond(std::move(C)), ShortCircuitTarget(L) {} | 
|  | const CondWhenTrue Cond; | 
|  | InstARM32Label *const ShortCircuitTarget; | 
|  |  | 
|  | CondWhenTrue assertNoLabelAndReturnCond() const { | 
|  | assert(ShortCircuitTarget == nullptr); | 
|  | return Cond; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// lowerInt1ForBranch expands Boolean, and returns the condition codes that | 
|  | /// are to be used for branching to the branch's TrueTarget. It may return a | 
|  | /// label that the expansion of Boolean used to short circuit the chain's | 
|  | /// evaluation. | 
|  | ShortCircuitCondAndLabel | 
|  | lowerInt1ForBranch(Operand *Boolean, const LowerInt1BranchTarget &TargetTrue, | 
|  | const LowerInt1BranchTarget &TargetFalse, | 
|  | uint32_t ShortCircuitable); | 
|  |  | 
|  | // _br is a convenience wrapper that emits br instructions to Target. | 
|  | void _br(const LowerInt1BranchTarget &BrTarget, | 
|  | CondARM32::Cond Cond = CondARM32::AL) { | 
|  | assert((BrTarget.NodeTarget == nullptr) != | 
|  | (BrTarget.LabelTarget == nullptr)); | 
|  | if (BrTarget.NodeTarget != nullptr) | 
|  | _br(BrTarget.NodeTarget, Cond); | 
|  | else | 
|  | _br(BrTarget.LabelTarget, Cond); | 
|  | } | 
|  |  | 
|  | // _br_short_circuit is used when lowering InstArithmetic::And and | 
|  | // InstArithmetic::Or and a short circuit branch is needed. | 
|  | void _br_short_circuit(const LowerInt1BranchTarget &Target, | 
|  | const CondWhenTrue &Cond) { | 
|  | if (Cond.WhenTrue1 != CondARM32::kNone) { | 
|  | _br(Target, Cond.WhenTrue1); | 
|  | } | 
|  | if (Cond.WhenTrue0 != CondARM32::kNone) { | 
|  | _br(Target, Cond.WhenTrue0); | 
|  | } | 
|  | } | 
|  | // End of bool folding machinery | 
|  | // -------------------------------------------------------------------------- | 
|  |  | 
|  | /// The Operand can only be a 16-bit immediate or a ConstantRelocatable (with | 
|  | /// an upper16 relocation). | 
|  | void _movt(Variable *Dest, Operand *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Movt>(Dest, Src0, Pred); | 
|  | } | 
|  | void _movw(Variable *Dest, Operand *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Movw>(Dest, Src0, Pred); | 
|  | } | 
|  | void _mul(Variable *Dest, Variable *Src0, Variable *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Mul>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _mvn(Variable *Dest, Operand *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Mvn>(Dest, Src0, Pred); | 
|  | } | 
|  | void _orr(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Orr>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _orrs(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | constexpr bool SetFlags = true; | 
|  | Context.insert<InstARM32Orr>(Dest, Src0, Src1, Pred, SetFlags); | 
|  | if (SetFlags) { | 
|  | Context.insert<InstFakeUse>(Dest); | 
|  | } | 
|  | } | 
|  | void _push(const VarList &Sources) { Context.insert<InstARM32Push>(Sources); } | 
|  | void _pop(const VarList &Dests) { | 
|  | Context.insert<InstARM32Pop>(Dests); | 
|  | // Mark dests as modified. | 
|  | for (Variable *Dest : Dests) | 
|  | Context.insert<InstFakeDef>(Dest); | 
|  | } | 
|  | void _rbit(Variable *Dest, Variable *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Rbit>(Dest, Src0, Pred); | 
|  | } | 
|  | void _rev(Variable *Dest, Variable *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Rev>(Dest, Src0, Pred); | 
|  | } | 
|  | void _ret(Variable *LR, Variable *Src0 = nullptr) { | 
|  | Context.insert<InstARM32Ret>(LR, Src0); | 
|  | } | 
|  | void _rscs(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | constexpr bool SetFlags = true; | 
|  | Context.insert<InstARM32Rsc>(Dest, Src0, Src1, Pred, SetFlags); | 
|  | if (SetFlags) { | 
|  | Context.insert<InstFakeUse>(Dest); | 
|  | } | 
|  | } | 
|  | void _rsc(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Rsc>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _rsbs(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | constexpr bool SetFlags = true; | 
|  | Context.insert<InstARM32Rsb>(Dest, Src0, Src1, Pred, SetFlags); | 
|  | if (SetFlags) { | 
|  | Context.insert<InstFakeUse>(Dest); | 
|  | } | 
|  | } | 
|  | void _rsb(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Rsb>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _sbc(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Sbc>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _sbcs(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | constexpr bool SetFlags = true; | 
|  | Context.insert<InstARM32Sbc>(Dest, Src0, Src1, Pred, SetFlags); | 
|  | if (SetFlags) { | 
|  | Context.insert<InstFakeUse>(Dest); | 
|  | } | 
|  | } | 
|  | void _sdiv(Variable *Dest, Variable *Src0, Variable *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Sdiv>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | /// _str, for all your Variable to memory transfers. Addr has the same | 
|  | /// restrictions that it does in _ldr. | 
|  | void _str(Variable *Value, OperandARM32Mem *Addr, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Str>(Value, Addr, Pred); | 
|  | } | 
|  | InstARM32Strex *_strex(Variable *Dest, Variable *Value, OperandARM32Mem *Addr, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | if (auto *Value64 = llvm::dyn_cast<Variable64On32>(Value)) { | 
|  | Context.insert<InstFakeUse>(Value64->getLo()); | 
|  | Context.insert<InstFakeUse>(Value64->getHi()); | 
|  | } | 
|  | return Context.insert<InstARM32Strex>(Dest, Value, Addr, Pred); | 
|  | } | 
|  | void _sub(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Sub>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _subs(Variable *Dest, Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | constexpr bool SetFlags = true; | 
|  | Context.insert<InstARM32Sub>(Dest, Src0, Src1, Pred, SetFlags); | 
|  | if (SetFlags) { | 
|  | Context.insert<InstFakeUse>(Dest); | 
|  | } | 
|  | } | 
|  | void _sxt(Variable *Dest, Variable *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Sxt>(Dest, Src0, Pred); | 
|  | } | 
|  | void _tst(Variable *Src0, Operand *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Tst>(Src0, Src1, Pred); | 
|  | } | 
|  | void _trap() { Context.insert<InstARM32Trap>(); } | 
|  | void _udiv(Variable *Dest, Variable *Src0, Variable *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Udiv>(Dest, Src0, Src1, Pred); | 
|  | } | 
|  | void _umull(Variable *DestLo, Variable *DestHi, Variable *Src0, | 
|  | Variable *Src1, CondARM32::Cond Pred = CondARM32::AL) { | 
|  | // umull requires DestLo and DestHi to be assigned to different GPRs. The | 
|  | // following lines create overlapping liveness ranges for both variables. If | 
|  | // either one of them is live, then they are both going to be live, and thus | 
|  | // assigned to different registers; if they are both dead, then DCE will | 
|  | // kick in and delete the following three instructions. | 
|  | Context.insert<InstFakeDef>(DestHi); | 
|  | Context.insert<InstARM32Umull>(DestLo, DestHi, Src0, Src1, Pred); | 
|  | Context.insert<InstFakeDef>(DestHi, DestLo)->setDestRedefined(); | 
|  | Context.insert<InstFakeUse>(DestHi); | 
|  | } | 
|  | void _uxt(Variable *Dest, Variable *Src0, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Uxt>(Dest, Src0, Pred); | 
|  | } | 
|  | void _vabs(Variable *Dest, Variable *Src, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vabs>(Dest, Src, Pred); | 
|  | } | 
|  | void _vadd(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vadd>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vand(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vand>(Dest, Src0, Src1); | 
|  | } | 
|  | InstARM32Vbsl *_vbsl(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | return Context.insert<InstARM32Vbsl>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vceq(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vceq>(Dest, Src0, Src1); | 
|  | } | 
|  | InstARM32Vcge *_vcge(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | return Context.insert<InstARM32Vcge>(Dest, Src0, Src1); | 
|  | } | 
|  | InstARM32Vcgt *_vcgt(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | return Context.insert<InstARM32Vcgt>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vcvt(Variable *Dest, Variable *Src, InstARM32Vcvt::VcvtVariant Variant, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vcvt>(Dest, Src, Variant, Pred); | 
|  | } | 
|  | void _vdiv(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vdiv>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vcmp(Variable *Src0, Variable *Src1, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vcmp>(Src0, Src1, Pred); | 
|  | } | 
|  | void _vcmp(Variable *Src0, OperandARM32FlexFpZero *FpZero, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vcmp>(Src0, FpZero, Pred); | 
|  | } | 
|  | void _vdup(Variable *Dest, Variable *Src, int Idx) { | 
|  | Context.insert<InstARM32Vdup>(Dest, Src, Idx); | 
|  | } | 
|  | void _veor(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Veor>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vldr1d(Variable *Dest, OperandARM32Mem *Addr, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vldr1d>(Dest, Addr, Pred); | 
|  | } | 
|  | void _vldr1q(Variable *Dest, OperandARM32Mem *Addr, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vldr1q>(Dest, Addr, Pred); | 
|  | } | 
|  | void _vmrs(CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vmrs>(Pred); | 
|  | } | 
|  | void _vmla(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vmla>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vmlap(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vmlap>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vmls(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vmls>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vmovl(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vmovl>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vmovh(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vmovh>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vmovhl(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vmovhl>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vmovlh(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vmovlh>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vmul(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vmul>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vmulh(Variable *Dest, Variable *Src0, Variable *Src1, bool Unsigned) { | 
|  | Context.insert<InstARM32Vmulh>(Dest, Src0, Src1) | 
|  | ->setSignType(Unsigned ? InstARM32::FS_Unsigned : InstARM32::FS_Signed); | 
|  | } | 
|  | void _vmvn(Variable *Dest, Variable *Src0) { | 
|  | Context.insert<InstARM32Vmvn>(Dest, Src0, CondARM32::AL); | 
|  | } | 
|  | void _vneg(Variable *Dest, Variable *Src0) { | 
|  | Context.insert<InstARM32Vneg>(Dest, Src0, CondARM32::AL) | 
|  | ->setSignType(InstARM32::FS_Signed); | 
|  | } | 
|  | void _vorr(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vorr>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vqadd(Variable *Dest, Variable *Src0, Variable *Src1, bool Unsigned) { | 
|  | Context.insert<InstARM32Vqadd>(Dest, Src0, Src1) | 
|  | ->setSignType(Unsigned ? InstARM32::FS_Unsigned : InstARM32::FS_Signed); | 
|  | } | 
|  | void _vqmovn2(Variable *Dest, Variable *Src0, Variable *Src1, bool Unsigned, | 
|  | bool Saturating) { | 
|  | Context.insert<InstARM32Vqmovn2>(Dest, Src0, Src1) | 
|  | ->setSignType(Saturating ? (Unsigned ? InstARM32::FS_Unsigned | 
|  | : InstARM32::FS_Signed) | 
|  | : InstARM32::FS_None); | 
|  | } | 
|  | void _vqsub(Variable *Dest, Variable *Src0, Variable *Src1, bool Unsigned) { | 
|  | Context.insert<InstARM32Vqsub>(Dest, Src0, Src1) | 
|  | ->setSignType(Unsigned ? InstARM32::FS_Unsigned : InstARM32::FS_Signed); | 
|  | } | 
|  | InstARM32Vshl *_vshl(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | return Context.insert<InstARM32Vshl>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vshl(Variable *Dest, Variable *Src0, ConstantInteger32 *Src1) { | 
|  | Context.insert<InstARM32Vshl>(Dest, Src0, Src1) | 
|  | ->setSignType(InstARM32::FS_Unsigned); | 
|  | } | 
|  | InstARM32Vshr *_vshr(Variable *Dest, Variable *Src0, | 
|  | ConstantInteger32 *Src1) { | 
|  | return Context.insert<InstARM32Vshr>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vsqrt(Variable *Dest, Variable *Src, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vsqrt>(Dest, Src, Pred); | 
|  | } | 
|  | void _vstr1d(Variable *Value, OperandARM32Mem *Addr, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vstr1>(Value, Addr, Pred, 32); | 
|  | } | 
|  | void _vstr1q(Variable *Value, OperandARM32Mem *Addr, | 
|  | CondARM32::Cond Pred = CondARM32::AL) { | 
|  | Context.insert<InstARM32Vstr1>(Value, Addr, Pred, 64); | 
|  | } | 
|  | void _vsub(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vsub>(Dest, Src0, Src1); | 
|  | } | 
|  | void _vzip(Variable *Dest, Variable *Src0, Variable *Src1) { | 
|  | Context.insert<InstARM32Vzip>(Dest, Src0, Src1); | 
|  | } | 
|  |  | 
|  | // Iterates over the CFG and determines the maximum outgoing stack arguments | 
|  | // bytes. This information is later used during addProlog() to pre-allocate | 
|  | // the outargs area. | 
|  | // TODO(jpp): This could live in the Parser, if we provided a Target-specific | 
|  | // method that the Parser could call. | 
|  | void findMaxStackOutArgsSize(); | 
|  |  | 
|  | /// Returns true if the given Offset can be represented in a Load/Store Mem | 
|  | /// Operand. | 
|  | bool isLegalMemOffset(Type Ty, int32_t Offset) const; | 
|  |  | 
|  | void postLowerLegalization(); | 
|  |  | 
|  | /// Manages the GotPtr variable, which is used for Nonsfi sandboxing. | 
|  | /// @{ | 
|  | void createGotPtr(); | 
|  | void insertGotPtrInitPlaceholder(); | 
|  | VariableDeclaration *createGotRelocation(RelocOffset *AddPcReloc); | 
|  | void materializeGotAddr(CfgNode *Node); | 
|  | Variable *GotPtr = nullptr; | 
|  | // TODO(jpp): use CfgLocalAllocator. | 
|  | /// @} | 
|  |  | 
|  | /// Manages the Gotoff relocations created during the function lowering. A | 
|  | /// single Gotoff relocation is created for each global variable used by the | 
|  | /// function being lowered. | 
|  | /// @{ | 
|  | // TODO(jpp): if the same global G is used in different functions, then this | 
|  | // method will emit one G(gotoff) relocation per function. | 
|  | GlobalString createGotoffRelocation(const ConstantRelocatable *CR); | 
|  | CfgUnorderedSet<GlobalString> KnownGotoffs; | 
|  | /// @} | 
|  |  | 
|  | /// Loads the constant relocatable Name to Register. Then invoke Finish to | 
|  | /// finish the relocatable lowering. Finish **must** use PC in its first | 
|  | /// emitted instruction, or the relocatable in Register will contain the wrong | 
|  | /// value. | 
|  | // | 
|  | // Lowered sequence: | 
|  | // | 
|  | // Movw: | 
|  | //     movw Register, #:lower16:Name - (End - Movw) - 8 . | 
|  | // Movt: | 
|  | //     movt Register, #:upper16:Name - (End - Movt) - 8 . | 
|  | //     PC = fake-def | 
|  | // End: | 
|  | //     Finish(PC) | 
|  | // | 
|  | // The -8 in movw/movt above is to account for the PC value that the first | 
|  | // instruction emitted by Finish(PC) will read. | 
|  | void | 
|  | loadNamedConstantRelocatablePIC(GlobalString Name, Variable *Register, | 
|  | std::function<void(Variable *PC)> Finish); | 
|  |  | 
|  | /// Sandboxer defines methods for ensuring that "dangerous" operations are | 
|  | /// masked during sandboxed code emission. For regular, non-sandboxed code | 
|  | /// emission, its methods are simple pass-through methods. | 
|  | /// | 
|  | /// The Sandboxer also emits BundleLock/BundleUnlock pseudo-instructions | 
|  | /// in the constructor/destructor during sandboxed code emission. Therefore, | 
|  | /// it is a bad idea to create an object of this type and "keep it around." | 
|  | /// The recommended usage is: | 
|  | /// | 
|  | /// AutoSandboxing(this).<<operation>>(...); | 
|  | /// | 
|  | /// This usage ensures that no other instructions are inadvertently added to | 
|  | /// the bundle. | 
|  | class Sandboxer { | 
|  | Sandboxer() = delete; | 
|  | Sandboxer(const Sandboxer &) = delete; | 
|  | Sandboxer &operator=(const Sandboxer &) = delete; | 
|  |  | 
|  | public: | 
|  | explicit Sandboxer( | 
|  | TargetARM32 *Target, | 
|  | InstBundleLock::Option BundleOption = InstBundleLock::Opt_None); | 
|  | ~Sandboxer(); | 
|  |  | 
|  | /// Increments sp: | 
|  | /// | 
|  | ///   add sp, sp, AddAmount | 
|  | ///   bic sp, sp, 0xc0000000 | 
|  | /// | 
|  | /// (for the rationale, see the ARM 32-bit Sandbox Specification.) | 
|  | void add_sp(Operand *AddAmount); | 
|  |  | 
|  | /// Emits code to align sp to the specified alignment: | 
|  | /// | 
|  | ///   bic/and sp, sp, Alignment | 
|  | ///   bic, sp, sp, 0xc0000000 | 
|  | void align_sp(size_t Alignment); | 
|  |  | 
|  | /// Emits a call instruction. If CallTarget is a Variable, it emits | 
|  | /// | 
|  | ///   bic CallTarget, CallTarget, 0xc000000f | 
|  | ///   bl CallTarget | 
|  | /// | 
|  | /// Otherwise, it emits | 
|  | /// | 
|  | ///   bl CallTarget | 
|  | /// | 
|  | /// Note: in sandboxed code calls are always emitted in addresses 12 mod 16. | 
|  | InstARM32Call *bl(Variable *ReturnReg, Operand *CallTarget); | 
|  |  | 
|  | /// Emits a load: | 
|  | /// | 
|  | ///   bic rBase, rBase, 0xc0000000 | 
|  | ///   ldr rDest, [rBase, #Offset] | 
|  | /// | 
|  | /// Exception: if rBase is r9 or sp, then the load is emitted as: | 
|  | /// | 
|  | ///   ldr rDest, [rBase, #Offset] | 
|  | /// | 
|  | /// because the NaCl ARM 32-bit Sandbox Specification guarantees they are | 
|  | /// always valid. | 
|  | void ldr(Variable *Dest, OperandARM32Mem *Mem, CondARM32::Cond Pred); | 
|  |  | 
|  | /// Emits a load exclusive: | 
|  | /// | 
|  | ///   bic rBase, rBase, 0xc0000000 | 
|  | ///   ldrex rDest, [rBase] | 
|  | /// | 
|  | /// Exception: if rBase is r9 or sp, then the load is emitted as: | 
|  | /// | 
|  | ///   ldrex rDest, [rBase] | 
|  | /// | 
|  | /// because the NaCl ARM 32-bit Sandbox Specification guarantees they are | 
|  | /// always valid. | 
|  | void ldrex(Variable *Dest, OperandARM32Mem *Mem, CondARM32::Cond Pred); | 
|  |  | 
|  | /// Resets sp to Src: | 
|  | /// | 
|  | ///   mov sp, Src | 
|  | ///   bic sp, sp, 0xc0000000 | 
|  | void reset_sp(Variable *Src); | 
|  |  | 
|  | /// Emits code to return from a function: | 
|  | /// | 
|  | ///   bic lr, lr, 0xc000000f | 
|  | ///   bx lr | 
|  | void ret(Variable *RetAddr, Variable *RetValue); | 
|  |  | 
|  | /// Emits a store: | 
|  | /// | 
|  | ///   bic rBase, rBase, 0xc0000000 | 
|  | ///   str rSrc, [rBase, #Offset] | 
|  | /// | 
|  | /// Exception: if rBase is r9 or sp, then the store is emitted as: | 
|  | /// | 
|  | ///   str rDest, [rBase, #Offset] | 
|  | /// | 
|  | /// because the NaCl ARM 32-bit Sandbox Specification guarantees they are | 
|  | /// always valid. | 
|  | void str(Variable *Src, OperandARM32Mem *Mem, CondARM32::Cond Pred); | 
|  |  | 
|  | /// Emits a store exclusive: | 
|  | /// | 
|  | ///   bic rBase, rBase, 0xc0000000 | 
|  | ///   strex rDest, rSrc, [rBase] | 
|  | /// | 
|  | /// Exception: if rBase is r9 or sp, then the store is emitted as: | 
|  | /// | 
|  | ///   strex rDest, rSrc, [rBase] | 
|  | /// | 
|  | /// because the NaCl ARM 32-bit Sandbox Specification guarantees they are | 
|  | /// always valid. | 
|  | void strex(Variable *Dest, Variable *Src, OperandARM32Mem *Mem, | 
|  | CondARM32::Cond Pred); | 
|  |  | 
|  | /// Decrements sp: | 
|  | /// | 
|  | ///   sub sp, sp, SubAmount | 
|  | ///   bic sp, sp, 0xc0000000 | 
|  | void sub_sp(Operand *SubAmount); | 
|  |  | 
|  | private: | 
|  | TargetARM32 *const Target; | 
|  | const InstBundleLock::Option BundleOption; | 
|  | std::unique_ptr<AutoBundle> Bundler; | 
|  |  | 
|  | void createAutoBundle(); | 
|  | }; | 
|  |  | 
|  | class PostLoweringLegalizer { | 
|  | PostLoweringLegalizer() = delete; | 
|  | PostLoweringLegalizer(const PostLoweringLegalizer &) = delete; | 
|  | PostLoweringLegalizer &operator=(const PostLoweringLegalizer &) = delete; | 
|  |  | 
|  | public: | 
|  | explicit PostLoweringLegalizer(TargetARM32 *Target) | 
|  | : Target(Target), StackOrFrameReg(Target->getPhysicalRegister( | 
|  | Target->getFrameOrStackReg())) {} | 
|  |  | 
|  | void resetTempBaseIfClobberedBy(const Inst *Instr); | 
|  |  | 
|  | // Ensures that the TempBase register held by the this legalizer (if any) is | 
|  | // assigned to IP. | 
|  | void assertNoTempOrAssignedToIP() const { | 
|  | assert(TempBaseReg == nullptr || | 
|  | TempBaseReg->getRegNum() == Target->getReservedTmpReg()); | 
|  | } | 
|  |  | 
|  | // Legalizes Mem. if Mem.Base is a Reamaterializable variable, Mem.Offset is | 
|  | // fixed up. | 
|  | OperandARM32Mem *legalizeMemOperand(OperandARM32Mem *Mem, | 
|  | bool AllowOffsets = true); | 
|  |  | 
|  | /// Legalizes Mov if its Source (or Destination) is a spilled Variable, or | 
|  | /// if its Source is a Rematerializable variable (this form is used in lieu | 
|  | /// of lea, which is not available in ARM.) | 
|  | /// | 
|  | /// Moves to memory become store instructions, and moves from memory, loads. | 
|  | void legalizeMov(InstARM32Mov *Mov); | 
|  |  | 
|  | private: | 
|  | /// Creates a new Base register centered around [Base, +/- Offset]. | 
|  | Variable *newBaseRegister(Variable *Base, int32_t Offset, | 
|  | RegNumT ScratchRegNum); | 
|  |  | 
|  | /// Creates a new, legal OperandARM32Mem for accessing Base + Offset. | 
|  | /// The returned mem operand is a legal operand for accessing memory that is | 
|  | /// of type Ty. | 
|  | /// | 
|  | /// If [Base, #Offset] is encodable, then the method returns a Mem operand | 
|  | /// expressing it. Otherwise, | 
|  | /// | 
|  | /// if [TempBaseReg, #Offset-TempBaseOffset] is a valid memory operand, the | 
|  | /// method will return that. Otherwise, | 
|  | /// | 
|  | /// a new base register ip=Base+Offset is created, and the method returns a | 
|  | /// memory operand expressing [ip, #0]. | 
|  | OperandARM32Mem *createMemOperand(Type Ty, Variable *Base, int32_t Offset, | 
|  | bool AllowOffsets = true); | 
|  | TargetARM32 *const Target; | 
|  | Variable *const StackOrFrameReg; | 
|  | Variable *TempBaseReg = nullptr; | 
|  | int32_t TempBaseOffset = 0; | 
|  | }; | 
|  |  | 
|  | const bool NeedSandboxing; | 
|  | TargetARM32Features CPUFeatures; | 
|  | bool UsesFramePointer = false; | 
|  | bool NeedsStackAlignment = false; | 
|  | bool MaybeLeafFunc = true; | 
|  | size_t SpillAreaSizeBytes = 0; | 
|  | size_t FixedAllocaSizeBytes = 0; | 
|  | size_t FixedAllocaAlignBytes = 0; | 
|  | bool PrologEmitsFixedAllocas = false; | 
|  | uint32_t MaxOutArgsSizeBytes = 0; | 
|  | // TODO(jpp): std::array instead of array. | 
|  | static SmallBitVector TypeToRegisterSet[RegARM32::RCARM32_NUM]; | 
|  | static SmallBitVector TypeToRegisterSetUnfiltered[RegARM32::RCARM32_NUM]; | 
|  | static SmallBitVector RegisterAliases[RegARM32::Reg_NUM]; | 
|  | SmallBitVector RegsUsed; | 
|  | VarList PhysicalRegisters[IceType_NUM]; | 
|  | VarList PreservedGPRs; | 
|  | VarList PreservedSRegs; | 
|  |  | 
|  | /// Helper class that understands the Calling Convention and register | 
|  | /// assignments. The first few integer type parameters can use r0-r3, | 
|  | /// regardless of their position relative to the floating-point/vector | 
|  | /// arguments in the argument list. Floating-point and vector arguments | 
|  | /// can use q0-q3 (aka d0-d7, s0-s15). For more information on the topic, | 
|  | /// see the ARM Architecture Procedure Calling Standards (AAPCS). | 
|  | /// | 
|  | /// Technically, arguments that can start with registers but extend beyond the | 
|  | /// available registers can be split between the registers and the stack. | 
|  | /// However, this is typically  for passing GPR structs by value, and PNaCl | 
|  | /// transforms expand this out. | 
|  | /// | 
|  | /// At (public) function entry, the stack must be 8-byte aligned. | 
|  | class CallingConv { | 
|  | CallingConv(const CallingConv &) = delete; | 
|  | CallingConv &operator=(const CallingConv &) = delete; | 
|  |  | 
|  | public: | 
|  | CallingConv(); | 
|  | ~CallingConv() = default; | 
|  |  | 
|  | /// argInGPR returns true if there is a GPR available for the requested | 
|  | /// type, and false otherwise. If it returns true, Reg is set to the | 
|  | /// appropriate register number. Note that, when Ty == IceType_i64, Reg will | 
|  | /// be an I64 register pair. | 
|  | bool argInGPR(Type Ty, RegNumT *Reg); | 
|  |  | 
|  | /// argInVFP is to floating-point/vector types what argInGPR is for integer | 
|  | /// types. | 
|  | bool argInVFP(Type Ty, RegNumT *Reg); | 
|  |  | 
|  | private: | 
|  | void discardUnavailableGPRsAndTheirAliases(CfgVector<RegNumT> *Regs); | 
|  | SmallBitVector GPRegsUsed; | 
|  | CfgVector<RegNumT> GPRArgs; | 
|  | CfgVector<RegNumT> I64Args; | 
|  |  | 
|  | void discardUnavailableVFPRegs(CfgVector<RegNumT> *Regs); | 
|  | SmallBitVector VFPRegsUsed; | 
|  | CfgVector<RegNumT> FP32Args; | 
|  | CfgVector<RegNumT> FP64Args; | 
|  | CfgVector<RegNumT> Vec128Args; | 
|  | }; | 
|  |  | 
|  | private: | 
|  | ENABLE_MAKE_UNIQUE; | 
|  |  | 
|  | OperandARM32Mem *formAddressingMode(Type Ty, Cfg *Func, const Inst *LdSt, | 
|  | Operand *Base); | 
|  |  | 
|  | void postambleCtpop64(const InstCall *Instr); | 
|  | void preambleDivRem(const InstCall *Instr); | 
|  | CfgUnorderedMap<Operand *, void (TargetARM32::*)(const InstCall *Instr)> | 
|  | ARM32HelpersPreamble; | 
|  | CfgUnorderedMap<Operand *, void (TargetARM32::*)(const InstCall *Instr)> | 
|  | ARM32HelpersPostamble; | 
|  |  | 
|  | class ComputationTracker { | 
|  | public: | 
|  | ComputationTracker() = default; | 
|  | ~ComputationTracker() = default; | 
|  |  | 
|  | void forgetProducers() { KnownComputations.clear(); } | 
|  | void recordProducers(CfgNode *Node); | 
|  |  | 
|  | const Inst *getProducerOf(const Operand *Opnd) const { | 
|  | auto *Var = llvm::dyn_cast<Variable>(Opnd); | 
|  | if (Var == nullptr) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | auto Iter = KnownComputations.find(Var->getIndex()); | 
|  | if (Iter == KnownComputations.end()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return Iter->second.Instr; | 
|  | } | 
|  |  | 
|  | void dump(const Cfg *Func) const { | 
|  | if (!BuildDefs::dump() || !Func->isVerbose(IceV_Folding)) | 
|  | return; | 
|  | OstreamLocker L(Func->getContext()); | 
|  | Ostream &Str = Func->getContext()->getStrDump(); | 
|  | Str << "foldable producer:\n"; | 
|  | for (const auto &Computation : KnownComputations) { | 
|  | Str << "    "; | 
|  | Computation.second.Instr->dump(Func); | 
|  | Str << "\n"; | 
|  | } | 
|  | Str << "\n"; | 
|  | } | 
|  |  | 
|  | private: | 
|  | class ComputationEntry { | 
|  | public: | 
|  | ComputationEntry(Inst *I, Type Ty) : Instr(I), ComputationType(Ty) {} | 
|  | Inst *const Instr; | 
|  | // Boolean folding is disabled for variables whose live range is multi | 
|  | // block. We conservatively initialize IsLiveOut to true, and set it to | 
|  | // false once we find the end of the live range for the variable defined | 
|  | // by this instruction. If liveness analysis is not performed (e.g., in | 
|  | // Om1 mode) IsLiveOut will never be set to false, and folding will be | 
|  | // disabled. | 
|  | bool IsLiveOut = true; | 
|  | int32_t NumUses = 0; | 
|  | Type ComputationType; | 
|  | }; | 
|  |  | 
|  | // ComputationMap maps a Variable number to a payload identifying which | 
|  | // instruction defined it. | 
|  | using ComputationMap = CfgUnorderedMap<SizeT, ComputationEntry>; | 
|  | ComputationMap KnownComputations; | 
|  | }; | 
|  |  | 
|  | ComputationTracker Computations; | 
|  |  | 
|  | // AllowTemporaryWithNoReg indicates if TargetARM32::makeReg() can be invoked | 
|  | // without specifying a physical register. This is needed for creating unbound | 
|  | // temporaries during Ice -> ARM lowering, but before register allocation. | 
|  | // This a safe-guard that no unbound temporaries are created during the | 
|  | // legalization post-passes. | 
|  | bool AllowTemporaryWithNoReg = true; | 
|  | // ForbidTemporaryWithoutReg is a RAII class that manages | 
|  | // AllowTemporaryWithNoReg. | 
|  | class ForbidTemporaryWithoutReg { | 
|  | ForbidTemporaryWithoutReg() = delete; | 
|  | ForbidTemporaryWithoutReg(const ForbidTemporaryWithoutReg &) = delete; | 
|  | ForbidTemporaryWithoutReg & | 
|  | operator=(const ForbidTemporaryWithoutReg &) = delete; | 
|  |  | 
|  | public: | 
|  | explicit ForbidTemporaryWithoutReg(TargetARM32 *Target) : Target(Target) { | 
|  | Target->AllowTemporaryWithNoReg = false; | 
|  | } | 
|  | ~ForbidTemporaryWithoutReg() { Target->AllowTemporaryWithNoReg = true; } | 
|  |  | 
|  | private: | 
|  | TargetARM32 *const Target; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | class TargetDataARM32 final : public TargetDataLowering { | 
|  | TargetDataARM32() = delete; | 
|  | TargetDataARM32(const TargetDataARM32 &) = delete; | 
|  | TargetDataARM32 &operator=(const TargetDataARM32 &) = delete; | 
|  |  | 
|  | public: | 
|  | static std::unique_ptr<TargetDataLowering> create(GlobalContext *Ctx) { | 
|  | return std::unique_ptr<TargetDataLowering>(new TargetDataARM32(Ctx)); | 
|  | } | 
|  |  | 
|  | void lowerGlobals(const VariableDeclarationList &Vars, | 
|  | const std::string &SectionSuffix) override; | 
|  | void lowerConstants() override; | 
|  | void lowerJumpTables() override; | 
|  |  | 
|  | protected: | 
|  | explicit TargetDataARM32(GlobalContext *Ctx); | 
|  |  | 
|  | private: | 
|  | ~TargetDataARM32() override = default; | 
|  | }; | 
|  |  | 
|  | class TargetHeaderARM32 final : public TargetHeaderLowering { | 
|  | TargetHeaderARM32() = delete; | 
|  | TargetHeaderARM32(const TargetHeaderARM32 &) = delete; | 
|  | TargetHeaderARM32 &operator=(const TargetHeaderARM32 &) = delete; | 
|  |  | 
|  | public: | 
|  | static std::unique_ptr<TargetHeaderLowering> create(GlobalContext *Ctx) { | 
|  | return std::unique_ptr<TargetHeaderLowering>(new TargetHeaderARM32(Ctx)); | 
|  | } | 
|  |  | 
|  | void lower() override; | 
|  |  | 
|  | protected: | 
|  | explicit TargetHeaderARM32(GlobalContext *Ctx); | 
|  |  | 
|  | private: | 
|  | ~TargetHeaderARM32() = default; | 
|  |  | 
|  | TargetARM32Features CPUFeatures; | 
|  | }; | 
|  |  | 
|  | } // end of namespace ARM32 | 
|  | } // end of namespace Ice | 
|  |  | 
|  | #endif // SUBZERO_SRC_ICETARGETLOWERINGARM32_H |