diff options
Diffstat (limited to 'lib/Target/ARM/AsmParser/ARMAsmParser.cpp')
-rw-r--r-- | lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 516 |
1 files changed, 380 insertions, 136 deletions
diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index 80e5c6e..e3f9e0d 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -7,6 +7,9 @@ // //===----------------------------------------------------------------------===// +#include "ARMBuildAttrs.h" +#include "ARMFPUName.h" +#include "ARMFeatures.h" #include "llvm/MC/MCTargetAsmParser.h" #include "MCTargetDesc/ARMAddressingModes.h" #include "MCTargetDesc/ARMBaseInfo.h" @@ -24,6 +27,7 @@ #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCAsmParser.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" @@ -47,8 +51,14 @@ enum VectorLaneTy { NoLanes, AllLanes, IndexedLane }; class ARMAsmParser : public MCTargetAsmParser { MCSubtargetInfo &STI; MCAsmParser &Parser; + const MCInstrInfo &MII; const MCRegisterInfo *MRI; + ARMTargetStreamer &getTargetStreamer() { + MCTargetStreamer &TS = getParser().getStreamer().getTargetStreamer(); + return static_cast<ARMTargetStreamer &>(TS); + } + // Unwind directives state SMLoc FnStartLoc; SMLoc CantUnwindLoc; @@ -66,6 +76,8 @@ class ARMAsmParser : public MCTargetAsmParser { // Map of register aliases registers via the .req directive. StringMap<unsigned> RegisterReqs; + bool NextSymbolIsThumb; + struct { ARMCC::CondCodes Cond; // Condition for IT block. unsigned Mask:4; // Condition mask for instructions. @@ -127,6 +139,8 @@ class ARMAsmParser : public MCTargetAsmParser { bool parseDirectiveUnreq(SMLoc L); bool parseDirectiveArch(SMLoc L); bool parseDirectiveEabiAttr(SMLoc L); + bool parseDirectiveCPU(SMLoc L); + bool parseDirectiveFPU(SMLoc L); bool parseDirectiveFnStart(SMLoc L); bool parseDirectiveFnEnd(SMLoc L); bool parseDirectiveCantUnwind(SMLoc L); @@ -139,7 +153,8 @@ class ARMAsmParser : public MCTargetAsmParser { StringRef splitMnemonic(StringRef Mnemonic, unsigned &PredicationCode, bool &CarrySetting, unsigned &ProcessorIMod, StringRef &ITMask); - void getMnemonicAcceptInfo(StringRef Mnemonic, bool &CanAcceptCarrySet, + void getMnemonicAcceptInfo(StringRef Mnemonic, StringRef FullInst, + bool &CanAcceptCarrySet, bool &CanAcceptPredicationCode); bool isThumb() const { @@ -158,6 +173,9 @@ class ARMAsmParser : public MCTargetAsmParser { bool hasV6Ops() const { return STI.getFeatureBits() & ARM::HasV6Ops; } + bool hasV6MOps() const { + return STI.getFeatureBits() & ARM::HasV6MOps; + } bool hasV7Ops() const { return STI.getFeatureBits() & ARM::HasV7Ops; } @@ -221,6 +239,9 @@ class ARMAsmParser : public MCTargetAsmParser { // Asm Match Converter Methods void cvtThumbMultiply(MCInst &Inst, const SmallVectorImpl<MCParsedAsmOperand*> &); + void cvtThumbBranches(MCInst &Inst, + const SmallVectorImpl<MCParsedAsmOperand*> &); + bool validateInstruction(MCInst &Inst, const SmallVectorImpl<MCParsedAsmOperand*> &Ops); bool processInstruction(MCInst &Inst, @@ -229,8 +250,6 @@ class ARMAsmParser : public MCTargetAsmParser { SmallVectorImpl<MCParsedAsmOperand*> &Operands); bool shouldOmitPredicateOperand(StringRef Mnemonic, SmallVectorImpl<MCParsedAsmOperand*> &Operands); - bool isDeprecated(MCInst &Inst, StringRef &Info); - public: enum ARMMatchResultTy { Match_RequiresITBlock = FIRST_TARGET_MATCH_RESULT_TY, @@ -242,8 +261,9 @@ public: }; - ARMAsmParser(MCSubtargetInfo &_STI, MCAsmParser &_Parser) - : MCTargetAsmParser(), STI(_STI), Parser(_Parser), FPReg(-1) { + ARMAsmParser(MCSubtargetInfo &_STI, MCAsmParser &_Parser, + const MCInstrInfo &MII) + : MCTargetAsmParser(), STI(_STI), Parser(_Parser), MII(MII), FPReg(-1) { MCAsmParserExtension::Initialize(_Parser); // Cache the MCRegisterInfo. @@ -255,12 +275,7 @@ public: // Not in an ITBlock to start with. ITState.CurPosition = ~0U; - // Set ELF header flags. - // FIXME: This should eventually end up somewhere else where more - // intelligent flag decisions can be made. For now we are just maintaining - // the statu/parseDirects quo for ARM and setting EF_ARM_EABI_VER5 as the default. - if (MCELFStreamer *MES = dyn_cast<MCELFStreamer>(&Parser.getStreamer())) - MES->getAssembler().setELFHeaderEFlags(ELF::EF_ARM_EABI_VER5); + NextSymbolIsThumb = false; } // Implementation of the MCTargetAsmParser interface: @@ -277,6 +292,8 @@ public: SmallVectorImpl<MCParsedAsmOperand*> &Operands, MCStreamer &Out, unsigned &ErrorInfo, bool MatchingInlineAsm); + void onLabelParsed(MCSymbol *Symbol); + }; } // end anonymous namespace @@ -601,7 +618,7 @@ public: template<unsigned width, unsigned scale> bool isUnsignedOffset() const { if (!isImm()) return false; - if (dyn_cast<MCSymbolRefExpr>(Imm.Val)) return true; + if (isa<MCSymbolRefExpr>(Imm.Val)) return true; if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Imm.Val)) { int64_t Val = CE->getValue(); int64_t Align = 1LL << scale; @@ -610,6 +627,22 @@ public: } return false; } + // checks whether this operand is an signed offset which fits is a field + // of specified width and scaled by a specific number of bits + template<unsigned width, unsigned scale> + bool isSignedOffset() const { + if (!isImm()) return false; + if (isa<MCSymbolRefExpr>(Imm.Val)) return true; + if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Imm.Val)) { + int64_t Val = CE->getValue(); + int64_t Align = 1LL << scale; + int64_t Max = Align * ((1LL << (width-1)) - 1); + int64_t Min = -Align * (1LL << (width-1)); + return ((Val % Align) == 0) && (Val >= Min) && (Val <= Max); + } + return false; + } + // checks whether this operand is a memory operand computed as an offset // applied to PC. the offset may have 8 bits of magnitude and is represented // with two bits of shift. textually it may be either [pc, #imm], #imm or @@ -628,7 +661,7 @@ public: Val = Memory.OffsetImm->getValue(); } else return false; - return ((Val % 4) == 0) && (Val >= -1020) && (Val <= 1020); + return ((Val % 4) == 0) && (Val >= 0) && (Val <= 1020); } bool isFPImm() const { if (!isImm()) return false; @@ -658,13 +691,6 @@ public: int64_t Value = CE->getValue(); return ((Value & 3) == 0) && Value >= -1020 && Value <= 1020; } - bool isImm0_4() const { - if (!isImm()) return false; - const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); - if (!CE) return false; - int64_t Value = CE->getValue(); - return Value >= 0 && Value < 5; - } bool isImm0_1020s4() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); @@ -687,6 +713,13 @@ public: // explicitly exclude zero. we want that to use the normal 0_508 version. return ((Value & 3) == 0) && Value > 0 && Value <= 508; } + bool isImm0_239() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + if (!CE) return false; + int64_t Value = CE->getValue(); + return Value >= 0 && Value < 240; + } bool isImm0_255() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); @@ -848,6 +881,15 @@ public: int64_t Value = CE->getValue(); return Value >= 0 && Value < 65536; } + bool isImm256_65535Expr() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); + // If it's not a constant expression, it'll generate a fixup and be + // handled later. + if (!CE) return true; + int64_t Value = CE->getValue(); + return Value >= 256 && Value < 65536; + } bool isImm0_65535Expr() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); @@ -927,7 +969,8 @@ public: const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm()); if (!CE) return false; int64_t Value = CE->getValue(); - return ARM_AM::getT2SOImmVal(~Value) != -1; + return ARM_AM::getT2SOImmVal(Value) == -1 && + ARM_AM::getT2SOImmVal(~Value) != -1; } bool isT2SOImmNeg() const { if (!isImm()) return false; @@ -1773,8 +1816,6 @@ public: void addMemPCRelImm12Operands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); int32_t Imm = Memory.OffsetImm->getValue(); - // FIXME: Handle #-0 - if (Imm == INT32_MIN) Imm = 0; Inst.addOperand(MCOperand::CreateImm(Imm)); } @@ -2494,7 +2535,7 @@ void ARMOperand::print(raw_ostream &OS) const { getImm()->print(OS); break; case k_MemBarrierOpt: - OS << "<ARM_MB::" << MemBOptToString(getMemBarrierOpt()) << ">"; + OS << "<ARM_MB::" << MemBOptToString(getMemBarrierOpt(), false) << ">"; break; case k_InstSyncBarrierOpt: OS << "<ARM_ISB::" << InstSyncBOptToString(getInstSyncBarrierOpt()) << ">"; @@ -2831,8 +2872,9 @@ static int MatchCoprocessorOperandName(StringRef Name, char CoprocOp) { return -1; switch (Name[2]) { default: return -1; - case '0': return 10; - case '1': return 11; + // p10 and p11 are invalid for coproc instructions (reserved for FP/NEON) + case '0': return CoprocOp == 'p'? -1: 10; + case '1': return CoprocOp == 'p'? -1: 11; case '2': return 12; case '3': return 13; case '4': return 14; @@ -3439,18 +3481,27 @@ parseMemBarrierOptOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands) { Opt = StringSwitch<unsigned>(OptStr.slice(0, OptStr.size()).lower()) .Case("sy", ARM_MB::SY) .Case("st", ARM_MB::ST) + .Case("ld", ARM_MB::LD) .Case("sh", ARM_MB::ISH) .Case("ish", ARM_MB::ISH) .Case("shst", ARM_MB::ISHST) .Case("ishst", ARM_MB::ISHST) + .Case("ishld", ARM_MB::ISHLD) .Case("nsh", ARM_MB::NSH) .Case("un", ARM_MB::NSH) .Case("nshst", ARM_MB::NSHST) + .Case("nshld", ARM_MB::NSHLD) .Case("unst", ARM_MB::NSHST) .Case("osh", ARM_MB::OSH) .Case("oshst", ARM_MB::OSHST) + .Case("oshld", ARM_MB::OSHLD) .Default(~0U); + // ishld, oshld, nshld and ld are only available from ARMv8. + if (!hasV8Ops() && (Opt == ARM_MB::ISHLD || Opt == ARM_MB::OSHLD || + Opt == ARM_MB::NSHLD || Opt == ARM_MB::LD)) + Opt = ~0U; + if (Opt == ~0U) return MatchOperand_NoMatch; @@ -3498,7 +3549,7 @@ parseInstSyncBarrierOptOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands) { if (Tok.is(AsmToken::Identifier)) { StringRef OptStr = Tok.getString(); - if (OptStr.lower() == "sy") + if (OptStr.equals_lower("sy")) Opt = ARM_ISB::SY; else return MatchOperand_NoMatch; @@ -4102,6 +4153,65 @@ cvtThumbMultiply(MCInst &Inst, ((ARMOperand*)Operands[2])->addCondCodeOperands(Inst, 2); } +void ARMAsmParser:: +cvtThumbBranches(MCInst &Inst, + const SmallVectorImpl<MCParsedAsmOperand*> &Operands) { + int CondOp = -1, ImmOp = -1; + switch(Inst.getOpcode()) { + case ARM::tB: + case ARM::tBcc: CondOp = 1; ImmOp = 2; break; + + case ARM::t2B: + case ARM::t2Bcc: CondOp = 1; ImmOp = 3; break; + + default: llvm_unreachable("Unexpected instruction in cvtThumbBranches"); + } + // first decide whether or not the branch should be conditional + // by looking at it's location relative to an IT block + if(inITBlock()) { + // inside an IT block we cannot have any conditional branches. any + // such instructions needs to be converted to unconditional form + switch(Inst.getOpcode()) { + case ARM::tBcc: Inst.setOpcode(ARM::tB); break; + case ARM::t2Bcc: Inst.setOpcode(ARM::t2B); break; + } + } else { + // outside IT blocks we can only have unconditional branches with AL + // condition code or conditional branches with non-AL condition code + unsigned Cond = static_cast<ARMOperand*>(Operands[CondOp])->getCondCode(); + switch(Inst.getOpcode()) { + case ARM::tB: + case ARM::tBcc: + Inst.setOpcode(Cond == ARMCC::AL ? ARM::tB : ARM::tBcc); + break; + case ARM::t2B: + case ARM::t2Bcc: + Inst.setOpcode(Cond == ARMCC::AL ? ARM::t2B : ARM::t2Bcc); + break; + } + } + + // now decide on encoding size based on branch target range + switch(Inst.getOpcode()) { + // classify tB as either t2B or t1B based on range of immediate operand + case ARM::tB: { + ARMOperand* op = static_cast<ARMOperand*>(Operands[ImmOp]); + if(!op->isSignedOffset<11, 1>() && isThumbTwo()) + Inst.setOpcode(ARM::t2B); + break; + } + // classify tBcc as either t2Bcc or t1Bcc based on range of immediate operand + case ARM::tBcc: { + ARMOperand* op = static_cast<ARMOperand*>(Operands[ImmOp]); + if(!op->isSignedOffset<8, 1>() && isThumbTwo()) + Inst.setOpcode(ARM::t2Bcc); + break; + } + } + ((ARMOperand*)Operands[ImmOp])->addImmOperands(Inst, 1); + ((ARMOperand*)Operands[CondOp])->addCondCodeOperands(Inst, 2); +} + /// Parse an ARM memory expression, return false if successful else return true /// or an error. The first token must be a '[' when called. bool ARMAsmParser:: @@ -4601,7 +4711,7 @@ StringRef ARMAsmParser::splitMnemonic(StringRef Mnemonic, Mnemonic == "mls" || Mnemonic == "smmls" || Mnemonic == "vcls" || Mnemonic == "vmls" || Mnemonic == "vnmls" || Mnemonic == "vacge" || Mnemonic == "vcge" || Mnemonic == "vclt" || Mnemonic == "vacgt" || - Mnemonic == "vaclt" || Mnemonic == "vacle" || + Mnemonic == "vaclt" || Mnemonic == "vacle" || Mnemonic == "hlt" || Mnemonic == "vcgt" || Mnemonic == "vcle" || Mnemonic == "smlal" || Mnemonic == "umaal" || Mnemonic == "umlal" || Mnemonic == "vabal" || Mnemonic == "vmlal" || Mnemonic == "vpadal" || Mnemonic == "vqdmlal" || @@ -4688,8 +4798,8 @@ StringRef ARMAsmParser::splitMnemonic(StringRef Mnemonic, // // FIXME: It would be nice to autogen this. void ARMAsmParser:: -getMnemonicAcceptInfo(StringRef Mnemonic, bool &CanAcceptCarrySet, - bool &CanAcceptPredicationCode) { +getMnemonicAcceptInfo(StringRef Mnemonic, StringRef FullInst, + bool &CanAcceptCarrySet, bool &CanAcceptPredicationCode) { if (Mnemonic == "and" || Mnemonic == "lsl" || Mnemonic == "lsr" || Mnemonic == "rrx" || Mnemonic == "ror" || Mnemonic == "sub" || Mnemonic == "add" || Mnemonic == "adc" || @@ -4707,12 +4817,14 @@ getMnemonicAcceptInfo(StringRef Mnemonic, bool &CanAcceptCarrySet, if (Mnemonic == "bkpt" || Mnemonic == "cbnz" || Mnemonic == "setend" || Mnemonic == "cps" || Mnemonic == "it" || Mnemonic == "cbz" || - Mnemonic == "trap" || Mnemonic == "setend" || + Mnemonic == "trap" || Mnemonic == "hlt" || Mnemonic.startswith("crc32") || Mnemonic.startswith("cps") || Mnemonic.startswith("vsel") || Mnemonic == "vmaxnm" || Mnemonic == "vminnm" || Mnemonic == "vcvta" || Mnemonic == "vcvtn" || Mnemonic == "vcvtp" || Mnemonic == "vcvtm" || Mnemonic == "vrinta" || Mnemonic == "vrintn" || Mnemonic == "vrintp" || - Mnemonic == "vrintm") { + Mnemonic == "vrintm" || Mnemonic.startswith("aes") || + Mnemonic.startswith("sha1") || Mnemonic.startswith("sha256") || + (FullInst.startswith("vmull") && FullInst.endswith(".p64"))) { // These mnemonics are never predicable CanAcceptPredicationCode = false; } else if (!isThumb()) { @@ -4726,7 +4838,10 @@ getMnemonicAcceptInfo(StringRef Mnemonic, bool &CanAcceptCarrySet, Mnemonic != "stc2" && Mnemonic != "stc2l" && !Mnemonic.startswith("rfe") && !Mnemonic.startswith("srs"); } else if (isThumbOne()) { - CanAcceptPredicationCode = Mnemonic != "nop" && Mnemonic != "movs"; + if (hasV6MOps()) + CanAcceptPredicationCode = Mnemonic != "movs"; + else + CanAcceptPredicationCode = Mnemonic != "nop" && Mnemonic != "movs"; } else CanAcceptPredicationCode = true; } @@ -4877,14 +4992,6 @@ bool ARMAsmParser::shouldOmitPredicateOperand( return false; } -bool ARMAsmParser::isDeprecated(MCInst &Inst, StringRef &Info) { - if (hasV8Ops() && Inst.getOpcode() == ARM::SETEND) { - Info = "armv8"; - return true; - } - return false; -} - static bool isDataTypeToken(StringRef Tok) { return Tok == ".8" || Tok == ".16" || Tok == ".32" || Tok == ".64" || Tok == ".i8" || Tok == ".i16" || Tok == ".i32" || Tok == ".i64" || @@ -4980,7 +5087,7 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, // the matcher deal with finding the right instruction or generating an // appropriate error. bool CanAcceptCarrySet, CanAcceptPredicationCode; - getMnemonicAcceptInfo(Mnemonic, CanAcceptCarrySet, CanAcceptPredicationCode); + getMnemonicAcceptInfo(Mnemonic, Name, CanAcceptCarrySet, CanAcceptPredicationCode); // If we had a carry-set on an instruction that can't do that, issue an // error. @@ -5115,8 +5222,9 @@ bool ARMAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, // expressed as a GPRPair, so we have to manually merge them. // FIXME: We would really like to be able to tablegen'erate this. if (!isThumb() && Operands.size() > 4 && - (Mnemonic == "ldrexd" || Mnemonic == "strexd")) { - bool isLoad = (Mnemonic == "ldrexd"); + (Mnemonic == "ldrexd" || Mnemonic == "strexd" || Mnemonic == "ldaexd" || + Mnemonic == "stlexd")) { + bool isLoad = (Mnemonic == "ldrexd" || Mnemonic == "ldaexd"); unsigned Idx = isLoad ? 2 : 3; ARMOperand* Op1 = static_cast<ARMOperand*>(Operands[Idx]); ARMOperand* Op2 = static_cast<ARMOperand*>(Operands[Idx+1]); @@ -5200,45 +5308,44 @@ static bool listContainsReg(MCInst &Inst, unsigned OpNo, unsigned Reg) { return false; } -// FIXME: We would really prefer to have MCInstrInfo (the wrapper around -// the ARMInsts array) instead. Getting that here requires awkward -// API changes, though. Better way? -namespace llvm { -extern const MCInstrDesc ARMInsts[]; -} -static const MCInstrDesc &getInstDesc(unsigned Opcode) { - return ARMInsts[Opcode]; +// Return true if instruction has the interesting property of being +// allowed in IT blocks, but not being predicable. +static bool instIsBreakpoint(const MCInst &Inst) { + return Inst.getOpcode() == ARM::tBKPT || + Inst.getOpcode() == ARM::BKPT || + Inst.getOpcode() == ARM::tHLT || + Inst.getOpcode() == ARM::HLT; + } // FIXME: We would really like to be able to tablegen'erate this. bool ARMAsmParser:: validateInstruction(MCInst &Inst, const SmallVectorImpl<MCParsedAsmOperand*> &Operands) { - const MCInstrDesc &MCID = getInstDesc(Inst.getOpcode()); + const MCInstrDesc &MCID = MII.get(Inst.getOpcode()); SMLoc Loc = Operands[0]->getStartLoc(); + // Check the IT block state first. - // NOTE: BKPT instruction has the interesting property of being - // allowed in IT blocks, but not being predicable. It just always - // executes. - if (inITBlock() && Inst.getOpcode() != ARM::tBKPT && - Inst.getOpcode() != ARM::BKPT) { - unsigned bit = 1; + // NOTE: BKPT and HLT instructions have the interesting property of being + // allowed in IT blocks, but not being predicable. They just always execute. + if (inITBlock() && !instIsBreakpoint(Inst)) { + unsigned Bit = 1; if (ITState.FirstCond) ITState.FirstCond = false; else - bit = (ITState.Mask >> (5 - ITState.CurPosition)) & 1; + Bit = (ITState.Mask >> (5 - ITState.CurPosition)) & 1; // The instruction must be predicable. if (!MCID.isPredicable()) return Error(Loc, "instructions in IT block must be predicable"); unsigned Cond = Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm(); - unsigned ITCond = bit ? ITState.Cond : + unsigned ITCond = Bit ? ITState.Cond : ARMCC::getOppositeCondition(ITState.Cond); if (Cond != ITCond) { // Find the condition code Operand to get its SMLoc information. SMLoc CondLoc; - for (unsigned i = 1; i < Operands.size(); ++i) - if (static_cast<ARMOperand*>(Operands[i])->isCondCode()) - CondLoc = Operands[i]->getStartLoc(); + for (unsigned I = 1; I < Operands.size(); ++I) + if (static_cast<ARMOperand*>(Operands[I])->isCondCode()) + CondLoc = Operands[I]->getStartLoc(); return Error(CondLoc, "incorrect condition in IT block; got '" + StringRef(ARMCondCodeToString(ARMCC::CondCodes(Cond))) + "', but expected '" + @@ -5247,20 +5354,55 @@ validateInstruction(MCInst &Inst, // Check for non-'al' condition codes outside of the IT block. } else if (isThumbTwo() && MCID.isPredicable() && Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm() != - ARMCC::AL && Inst.getOpcode() != ARM::tB && - Inst.getOpcode() != ARM::t2B) + ARMCC::AL && Inst.getOpcode() != ARM::tBcc && + Inst.getOpcode() != ARM::t2Bcc) return Error(Loc, "predicated instructions must be in IT block"); - switch (Inst.getOpcode()) { + const unsigned Opcode = Inst.getOpcode(); + switch (Opcode) { case ARM::LDRD: case ARM::LDRD_PRE: case ARM::LDRD_POST: { + const unsigned RtReg = Inst.getOperand(0).getReg(); + + // Rt can't be R14. + if (RtReg == ARM::LR) + return Error(Operands[3]->getStartLoc(), + "Rt can't be R14"); + + const unsigned Rt = MRI->getEncodingValue(RtReg); + // Rt must be even-numbered. + if ((Rt & 1) == 1) + return Error(Operands[3]->getStartLoc(), + "Rt must be even-numbered"); + // Rt2 must be Rt + 1. - unsigned Rt = MRI->getEncodingValue(Inst.getOperand(0).getReg()); - unsigned Rt2 = MRI->getEncodingValue(Inst.getOperand(1).getReg()); + const unsigned Rt2 = MRI->getEncodingValue(Inst.getOperand(1).getReg()); if (Rt2 != Rt + 1) return Error(Operands[3]->getStartLoc(), "destination operands must be sequential"); + + if (Opcode == ARM::LDRD_PRE || Opcode == ARM::LDRD_POST) { + const unsigned Rn = MRI->getEncodingValue(Inst.getOperand(3).getReg()); + // For addressing modes with writeback, the base register needs to be + // different from the destination registers. + if (Rn == Rt || Rn == Rt2) + return Error(Operands[3]->getStartLoc(), + "base register needs to be different from destination " + "registers"); + } + + return false; + } + case ARM::t2LDRDi8: + case ARM::t2LDRD_PRE: + case ARM::t2LDRD_POST: { + // Rt2 must be different from Rt. + unsigned Rt = MRI->getEncodingValue(Inst.getOperand(0).getReg()); + unsigned Rt2 = MRI->getEncodingValue(Inst.getOperand(1).getReg()); + if (Rt2 == Rt) + return Error(Operands[3]->getStartLoc(), + "destination operands can't be identical"); return false; } case ARM::STRD: { @@ -5284,50 +5426,77 @@ validateInstruction(MCInst &Inst, } case ARM::SBFX: case ARM::UBFX: { - // width must be in range [1, 32-lsb] - unsigned lsb = Inst.getOperand(2).getImm(); - unsigned widthm1 = Inst.getOperand(3).getImm(); - if (widthm1 >= 32 - lsb) + // Width must be in range [1, 32-lsb]. + unsigned LSB = Inst.getOperand(2).getImm(); + unsigned Widthm1 = Inst.getOperand(3).getImm(); + if (Widthm1 >= 32 - LSB) return Error(Operands[5]->getStartLoc(), "bitfield width must be in range [1,32-lsb]"); return false; } + // Notionally handles ARM::tLDMIA_UPD too. case ARM::tLDMIA: { // If we're parsing Thumb2, the .w variant is available and handles - // most cases that are normally illegal for a Thumb1 LDM - // instruction. We'll make the transformation in processInstruction() - // if necessary. + // most cases that are normally illegal for a Thumb1 LDM instruction. + // We'll make the transformation in processInstruction() if necessary. // // Thumb LDM instructions are writeback iff the base register is not // in the register list. unsigned Rn = Inst.getOperand(0).getReg(); - bool hasWritebackToken = + bool HasWritebackToken = (static_cast<ARMOperand*>(Operands[3])->isToken() && static_cast<ARMOperand*>(Operands[3])->getToken() == "!"); - bool listContainsBase; - if (checkLowRegisterList(Inst, 3, Rn, 0, listContainsBase) && !isThumbTwo()) - return Error(Operands[3 + hasWritebackToken]->getStartLoc(), + bool ListContainsBase; + if (checkLowRegisterList(Inst, 3, Rn, 0, ListContainsBase) && !isThumbTwo()) + return Error(Operands[3 + HasWritebackToken]->getStartLoc(), "registers must be in range r0-r7"); // If we should have writeback, then there should be a '!' token. - if (!listContainsBase && !hasWritebackToken && !isThumbTwo()) + if (!ListContainsBase && !HasWritebackToken && !isThumbTwo()) return Error(Operands[2]->getStartLoc(), "writeback operator '!' expected"); // If we should not have writeback, there must not be a '!'. This is // true even for the 32-bit wide encodings. - if (listContainsBase && hasWritebackToken) + if (ListContainsBase && HasWritebackToken) return Error(Operands[3]->getStartLoc(), "writeback operator '!' not allowed when base register " "in register list"); break; } - case ARM::t2LDMIA_UPD: { + case ARM::LDMIA_UPD: + case ARM::LDMDB_UPD: + case ARM::LDMIB_UPD: + case ARM::LDMDA_UPD: + // ARM variants loading and updating the same register are only officially + // UNPREDICTABLE on v7 upwards. Goodness knows what they did before. + if (!hasV7Ops()) + break; + // Fallthrough + case ARM::t2LDMIA_UPD: + case ARM::t2LDMDB_UPD: + case ARM::t2STMIA_UPD: + case ARM::t2STMDB_UPD: { if (listContainsReg(Inst, 3, Inst.getOperand(0).getReg())) - return Error(Operands[4]->getStartLoc(), - "writeback operator '!' not allowed when base register " - "in register list"); + return Error(Operands.back()->getStartLoc(), + "writeback register not allowed in register list"); break; } + case ARM::sysLDMIA_UPD: + case ARM::sysLDMDA_UPD: + case ARM::sysLDMDB_UPD: + case ARM::sysLDMIB_UPD: + if (!listContainsReg(Inst, 3, ARM::PC)) + return Error(Operands[4]->getStartLoc(), + "writeback register only allowed on system LDM " + "if PC in register-list"); + break; + case ARM::sysSTMIA_UPD: + case ARM::sysSTMDA_UPD: + case ARM::sysSTMDB_UPD: + case ARM::sysSTMIB_UPD: + return Error(Operands[2]->getStartLoc(), + "system STM cannot have writeback register"); + break; case ARM::tMUL: { // The second source operand must be the same register as the destination // operand. @@ -5351,26 +5520,35 @@ validateInstruction(MCInst &Inst, // so only issue a diagnostic for thumb1. The instructions will be // switched to the t2 encodings in processInstruction() if necessary. case ARM::tPOP: { - bool listContainsBase; - if (checkLowRegisterList(Inst, 2, 0, ARM::PC, listContainsBase) && + bool ListContainsBase; + if (checkLowRegisterList(Inst, 2, 0, ARM::PC, ListContainsBase) && !isThumbTwo()) return Error(Operands[2]->getStartLoc(), "registers must be in range r0-r7 or pc"); break; } case ARM::tPUSH: { - bool listContainsBase; - if (checkLowRegisterList(Inst, 2, 0, ARM::LR, listContainsBase) && + bool ListContainsBase; + if (checkLowRegisterList(Inst, 2, 0, ARM::LR, ListContainsBase) && !isThumbTwo()) return Error(Operands[2]->getStartLoc(), "registers must be in range r0-r7 or lr"); break; } case ARM::tSTMIA_UPD: { - bool listContainsBase; - if (checkLowRegisterList(Inst, 4, 0, 0, listContainsBase) && !isThumbTwo()) + bool ListContainsBase, InvalidLowList; + InvalidLowList = checkLowRegisterList(Inst, 4, Inst.getOperand(0).getReg(), + 0, ListContainsBase); + if (InvalidLowList && !isThumbTwo()) return Error(Operands[4]->getStartLoc(), "registers must be in range r0-r7"); + + // This would be converted to a 32-bit stm, but that's not valid if the + // writeback register is in the list. + if (InvalidLowList && ListContainsBase) + return Error(Operands[4]->getStartLoc(), + "writeback operator '!' not allowed when base register " + "in register list"); break; } case ARM::tADDrSP: { @@ -5383,11 +5561,29 @@ validateInstruction(MCInst &Inst, } break; } + // Final range checking for Thumb unconditional branch instructions. + case ARM::tB: + if (!(static_cast<ARMOperand*>(Operands[2]))->isSignedOffset<11, 1>()) + return Error(Operands[2]->getStartLoc(), "branch target out of range"); + break; + case ARM::t2B: { + int op = (Operands[2]->isImm()) ? 2 : 3; + if (!(static_cast<ARMOperand*>(Operands[op]))->isSignedOffset<24, 1>()) + return Error(Operands[op]->getStartLoc(), "branch target out of range"); + break; + } + // Final range checking for Thumb conditional branch instructions. + case ARM::tBcc: + if (!(static_cast<ARMOperand*>(Operands[2]))->isSignedOffset<8, 1>()) + return Error(Operands[2]->getStartLoc(), "branch target out of range"); + break; + case ARM::t2Bcc: { + int Op = (Operands[2]->isImm()) ? 2 : 3; + if (!(static_cast<ARMOperand*>(Operands[Op]))->isSignedOffset<20, 1>()) + return Error(Operands[Op]->getStartLoc(), "branch target out of range"); + break; + } } - - StringRef DepInfo; - if (isDeprecated(Inst, DepInfo)) - Warning(Loc, "deprecated on " + DepInfo); return false; } @@ -7425,7 +7621,7 @@ unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) { // 16-bit thumb arithmetic instructions either require or preclude the 'S' // suffix depending on whether they're in an IT block or not. unsigned Opc = Inst.getOpcode(); - const MCInstrDesc &MCID = getInstDesc(Opc); + const MCInstrDesc &MCID = MII.get(Opc); if (MCID.TSFlags & ARMII::ThumbArithFlagSetting) { assert(MCID.hasOptionalDef() && "optionally flag setting instruction missing optional def operand"); @@ -7486,12 +7682,22 @@ MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, return true; } - // Some instructions need post-processing to, for example, tweak which - // encoding is selected. Loop on it while changes happen so the - // individual transformations can chain off each other. E.g., - // tPOP(r8)->t2LDMIA_UPD(sp,r8)->t2STR_POST(sp,r8) - while (processInstruction(Inst, Operands)) - ; + { // processInstruction() updates inITBlock state, we need to save it away + bool wasInITBlock = inITBlock(); + + // Some instructions need post-processing to, for example, tweak which + // encoding is selected. Loop on it while changes happen so the + // individual transformations can chain off each other. E.g., + // tPOP(r8)->t2LDMIA_UPD(sp,r8)->t2STR_POST(sp,r8) + while (processInstruction(Inst, Operands)) + ; + + // Only after the instruction is fully processed, we can validate it + if (wasInITBlock && hasV8Ops() && isThumb() && + !isV8EligibleForIT(&Inst, 2)) { + Warning(IDLoc, "deprecated instruction in IT block"); + } + } // Only move forward at the very end so that everything in validate // and process gets a consistent answer about whether we're in an IT @@ -7544,15 +7750,15 @@ MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, return Error(IDLoc, "instruction variant requires ARMv6 or later"); case Match_RequiresThumb2: return Error(IDLoc, "instruction variant requires Thumb2"); - case Match_ImmRange0_4: { + case Match_ImmRange0_15: { SMLoc ErrorLoc = ((ARMOperand*)Operands[ErrorInfo])->getStartLoc(); if (ErrorLoc == SMLoc()) ErrorLoc = IDLoc; - return Error(ErrorLoc, "immediate operand must be in the range [0,4]"); + return Error(ErrorLoc, "immediate operand must be in the range [0,15]"); } - case Match_ImmRange0_15: { + case Match_ImmRange0_239: { SMLoc ErrorLoc = ((ARMOperand*)Operands[ErrorInfo])->getStartLoc(); if (ErrorLoc == SMLoc()) ErrorLoc = IDLoc; - return Error(ErrorLoc, "immediate operand must be in the range [0,15]"); + return Error(ErrorLoc, "immediate operand must be in the range [0,239]"); } } @@ -7580,6 +7786,10 @@ bool ARMAsmParser::ParseDirective(AsmToken DirectiveID) { return parseDirectiveArch(DirectiveID.getLoc()); else if (IDVal == ".eabi_attribute") return parseDirectiveEabiAttr(DirectiveID.getLoc()); + else if (IDVal == ".cpu") + return parseDirectiveCPU(DirectiveID.getLoc()); + else if (IDVal == ".fpu") + return parseDirectiveFPU(DirectiveID.getLoc()); else if (IDVal == ".fnstart") return parseDirectiveFnStart(DirectiveID.getLoc()); else if (IDVal == ".fnend") @@ -7658,13 +7868,18 @@ bool ARMAsmParser::parseDirectiveARM(SMLoc L) { return false; } +void ARMAsmParser::onLabelParsed(MCSymbol *Symbol) { + if (NextSymbolIsThumb) { + getParser().getStreamer().EmitThumbFunc(Symbol); + NextSymbolIsThumb = false; + } +} + /// parseDirectiveThumbFunc /// ::= .thumbfunc symbol_name bool ARMAsmParser::parseDirectiveThumbFunc(SMLoc L) { const MCAsmInfo *MAI = getParser().getStreamer().getContext().getAsmInfo(); bool isMachO = MAI->hasSubsectionsViaSymbols(); - StringRef Name; - bool needFuncName = true; // Darwin asm has (optionally) function name after .thumb_func direction // ELF doesn't @@ -7673,29 +7888,19 @@ bool ARMAsmParser::parseDirectiveThumbFunc(SMLoc L) { if (Tok.isNot(AsmToken::EndOfStatement)) { if (Tok.isNot(AsmToken::Identifier) && Tok.isNot(AsmToken::String)) return Error(L, "unexpected token in .thumb_func directive"); - Name = Tok.getIdentifier(); + MCSymbol *Func = + getParser().getContext().GetOrCreateSymbol(Tok.getIdentifier()); + getParser().getStreamer().EmitThumbFunc(Func); Parser.Lex(); // Consume the identifier token. - needFuncName = false; + return false; } } if (getLexer().isNot(AsmToken::EndOfStatement)) return Error(L, "unexpected token in directive"); - // Eat the end of statement and any blank lines that follow. - while (getLexer().is(AsmToken::EndOfStatement)) - Parser.Lex(); + NextSymbolIsThumb = true; - // FIXME: assuming function name will be the line following .thumb_func - // We really should be checking the next symbol definition even if there's - // stuff in between. - if (needFuncName) { - Name = Parser.getTok().getIdentifier(); - } - - // Mark symbol as a thumb symbol. - MCSymbol *Func = getParser().getContext().GetOrCreateSymbol(Name); - getParser().getStreamer().EmitThumbFunc(Func); return false; } @@ -7807,7 +8012,48 @@ bool ARMAsmParser::parseDirectiveArch(SMLoc L) { /// parseDirectiveEabiAttr /// ::= .eabi_attribute int, int bool ARMAsmParser::parseDirectiveEabiAttr(SMLoc L) { - return true; + if (Parser.getTok().isNot(AsmToken::Integer)) + return Error(L, "integer expected"); + int64_t Tag = Parser.getTok().getIntVal(); + Parser.Lex(); // eat tag integer + + if (Parser.getTok().isNot(AsmToken::Comma)) + return Error(L, "comma expected"); + Parser.Lex(); // skip comma + + L = Parser.getTok().getLoc(); + if (Parser.getTok().isNot(AsmToken::Integer)) + return Error(L, "integer expected"); + int64_t Value = Parser.getTok().getIntVal(); + Parser.Lex(); // eat value integer + + getTargetStreamer().emitAttribute(Tag, Value); + return false; +} + +/// parseDirectiveCPU +/// ::= .cpu str +bool ARMAsmParser::parseDirectiveCPU(SMLoc L) { + StringRef CPU = getParser().parseStringToEndOfStatement().trim(); + getTargetStreamer().emitTextAttribute(ARMBuildAttrs::CPU_name, CPU); + return false; +} + +/// parseDirectiveFPU +/// ::= .fpu str +bool ARMAsmParser::parseDirectiveFPU(SMLoc L) { + StringRef FPU = getParser().parseStringToEndOfStatement().trim(); + + unsigned ID = StringSwitch<unsigned>(FPU) +#define ARM_FPU_NAME(NAME, ID) .Case(NAME, ARM::ID) +#include "ARMFPUName.def" + .Default(ARM::INVALID_FPU); + + if (ID == ARM::INVALID_FPU) + return Error(L, "Unknown FPU name"); + + getTargetStreamer().emitFPU(ID); + return false; } /// parseDirectiveFnStart @@ -7820,7 +8066,7 @@ bool ARMAsmParser::parseDirectiveFnStart(SMLoc L) { } FnStartLoc = L; - getParser().getStreamer().EmitFnStart(); + getTargetStreamer().emitFnStart(); return false; } @@ -7833,8 +8079,7 @@ bool ARMAsmParser::parseDirectiveFnEnd(SMLoc L) { // Reset the unwind directives parser state resetUnwindDirectiveParserState(); - - getParser().getStreamer().EmitFnEnd(); + getTargetStreamer().emitFnEnd(); return false; } @@ -7856,7 +8101,7 @@ bool ARMAsmParser::parseDirectiveCantUnwind(SMLoc L) { return true; } - getParser().getStreamer().EmitCantUnwind(); + getTargetStreamer().emitCantUnwind(); return false; } @@ -7887,7 +8132,7 @@ bool ARMAsmParser::parseDirectivePersonality(SMLoc L) { Parser.Lex(); MCSymbol *PR = getParser().getContext().GetOrCreateSymbol(Name); - getParser().getStreamer().EmitPersonality(PR); + getTargetStreamer().emitPersonality(PR); return false; } @@ -7904,7 +8149,7 @@ bool ARMAsmParser::parseDirectiveHandlerData(SMLoc L) { return true; } - getParser().getStreamer().EmitHandlerData(); + getTargetStreamer().emitHandlerData(); return false; } @@ -7964,9 +8209,8 @@ bool ARMAsmParser::parseDirectiveSetFP(SMLoc L) { Offset = CE->getValue(); } - getParser().getStreamer().EmitSetFP(static_cast<unsigned>(NewFPReg), - static_cast<unsigned>(NewSPReg), - Offset); + getTargetStreamer().emitSetFP(static_cast<unsigned>(NewFPReg), + static_cast<unsigned>(NewSPReg), Offset); return false; } @@ -7995,7 +8239,7 @@ bool ARMAsmParser::parseDirectivePad(SMLoc L) { if (!CE) return Error(ExLoc, "pad offset must be an immediate"); - getParser().getStreamer().EmitPad(CE->getValue()); + getTargetStreamer().emitPad(CE->getValue()); return false; } @@ -8027,7 +8271,7 @@ bool ARMAsmParser::parseDirectiveRegSave(SMLoc L, bool IsVector) { if (IsVector && !Op->isDPRRegList()) return Error(L, ".vsave expects DPR registers"); - getParser().getStreamer().EmitRegSave(Op->getRegList(), IsVector); + getTargetStreamer().emitRegSave(Op->getRegList(), IsVector); return false; } |