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