| //===- subzero/src/IceInstARM32.cpp - ARM32 instruction implementation ----===// | 
 | // | 
 | //                        The Subzero Code Generator | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | /// | 
 | /// \file | 
 | /// \brief Implements the InstARM32 and OperandARM32 classes, primarily the | 
 | /// constructors and the dump()/emit() methods. | 
 | /// | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "IceInstARM32.h" | 
 |  | 
 | #include "IceAssemblerARM32.h" | 
 | #include "IceCfg.h" | 
 | #include "IceCfgNode.h" | 
 | #include "IceInst.h" | 
 | #include "IceOperand.h" | 
 | #include "IceTargetLoweringARM32.h" | 
 |  | 
 | namespace Ice { | 
 | namespace ARM32 { | 
 |  | 
 | namespace { | 
 |  | 
 | using Register = RegARM32::AllRegisters; | 
 |  | 
 | // maximum number of registers allowed in vpush/vpop. | 
 | static constexpr SizeT VpushVpopMaxConsecRegs = 16; | 
 |  | 
 | const struct TypeARM32Attributes_ { | 
 |   const char *WidthString;     // b, h, <blank>, or d | 
 |   const char *FpWidthString;   // i8, i16, i32, f32, f64 | 
 |   const char *SVecWidthString; // s8, s16, s32, f32 | 
 |   const char *UVecWidthString; // u8, u16, u32, f32 | 
 |   int8_t SExtAddrOffsetBits; | 
 |   int8_t ZExtAddrOffsetBits; | 
 | } TypeARM32Attributes[] = { | 
 | #define X(tag, elementty, int_width, fp_width, uvec_width, svec_width, sbits,  \ | 
 |           ubits, rraddr, shaddr)                                               \ | 
 |   { int_width, fp_width, svec_width, uvec_width, sbits, ubits }                \ | 
 |   , | 
 |     ICETYPEARM32_TABLE | 
 | #undef X | 
 | }; | 
 |  | 
 | const struct InstARM32ShiftAttributes_ { | 
 |   const char *EmitString; | 
 | } InstARM32ShiftAttributes[] = { | 
 | #define X(tag, emit)                                                           \ | 
 |   { emit }                                                                     \ | 
 |   , | 
 |     ICEINSTARM32SHIFT_TABLE | 
 | #undef X | 
 | }; | 
 |  | 
 | const struct InstARM32CondAttributes_ { | 
 |   CondARM32::Cond Opposite; | 
 |   const char *EmitString; | 
 | } InstARM32CondAttributes[] = { | 
 | #define X(tag, encode, opp, emit)                                              \ | 
 |   { CondARM32::opp, emit }                                                     \ | 
 |   , | 
 |     ICEINSTARM32COND_TABLE | 
 | #undef X | 
 | }; | 
 |  | 
 | size_t getVecElmtBitsize(Type Ty) { | 
 |   return typeWidthInBytes(typeElementType(Ty)) * CHAR_BIT; | 
 | } | 
 |  | 
 | const char *getWidthString(Type Ty) { | 
 |   return TypeARM32Attributes[Ty].WidthString; | 
 | } | 
 |  | 
 | const char *getFpWidthString(Type Ty) { | 
 |   return TypeARM32Attributes[Ty].FpWidthString; | 
 | } | 
 |  | 
 | const char *getSVecWidthString(Type Ty) { | 
 |   return TypeARM32Attributes[Ty].SVecWidthString; | 
 | } | 
 |  | 
 | const char *getUVecWidthString(Type Ty) { | 
 |   return TypeARM32Attributes[Ty].UVecWidthString; | 
 | } | 
 |  | 
 | const char *getVWidthString(Type Ty, InstARM32::FPSign SignType) { | 
 |   switch (SignType) { | 
 |   case InstARM32::FS_None: | 
 |     return getFpWidthString(Ty); | 
 |   case InstARM32::FS_Signed: | 
 |     return getSVecWidthString(Ty); | 
 |   case InstARM32::FS_Unsigned: | 
 |     return getUVecWidthString(Ty); | 
 |   } | 
 |   llvm_unreachable("Invalid Sign Type."); | 
 |   return getFpWidthString(Ty); | 
 | } | 
 |  | 
 | } // end of anonymous namespace | 
 |  | 
 | const char *InstARM32Pred::predString(CondARM32::Cond Pred) { | 
 |   return InstARM32CondAttributes[Pred].EmitString; | 
 | } | 
 |  | 
 | void InstARM32Pred::dumpOpcodePred(Ostream &Str, const char *Opcode, | 
 |                                    Type Ty) const { | 
 |   Str << Opcode << getPredicate() << "." << Ty; | 
 | } | 
 |  | 
 | CondARM32::Cond InstARM32::getOppositeCondition(CondARM32::Cond Cond) { | 
 |   return InstARM32CondAttributes[Cond].Opposite; | 
 | } | 
 |  | 
 | void InstARM32::startNextInst(const Cfg *Func) const { | 
 |   if (auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>()) | 
 |     Asm->incEmitTextSize(InstSize); | 
 | } | 
 |  | 
 | void InstARM32::emitUsingTextFixup(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   GlobalContext *Ctx = Func->getContext(); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   if (getFlags().getDisableHybridAssembly() && | 
 |       getFlags().getSkipUnimplemented()) { | 
 |     Asm->trap(); | 
 |     Asm->resetNeedsTextFixup(); | 
 |     return; | 
 |   } | 
 |   std::string Buffer; | 
 |   llvm::raw_string_ostream StrBuf(Buffer); | 
 |   OstreamLocker L(Ctx); | 
 |   Ostream &OldStr = Ctx->getStrEmit(); | 
 |   Ctx->setStrEmit(StrBuf); | 
 |   // Start counting instructions here, so that emit() methods don't | 
 |   // need to call this for the first instruction. | 
 |   Asm->resetEmitTextSize(); | 
 |   Asm->incEmitTextSize(InstSize); | 
 |   emit(Func); | 
 |   Ctx->setStrEmit(OldStr); | 
 |   if (getFlags().getDisableHybridAssembly()) { | 
 |     if (getFlags().getSkipUnimplemented()) { | 
 |       Asm->trap(); | 
 |     } else { | 
 |       llvm::errs() << "Can't assemble: " << StrBuf.str() << "\n"; | 
 |       UnimplementedError(getFlags()); | 
 |     } | 
 |     Asm->resetNeedsTextFixup(); | 
 |     return; | 
 |   } | 
 |   Asm->emitTextInst(StrBuf.str(), Asm->getEmitTextSize()); | 
 | } | 
 |  | 
 | void InstARM32::emitIAS(const Cfg *Func) const { emitUsingTextFixup(Func); } | 
 |  | 
 | void InstARM32Pred::emitUnaryopGPR(const char *Opcode, | 
 |                                    const InstARM32Pred *Instr, const Cfg *Func, | 
 |                                    bool NeedsWidthSuffix) { | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(Instr->getSrcSize() == 1); | 
 |   Type SrcTy = Instr->getSrc(0)->getType(); | 
 |   Str << "\t" << Opcode; | 
 |   if (NeedsWidthSuffix) | 
 |     Str << getWidthString(SrcTy); | 
 |   Str << Instr->getPredicate() << "\t"; | 
 |   Instr->getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(0)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Pred::emitUnaryopFP(const char *Opcode, FPSign Sign, | 
 |                                   const InstARM32Pred *Instr, const Cfg *Func) { | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(Instr->getSrcSize() == 1); | 
 |   Type SrcTy = Instr->getSrc(0)->getType(); | 
 |   Str << "\t" << Opcode << Instr->getPredicate(); | 
 |   switch (Sign) { | 
 |   case FS_None: | 
 |     Str << getFpWidthString(SrcTy); | 
 |     break; | 
 |   case FS_Signed: | 
 |     Str << getSVecWidthString(SrcTy); | 
 |     break; | 
 |   case FS_Unsigned: | 
 |     Str << getUVecWidthString(SrcTy); | 
 |     break; | 
 |   } | 
 |   Str << "\t"; | 
 |   Instr->getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(0)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Pred::emitTwoAddr(const char *Opcode, const InstARM32Pred *Instr, | 
 |                                 const Cfg *Func) { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(Instr->getSrcSize() == 2); | 
 |   Variable *Dest = Instr->getDest(); | 
 |   assert(Dest == Instr->getSrc(0)); | 
 |   Str << "\t" << Opcode << Instr->getPredicate() << "\t"; | 
 |   Dest->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(1)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Pred::emitThreeAddr(const char *Opcode, | 
 |                                   const InstARM32Pred *Instr, const Cfg *Func, | 
 |                                   bool SetFlags) { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(Instr->getSrcSize() == 2); | 
 |   Str << "\t" << Opcode << (SetFlags ? "s" : "") << Instr->getPredicate() | 
 |       << "\t"; | 
 |   Instr->getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(0)->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(1)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32::emitThreeAddrFP(const char *Opcode, FPSign SignType, | 
 |                                 const InstARM32 *Instr, const Cfg *Func, | 
 |                                 Type OpType) { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(Instr->getSrcSize() == 2); | 
 |   Str << "\t" << Opcode << getVWidthString(OpType, SignType) << "\t"; | 
 |   Instr->getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(0)->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(1)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32::emitFourAddrFP(const char *Opcode, FPSign SignType, | 
 |                                const InstARM32 *Instr, const Cfg *Func) { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(Instr->getSrcSize() == 3); | 
 |   assert(Instr->getSrc(0) == Instr->getDest()); | 
 |   Str << "\t" << Opcode | 
 |       << getVWidthString(Instr->getDest()->getType(), SignType) << "\t"; | 
 |   Instr->getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(1)->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(2)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Pred::emitFourAddr(const char *Opcode, const InstARM32Pred *Instr, | 
 |                                  const Cfg *Func) { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(Instr->getSrcSize() == 3); | 
 |   Str << "\t" << Opcode << Instr->getPredicate() << "\t"; | 
 |   Instr->getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(0)->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(1)->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(2)->emit(Func); | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32FourAddrGPR<K>::emitIAS(const Cfg *Func) const { | 
 |   emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32FourAddrFP<K>::emitIAS(const Cfg *Func) const { | 
 |   emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32ThreeAddrFP<K>::emitIAS(const Cfg *Func) const { | 
 |   emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32ThreeAddrSignAwareFP<K>::emitIAS(const Cfg *Func) const { | 
 |   InstARM32::emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Mla::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 3); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->mla(getDest(), getSrc(0), getSrc(1), getSrc(2), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Mls::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 3); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->mls(getDest(), getSrc(0), getSrc(1), getSrc(2), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | void InstARM32Pred::emitCmpLike(const char *Opcode, const InstARM32Pred *Instr, | 
 |                                 const Cfg *Func) { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(Instr->getSrcSize() == 2); | 
 |   Str << "\t" << Opcode << Instr->getPredicate() << "\t"; | 
 |   Instr->getSrc(0)->emit(Func); | 
 |   Str << ", "; | 
 |   Instr->getSrc(1)->emit(Func); | 
 | } | 
 |  | 
 | OperandARM32Mem::OperandARM32Mem(Cfg * /* Func */, Type Ty, Variable *Base, | 
 |                                  ConstantInteger32 *ImmOffset, AddrMode Mode) | 
 |     : OperandARM32(kMem, Ty), Base(Base), ImmOffset(ImmOffset), Index(nullptr), | 
 |       ShiftOp(kNoShift), ShiftAmt(0), Mode(Mode) { | 
 |   // The Neg modes are only needed for Reg +/- Reg. | 
 |   assert(!isNegAddrMode()); | 
 |   NumVars = 1; | 
 |   Vars = &this->Base; | 
 | } | 
 |  | 
 | OperandARM32Mem::OperandARM32Mem(Cfg *Func, Type Ty, Variable *Base, | 
 |                                  Variable *Index, ShiftKind ShiftOp, | 
 |                                  uint16_t ShiftAmt, AddrMode Mode) | 
 |     : OperandARM32(kMem, Ty), Base(Base), ImmOffset(0), Index(Index), | 
 |       ShiftOp(ShiftOp), ShiftAmt(ShiftAmt), Mode(Mode) { | 
 |   if (Index->isRematerializable()) { | 
 |     llvm::report_fatal_error("Rematerializable Index Register is not allowed."); | 
 |   } | 
 |   NumVars = 2; | 
 |   Vars = Func->allocateArrayOf<Variable *>(2); | 
 |   Vars[0] = Base; | 
 |   Vars[1] = Index; | 
 | } | 
 |  | 
 | OperandARM32ShAmtImm::OperandARM32ShAmtImm(ConstantInteger32 *SA) | 
 |     : OperandARM32(kShAmtImm, IceType_i8), ShAmt(SA) {} | 
 |  | 
 | bool OperandARM32Mem::canHoldOffset(Type Ty, bool SignExt, int32_t Offset) { | 
 |   int32_t Bits = SignExt ? TypeARM32Attributes[Ty].SExtAddrOffsetBits | 
 |                          : TypeARM32Attributes[Ty].ZExtAddrOffsetBits; | 
 |   if (Bits == 0) | 
 |     return Offset == 0; | 
 |   // Note that encodings for offsets are sign-magnitude for ARM, so we check | 
 |   // with IsAbsoluteUint(). | 
 |   // Scalar fp, and vector types require an offset that is aligned to a multiple | 
 |   // of 4. | 
 |   if (isScalarFloatingType(Ty) || isVectorType(Ty)) | 
 |     return Utils::IsAligned(Offset, 4) && Utils::IsAbsoluteUint(Bits, Offset); | 
 |   return Utils::IsAbsoluteUint(Bits, Offset); | 
 | } | 
 |  | 
 | OperandARM32FlexImm::OperandARM32FlexImm(Cfg * /* Func */, Type Ty, | 
 |                                          uint32_t Imm, uint32_t RotateAmt) | 
 |     : OperandARM32Flex(kFlexImm, Ty), Imm(Imm), RotateAmt(RotateAmt) { | 
 |   NumVars = 0; | 
 |   Vars = nullptr; | 
 | } | 
 |  | 
 | bool OperandARM32FlexImm::canHoldImm(uint32_t Immediate, uint32_t *RotateAmt, | 
 |                                      uint32_t *Immed_8) { | 
 |   // Avoid the more expensive test for frequent small immediate values. | 
 |   if (Immediate <= 0xFF) { | 
 |     *RotateAmt = 0; | 
 |     *Immed_8 = Immediate; | 
 |     return true; | 
 |   } | 
 |   // Note that immediate must be unsigned for the test to work correctly. | 
 |   for (int Rot = 1; Rot < 16; Rot++) { | 
 |     uint32_t Imm8 = Utils::rotateLeft32(Immediate, 2 * Rot); | 
 |     if (Imm8 <= 0xFF) { | 
 |       *RotateAmt = Rot; | 
 |       *Immed_8 = Imm8; | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | OperandARM32FlexFpImm::OperandARM32FlexFpImm(Cfg * /*Func*/, Type Ty, | 
 |                                              uint32_t ModifiedImm) | 
 |     : OperandARM32Flex(kFlexFpImm, Ty), ModifiedImm(ModifiedImm) {} | 
 |  | 
 | bool OperandARM32FlexFpImm::canHoldImm(const Operand *C, | 
 |                                        uint32_t *ModifiedImm) { | 
 |   switch (C->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Unhandled fp constant type."); | 
 |   case IceType_f32: { | 
 |     // We violate llvm naming conventions a bit here so that the constants are | 
 |     // named after the bit fields they represent. See "A7.5.1 Operation of | 
 |     // modified immediate constants, Floating-point" in the ARM ARM. | 
 |     static constexpr uint32_t a = 0x80000000u; | 
 |     static constexpr uint32_t B = 0x40000000; | 
 |     static constexpr uint32_t bbbbb = 0x3E000000; | 
 |     static constexpr uint32_t cdefgh = 0x01F80000; | 
 |     static constexpr uint32_t AllowedBits = a | B | bbbbb | cdefgh; | 
 |     static_assert(AllowedBits == 0xFFF80000u, | 
 |                   "Invalid mask for f32 modified immediates."); | 
 |     const float F32 = llvm::cast<const ConstantFloat>(C)->getValue(); | 
 |     const uint32_t I32 = Utils::bitCopy<uint32_t>(F32); | 
 |     if (I32 & ~AllowedBits) { | 
 |       // constant has disallowed bits. | 
 |       return false; | 
 |     } | 
 |  | 
 |     if ((I32 & bbbbb) != bbbbb && (I32 & bbbbb)) { | 
 |       // not all bbbbb bits are 0 or 1. | 
 |       return false; | 
 |     } | 
 |  | 
 |     if (((I32 & B) != 0) == ((I32 & bbbbb) != 0)) { | 
 |       // B ^ b = 0; | 
 |       return false; | 
 |     } | 
 |  | 
 |     *ModifiedImm = ((I32 & a) ? 0x80 : 0x00) | ((I32 & bbbbb) ? 0x40 : 0x00) | | 
 |                    ((I32 & cdefgh) >> 19); | 
 |     return true; | 
 |   } | 
 |   case IceType_f64: { | 
 |     static constexpr uint32_t a = 0x80000000u; | 
 |     static constexpr uint32_t B = 0x40000000; | 
 |     static constexpr uint32_t bbbbbbbb = 0x3FC00000; | 
 |     static constexpr uint32_t cdefgh = 0x003F0000; | 
 |     static constexpr uint32_t AllowedBits = a | B | bbbbbbbb | cdefgh; | 
 |     static_assert(AllowedBits == 0xFFFF0000u, | 
 |                   "Invalid mask for f64 modified immediates."); | 
 |     const double F64 = llvm::cast<const ConstantDouble>(C)->getValue(); | 
 |     const uint64_t I64 = Utils::bitCopy<uint64_t>(F64); | 
 |     if (I64 & 0xFFFFFFFFu) { | 
 |       // constant has disallowed bits. | 
 |       return false; | 
 |     } | 
 |     const uint32_t I32 = I64 >> 32; | 
 |  | 
 |     if (I32 & ~AllowedBits) { | 
 |       // constant has disallowed bits. | 
 |       return false; | 
 |     } | 
 |  | 
 |     if ((I32 & bbbbbbbb) != bbbbbbbb && (I32 & bbbbbbbb)) { | 
 |       // not all bbbbb bits are 0 or 1. | 
 |       return false; | 
 |     } | 
 |  | 
 |     if (((I32 & B) != 0) == ((I32 & bbbbbbbb) != 0)) { | 
 |       // B ^ b = 0; | 
 |       return false; | 
 |     } | 
 |  | 
 |     *ModifiedImm = ((I32 & a) ? 0x80 : 0x00) | | 
 |                    ((I32 & bbbbbbbb) ? 0x40 : 0x00) | ((I32 & cdefgh) >> 16); | 
 |     return true; | 
 |   } | 
 |   } | 
 | } | 
 |  | 
 | OperandARM32FlexFpZero::OperandARM32FlexFpZero(Cfg * /*Func*/, Type Ty) | 
 |     : OperandARM32Flex(kFlexFpZero, Ty) {} | 
 |  | 
 | OperandARM32FlexReg::OperandARM32FlexReg(Cfg *Func, Type Ty, Variable *Reg, | 
 |                                          ShiftKind ShiftOp, Operand *ShiftAmt) | 
 |     : OperandARM32Flex(kFlexReg, Ty), Reg(Reg), ShiftOp(ShiftOp), | 
 |       ShiftAmt(ShiftAmt) { | 
 |   NumVars = 1; | 
 |   auto *ShiftVar = llvm::dyn_cast_or_null<Variable>(ShiftAmt); | 
 |   if (ShiftVar) | 
 |     ++NumVars; | 
 |   Vars = Func->allocateArrayOf<Variable *>(NumVars); | 
 |   Vars[0] = Reg; | 
 |   if (ShiftVar) | 
 |     Vars[1] = ShiftVar; | 
 | } | 
 |  | 
 | InstARM32Br::InstARM32Br(Cfg *Func, const CfgNode *TargetTrue, | 
 |                          const CfgNode *TargetFalse, | 
 |                          const InstARM32Label *Label, CondARM32::Cond Pred) | 
 |     : InstARM32Pred(Func, InstARM32::Br, 0, nullptr, Pred), | 
 |       TargetTrue(TargetTrue), TargetFalse(TargetFalse), Label(Label) {} | 
 |  | 
 | bool InstARM32Br::optimizeBranch(const CfgNode *NextNode) { | 
 |   // If there is no next block, then there can be no fallthrough to optimize. | 
 |   if (NextNode == nullptr) | 
 |     return false; | 
 |   // Intra-block conditional branches can't be optimized. | 
 |   if (Label) | 
 |     return false; | 
 |   // If there is no fallthrough node, such as a non-default case label for a | 
 |   // switch instruction, then there is no opportunity to optimize. | 
 |   if (getTargetFalse() == nullptr) | 
 |     return false; | 
 |  | 
 |   // Unconditional branch to the next node can be removed. | 
 |   if (isUnconditionalBranch() && getTargetFalse() == NextNode) { | 
 |     assert(getTargetTrue() == nullptr); | 
 |     setDeleted(); | 
 |     return true; | 
 |   } | 
 |   // If the fallthrough is to the next node, set fallthrough to nullptr to | 
 |   // indicate. | 
 |   if (getTargetFalse() == NextNode) { | 
 |     TargetFalse = nullptr; | 
 |     return true; | 
 |   } | 
 |   // If TargetTrue is the next node, and TargetFalse is not nullptr (which was | 
 |   // already tested above), then invert the branch condition, swap the targets, | 
 |   // and set new fallthrough to nullptr. | 
 |   if (getTargetTrue() == NextNode) { | 
 |     assert(Predicate != CondARM32::AL); | 
 |     setPredicate(getOppositeCondition(getPredicate())); | 
 |     TargetTrue = getTargetFalse(); | 
 |     TargetFalse = nullptr; | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool InstARM32Br::repointEdges(CfgNode *OldNode, CfgNode *NewNode) { | 
 |   bool Found = false; | 
 |   if (TargetFalse == OldNode) { | 
 |     TargetFalse = NewNode; | 
 |     Found = true; | 
 |   } | 
 |   if (TargetTrue == OldNode) { | 
 |     TargetTrue = NewNode; | 
 |     Found = true; | 
 |   } | 
 |   return Found; | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32ThreeAddrGPR<K>::emitIAS(const Cfg *Func) const { | 
 |   emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Adc::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->adc(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Add::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->add(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32And::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->and_(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Bic::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->bic(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Eor::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->eor(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Asr::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->asr(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Lsl::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->lsl(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Lsr::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->lsr(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Orr::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->orr(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Mul::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->mul(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Rsb::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->rsb(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Rsc::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->rsc(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Sbc::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->sbc(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Sdiv::emitIAS(const Cfg *Func) const { | 
 |   assert(!SetFlags); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->sdiv(getDest(), getSrc(0), getSrc(1), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Sub::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->sub(getDest(), getSrc(0), getSrc(1), SetFlags, getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Udiv::emitIAS(const Cfg *Func) const { | 
 |   assert(!SetFlags); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->udiv(getDest(), getSrc(0), getSrc(1), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Vadd::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   Type DestTy = Dest->getType(); | 
 |   switch (DestTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vadd not defined on type " + | 
 |                              typeStdString(DestTy)); | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     Asm->vaddqi(typeElementType(DestTy), Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   case IceType_v4f32: | 
 |     Asm->vaddqf(Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   case IceType_f32: | 
 |     Asm->vadds(Dest, getSrc(0), getSrc(1), CondARM32::AL); | 
 |     break; | 
 |   case IceType_f64: | 
 |     Asm->vaddd(Dest, getSrc(0), getSrc(1), CondARM32::AL); | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vand::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vand not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     Asm->vandq(Dest, getSrc(0), getSrc(1)); | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vceq::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   const Type SrcTy = getSrc(0)->getType(); | 
 |   switch (SrcTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vceq not defined on type " + | 
 |                              typeStdString(SrcTy)); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     Asm->vceqqi(typeElementType(SrcTy), Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   case IceType_v4f32: | 
 |     Asm->vceqqs(Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vcge::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   const Type SrcTy = getSrc(0)->getType(); | 
 |   switch (SrcTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vcge not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: { | 
 |     const Type ElmtTy = typeElementType(SrcTy); | 
 |     assert(Sign != InstARM32::FS_None); | 
 |     switch (Sign) { | 
 |     case InstARM32::FS_None: // defaults to unsigned. | 
 |       llvm_unreachable("Sign should not be FS_None."); | 
 |     case InstARM32::FS_Unsigned: | 
 |       Asm->vcugeqi(ElmtTy, Dest, getSrc(0), getSrc(1)); | 
 |       break; | 
 |     case InstARM32::FS_Signed: | 
 |       Asm->vcgeqi(ElmtTy, Dest, getSrc(0), getSrc(1)); | 
 |       break; | 
 |     } | 
 |   } break; | 
 |   case IceType_v4f32: | 
 |     Asm->vcgeqs(Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vcgt::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   const Type SrcTy = getSrc(0)->getType(); | 
 |   switch (SrcTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vcgt not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: { | 
 |     const Type ElmtTy = typeElementType(SrcTy); | 
 |     assert(Sign != InstARM32::FS_None); | 
 |     switch (Sign) { | 
 |     case InstARM32::FS_None: // defaults to unsigned. | 
 |       llvm_unreachable("Sign should not be FS_None."); | 
 |     case InstARM32::FS_Unsigned: | 
 |       Asm->vcugtqi(ElmtTy, Dest, getSrc(0), getSrc(1)); | 
 |       break; | 
 |     case InstARM32::FS_Signed: | 
 |       Asm->vcgtqi(ElmtTy, Dest, getSrc(0), getSrc(1)); | 
 |       break; | 
 |     } | 
 |   } break; | 
 |   case IceType_v4f32: | 
 |     Asm->vcgtqs(Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vbsl::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vbsl not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     Asm->vbslq(Dest, getSrc(0), getSrc(1)); | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vdiv::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     // TODO(kschimpf) Figure if more cases are needed. | 
 |     Asm->setNeedsTextFixup(); | 
 |     break; | 
 |   case IceType_f32: | 
 |     Asm->vdivs(getDest(), getSrc(0), getSrc(1), CondARM32::AL); | 
 |     break; | 
 |   case IceType_f64: | 
 |     Asm->vdivd(getDest(), getSrc(0), getSrc(1), CondARM32::AL); | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Veor::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   if (isVectorType(Dest->getType())) { | 
 |     Asm->veorq(Dest, getSrc(0), getSrc(1)); | 
 |     assert(!Asm->needsTextFixup()); | 
 |     return; | 
 |   } | 
 |   assert(Dest->getType() == IceType_f64); | 
 |   Asm->veord(Dest, getSrc(0), getSrc(1)); | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vmla::emitIAS(const Cfg *Func) const { | 
 |   // Note: Dest == getSrc(0) for four address FP instructions. | 
 |   assert(getSrcSize() == 3); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     // TODO(kschimpf) Figure out how vector operations apply. | 
 |     emitUsingTextFixup(Func); | 
 |     return; | 
 |   case IceType_f32: | 
 |     Asm->vmlas(getDest(), getSrc(1), getSrc(2), CondARM32::AL); | 
 |     assert(!Asm->needsTextFixup()); | 
 |     return; | 
 |   case IceType_f64: | 
 |     Asm->vmlad(getDest(), getSrc(1), getSrc(2), CondARM32::AL); | 
 |     assert(!Asm->needsTextFixup()); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vmls::emitIAS(const Cfg *Func) const { | 
 |   // Note: Dest == getSrc(0) for four address FP instructions. | 
 |   assert(getSrcSize() == 3); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     // TODO(kschimpf) Figure out how vector operations apply. | 
 |     emitUsingTextFixup(Func); | 
 |     return; | 
 |   case IceType_f32: | 
 |     Asm->vmlss(getDest(), getSrc(1), getSrc(2), CondARM32::AL); | 
 |     assert(!Asm->needsTextFixup()); | 
 |     return; | 
 |   case IceType_f64: | 
 |     Asm->vmlsd(getDest(), getSrc(1), getSrc(2), CondARM32::AL); | 
 |     assert(!Asm->needsTextFixup()); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vmvn::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vmvn not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |   case IceType_v4f32: { | 
 |     Asm->vmvnq(Dest, getSrc(0)); | 
 |   } break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vmovl::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vmovlq not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |   case IceType_v4f32: { | 
 |     Asm->vmovlq(Dest, getSrc(0), getSrc(1)); | 
 |   } break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vmovh::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vmovhq not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |   case IceType_v4f32: { | 
 |     Asm->vmovhq(Dest, getSrc(0), getSrc(1)); | 
 |   } break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vmovhl::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vmovhlq not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |   case IceType_v4f32: { | 
 |     Asm->vmovhlq(Dest, getSrc(0), getSrc(1)); | 
 |   } break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vmovlh::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vmovlhq not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |   case IceType_v4f32: { | 
 |     Asm->vmovlhq(Dest, getSrc(0), getSrc(1)); | 
 |   } break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vneg::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   const Type DestTy = Dest->getType(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vneg not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |   case IceType_v4f32: { | 
 |     const Type ElmtTy = typeElementType(DestTy); | 
 |     Asm->vnegqs(ElmtTy, Dest, getSrc(0)); | 
 |   } break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vorr::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vorr not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     Asm->vorrq(Dest, getSrc(0), getSrc(1)); | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vshl::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   const Type DestTy = Dest->getType(); | 
 |   switch (DestTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vshl not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   // TODO(jpp): handle i1 vectors in terms of element count instead of element | 
 |   // type. | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: { | 
 |     const Type ElmtTy = typeElementType(DestTy); | 
 |     assert(Sign != InstARM32::FS_None); | 
 |     switch (Sign) { | 
 |     case InstARM32::FS_None: // defaults to unsigned. | 
 |     case InstARM32::FS_Unsigned: | 
 |       if (const auto *Imm6 = llvm::dyn_cast<ConstantInteger32>(getSrc(1))) { | 
 |         Asm->vshlqc(ElmtTy, Dest, getSrc(0), Imm6); | 
 |       } else { | 
 |         Asm->vshlqu(ElmtTy, Dest, getSrc(0), getSrc(1)); | 
 |       } | 
 |       break; | 
 |     case InstARM32::FS_Signed: | 
 |       if (const auto *Imm6 = llvm::dyn_cast<ConstantInteger32>(getSrc(1))) { | 
 |         Asm->vshlqc(ElmtTy, Dest, getSrc(0), Imm6); | 
 |       } else { | 
 |         Asm->vshlqi(ElmtTy, Dest, getSrc(0), getSrc(1)); | 
 |       } | 
 |       break; | 
 |     } | 
 |   } break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vshr::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   const Type DestTy = Dest->getType(); | 
 |   switch (DestTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vshr not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   // TODO(jpp): handle i1 vectors in terms of element count instead of element | 
 |   // type. | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: { | 
 |     const Type ElmtTy = typeElementType(DestTy); | 
 |     const auto *Imm6 = llvm::cast<ConstantInteger32>(getSrc(1)); | 
 |     switch (Sign) { | 
 |     case InstARM32::FS_Signed: | 
 |     case InstARM32::FS_Unsigned: | 
 |       Asm->vshrqc(ElmtTy, Dest, getSrc(0), Imm6, Sign); | 
 |       break; | 
 |     default: | 
 |       assert(false && "Vshr requires signedness specification."); | 
 |     } | 
 |   } break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vsub::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   Type DestTy = Dest->getType(); | 
 |   switch (DestTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vsub not defined on type " + | 
 |                              typeStdString(DestTy)); | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     Asm->vsubqi(typeElementType(DestTy), Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   case IceType_v4f32: | 
 |     Asm->vsubqf(Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   case IceType_f32: | 
 |     Asm->vsubs(getDest(), getSrc(0), getSrc(1), CondARM32::AL); | 
 |     break; | 
 |   case IceType_f64: | 
 |     Asm->vsubd(getDest(), getSrc(0), getSrc(1), CondARM32::AL); | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vqadd::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   Type DestTy = Dest->getType(); | 
 |   switch (DestTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vqadd not defined on type " + | 
 |                              typeStdString(DestTy)); | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     switch (Sign) { | 
 |     case InstARM32::FS_None: // defaults to unsigned. | 
 |     case InstARM32::FS_Unsigned: | 
 |       Asm->vqaddqu(typeElementType(DestTy), Dest, getSrc(0), getSrc(1)); | 
 |       break; | 
 |     case InstARM32::FS_Signed: | 
 |       Asm->vqaddqi(typeElementType(DestTy), Dest, getSrc(0), getSrc(1)); | 
 |       break; | 
 |     } | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vqsub::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   Type DestTy = Dest->getType(); | 
 |   switch (DestTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vqsub not defined on type " + | 
 |                              typeStdString(DestTy)); | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     switch (Sign) { | 
 |     case InstARM32::FS_None: // defaults to unsigned. | 
 |     case InstARM32::FS_Unsigned: | 
 |       Asm->vqsubqu(typeElementType(DestTy), Dest, getSrc(0), getSrc(1)); | 
 |       break; | 
 |     case InstARM32::FS_Signed: | 
 |       Asm->vqsubqi(typeElementType(DestTy), Dest, getSrc(0), getSrc(1)); | 
 |       break; | 
 |     } | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vqmovn2::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Operand *Src0 = getSrc(0); | 
 |   const Operand *Src1 = getSrc(1); | 
 |   Type SrcTy = Src0->getType(); | 
 |   Type DestTy = Dest->getType(); | 
 |   bool Unsigned = true; | 
 |   bool Saturating = true; | 
 |   switch (SrcTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vqmovn2 not defined on type " + | 
 |                              typeStdString(SrcTy)); | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     switch (Sign) { | 
 |     case InstARM32::FS_None: | 
 |       Unsigned = true; | 
 |       Saturating = false; | 
 |       Asm->vqmovn2(typeElementType(DestTy), Dest, Src0, Src1, Unsigned, | 
 |                    Saturating); | 
 |       break; | 
 |     case InstARM32::FS_Unsigned: | 
 |       Unsigned = true; | 
 |       Saturating = true; | 
 |       Asm->vqmovn2(typeElementType(DestTy), Dest, Src0, Src1, Unsigned, | 
 |                    Saturating); | 
 |       break; | 
 |     case InstARM32::FS_Signed: | 
 |       Unsigned = false; | 
 |       Saturating = true; | 
 |       Asm->vqmovn2(typeElementType(DestTy), Dest, Src0, Src1, Unsigned, | 
 |                    Saturating); | 
 |       break; | 
 |     } | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vmulh::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Operand *Src0 = getSrc(0); | 
 |   Type SrcTy = Src0->getType(); | 
 |   bool Unsigned = true; | 
 |   switch (SrcTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vmulh not defined on type " + | 
 |                              typeStdString(SrcTy)); | 
 |   case IceType_v8i16: | 
 |     switch (Sign) { | 
 |     case InstARM32::FS_None: // defaults to unsigned. | 
 |     case InstARM32::FS_Unsigned: | 
 |       Unsigned = true; | 
 |       Asm->vmulh(typeElementType(SrcTy), Dest, getSrc(0), getSrc(1), Unsigned); | 
 |       break; | 
 |     case InstARM32::FS_Signed: | 
 |       Unsigned = false; | 
 |       Asm->vmulh(typeElementType(SrcTy), Dest, getSrc(0), getSrc(1), Unsigned); | 
 |       break; | 
 |     } | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vmlap::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Operand *Src0 = getSrc(0); | 
 |   const Operand *Src1 = getSrc(1); | 
 |   Type SrcTy = Src0->getType(); | 
 |   switch (SrcTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vmlap not defined on type " + | 
 |                              typeStdString(SrcTy)); | 
 |   case IceType_v8i16: | 
 |     Asm->vmlap(typeElementType(SrcTy), Dest, Src0, Src1); | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vzip::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Operand *Src0 = getSrc(0); | 
 |   const Operand *Src1 = getSrc(1); | 
 |   Type DestTy = Dest->getType(); | 
 |   Asm->vzip(typeElementType(DestTy), Dest, Src0, Src1); | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vmul::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   const Type DestTy = Dest->getType(); | 
 |   switch (DestTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Vmul not defined on type " + | 
 |                              typeStdString(DestTy)); | 
 |  | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |     Asm->vmulqi(typeElementType(DestTy), Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   case IceType_v4f32: | 
 |     Asm->vmulqf(Dest, getSrc(0), getSrc(1)); | 
 |     break; | 
 |   case IceType_f32: | 
 |     Asm->vmuls(Dest, getSrc(0), getSrc(1), CondARM32::AL); | 
 |     break; | 
 |   case IceType_f64: | 
 |     Asm->vmuld(Dest, getSrc(0), getSrc(1), CondARM32::AL); | 
 |     break; | 
 |   } | 
 | } | 
 |  | 
 | InstARM32Call::InstARM32Call(Cfg *Func, Variable *Dest, Operand *CallTarget) | 
 |     : InstARM32(Func, InstARM32::Call, 1, Dest) { | 
 |   HasSideEffects = true; | 
 |   addSource(CallTarget); | 
 | } | 
 |  | 
 | InstARM32Label::InstARM32Label(Cfg *Func, TargetARM32 *Target) | 
 |     : InstARM32(Func, InstARM32::Label, 0, nullptr), | 
 |       Number(Target->makeNextLabelNumber()) { | 
 |   if (BuildDefs::dump()) { | 
 |     Name = GlobalString::createWithString( | 
 |         Func->getContext(), | 
 |         ".L" + Func->getFunctionName() + "$local$__" + std::to_string(Number)); | 
 |   } else { | 
 |     Name = GlobalString::createWithoutString(Func->getContext()); | 
 |   } | 
 | } | 
 |  | 
 | namespace { | 
 | // Requirements for Push/Pop: | 
 | //  1) All the Variables have the same type; | 
 | //  2) All the variables have registers assigned to them. | 
 | void validatePushOrPopRegisterListOrDie(const VarList &RegList) { | 
 |   Type PreviousTy = IceType_void; | 
 |   for (Variable *Reg : RegList) { | 
 |     if (PreviousTy != IceType_void && Reg->getType() != PreviousTy) { | 
 |       llvm::report_fatal_error("Type mismatch when popping/pushing " | 
 |                                "registers."); | 
 |     } | 
 |  | 
 |     if (!Reg->hasReg()) { | 
 |       llvm::report_fatal_error("Push/pop operand does not have a register " | 
 |                                "assigned to it."); | 
 |     } | 
 |  | 
 |     PreviousTy = Reg->getType(); | 
 |   } | 
 | } | 
 | } // end of anonymous namespace | 
 |  | 
 | void InstARM32RegisterStackOp::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   emitUsingForm(Func, Emit_Text); | 
 | } | 
 |  | 
 | void InstARM32RegisterStackOp::emitIAS(const Cfg *Func) const { | 
 |   emitUsingForm(Func, Emit_Binary); | 
 |   assert(!Func->getAssembler<ARM32::AssemblerARM32>()->needsTextFixup()); | 
 | } | 
 |  | 
 | void InstARM32RegisterStackOp::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Str << getDumpOpcode() << " "; | 
 |   SizeT NumRegs = getNumStackRegs(); | 
 |   for (SizeT I = 0; I < NumRegs; ++I) { | 
 |     if (I > 0) | 
 |       Str << ", "; | 
 |     getStackReg(I)->dump(Func); | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32RegisterStackOp::emitGPRsAsText(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   Str << "\t" << getGPROpcode() << "\t{"; | 
 |   getStackReg(0)->emit(Func); | 
 |   const SizeT NumRegs = getNumStackRegs(); | 
 |   for (SizeT i = 1; i < NumRegs; ++i) { | 
 |     Str << ", "; | 
 |     getStackReg(i)->emit(Func); | 
 |   } | 
 |   Str << "}"; | 
 | } | 
 |  | 
 | void InstARM32RegisterStackOp::emitSRegsAsText(const Cfg *Func, | 
 |                                                const Variable *BaseReg, | 
 |                                                SizeT RegCount) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   Str << "\t" << getSRegOpcode() << "\t{"; | 
 |   bool IsFirst = true; | 
 |   const auto Base = BaseReg->getRegNum(); | 
 |   for (SizeT i = 0; i < RegCount; ++i) { | 
 |     if (IsFirst) | 
 |       IsFirst = false; | 
 |     else | 
 |       Str << ", "; | 
 |     Str << RegARM32::getRegName(RegNumT::fixme(Base + i)); | 
 |   } | 
 |   Str << "}"; | 
 | } | 
 |  | 
 | void InstARM32RegisterStackOp::emitSRegsOp(const Cfg *Func, EmitForm Form, | 
 |                                            const Variable *BaseReg, | 
 |                                            SizeT RegCount, | 
 |                                            SizeT InstIndex) const { | 
 |   if (Form == Emit_Text && BuildDefs::dump() && InstIndex > 0) { | 
 |     startNextInst(Func); | 
 |     Func->getContext()->getStrEmit() << "\n"; | 
 |   } | 
 |   emitSRegs(Func, Form, BaseReg, RegCount); | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | bool isAssignedConsecutiveRegisters(const Variable *Before, | 
 |                                     const Variable *After) { | 
 |   assert(Before->hasReg()); | 
 |   assert(After->hasReg()); | 
 |   return RegNumT::fixme(Before->getRegNum() + 1) == After->getRegNum(); | 
 | } | 
 |  | 
 | } // end of anonymous namespace | 
 |  | 
 | void InstARM32RegisterStackOp::emitUsingForm(const Cfg *Func, | 
 |                                              const EmitForm Form) const { | 
 |   SizeT NumRegs = getNumStackRegs(); | 
 |   assert(NumRegs); | 
 |  | 
 |   const auto *Reg = llvm::cast<Variable>(getStackReg(0)); | 
 |   if (isScalarIntegerType(Reg->getType())) { | 
 |     // Push/pop GPR registers. | 
 |     SizeT IntegerCount = 0; | 
 |     ARM32::IValueT GPRegisters = 0; | 
 |     const Variable *LastDest = nullptr; | 
 |     for (SizeT i = 0; i < NumRegs; ++i) { | 
 |       const Variable *Var = getStackReg(i); | 
 |       assert(Var->hasReg() && "stack op only applies to registers"); | 
 |       const RegARM32::GPRRegister Reg = | 
 |           RegARM32::getEncodedGPR(Var->getRegNum()); | 
 |       LastDest = Var; | 
 |       GPRegisters |= (1 << Reg); | 
 |       ++IntegerCount; | 
 |     } | 
 |     if (IntegerCount == 1) { | 
 |       emitSingleGPR(Func, Form, LastDest); | 
 |     } else { | 
 |       emitMultipleGPRs(Func, Form, GPRegisters); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   // Push/pop floating point registers. Divide into a list of instructions, | 
 |   // defined on consecutive register ranges. Then generate the corresponding | 
 |   // instructions. | 
 |  | 
 |   // Typical max number of registers ranges pushed/popd is no more than 5. | 
 |   llvm::SmallVector<std::pair<const Variable *, SizeT>, 5> InstData; | 
 |   const Variable *BaseReg = nullptr; | 
 |   SizeT RegCount = 0; | 
 |   for (SizeT i = 0; i < NumRegs; ++i) { | 
 |     const Variable *NextReg = getStackReg(i); | 
 |     assert(NextReg->hasReg()); | 
 |     if (BaseReg == nullptr) { | 
 |       BaseReg = NextReg; | 
 |       RegCount = 1; | 
 |     } else if (RegCount < VpushVpopMaxConsecRegs && | 
 |                isAssignedConsecutiveRegisters(Reg, NextReg)) { | 
 |       ++RegCount; | 
 |     } else { | 
 |       InstData.emplace_back(BaseReg, RegCount); | 
 |       BaseReg = NextReg; | 
 |       RegCount = 1; | 
 |     } | 
 |     Reg = NextReg; | 
 |   } | 
 |   if (RegCount) { | 
 |     InstData.emplace_back(BaseReg, RegCount); | 
 |   } | 
 |   SizeT InstCount = 0; | 
 |   if (llvm::isa<InstARM32Push>(*this)) { | 
 |     for (const auto &Pair : InstData) | 
 |       emitSRegsOp(Func, Form, Pair.first, Pair.second, InstCount++); | 
 |     return; | 
 |   } | 
 |   assert(llvm::isa<InstARM32Pop>(*this)); | 
 |   for (const auto &Pair : reverse_range(InstData)) | 
 |     emitSRegsOp(Func, Form, Pair.first, Pair.second, InstCount++); | 
 | } | 
 |  | 
 | InstARM32Pop::InstARM32Pop(Cfg *Func, const VarList &Dests) | 
 |     : InstARM32RegisterStackOp(Func, InstARM32::Pop, 0, nullptr), Dests(Dests) { | 
 |   // Track modifications to Dests separately via FakeDefs. Also, a pop | 
 |   // instruction affects the stack pointer and so it should not be allowed to | 
 |   // be automatically dead-code eliminated. This is automatic since we leave | 
 |   // the Dest as nullptr. | 
 |   validatePushOrPopRegisterListOrDie(Dests); | 
 | } | 
 |  | 
 | InstARM32Push::InstARM32Push(Cfg *Func, const VarList &Srcs) | 
 |     : InstARM32RegisterStackOp(Func, InstARM32::Push, Srcs.size(), nullptr) { | 
 |   validatePushOrPopRegisterListOrDie(Srcs); | 
 |   for (Variable *Source : Srcs) { | 
 |     addSource(Source); | 
 |   } | 
 | } | 
 |  | 
 | InstARM32Ret::InstARM32Ret(Cfg *Func, Variable *LR, Variable *Source) | 
 |     : InstARM32(Func, InstARM32::Ret, Source ? 2 : 1, nullptr) { | 
 |   addSource(LR); | 
 |   if (Source) | 
 |     addSource(Source); | 
 | } | 
 |  | 
 | InstARM32Str::InstARM32Str(Cfg *Func, Variable *Value, OperandARM32Mem *Mem, | 
 |                            CondARM32::Cond Predicate) | 
 |     : InstARM32Pred(Func, InstARM32::Str, 2, nullptr, Predicate) { | 
 |   addSource(Value); | 
 |   addSource(Mem); | 
 | } | 
 |  | 
 | InstARM32Strex::InstARM32Strex(Cfg *Func, Variable *Dest, Variable *Value, | 
 |                                OperandARM32Mem *Mem, CondARM32::Cond Predicate) | 
 |     : InstARM32Pred(Func, InstARM32::Strex, 2, Dest, Predicate) { | 
 |   addSource(Value); | 
 |   addSource(Mem); | 
 | } | 
 |  | 
 | InstARM32Vstr1::InstARM32Vstr1(Cfg *Func, Variable *Value, OperandARM32Mem *Mem, | 
 |                                CondARM32::Cond Predicate, SizeT Size) | 
 |     : InstARM32Pred(Func, InstARM32::Vstr1, 2, nullptr, Predicate) { | 
 |   addSource(Value); | 
 |   addSource(Mem); | 
 |   this->Size = Size; | 
 | } | 
 |  | 
 | InstARM32Vdup::InstARM32Vdup(Cfg *Func, Variable *Dest, Variable *Src, | 
 |                              IValueT Idx) | 
 |     : InstARM32Pred(Func, InstARM32::Vdup, 1, Dest, CondARM32::AL), Idx(Idx) { | 
 |   addSource(Src); | 
 | } | 
 |  | 
 | InstARM32Trap::InstARM32Trap(Cfg *Func) | 
 |     : InstARM32(Func, InstARM32::Trap, 0, nullptr) {} | 
 |  | 
 | InstARM32Umull::InstARM32Umull(Cfg *Func, Variable *DestLo, Variable *DestHi, | 
 |                                Variable *Src0, Variable *Src1, | 
 |                                CondARM32::Cond Predicate) | 
 |     : InstARM32Pred(Func, InstARM32::Umull, 2, DestLo, Predicate), | 
 |       // DestHi is expected to have a FakeDef inserted by the lowering code. | 
 |       DestHi(DestHi) { | 
 |   addSource(Src0); | 
 |   addSource(Src1); | 
 | } | 
 |  | 
 | InstARM32Vcvt::InstARM32Vcvt(Cfg *Func, Variable *Dest, Variable *Src, | 
 |                              VcvtVariant Variant, CondARM32::Cond Predicate) | 
 |     : InstARM32Pred(Func, InstARM32::Vcvt, 1, Dest, Predicate), | 
 |       Variant(Variant) { | 
 |   addSource(Src); | 
 | } | 
 |  | 
 | InstARM32Mov::InstARM32Mov(Cfg *Func, Variable *Dest, Operand *Src, | 
 |                            CondARM32::Cond Predicate) | 
 |     : InstARM32Pred(Func, InstARM32::Mov, 2, Dest, Predicate) { | 
 |   auto *Dest64 = llvm::dyn_cast<Variable64On32>(Dest); | 
 |   auto *Src64 = llvm::dyn_cast<Variable64On32>(Src); | 
 |  | 
 |   assert(Dest64 == nullptr || Src64 == nullptr); | 
 |  | 
 |   if (Dest64 != nullptr) { | 
 |     // this-> is needed below because there is a parameter named Dest. | 
 |     this->Dest = Dest64->getLo(); | 
 |     DestHi = Dest64->getHi(); | 
 |   } | 
 |  | 
 |   if (Src64 == nullptr) { | 
 |     addSource(Src); | 
 |   } else { | 
 |     addSource(Src64->getLo()); | 
 |     addSource(Src64->getHi()); | 
 |   } | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | // These next two functions find the D register that maps to the half of the Q | 
 | // register that this instruction is accessing. | 
 | Register getDRegister(const Variable *Src, uint32_t Index) { | 
 |   assert(Src->hasReg()); | 
 |   const auto SrcReg = Src->getRegNum(); | 
 |  | 
 |   const RegARM32::RegTableType &SrcEntry = RegARM32::RegTable[SrcReg]; | 
 |   assert(SrcEntry.IsVec128); | 
 |  | 
 |   const uint32_t NumElements = typeNumElements(Src->getType()); | 
 |  | 
 |   // This code assumes the Aliases list goes Q_n, S_2n, S_2n+1. The asserts in | 
 |   // the next two branches help to check that this is still true. | 
 |   if (Index < NumElements / 2) { | 
 |     // We have a Q register that's made up of two D registers. This assert is | 
 |     // to help ensure that we picked the right D register. | 
 |     // | 
 |     // TODO(jpp): find a way to do this that doesn't rely on ordering of the | 
 |     // alias list. | 
 |     assert(RegARM32::RegTable[SrcEntry.Aliases[1]].Encoding + 1 == | 
 |            RegARM32::RegTable[SrcEntry.Aliases[2]].Encoding); | 
 |     return static_cast<Register>(SrcEntry.Aliases[1]); | 
 |   } else { | 
 |     // We have a Q register that's made up of two D registers. This assert is | 
 |     // to help ensure that we picked the right D register. | 
 |     // | 
 |     // TODO(jpp): find a way to do this that doesn't rely on ordering of the | 
 |     // alias list. | 
 |     assert(RegARM32::RegTable[SrcEntry.Aliases[2]].Encoding - 1 == | 
 |            RegARM32::RegTable[SrcEntry.Aliases[1]].Encoding); | 
 |     return static_cast<Register>(SrcEntry.Aliases[2]); | 
 |   } | 
 | } | 
 |  | 
 | uint32_t adjustDIndex(Type Ty, uint32_t DIndex) { | 
 |   // If Ty is a vector of i1, we may need to adjust DIndex. This is needed | 
 |   // because, e.g., the second i1 in a v4i1 is accessed with a | 
 |   // | 
 |   // vmov.s8 Qd[4], Rn | 
 |   switch (Ty) { | 
 |   case IceType_v4i1: | 
 |     return DIndex * 4; | 
 |   case IceType_v8i1: | 
 |     return DIndex * 2; | 
 |   case IceType_v16i1: | 
 |     return DIndex; | 
 |   default: | 
 |     return DIndex; | 
 |   } | 
 | } | 
 |  | 
 | uint32_t getDIndex(Type Ty, uint32_t NumElements, uint32_t Index) { | 
 |   const uint32_t DIndex = | 
 |       (Index < NumElements / 2) ? Index : Index - (NumElements / 2); | 
 |   return adjustDIndex(Ty, DIndex); | 
 | } | 
 |  | 
 | // For floating point values, we can insertelement or extractelement by moving | 
 | // directly from an S register. This function finds the right one. | 
 | Register getSRegister(const Variable *Src, uint32_t Index) { | 
 |   assert(Src->hasReg()); | 
 |   const auto SrcReg = Src->getRegNum(); | 
 |  | 
 |   // For floating point values, we need to be allocated to Q0 - Q7, so we can | 
 |   // directly access the value we want as one of the S registers. | 
 |   assert(Src->getType() == IceType_v4f32); | 
 |   assert(SrcReg < RegARM32::Reg_q8); | 
 |  | 
 |   // This part assumes the register alias list goes q0, d0, d1, s0, s1, s2, s3. | 
 |   assert(Index < 4); | 
 |  | 
 |   // TODO(jpp): find a way to do this that doesn't rely on ordering of the alias | 
 |   // list. | 
 |   return static_cast<Register>(RegARM32::RegTable[SrcReg].Aliases[Index + 3]); | 
 | } | 
 |  | 
 | } // end of anonymous namespace | 
 |  | 
 | void InstARM32Extract::emit(const Cfg *Func) const { | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   const Type DestTy = getDest()->getType(); | 
 |  | 
 |   const auto *Src = llvm::cast<Variable>(getSrc(0)); | 
 |  | 
 |   if (isIntegerType(DestTy)) { | 
 |     Str << "\t" | 
 |         << "vmov" << getPredicate(); | 
 |     const uint32_t BitSize = typeWidthInBytes(DestTy) * CHAR_BIT; | 
 |     if (BitSize < 32) { | 
 |       Str << ".s" << BitSize; | 
 |     } else { | 
 |       Str << "." << BitSize; | 
 |     } | 
 |     Str << "\t"; | 
 |     getDest()->emit(Func); | 
 |     Str << ", "; | 
 |  | 
 |     const Type SrcTy = Src->getType(); | 
 |     const size_t VectorSize = typeNumElements(SrcTy); | 
 |  | 
 |     const Register SrcReg = getDRegister(Src, Index); | 
 |  | 
 |     Str << RegARM32::RegTable[SrcReg].Name; | 
 |     Str << "[" << getDIndex(SrcTy, VectorSize, Index) << "]"; | 
 |   } else if (isFloatingType(DestTy)) { | 
 |     const Register SrcReg = getSRegister(Src, Index); | 
 |  | 
 |     Str << "\t" | 
 |         << "vmov" << getPredicate() << ".f32" | 
 |         << "\t"; | 
 |     getDest()->emit(Func); | 
 |     Str << ", " << RegARM32::RegTable[SrcReg].Name; | 
 |   } else { | 
 |     assert(false && "Invalid extract type"); | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Extract::emitIAS(const Cfg *Func) const { | 
 |   const Operand *Dest = getDest(); | 
 |   const Type DestTy = Dest->getType(); | 
 |   const Operand *Src = getSrc(0); | 
 |   const Type SrcTy = Src->getType(); | 
 |   assert(isVectorType(Src->getType())); | 
 |   assert(DestTy == typeElementType(Src->getType())); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   if (isIntegerType(DestTy)) { | 
 |     Asm->vmovrqi(Dest, Src, adjustDIndex(SrcTy, Index), getPredicate()); | 
 |     assert(!Asm->needsTextFixup()); | 
 |     return; | 
 |   } | 
 |   assert(isFloatingType(DestTy)); | 
 |   Asm->vmovsqi(Dest, Src, Index, getPredicate()); | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | namespace { | 
 | Type insertionType(Type Ty) { | 
 |   assert(isVectorType(Ty)); | 
 |   switch (Ty) { | 
 |   case IceType_v4i1: | 
 |     return IceType_v4i32; | 
 |   case IceType_v8i1: | 
 |     return IceType_v8i16; | 
 |   case IceType_v16i1: | 
 |     return IceType_v16i8; | 
 |   default: | 
 |     return Ty; | 
 |   } | 
 | } | 
 | } // end of anonymous namespace | 
 |  | 
 | void InstARM32Insert::emit(const Cfg *Func) const { | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   const Variable *Dest = getDest(); | 
 |   const auto *Src = llvm::cast<Variable>(getSrc(0)); | 
 |   const Type DestTy = insertionType(getDest()->getType()); | 
 |   assert(isVectorType(DestTy)); | 
 |  | 
 |   if (isIntegerType(DestTy)) { | 
 |     Str << "\t" | 
 |         << "vmov" << getPredicate(); | 
 |     const size_t BitSize = typeWidthInBytes(typeElementType(DestTy)) * CHAR_BIT; | 
 |     Str << "." << BitSize << "\t"; | 
 |  | 
 |     const size_t VectorSize = typeNumElements(DestTy); | 
 |     const Register DestReg = getDRegister(Dest, Index); | 
 |     const uint32_t Index = | 
 |         getDIndex(insertionType(DestTy), VectorSize, this->Index); | 
 |     Str << RegARM32::RegTable[DestReg].Name; | 
 |     Str << "[" << Index << "], "; | 
 |     Src->emit(Func); | 
 |   } else if (isFloatingType(DestTy)) { | 
 |     Str << "\t" | 
 |         << "vmov" << getPredicate() << ".f32" | 
 |         << "\t"; | 
 |     const Register DestReg = getSRegister(Dest, Index); | 
 |     Str << RegARM32::RegTable[DestReg].Name << ", "; | 
 |     Src->emit(Func); | 
 |   } else { | 
 |     assert(false && "Invalid insert type"); | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Insert::emitIAS(const Cfg *Func) const { | 
 |   const Variable *Dest = getDest(); | 
 |   const auto *Src = llvm::cast<Variable>(getSrc(0)); | 
 |   const Type DestTy = insertionType(Dest->getType()); | 
 |   const Type SrcTy = typeElementType(DestTy); | 
 |   assert(SrcTy == Src->getType() || Src->getType() == IceType_i1); | 
 |   assert(isVectorType(DestTy)); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   if (isIntegerType(SrcTy)) { | 
 |     Asm->vmovqir(Dest->asType(Func, DestTy, Dest->getRegNum()), | 
 |                  adjustDIndex(DestTy, Index), | 
 |                  Src->asType(Func, SrcTy, Src->getRegNum()), getPredicate()); | 
 |     assert(!Asm->needsTextFixup()); | 
 |     return; | 
 |   } | 
 |   assert(isFloatingType(SrcTy)); | 
 |   Asm->vmovqis(Dest, Index, Src, getPredicate()); | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32CmpLike<K>::emitIAS(const Cfg *Func) const { | 
 |   emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Cmn::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 2); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->cmn(getSrc(0), getSrc(1), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Cmp::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 2); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->cmp(getSrc(0), getSrc(1), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Tst::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 2); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->tst(getSrc(0), getSrc(1), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | InstARM32Dmb::InstARM32Dmb(Cfg *Func) | 
 |     : InstARM32Pred(Func, InstARM32::Dmb, 0, nullptr, CondARM32::AL) {} | 
 |  | 
 | InstARM32Nop::InstARM32Nop(Cfg *Func) | 
 |     : InstARM32Pred(Func, InstARM32::Nop, 0, nullptr, CondARM32::AL) {} | 
 |  | 
 | InstARM32Vcmp::InstARM32Vcmp(Cfg *Func, Variable *Src0, Operand *Src1, | 
 |                              CondARM32::Cond Predicate) | 
 |     : InstARM32Pred(Func, InstARM32::Vcmp, 2, nullptr, Predicate) { | 
 |   HasSideEffects = true; | 
 |   addSource(Src0); | 
 |   addSource(Src1); | 
 | } | 
 |  | 
 | InstARM32Vmrs::InstARM32Vmrs(Cfg *Func, CondARM32::Cond Predicate) | 
 |     : InstARM32Pred(Func, InstARM32::Vmrs, 0, nullptr, Predicate) { | 
 |   HasSideEffects = true; | 
 | } | 
 |  | 
 | InstARM32Vabs::InstARM32Vabs(Cfg *Func, Variable *Dest, Variable *Src, | 
 |                              CondARM32::Cond Predicate) | 
 |     : InstARM32Pred(Func, InstARM32::Vabs, 1, Dest, Predicate) { | 
 |   addSource(Src); | 
 | } | 
 |  | 
 | // ======================== Dump routines ======================== // | 
 |  | 
 | // Two-addr ops | 
 | template <> const char *InstARM32Movt::Opcode = "movt"; | 
 | // Unary ops | 
 | template <> const char *InstARM32Movw::Opcode = "movw"; | 
 | template <> const char *InstARM32Clz::Opcode = "clz"; | 
 | template <> const char *InstARM32Mvn::Opcode = "mvn"; | 
 | template <> const char *InstARM32Rbit::Opcode = "rbit"; | 
 | template <> const char *InstARM32Rev::Opcode = "rev"; | 
 | template <> const char *InstARM32Sxt::Opcode = "sxt"; // still requires b/h | 
 | template <> const char *InstARM32Uxt::Opcode = "uxt"; // still requires b/h | 
 | // FP | 
 | template <> const char *InstARM32Vsqrt::Opcode = "vsqrt"; | 
 | // Mov-like ops | 
 | template <> const char *InstARM32Ldr::Opcode = "ldr"; | 
 | template <> const char *InstARM32Ldrex::Opcode = "ldrex"; | 
 | template <> const char *InstARM32Vldr1d::Opcode = "vldr1d"; | 
 | template <> const char *InstARM32Vldr1q::Opcode = "vldr1q"; | 
 | // Three-addr ops | 
 | template <> const char *InstARM32Adc::Opcode = "adc"; | 
 | template <> const char *InstARM32Add::Opcode = "add"; | 
 | template <> const char *InstARM32And::Opcode = "and"; | 
 | template <> const char *InstARM32Asr::Opcode = "asr"; | 
 | template <> const char *InstARM32Bic::Opcode = "bic"; | 
 | template <> const char *InstARM32Eor::Opcode = "eor"; | 
 | template <> const char *InstARM32Lsl::Opcode = "lsl"; | 
 | template <> const char *InstARM32Lsr::Opcode = "lsr"; | 
 | template <> const char *InstARM32Mul::Opcode = "mul"; | 
 | template <> const char *InstARM32Orr::Opcode = "orr"; | 
 | template <> const char *InstARM32Rsb::Opcode = "rsb"; | 
 | template <> const char *InstARM32Rsc::Opcode = "rsc"; | 
 | template <> const char *InstARM32Sbc::Opcode = "sbc"; | 
 | template <> const char *InstARM32Sdiv::Opcode = "sdiv"; | 
 | template <> const char *InstARM32Sub::Opcode = "sub"; | 
 | template <> const char *InstARM32Udiv::Opcode = "udiv"; | 
 | // FP | 
 | template <> const char *InstARM32Vadd::Opcode = "vadd"; | 
 | template <> const char *InstARM32Vand::Opcode = "vand"; | 
 | template <> const char *InstARM32Vbsl::Opcode = "vbsl"; | 
 | template <> const char *InstARM32Vceq::Opcode = "vceq"; | 
 | template <> const char *InstARM32ThreeAddrFP<InstARM32::Vcge>::Opcode = "vcge"; | 
 | template <> const char *InstARM32ThreeAddrFP<InstARM32::Vcgt>::Opcode = "vcgt"; | 
 | template <> const char *InstARM32Vdiv::Opcode = "vdiv"; | 
 | template <> const char *InstARM32Veor::Opcode = "veor"; | 
 | template <> const char *InstARM32Vmla::Opcode = "vmla"; | 
 | template <> const char *InstARM32Vmls::Opcode = "vmls"; | 
 | template <> const char *InstARM32Vmul::Opcode = "vmul"; | 
 | template <> const char *InstARM32Vmvn::Opcode = "vmvn"; | 
 | template <> const char *InstARM32Vmovl::Opcode = "vmovl"; | 
 | template <> const char *InstARM32Vmovh::Opcode = "vmovh"; | 
 | template <> const char *InstARM32Vmovhl::Opcode = "vmovhl"; | 
 | template <> const char *InstARM32Vmovlh::Opcode = "vmovlh"; | 
 | template <> const char *InstARM32Vorr::Opcode = "vorr"; | 
 | template <> const char *InstARM32UnaryopFP<InstARM32::Vneg>::Opcode = "vneg"; | 
 | template <> const char *InstARM32ThreeAddrFP<InstARM32::Vshl>::Opcode = "vshl"; | 
 | template <> const char *InstARM32ThreeAddrFP<InstARM32::Vshr>::Opcode = "vshr"; | 
 | template <> const char *InstARM32Vsub::Opcode = "vsub"; | 
 | template <> | 
 | const char *InstARM32ThreeAddrFP<InstARM32::Vqadd>::Opcode = "vqadd"; | 
 | template <> | 
 | const char *InstARM32ThreeAddrFP<InstARM32::Vqsub>::Opcode = "vqsub"; | 
 | template <> | 
 | const char *InstARM32ThreeAddrFP<InstARM32::Vqmovn2>::Opcode = "vqmovn2"; | 
 | template <> | 
 | const char *InstARM32ThreeAddrFP<InstARM32::Vmulh>::Opcode = "vmulh"; | 
 | template <> | 
 | const char *InstARM32ThreeAddrFP<InstARM32::Vmlap>::Opcode = "vmlap"; | 
 | template <> const char *InstARM32ThreeAddrFP<InstARM32::Vzip>::Opcode = "vzip"; | 
 | // Four-addr ops | 
 | template <> const char *InstARM32Mla::Opcode = "mla"; | 
 | template <> const char *InstARM32Mls::Opcode = "mls"; | 
 | // Cmp-like ops | 
 | template <> const char *InstARM32Cmn::Opcode = "cmn"; | 
 | template <> const char *InstARM32Cmp::Opcode = "cmp"; | 
 | template <> const char *InstARM32Tst::Opcode = "tst"; | 
 |  | 
 | void InstARM32::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Str << "[ARM32] "; | 
 |   Inst::dump(Func); | 
 | } | 
 |  | 
 | void InstARM32Mov::emitMultiDestSingleSource(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   Variable *DestLo = getDest(); | 
 |   Variable *DestHi = getDestHi(); | 
 |   auto *Src = llvm::cast<Variable>(getSrc(0)); | 
 |  | 
 |   assert(DestHi->hasReg()); | 
 |   assert(DestLo->hasReg()); | 
 |   assert(Src->hasReg()); | 
 |  | 
 |   Str << "\t" | 
 |          "vmov" << getPredicate() << "\t"; | 
 |   DestLo->emit(Func); | 
 |   Str << ", "; | 
 |   DestHi->emit(Func); | 
 |   Str << ", "; | 
 |   Src->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Mov::emitSingleDestMultiSource(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   Variable *Dest = getDest(); | 
 |   auto *SrcLo = llvm::cast<Variable>(getSrc(0)); | 
 |   auto *SrcHi = llvm::cast<Variable>(getSrc(1)); | 
 |  | 
 |   assert(SrcHi->hasReg()); | 
 |   assert(SrcLo->hasReg()); | 
 |   assert(Dest->hasReg()); | 
 |   assert(getSrcSize() == 2); | 
 |  | 
 |   Str << "\t" | 
 |          "vmov" << getPredicate() << "\t"; | 
 |   Dest->emit(Func); | 
 |   Str << ", "; | 
 |   SrcLo->emit(Func); | 
 |   Str << ", "; | 
 |   SrcHi->emit(Func); | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | bool isVariableWithoutRegister(const Operand *Op) { | 
 |   if (const auto *OpV = llvm::dyn_cast<Variable>(Op)) { | 
 |     return !OpV->hasReg(); | 
 |   } | 
 |   return false; | 
 | } | 
 | bool isMemoryAccess(Operand *Op) { | 
 |   return isVariableWithoutRegister(Op) || llvm::isa<OperandARM32Mem>(Op); | 
 | } | 
 |  | 
 | bool isMoveBetweenCoreAndVFPRegisters(Variable *Dest, Operand *Src) { | 
 |   const Type DestTy = Dest->getType(); | 
 |   const Type SrcTy = Src->getType(); | 
 |   return !isVectorType(DestTy) && !isVectorType(SrcTy) && | 
 |          (isScalarIntegerType(DestTy) == isScalarFloatingType(SrcTy)); | 
 | } | 
 |  | 
 | } // end of anonymous namespace | 
 |  | 
 | void InstARM32Mov::emitSingleDestSingleSource(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   Variable *Dest = getDest(); | 
 |  | 
 |   if (!Dest->hasReg()) { | 
 |     llvm::report_fatal_error("mov can't store."); | 
 |   } | 
 |  | 
 |   Operand *Src0 = getSrc(0); | 
 |   if (isMemoryAccess(Src0)) { | 
 |     llvm::report_fatal_error("mov can't load."); | 
 |   } | 
 |  | 
 |   Type Ty = Dest->getType(); | 
 |   const bool IsVector = isVectorType(Ty); | 
 |   const bool IsScalarFP = isScalarFloatingType(Ty); | 
 |   const bool CoreVFPMove = isMoveBetweenCoreAndVFPRegisters(Dest, Src0); | 
 |   const bool IsVMove = (IsVector || IsScalarFP || CoreVFPMove); | 
 |   const char *Opcode = IsVMove ? "vmov" : "mov"; | 
 |   // when vmov{c}'ing, we need to emit a width string. Otherwise, the | 
 |   // assembler might be tempted to assume we want a vector vmov{c}, and that | 
 |   // is disallowed because ARM. | 
 |   const char *WidthString = !CoreVFPMove ? getFpWidthString(Ty) : ""; | 
 |   CondARM32::Cond Cond = getPredicate(); | 
 |   if (IsVector) | 
 |     assert(CondARM32::isUnconditional(Cond) && | 
 |            "Moves on vectors must be unconditional!"); | 
 |   Str << "\t" << Opcode; | 
 |   if (IsVMove) { | 
 |     Str << Cond << WidthString; | 
 |   } else { | 
 |     Str << WidthString << Cond; | 
 |   } | 
 |   Str << "\t"; | 
 |   Dest->emit(Func); | 
 |   Str << ", "; | 
 |   Src0->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Mov::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   assert(!(isMultiDest() && isMultiSource()) && "Invalid vmov type."); | 
 |   if (isMultiDest()) { | 
 |     emitMultiDestSingleSource(Func); | 
 |     return; | 
 |   } | 
 |  | 
 |   if (isMultiSource()) { | 
 |     emitSingleDestMultiSource(Func); | 
 |     return; | 
 |   } | 
 |  | 
 |   emitSingleDestSingleSource(Func); | 
 | } | 
 |  | 
 | void InstARM32Mov::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   Operand *Src0 = getSrc(0); | 
 |   const CondARM32::Cond Cond = getPredicate(); | 
 |   if (!Dest->hasReg()) { | 
 |     llvm::report_fatal_error("mov can't store."); | 
 |   } | 
 |   if (isMemoryAccess(Src0)) { | 
 |     llvm::report_fatal_error("mov can't load."); | 
 |   } | 
 |  | 
 |   assert(!(isMultiDest() && isMultiSource()) && "Invalid vmov type."); | 
 |   if (isMultiDest()) { | 
 |     Asm->vmovrrd(Dest, getDestHi(), Src0, Cond); | 
 |     return; | 
 |   } | 
 |   if (isMultiSource()) { | 
 |     Asm->vmovdrr(Dest, Src0, getSrc(1), Cond); | 
 |     return; | 
 |   } | 
 |  | 
 |   const Type DestTy = Dest->getType(); | 
 |   const Type SrcTy = Src0->getType(); | 
 |   switch (DestTy) { | 
 |   default: | 
 |     break; // Error | 
 |   case IceType_i1: | 
 |   case IceType_i8: | 
 |   case IceType_i16: | 
 |   case IceType_i32: | 
 |     switch (SrcTy) { | 
 |     default: | 
 |       break; // Error | 
 |     case IceType_i1: | 
 |     case IceType_i8: | 
 |     case IceType_i16: | 
 |     case IceType_i32: | 
 |     case IceType_i64: | 
 |       Asm->mov(Dest, Src0, Cond); | 
 |       return; | 
 |     case IceType_f32: | 
 |       Asm->vmovrs(Dest, Src0, Cond); | 
 |       return; | 
 |     } | 
 |     break; // Error | 
 |   case IceType_i64: | 
 |     if (isScalarIntegerType(SrcTy)) { | 
 |       Asm->mov(Dest, Src0, Cond); | 
 |       return; | 
 |     } | 
 |     if (SrcTy == IceType_f64) { | 
 |       if (const auto *Var = llvm::dyn_cast<Variable>(Src0)) { | 
 |         Asm->vmovdd(Dest, Var, Cond); | 
 |         return; | 
 |       } | 
 |       if (const auto *FpImm = llvm::dyn_cast<OperandARM32FlexFpImm>(Src0)) { | 
 |         Asm->vmovd(Dest, FpImm, Cond); | 
 |         return; | 
 |       } | 
 |     } | 
 |     break; // Error | 
 |   case IceType_f32: | 
 |     switch (SrcTy) { | 
 |     default: | 
 |       break; // Error | 
 |     case IceType_i1: | 
 |     case IceType_i8: | 
 |     case IceType_i16: | 
 |     case IceType_i32: | 
 |       return Asm->vmovsr(Dest, Src0, Cond); | 
 |     case IceType_f32: | 
 |       if (const auto *Var = llvm::dyn_cast<Variable>(Src0)) { | 
 |         Asm->vmovss(Dest, Var, Cond); | 
 |         return; | 
 |       } | 
 |       if (const auto *FpImm = llvm::dyn_cast<OperandARM32FlexFpImm>(Src0)) { | 
 |         Asm->vmovs(Dest, FpImm, Cond); | 
 |         return; | 
 |       } | 
 |       break; // Error | 
 |     } | 
 |     break; // Error | 
 |   case IceType_f64: | 
 |     if (SrcTy == IceType_f64) { | 
 |       if (const auto *Var = llvm::dyn_cast<Variable>(Src0)) { | 
 |         Asm->vmovdd(Dest, Var, Cond); | 
 |         return; | 
 |       } | 
 |       if (const auto *FpImm = llvm::dyn_cast<OperandARM32FlexFpImm>(Src0)) { | 
 |         Asm->vmovd(Dest, FpImm, Cond); | 
 |         return; | 
 |       } | 
 |     } | 
 |     break; // Error | 
 |   // TODO(jpp): Remove vectors of i1. | 
 |   case IceType_v4i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v16i1: | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |   case IceType_v4f32: | 
 |     assert(CondARM32::isUnconditional(Cond) && | 
 |            "Moves on vector must be unconditional!"); | 
 |     if (isVectorType(SrcTy)) { | 
 |       // Mov between different Src and Dest types is used for bitcasting | 
 |       // vectors.  We still want to make sure SrcTy is a vector type. | 
 |       Asm->vorrq(Dest, Src0, Src0); | 
 |       return; | 
 |     } else if (const auto *C = llvm::dyn_cast<ConstantInteger32>(Src0)) { | 
 |       // Mov with constant argument, allowing the initializing all elements of | 
 |       // the vector. | 
 |       if (Asm->vmovqc(Dest, C)) | 
 |         return; | 
 |     } | 
 |   } | 
 |   llvm::report_fatal_error("Mov: don't know how to move " + | 
 |                            typeStdString(SrcTy) + " to " + | 
 |                            typeStdString(DestTy)); | 
 | } | 
 |  | 
 | void InstARM32Mov::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   assert(getSrcSize() == 1 || getSrcSize() == 2); | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Variable *Dest = getDest(); | 
 |   Variable *DestHi = getDestHi(); | 
 |   Dest->dump(Func); | 
 |   if (DestHi) { | 
 |     Str << ", "; | 
 |     DestHi->dump(Func); | 
 |   } | 
 |  | 
 |   dumpOpcodePred(Str, " = mov", getDest()->getType()); | 
 |   Str << " "; | 
 |  | 
 |   dumpSources(Func); | 
 | } | 
 |  | 
 | void InstARM32Br::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   Str << "\t" | 
 |          "b" << getPredicate() << "\t"; | 
 |   if (Label) { | 
 |     Str << Label->getLabelName(); | 
 |   } else { | 
 |     if (isUnconditionalBranch()) { | 
 |       Str << getTargetFalse()->getAsmName(); | 
 |     } else { | 
 |       Str << getTargetTrue()->getAsmName(); | 
 |       if (getTargetFalse()) { | 
 |         startNextInst(Func); | 
 |         Str << "\n\t" | 
 |             << "b" | 
 |             << "\t" << getTargetFalse()->getAsmName(); | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Br::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   if (Label) { | 
 |     Asm->b(Asm->getOrCreateLocalLabel(Label->getNumber()), getPredicate()); | 
 |   } else if (isUnconditionalBranch()) { | 
 |     Asm->b(Asm->getOrCreateCfgNodeLabel(getTargetFalse()->getIndex()), | 
 |            getPredicate()); | 
 |   } else { | 
 |     Asm->b(Asm->getOrCreateCfgNodeLabel(getTargetTrue()->getIndex()), | 
 |            getPredicate()); | 
 |     if (const CfgNode *False = getTargetFalse()) | 
 |       Asm->b(Asm->getOrCreateCfgNodeLabel(False->getIndex()), CondARM32::AL); | 
 |   } | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | void InstARM32Br::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Str << "br "; | 
 |  | 
 |   if (getPredicate() == CondARM32::AL) { | 
 |     if (Label) { | 
 |       Str << "label %" << Label->getLabelName(); | 
 |     } else { | 
 |       Str << "label %" << getTargetFalse()->getName(); | 
 |     } | 
 |     return; | 
 |   } | 
 |  | 
 |   if (Label) { | 
 |     Str << getPredicate() << ", label %" << Label->getLabelName(); | 
 |   } else { | 
 |     Str << getPredicate() << ", label %" << getTargetTrue()->getName(); | 
 |     if (getTargetFalse()) { | 
 |       Str << ", label %" << getTargetFalse()->getName(); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Call::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 1); | 
 |   if (llvm::isa<ConstantInteger32>(getCallTarget())) { | 
 |     // This shouldn't happen (typically have to copy the full 32-bits to a | 
 |     // register and do an indirect jump). | 
 |     llvm::report_fatal_error("ARM32Call to ConstantInteger32"); | 
 |   } else if (const auto *CallTarget = | 
 |                  llvm::dyn_cast<ConstantRelocatable>(getCallTarget())) { | 
 |     // Calls only have 24-bits, but the linker should insert veneers to extend | 
 |     // the range if needed. | 
 |     Str << "\t" | 
 |            "bl" | 
 |            "\t"; | 
 |     CallTarget->emitWithoutPrefix(Func->getTarget()); | 
 |   } else { | 
 |     Str << "\t" | 
 |            "blx" | 
 |            "\t"; | 
 |     getCallTarget()->emit(Func); | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Call::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   if (llvm::isa<ConstantInteger32>(getCallTarget())) { | 
 |     // This shouldn't happen (typically have to copy the full 32-bits to a | 
 |     // register and do an indirect jump). | 
 |     llvm::report_fatal_error("ARM32Call to ConstantInteger32"); | 
 |   } else if (const auto *CallTarget = | 
 |                  llvm::dyn_cast<ConstantRelocatable>(getCallTarget())) { | 
 |     // Calls only have 24-bits, but the linker should insert veneers to extend | 
 |     // the range if needed. | 
 |     Asm->bl(CallTarget); | 
 |   } else { | 
 |     Asm->blx(getCallTarget()); | 
 |   } | 
 |   if (Asm->needsTextFixup()) | 
 |     return emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | void InstARM32Call::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   if (getDest()) { | 
 |     dumpDest(Func); | 
 |     Str << " = "; | 
 |   } | 
 |   Str << "call "; | 
 |   getCallTarget()->dump(Func); | 
 | } | 
 |  | 
 | void InstARM32Label::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   // A label is not really an instruction. Hence, we need to fix the | 
 |   // emitted text size. | 
 |   if (auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>()) | 
 |     Asm->decEmitTextSize(InstSize); | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   Str << getLabelName() << ":"; | 
 | } | 
 |  | 
 | void InstARM32Label::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->bindLocalLabel(this, Number); | 
 |   if (OffsetReloc != nullptr) { | 
 |     Asm->bindRelocOffset(OffsetReloc); | 
 |   } | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | void InstARM32Label::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Str << getLabelName() << ":"; | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32LoadBase<K>::emitIAS(const Cfg *Func) const { | 
 |   emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Ldr::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 1); | 
 |   assert(getDest()->hasReg()); | 
 |   Variable *Dest = getDest(); | 
 |   Type Ty = Dest->getType(); | 
 |   const bool IsVector = isVectorType(Ty); | 
 |   const bool IsScalarFloat = isScalarFloatingType(Ty); | 
 |   const char *ActualOpcode = | 
 |       IsVector ? "vld1" : (IsScalarFloat ? "vldr" : "ldr"); | 
 |   const char *WidthString = IsVector ? "" : getWidthString(Ty); | 
 |   Str << "\t" << ActualOpcode; | 
 |   const bool IsVInst = IsVector || IsScalarFloat; | 
 |   if (IsVInst) { | 
 |     Str << getPredicate() << WidthString; | 
 |   } else { | 
 |     Str << WidthString << getPredicate(); | 
 |   } | 
 |   if (IsVector) | 
 |     Str << "." << getVecElmtBitsize(Ty); | 
 |   Str << "\t"; | 
 |   getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->emit(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Vldr1d::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 1); | 
 |   assert(getDest()->hasReg()); | 
 |   Variable *Dest = getDest(); | 
 |   Type Ty = Dest->getType(); | 
 |   const bool IsVector = isVectorType(Ty); | 
 |   const bool IsScalarFloat = isScalarFloatingType(Ty); | 
 |   const char *ActualOpcode = | 
 |       IsVector ? "vld1" : (IsScalarFloat ? "vldr" : "ldr"); | 
 |   const char *WidthString = IsVector ? "" : getWidthString(Ty); | 
 |   Str << "\t" << ActualOpcode; | 
 |   const bool IsVInst = IsVector || IsScalarFloat; | 
 |   if (IsVInst) { | 
 |     Str << getPredicate() << WidthString; | 
 |   } else { | 
 |     Str << WidthString << getPredicate(); | 
 |   } | 
 |   if (IsVector) | 
 |     Str << "." << getVecElmtBitsize(Ty); | 
 |   Str << "\t"; | 
 |   getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->emit(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Vldr1q::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 1); | 
 |   assert(getDest()->hasReg()); | 
 |   Variable *Dest = getDest(); | 
 |   Type Ty = Dest->getType(); | 
 |   const bool IsVector = isVectorType(Ty); | 
 |   const bool IsScalarFloat = isScalarFloatingType(Ty); | 
 |   const char *ActualOpcode = | 
 |       IsVector ? "vld1" : (IsScalarFloat ? "vldr" : "ldr"); | 
 |   const char *WidthString = IsVector ? "" : getWidthString(Ty); | 
 |   Str << "\t" << ActualOpcode; | 
 |   const bool IsVInst = IsVector || IsScalarFloat; | 
 |   if (IsVInst) { | 
 |     Str << getPredicate() << WidthString; | 
 |   } else { | 
 |     Str << WidthString << getPredicate(); | 
 |   } | 
 |   if (IsVector) | 
 |     Str << "." << getVecElmtBitsize(Ty); | 
 |   Str << "\t"; | 
 |   getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->emit(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Ldr::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Variable *Dest = getDest(); | 
 |   const Type DestTy = Dest->getType(); | 
 |   switch (DestTy) { | 
 |   default: | 
 |     llvm::report_fatal_error("Ldr on unknown type: " + typeStdString(DestTy)); | 
 |   case IceType_i1: | 
 |   case IceType_i8: | 
 |   case IceType_i16: | 
 |   case IceType_i32: | 
 |   case IceType_i64: | 
 |     Asm->ldr(Dest, getSrc(0), getPredicate(), Func->getTarget()); | 
 |     break; | 
 |   case IceType_f32: | 
 |     Asm->vldrs(Dest, getSrc(0), getPredicate(), Func->getTarget()); | 
 |     break; | 
 |   case IceType_f64: | 
 |     Asm->vldrd(Dest, getSrc(0), getPredicate(), Func->getTarget()); | 
 |     break; | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |   case IceType_v4f32: | 
 |   case IceType_v16i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v4i1: | 
 |     Asm->vld1qr(getVecElmtBitsize(DestTy), Dest, getSrc(0), Func->getTarget()); | 
 |     break; | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Vldr1d::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Variable *Dest = getDest(); | 
 |   Asm->vld1(32, Dest, getSrc(0), Func->getTarget()); | 
 | } | 
 |  | 
 | template <> void InstARM32Vldr1q::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Variable *Dest = getDest(); | 
 |   Asm->vld1(64, Dest, getSrc(0), Func->getTarget()); | 
 | } | 
 |  | 
 | template <> void InstARM32Ldrex::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 1); | 
 |   assert(getDest()->hasReg()); | 
 |   Variable *Dest = getDest(); | 
 |   Type DestTy = Dest->getType(); | 
 |   assert(isScalarIntegerType(DestTy)); | 
 |   const char *WidthString = getWidthString(DestTy); | 
 |   Str << "\t" << Opcode << WidthString << getPredicate() << "\t"; | 
 |   getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->emit(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Ldrex::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   assert(getDest()->hasReg()); | 
 |   Variable *Dest = getDest(); | 
 |   assert(isScalarIntegerType(Dest->getType())); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->ldrex(Dest, getSrc(0), getPredicate(), Func->getTarget()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32TwoAddrGPR<K>::emitIAS(const Cfg *Func) const { | 
 |   emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K, bool Nws> | 
 | void InstARM32UnaryopGPR<K, Nws>::emitIAS(const Cfg *Func) const { | 
 |   emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Rbit::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->rbit(getDest(), getSrc(0), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Rev::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->rev(getDest(), getSrc(0), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Movw::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 1); | 
 |   Str << "\t" << Opcode << getPredicate() << "\t"; | 
 |   getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   auto *Src0 = llvm::cast<Constant>(getSrc(0)); | 
 |   if (auto *CR = llvm::dyn_cast<ConstantRelocatable>(Src0)) { | 
 |     Str << "#:lower16:"; | 
 |     CR->emitWithoutPrefix(Func->getTarget()); | 
 |     if (getFlags().getUseNonsfi()) { | 
 |       Str << " - ."; | 
 |     } | 
 |   } else { | 
 |     Src0->emit(Func); | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Movw::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->movw(getDest(), getSrc(0), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Movt::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 2); | 
 |   Variable *Dest = getDest(); | 
 |   auto *Src1 = llvm::cast<Constant>(getSrc(1)); | 
 |   Str << "\t" << Opcode << getPredicate() << "\t"; | 
 |   Dest->emit(Func); | 
 |   Str << ", "; | 
 |   if (auto *CR = llvm::dyn_cast<ConstantRelocatable>(Src1)) { | 
 |     Str << "#:upper16:"; | 
 |     CR->emitWithoutPrefix(Func->getTarget()); | 
 |     if (getFlags().getUseNonsfi()) { | 
 |       Str << " - ."; | 
 |     } | 
 |   } else { | 
 |     Src1->emit(Func); | 
 |   } | 
 | } | 
 |  | 
 | template <> void InstARM32Movt::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 2); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->movt(getDest(), getSrc(1), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Clz::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->clz(getDest(), getSrc(0), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Mvn::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->mvn(getDest(), getSrc(0), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Sxt::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->sxt(getDest(), getSrc(0), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Uxt::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->uxt(getDest(), getSrc(0), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32UnaryopFP<K>::emitIAS(const Cfg *Func) const { | 
 |   emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <InstARM32::InstKindARM32 K> | 
 | void InstARM32UnaryopSignAwareFP<K>::emitIAS(const Cfg *Func) const { | 
 |   InstARM32::emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | template <> void InstARM32Vsqrt::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Operand *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   case IceType_f32: | 
 |     Asm->vsqrts(Dest, getSrc(0), getPredicate()); | 
 |     break; | 
 |   case IceType_f64: | 
 |     Asm->vsqrtd(Dest, getSrc(0), getPredicate()); | 
 |     break; | 
 |   default: | 
 |     llvm::report_fatal_error("Vsqrt of non-floating type"); | 
 |   } | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | const char *InstARM32Pop::getGPROpcode() const { return "pop"; } | 
 |  | 
 | const char *InstARM32Pop::getSRegOpcode() const { return "vpop"; } | 
 |  | 
 | Variable *InstARM32Pop::getStackReg(SizeT Index) const { return Dests[Index]; } | 
 |  | 
 | SizeT InstARM32Pop::getNumStackRegs() const { return Dests.size(); } | 
 |  | 
 | void InstARM32Pop::emitSingleGPR(const Cfg *Func, const EmitForm Form, | 
 |                                  const Variable *Reg) const { | 
 |   switch (Form) { | 
 |   case Emit_Text: | 
 |     emitGPRsAsText(Func); | 
 |     return; | 
 |   case Emit_Binary: | 
 |     Func->getAssembler<ARM32::AssemblerARM32>()->pop(Reg, CondARM32::AL); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Pop::emitMultipleGPRs(const Cfg *Func, const EmitForm Form, | 
 |                                     IValueT Registers) const { | 
 |   switch (Form) { | 
 |   case Emit_Text: | 
 |     emitGPRsAsText(Func); | 
 |     return; | 
 |   case Emit_Binary: | 
 |     Func->getAssembler<ARM32::AssemblerARM32>()->popList(Registers, | 
 |                                                          CondARM32::AL); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Pop::emitSRegs(const Cfg *Func, const EmitForm Form, | 
 |                              const Variable *BaseReg, SizeT RegCount) const { | 
 |   switch (Form) { | 
 |   case Emit_Text: | 
 |     emitSRegsAsText(Func, BaseReg, RegCount); | 
 |     return; | 
 |   case Emit_Binary: | 
 |     Func->getAssembler<ARM32::AssemblerARM32>()->vpop(BaseReg, RegCount, | 
 |                                                       CondARM32::AL); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | const char *InstARM32Push::getGPROpcode() const { return "push"; } | 
 |  | 
 | const char *InstARM32Push::getSRegOpcode() const { return "vpush"; } | 
 |  | 
 | Variable *InstARM32Push::getStackReg(SizeT Index) const { | 
 |   return llvm::cast<Variable>(getSrc(Index)); | 
 | } | 
 |  | 
 | SizeT InstARM32Push::getNumStackRegs() const { return getSrcSize(); } | 
 |  | 
 | void InstARM32Push::emitSingleGPR(const Cfg *Func, const EmitForm Form, | 
 |                                   const Variable *Reg) const { | 
 |   switch (Form) { | 
 |   case Emit_Text: | 
 |     emitGPRsAsText(Func); | 
 |     return; | 
 |   case Emit_Binary: | 
 |     Func->getAssembler<ARM32::AssemblerARM32>()->push(Reg, CondARM32::AL); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Push::emitMultipleGPRs(const Cfg *Func, const EmitForm Form, | 
 |                                      IValueT Registers) const { | 
 |   switch (Form) { | 
 |   case Emit_Text: | 
 |     emitGPRsAsText(Func); | 
 |     return; | 
 |   case Emit_Binary: | 
 |     Func->getAssembler<ARM32::AssemblerARM32>()->pushList(Registers, | 
 |                                                           CondARM32::AL); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Push::emitSRegs(const Cfg *Func, const EmitForm Form, | 
 |                               const Variable *BaseReg, SizeT RegCount) const { | 
 |   switch (Form) { | 
 |   case Emit_Text: | 
 |     emitSRegsAsText(Func, BaseReg, RegCount); | 
 |     return; | 
 |   case Emit_Binary: | 
 |     Func->getAssembler<ARM32::AssemblerARM32>()->vpush(BaseReg, RegCount, | 
 |                                                        CondARM32::AL); | 
 |     return; | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Ret::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   assert(getSrcSize() > 0); | 
 |   auto *LR = llvm::cast<Variable>(getSrc(0)); | 
 |   assert(LR->hasReg()); | 
 |   assert(LR->getRegNum() == RegARM32::Reg_lr); | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   Str << "\t" | 
 |          "bx" | 
 |          "\t"; | 
 |   LR->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Ret::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->bx(RegARM32::Encoded_Reg_lr); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | void InstARM32Ret::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Type Ty = (getSrcSize() == 1 ? IceType_void : getSrc(0)->getType()); | 
 |   Str << "ret." << Ty << " "; | 
 |   dumpSources(Func); | 
 | } | 
 |  | 
 | void InstARM32Str::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 2); | 
 |   Type Ty = getSrc(0)->getType(); | 
 |   const bool IsVectorStore = isVectorType(Ty); | 
 |   const bool IsScalarFloat = isScalarFloatingType(Ty); | 
 |   const char *Opcode = | 
 |       IsVectorStore ? "vst1" : (IsScalarFloat ? "vstr" : "str"); | 
 |   Str << "\t" << Opcode; | 
 |   const bool IsVInst = IsVectorStore || IsScalarFloat; | 
 |   if (IsVInst) { | 
 |     Str << getPredicate() << getWidthString(Ty); | 
 |   } else { | 
 |     Str << getWidthString(Ty) << getPredicate(); | 
 |   } | 
 |   if (IsVectorStore) | 
 |     Str << "." << getVecElmtBitsize(Ty); | 
 |   Str << "\t"; | 
 |   getSrc(0)->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(1)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Str::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 2); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Operand *Src0 = getSrc(0); | 
 |   const Operand *Src1 = getSrc(1); | 
 |   Type Ty = Src0->getType(); | 
 |   switch (Ty) { | 
 |   default: | 
 |     llvm::report_fatal_error("Str on unknown type: " + typeStdString(Ty)); | 
 |   case IceType_i1: | 
 |   case IceType_i8: | 
 |   case IceType_i16: | 
 |   case IceType_i32: | 
 |   case IceType_i64: | 
 |     Asm->str(Src0, Src1, getPredicate(), Func->getTarget()); | 
 |     break; | 
 |   case IceType_f32: | 
 |     Asm->vstrs(Src0, Src1, getPredicate(), Func->getTarget()); | 
 |     break; | 
 |   case IceType_f64: | 
 |     Asm->vstrd(Src0, Src1, getPredicate(), Func->getTarget()); | 
 |     break; | 
 |   case IceType_v16i8: | 
 |   case IceType_v8i16: | 
 |   case IceType_v4i32: | 
 |   case IceType_v4f32: | 
 |   case IceType_v16i1: | 
 |   case IceType_v8i1: | 
 |   case IceType_v4i1: | 
 |     Asm->vst1qr(getVecElmtBitsize(Ty), Src0, Src1, Func->getTarget()); | 
 |     break; | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Str::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Type Ty = getSrc(0)->getType(); | 
 |   dumpOpcodePred(Str, "str", Ty); | 
 |   Str << " "; | 
 |   getSrc(1)->dump(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->dump(Func); | 
 | } | 
 |  | 
 | void InstARM32Strex::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   assert(getSrcSize() == 2); | 
 |   Type Ty = getSrc(0)->getType(); | 
 |   assert(isScalarIntegerType(Ty)); | 
 |   Variable *Dest = getDest(); | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   static constexpr char Opcode[] = "strex"; | 
 |   const char *WidthString = getWidthString(Ty); | 
 |   Str << "\t" << Opcode << WidthString << getPredicate() << "\t"; | 
 |   Dest->emit(Func); | 
 |   Str << ", "; | 
 |   emitSources(Func); | 
 | } | 
 |  | 
 | void InstARM32Strex::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 2); | 
 |   const Operand *Src0 = getSrc(0); | 
 |   assert(isScalarIntegerType(Src0->getType())); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->strex(Dest, Src0, getSrc(1), getPredicate(), Func->getTarget()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | void InstARM32Strex::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Variable *Dest = getDest(); | 
 |   Dest->dump(Func); | 
 |   Str << " = "; | 
 |   Type Ty = getSrc(0)->getType(); | 
 |   dumpOpcodePred(Str, "strex", Ty); | 
 |   Str << " "; | 
 |   getSrc(1)->dump(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->dump(Func); | 
 | } | 
 |  | 
 | void InstARM32Vstr1::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 2); | 
 |   Type Ty = getSrc(0)->getType(); | 
 |   const bool IsVectorStore = isVectorType(Ty); | 
 |   const bool IsScalarFloat = isScalarFloatingType(Ty); | 
 |   const char *Opcode = | 
 |       IsVectorStore ? "vst1" : (IsScalarFloat ? "vstr" : "str"); | 
 |   Str << "\t" << Opcode; | 
 |   const bool IsVInst = IsVectorStore || IsScalarFloat; | 
 |   if (IsVInst) { | 
 |     Str << getPredicate() << getWidthString(Ty); | 
 |   } else { | 
 |     Str << getWidthString(Ty) << getPredicate(); | 
 |   } | 
 |   if (IsVectorStore) | 
 |     Str << "." << getVecElmtBitsize(Ty); | 
 |   Str << "\t"; | 
 |   getSrc(0)->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(1)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Vstr1::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 2); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Operand *Src0 = getSrc(0); | 
 |   const Operand *Src1 = getSrc(1); | 
 |   Asm->vst1(Size, Src0, Src1, Func->getTarget()); | 
 | } | 
 |  | 
 | void InstARM32Vstr1::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Type Ty = getSrc(0)->getType(); | 
 |   dumpOpcodePred(Str, "str", Ty); | 
 |   Str << " "; | 
 |   getSrc(1)->dump(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->dump(Func); | 
 | } | 
 |  | 
 | void InstARM32Vdup::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 2); | 
 |   Type Ty = getSrc(0)->getType(); | 
 |   const char *Opcode = "vdup"; | 
 |   Str << "\t" << Opcode; | 
 |   Str << getPredicate() << "." << getWidthString(Ty) << getVecElmtBitsize(Ty); | 
 |   Str << "\t"; | 
 |   getSrc(0)->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(1)->emit(Func); | 
 |   Str << ", " << Idx; | 
 | } | 
 |  | 
 | void InstARM32Vdup::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Operand *Dest = getDest(); | 
 |   const Operand *Src = getSrc(0); | 
 |   Type DestTy = Dest->getType(); | 
 |   Asm->vdup(typeElementType(DestTy), Dest, Src, Idx); | 
 | } | 
 |  | 
 | void InstARM32Vdup::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   dumpDest(Func); | 
 |   Str << " = "; | 
 |   dumpOpcodePred(Str, "vdup", getDest()->getType()); | 
 |   Str << " "; | 
 |   dumpSources(Func); | 
 |   Str << ", " << Idx; | 
 | } | 
 |  | 
 | void InstARM32Trap::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 0); | 
 |   // There isn't a mnemonic for the special NaCl Trap encoding, so dump | 
 |   // the raw bytes. | 
 |   Str << "\t.long 0x"; | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   for (uint8_t I : Asm->getNonExecBundlePadding()) { | 
 |     Str.write_hex(I); | 
 |   } | 
 | } | 
 |  | 
 | void InstARM32Trap::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->trap(); | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | void InstARM32Trap::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Str << "trap"; | 
 | } | 
 |  | 
 | void InstARM32Umull::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 2); | 
 |   assert(getDest()->hasReg()); | 
 |   Str << "\t" | 
 |          "umull" << getPredicate() << "\t"; | 
 |   getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   DestHi->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(1)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Umull::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 2); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->umull(getDest(), DestHi, getSrc(0), getSrc(1), getPredicate()); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | void InstARM32Umull::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   dumpDest(Func); | 
 |   Str << " = "; | 
 |   dumpOpcodePred(Str, "umull", getDest()->getType()); | 
 |   Str << " "; | 
 |   dumpSources(Func); | 
 | } | 
 |  | 
 | namespace { | 
 | const char *vcvtVariantSuffix(const InstARM32Vcvt::VcvtVariant Variant) { | 
 |   switch (Variant) { | 
 |   case InstARM32Vcvt::S2si: | 
 |     return ".s32.f32"; | 
 |   case InstARM32Vcvt::S2ui: | 
 |     return ".u32.f32"; | 
 |   case InstARM32Vcvt::Si2s: | 
 |     return ".f32.s32"; | 
 |   case InstARM32Vcvt::Ui2s: | 
 |     return ".f32.u32"; | 
 |   case InstARM32Vcvt::D2si: | 
 |     return ".s32.f64"; | 
 |   case InstARM32Vcvt::D2ui: | 
 |     return ".u32.f64"; | 
 |   case InstARM32Vcvt::Si2d: | 
 |     return ".f64.s32"; | 
 |   case InstARM32Vcvt::Ui2d: | 
 |     return ".f64.u32"; | 
 |   case InstARM32Vcvt::S2d: | 
 |     return ".f64.f32"; | 
 |   case InstARM32Vcvt::D2s: | 
 |     return ".f32.f64"; | 
 |   case InstARM32Vcvt::Vs2si: | 
 |     return ".s32.f32"; | 
 |   case InstARM32Vcvt::Vs2ui: | 
 |     return ".u32.f32"; | 
 |   case InstARM32Vcvt::Vsi2s: | 
 |     return ".f32.s32"; | 
 |   case InstARM32Vcvt::Vui2s: | 
 |     return ".f32.u32"; | 
 |   } | 
 |   llvm::report_fatal_error("Invalid VcvtVariant enum."); | 
 | } | 
 | } // end of anonymous namespace | 
 |  | 
 | void InstARM32Vcvt::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 1); | 
 |   assert(getDest()->hasReg()); | 
 |   Str << "\t" | 
 |          "vcvt" << getPredicate() << vcvtVariantSuffix(Variant) << "\t"; | 
 |   getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Vcvt::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   switch (Variant) { | 
 |   case S2si: | 
 |     Asm->vcvtis(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case S2ui: | 
 |     Asm->vcvtus(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case Si2s: | 
 |     Asm->vcvtsi(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case Ui2s: | 
 |     Asm->vcvtsu(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case D2si: | 
 |     Asm->vcvtid(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case D2ui: | 
 |     Asm->vcvtud(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case Si2d: | 
 |     Asm->vcvtdi(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case Ui2d: | 
 |     Asm->vcvtdu(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case S2d: | 
 |     Asm->vcvtds(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case D2s: | 
 |     Asm->vcvtsd(getDest(), getSrc(0), getPredicate()); | 
 |     break; | 
 |   case Vs2si: | 
 |     Asm->vcvtqsi(getDest(), getSrc(0)); | 
 |     break; | 
 |   case Vs2ui: | 
 |     Asm->vcvtqsu(getDest(), getSrc(0)); | 
 |     break; | 
 |   case Vsi2s: | 
 |     Asm->vcvtqis(getDest(), getSrc(0)); | 
 |     break; | 
 |   case Vui2s: | 
 |     Asm->vcvtqus(getDest(), getSrc(0)); | 
 |     break; | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | void InstARM32Vcvt::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   dumpDest(Func); | 
 |   Str << " = " | 
 |       << "vcvt" << getPredicate() << vcvtVariantSuffix(Variant) << " "; | 
 |   dumpSources(Func); | 
 | } | 
 |  | 
 | void InstARM32Vcmp::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 2); | 
 |   Str << "\t" | 
 |          "vcmp" << getPredicate() << getFpWidthString(getSrc(0)->getType()) | 
 |       << "\t"; | 
 |   getSrc(0)->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(1)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Vcmp::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 2); | 
 |   const Operand *Src0 = getSrc(0); | 
 |   const Type Ty = Src0->getType(); | 
 |   const Operand *Src1 = getSrc(1); | 
 |   const CondARM32::Cond Cond = getPredicate(); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   if (llvm::isa<OperandARM32FlexFpZero>(Src1)) { | 
 |     switch (Ty) { | 
 |     case IceType_f32: | 
 |       Asm->vcmpsz(Src0, Cond); | 
 |       break; | 
 |     case IceType_f64: | 
 |       Asm->vcmpdz(Src0, Cond); | 
 |       break; | 
 |     default: | 
 |       llvm::report_fatal_error("Vcvt on non floating value"); | 
 |     } | 
 |   } else { | 
 |     switch (Ty) { | 
 |     case IceType_f32: | 
 |       Asm->vcmps(Src0, Src1, Cond); | 
 |       break; | 
 |     case IceType_f64: | 
 |       Asm->vcmpd(Src0, Src1, Cond); | 
 |       break; | 
 |     default: | 
 |       llvm::report_fatal_error("Vcvt on non floating value"); | 
 |     } | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | void InstARM32Vcmp::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Str << "vcmp" << getPredicate() << getFpWidthString(getSrc(0)->getType()); | 
 |   dumpSources(Func); | 
 | } | 
 |  | 
 | void InstARM32Vmrs::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 0); | 
 |   Str << "\t" | 
 |          "vmrs" << getPredicate() << "\t" | 
 |                                      "APSR_nzcv" | 
 |                                      ", " | 
 |                                      "FPSCR"; | 
 | } | 
 |  | 
 | void InstARM32Vmrs::emitIAS(const Cfg *Func) const { | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   Asm->vmrsAPSR_nzcv(getPredicate()); | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | void InstARM32Vmrs::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   Str << "APSR{n,z,v,c} = vmrs" << getPredicate() << "\t" | 
 |                                                      "FPSCR{n,z,c,v}"; | 
 | } | 
 |  | 
 | void InstARM32Vabs::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 1); | 
 |   Str << "\t" | 
 |          "vabs" << getPredicate() << getFpWidthString(getSrc(0)->getType()) | 
 |       << "\t"; | 
 |   getDest()->emit(Func); | 
 |   Str << ", "; | 
 |   getSrc(0)->emit(Func); | 
 | } | 
 |  | 
 | void InstARM32Vabs::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 1); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   const Variable *Dest = getDest(); | 
 |   switch (Dest->getType()) { | 
 |   default: | 
 |     llvm::report_fatal_error("fabs not defined on type " + | 
 |                              typeStdString(Dest->getType())); | 
 |   case IceType_f32: | 
 |     Asm->vabss(Dest, getSrc(0), getPredicate()); | 
 |     break; | 
 |   case IceType_f64: | 
 |     Asm->vabsd(Dest, getSrc(0), getPredicate()); | 
 |     break; | 
 |   case IceType_v4f32: | 
 |     assert(CondARM32::isUnconditional(getPredicate()) && | 
 |            "fabs must be unconditional"); | 
 |     Asm->vabsq(Dest, getSrc(0)); | 
 |   } | 
 |   assert(!Asm->needsTextFixup()); | 
 | } | 
 |  | 
 | void InstARM32Vabs::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrDump(); | 
 |   dumpDest(Func); | 
 |   Str << " = vabs" << getPredicate() << getFpWidthString(getSrc(0)->getType()); | 
 | } | 
 |  | 
 | void InstARM32Dmb::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   assert(getSrcSize() == 0); | 
 |   Str << "\t" | 
 |          "dmb" | 
 |          "\t" | 
 |          "sy"; | 
 | } | 
 |  | 
 | void InstARM32Dmb::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 0); | 
 |   auto *Asm = Func->getAssembler<ARM32::AssemblerARM32>(); | 
 |   constexpr ARM32::IValueT SyOption = 0xF; // i.e. 1111 | 
 |   Asm->dmb(SyOption); | 
 |   if (Asm->needsTextFixup()) | 
 |     emitUsingTextFixup(Func); | 
 | } | 
 |  | 
 | void InstARM32Dmb::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Func->getContext()->getStrDump() << "dmb\t" | 
 |                                       "sy"; | 
 | } | 
 |  | 
 | void InstARM32Nop::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   assert(getSrcSize() == 0); | 
 |   Func->getContext()->getStrEmit() << "\t" | 
 |                                    << "nop"; | 
 | } | 
 |  | 
 | void InstARM32Nop::emitIAS(const Cfg *Func) const { | 
 |   assert(getSrcSize() == 0); | 
 |   Func->getAssembler<ARM32::AssemblerARM32>()->nop(); | 
 | } | 
 |  | 
 | void InstARM32Nop::dump(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   assert(getSrcSize() == 0); | 
 |   Func->getContext()->getStrDump() << "nop"; | 
 | } | 
 |  | 
 | void OperandARM32Mem::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   Str << "["; | 
 |   getBase()->emit(Func); | 
 |   switch (getAddrMode()) { | 
 |   case PostIndex: | 
 |   case NegPostIndex: | 
 |     Str << "]"; | 
 |     break; | 
 |   default: | 
 |     break; | 
 |   } | 
 |   if (isRegReg()) { | 
 |     Str << ", "; | 
 |     if (isNegAddrMode()) { | 
 |       Str << "-"; | 
 |     } | 
 |     getIndex()->emit(Func); | 
 |     if (getShiftOp() != kNoShift) { | 
 |       Str << ", " << InstARM32ShiftAttributes[getShiftOp()].EmitString << " #" | 
 |           << getShiftAmt(); | 
 |     } | 
 |   } else { | 
 |     ConstantInteger32 *Offset = getOffset(); | 
 |     if (Offset && Offset->getValue() != 0) { | 
 |       Str << ", "; | 
 |       Offset->emit(Func); | 
 |     } | 
 |   } | 
 |   switch (getAddrMode()) { | 
 |   case Offset: | 
 |   case NegOffset: | 
 |     Str << "]"; | 
 |     break; | 
 |   case PreIndex: | 
 |   case NegPreIndex: | 
 |     Str << "]!"; | 
 |     break; | 
 |   case PostIndex: | 
 |   case NegPostIndex: | 
 |     // Brace is already closed off. | 
 |     break; | 
 |   } | 
 | } | 
 |  | 
 | void OperandARM32Mem::dump(const Cfg *Func, Ostream &Str) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Str << "["; | 
 |   if (Func) | 
 |     getBase()->dump(Func); | 
 |   else | 
 |     getBase()->dump(Str); | 
 |   Str << ", "; | 
 |   if (isRegReg()) { | 
 |     if (isNegAddrMode()) { | 
 |       Str << "-"; | 
 |     } | 
 |     if (Func) | 
 |       getIndex()->dump(Func); | 
 |     else | 
 |       getIndex()->dump(Str); | 
 |     if (getShiftOp() != kNoShift) { | 
 |       Str << ", " << InstARM32ShiftAttributes[getShiftOp()].EmitString << " #" | 
 |           << getShiftAmt(); | 
 |     } | 
 |   } else { | 
 |     getOffset()->dump(Func, Str); | 
 |   } | 
 |   Str << "] AddrMode==" << getAddrMode(); | 
 | } | 
 |  | 
 | void OperandARM32ShAmtImm::emit(const Cfg *Func) const { ShAmt->emit(Func); } | 
 |  | 
 | void OperandARM32ShAmtImm::dump(const Cfg *, Ostream &Str) const { | 
 |   ShAmt->dump(Str); | 
 | } | 
 |  | 
 | OperandARM32FlexImm *OperandARM32FlexImm::create(Cfg *Func, Type Ty, | 
 |                                                  uint32_t Imm, | 
 |                                                  uint32_t RotateAmt) { | 
 |   // The assembler wants the smallest rotation. Rotate if needed. Note: Imm is | 
 |   // an 8-bit value. | 
 |   assert(Utils::IsUint(8, Imm) && | 
 |          "Flex immediates can only be defined on 8-bit immediates"); | 
 |   while ((Imm & 0x03) == 0 && RotateAmt > 0) { | 
 |     --RotateAmt; | 
 |     Imm = Imm >> 2; | 
 |   } | 
 |   return new (Func->allocate<OperandARM32FlexImm>()) | 
 |       OperandARM32FlexImm(Func, Ty, Imm, RotateAmt); | 
 | } | 
 |  | 
 | void OperandARM32FlexImm::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   uint32_t Imm = getImm(); | 
 |   uint32_t RotateAmt = getRotateAmt(); | 
 |   Str << "#" << Utils::rotateRight32(Imm, 2 * RotateAmt); | 
 | } | 
 |  | 
 | void OperandARM32FlexImm::dump(const Cfg * /* Func */, Ostream &Str) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   uint32_t Imm = getImm(); | 
 |   uint32_t RotateAmt = getRotateAmt(); | 
 |   Str << "#(" << Imm << " ror 2*" << RotateAmt << ")"; | 
 | } | 
 |  | 
 | namespace { | 
 | static constexpr uint32_t a = 0x80; | 
 | static constexpr uint32_t b = 0x40; | 
 | static constexpr uint32_t cdefgh = 0x3F; | 
 | static constexpr uint32_t AllowedBits = a | b | cdefgh; | 
 | static_assert(AllowedBits == 0xFF, | 
 |               "Invalid mask for f32/f64 constant rematerialization."); | 
 |  | 
 | // There's no loss in always returning the modified immediate as float. | 
 | // TODO(jpp): returning a double causes problems when outputting the constants | 
 | // for filetype=asm. Why? | 
 | float materializeFloatImmediate(uint32_t ModifiedImm) { | 
 |   const uint32_t Ret = ((ModifiedImm & a) ? 0x80000000 : 0) | | 
 |                        ((ModifiedImm & b) ? 0x3E000000 : 0x40000000) | | 
 |                        ((ModifiedImm & cdefgh) << 19); | 
 |   return Utils::bitCopy<float>(Ret); | 
 | } | 
 |  | 
 | } // end of anonymous namespace | 
 |  | 
 | void OperandARM32FlexFpImm::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   switch (Ty) { | 
 |   default: | 
 |     llvm::report_fatal_error("Invalid flex fp imm type."); | 
 |   case IceType_f64: | 
 |   case IceType_f32: | 
 |     Str << "#" << materializeFloatImmediate(ModifiedImm) | 
 |         << " @ Modified: " << ModifiedImm; | 
 |     break; | 
 |   } | 
 | } | 
 |  | 
 | void OperandARM32FlexFpImm::dump(const Cfg * /*Func*/, Ostream &Str) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Str << "#" << materializeFloatImmediate(ModifiedImm) << getFpWidthString(Ty); | 
 | } | 
 |  | 
 | void OperandARM32FlexFpZero::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   switch (Ty) { | 
 |   default: | 
 |     llvm::report_fatal_error("Invalid flex fp imm type."); | 
 |   case IceType_f64: | 
 |   case IceType_f32: | 
 |     Str << "#0.0"; | 
 |   } | 
 | } | 
 |  | 
 | void OperandARM32FlexFpZero::dump(const Cfg * /*Func*/, Ostream &Str) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Str << "#0.0" << getFpWidthString(Ty); | 
 | } | 
 |  | 
 | void OperandARM32FlexReg::emit(const Cfg *Func) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Ostream &Str = Func->getContext()->getStrEmit(); | 
 |   getReg()->emit(Func); | 
 |   if (getShiftOp() != kNoShift) { | 
 |     Str << ", " << InstARM32ShiftAttributes[getShiftOp()].EmitString << " "; | 
 |     getShiftAmt()->emit(Func); | 
 |   } | 
 | } | 
 |  | 
 | void OperandARM32FlexReg::dump(const Cfg *Func, Ostream &Str) const { | 
 |   if (!BuildDefs::dump()) | 
 |     return; | 
 |   Variable *Reg = getReg(); | 
 |   if (Func) | 
 |     Reg->dump(Func); | 
 |   else | 
 |     Reg->dump(Str); | 
 |   if (getShiftOp() != kNoShift) { | 
 |     Str << ", " << InstARM32ShiftAttributes[getShiftOp()].EmitString << " "; | 
 |     if (Func) | 
 |       getShiftAmt()->dump(Func); | 
 |     else | 
 |       getShiftAmt()->dump(Str); | 
 |   } | 
 | } | 
 |  | 
 | // Force instantition of template classes | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Adc>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Add>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::And>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Asr>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Bic>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Eor>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Lsl>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Lsr>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Mul>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Orr>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Rsb>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Rsc>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Sbc>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Sdiv>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Sub>; | 
 | template class InstARM32ThreeAddrGPR<InstARM32::Udiv>; | 
 |  | 
 | template class InstARM32ThreeAddrFP<InstARM32::Vadd>; | 
 | template class InstARM32ThreeAddrSignAwareFP<InstARM32::Vcge>; | 
 | template class InstARM32ThreeAddrSignAwareFP<InstARM32::Vcgt>; | 
 | template class InstARM32ThreeAddrFP<InstARM32::Vdiv>; | 
 | template class InstARM32ThreeAddrFP<InstARM32::Veor>; | 
 | template class InstARM32FourAddrFP<InstARM32::Vmla>; | 
 | template class InstARM32FourAddrFP<InstARM32::Vmls>; | 
 | template class InstARM32ThreeAddrFP<InstARM32::Vmul>; | 
 | template class InstARM32UnaryopSignAwareFP<InstARM32::Vneg>; | 
 | template class InstARM32ThreeAddrSignAwareFP<InstARM32::Vshl>; | 
 | template class InstARM32ThreeAddrSignAwareFP<InstARM32::Vshr>; | 
 | template class InstARM32ThreeAddrFP<InstARM32::Vsub>; | 
 | template class InstARM32ThreeAddrSignAwareFP<InstARM32::Vqadd>; | 
 | template class InstARM32ThreeAddrSignAwareFP<InstARM32::Vqsub>; | 
 | template class InstARM32ThreeAddrSignAwareFP<InstARM32::Vqmovn2>; | 
 | template class InstARM32ThreeAddrSignAwareFP<InstARM32::Vmulh>; | 
 | template class InstARM32ThreeAddrFP<InstARM32::Vmlap>; | 
 |  | 
 | template class InstARM32LoadBase<InstARM32::Ldr>; | 
 | template class InstARM32LoadBase<InstARM32::Ldrex>; | 
 | template class InstARM32LoadBase<InstARM32::Vldr1d>; | 
 | template class InstARM32LoadBase<InstARM32::Vldr1q>; | 
 | template class InstARM32ThreeAddrFP<InstARM32::Vzip>; | 
 | template class InstARM32TwoAddrGPR<InstARM32::Movt>; | 
 |  | 
 | template class InstARM32UnaryopGPR<InstARM32::Movw, false>; | 
 | template class InstARM32UnaryopGPR<InstARM32::Clz, false>; | 
 | template class InstARM32UnaryopGPR<InstARM32::Mvn, false>; | 
 | template class InstARM32UnaryopGPR<InstARM32::Rbit, false>; | 
 | template class InstARM32UnaryopGPR<InstARM32::Rev, false>; | 
 | template class InstARM32UnaryopGPR<InstARM32::Sxt, true>; | 
 | template class InstARM32UnaryopGPR<InstARM32::Uxt, true>; | 
 | template class InstARM32UnaryopFP<InstARM32::Vsqrt>; | 
 |  | 
 | template class InstARM32FourAddrGPR<InstARM32::Mla>; | 
 | template class InstARM32FourAddrGPR<InstARM32::Mls>; | 
 |  | 
 | template class InstARM32CmpLike<InstARM32::Cmn>; | 
 | template class InstARM32CmpLike<InstARM32::Cmp>; | 
 | template class InstARM32CmpLike<InstARM32::Tst>; | 
 |  | 
 | } // end of namespace ARM32 | 
 | } // end of namespace Ice |