| //===-- ARMAsmParser.cpp - Parse ARM assembly to MCInst instructions ------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ARM.h" |
| #include "llvm/MC/MCParser/MCAsmLexer.h" |
| #include "llvm/MC/MCParser/MCAsmParser.h" |
| #include "llvm/MC/MCParser/MCParsedAsmOperand.h" |
| #include "llvm/MC/MCStreamer.h" |
| #include "llvm/MC/MCExpr.h" |
| #include "llvm/MC/MCInst.h" |
| #include "llvm/Target/TargetRegistry.h" |
| #include "llvm/Target/TargetAsmParser.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Support/SourceMgr.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/Twine.h" |
| using namespace llvm; |
| |
| namespace { |
| struct ARMOperand; |
| |
| // The shift types for register controlled shifts in arm memory addressing |
| enum ShiftType { |
| Lsl, |
| Lsr, |
| Asr, |
| Ror, |
| Rrx |
| }; |
| |
| class ARMAsmParser : public TargetAsmParser { |
| MCAsmParser &Parser; |
| |
| private: |
| MCAsmParser &getParser() const { return Parser; } |
| |
| MCAsmLexer &getLexer() const { return Parser.getLexer(); } |
| |
| void Warning(SMLoc L, const Twine &Msg) { Parser.Warning(L, Msg); } |
| |
| bool Error(SMLoc L, const Twine &Msg) { return Parser.Error(L, Msg); } |
| |
| bool MaybeParseRegister(ARMOperand &Op, bool ParseWriteBack); |
| |
| bool ParseRegisterList(ARMOperand &Op); |
| |
| bool ParseMemory(ARMOperand &Op); |
| |
| bool ParseMemoryOffsetReg(bool &Negative, |
| bool &OffsetRegShifted, |
| enum ShiftType &ShiftType, |
| const MCExpr *&ShiftAmount, |
| const MCExpr *&Offset, |
| bool &OffsetIsReg, |
| int &OffsetRegNum); |
| |
| bool ParseShift(enum ShiftType &St, const MCExpr *&ShiftAmount); |
| |
| bool ParseOperand(ARMOperand &Op); |
| |
| bool ParseDirectiveWord(unsigned Size, SMLoc L); |
| |
| bool ParseDirectiveThumb(SMLoc L); |
| |
| bool ParseDirectiveThumbFunc(SMLoc L); |
| |
| bool ParseDirectiveCode(SMLoc L); |
| |
| bool ParseDirectiveSyntax(SMLoc L); |
| |
| // TODO - For now hacked versions of the next two are in here in this file to |
| // allow some parser testing until the table gen versions are implemented. |
| |
| /// @name Auto-generated Match Functions |
| /// { |
| bool MatchInstruction(const SmallVectorImpl<MCParsedAsmOperand*> &Operands, |
| MCInst &Inst); |
| |
| /// MatchRegisterName - Match the given string to a register name and return |
| /// its register number, or -1 if there is no match. To allow return values |
| /// to be used directly in register lists, arm registers have values between |
| /// 0 and 15. |
| int MatchRegisterName(const StringRef &Name); |
| |
| /// } |
| |
| |
| public: |
| ARMAsmParser(const Target &T, MCAsmParser &_Parser) |
| : TargetAsmParser(T), Parser(_Parser) {} |
| |
| virtual bool ParseInstruction(const StringRef &Name, SMLoc NameLoc, |
| SmallVectorImpl<MCParsedAsmOperand*> &Operands); |
| |
| virtual bool ParseDirective(AsmToken DirectiveID); |
| }; |
| |
| /// ARMOperand - Instances of this class represent a parsed ARM machine |
| /// instruction. |
| struct ARMOperand : public MCParsedAsmOperand { |
| enum { |
| Token, |
| Register, |
| Immediate, |
| Memory |
| } Kind; |
| |
| |
| union { |
| struct { |
| const char *Data; |
| unsigned Length; |
| } Tok; |
| |
| struct { |
| unsigned RegNum; |
| bool Writeback; |
| } Reg; |
| |
| struct { |
| const MCExpr *Val; |
| } Imm; |
| |
| // This is for all forms of ARM address expressions |
| struct { |
| unsigned BaseRegNum; |
| unsigned OffsetRegNum; // used when OffsetIsReg is true |
| const MCExpr *Offset; // used when OffsetIsReg is false |
| const MCExpr *ShiftAmount; // used when OffsetRegShifted is true |
| enum ShiftType ShiftType; // used when OffsetRegShifted is true |
| unsigned |
| OffsetRegShifted : 1, // only used when OffsetIsReg is true |
| Preindexed : 1, |
| Postindexed : 1, |
| OffsetIsReg : 1, |
| Negative : 1, // only used when OffsetIsReg is true |
| Writeback : 1; |
| } Mem; |
| |
| }; |
| |
| StringRef getToken() const { |
| assert(Kind == Token && "Invalid access!"); |
| return StringRef(Tok.Data, Tok.Length); |
| } |
| |
| unsigned getReg() const { |
| assert(Kind == Register && "Invalid access!"); |
| return Reg.RegNum; |
| } |
| |
| const MCExpr *getImm() const { |
| assert(Kind == Immediate && "Invalid access!"); |
| return Imm.Val; |
| } |
| |
| bool isToken() const {return Kind == Token; } |
| |
| bool isReg() const { return Kind == Register; } |
| |
| void addRegOperands(MCInst &Inst, unsigned N) const { |
| assert(N == 1 && "Invalid number of operands!"); |
| Inst.addOperand(MCOperand::CreateReg(getReg())); |
| } |
| |
| static ARMOperand CreateToken(StringRef Str) { |
| ARMOperand Res; |
| Res.Kind = Token; |
| Res.Tok.Data = Str.data(); |
| Res.Tok.Length = Str.size(); |
| return Res; |
| } |
| |
| static ARMOperand CreateReg(unsigned RegNum, bool Writeback) { |
| ARMOperand Res; |
| Res.Kind = Register; |
| Res.Reg.RegNum = RegNum; |
| Res.Reg.Writeback = Writeback; |
| return Res; |
| } |
| |
| static ARMOperand CreateImm(const MCExpr *Val) { |
| ARMOperand Res; |
| Res.Kind = Immediate; |
| Res.Imm.Val = Val; |
| return Res; |
| } |
| |
| static ARMOperand CreateMem(unsigned BaseRegNum, bool OffsetIsReg, |
| const MCExpr *Offset, unsigned OffsetRegNum, |
| bool OffsetRegShifted, enum ShiftType ShiftType, |
| const MCExpr *ShiftAmount, bool Preindexed, |
| bool Postindexed, bool Negative, bool Writeback) { |
| ARMOperand Res; |
| Res.Kind = Memory; |
| Res.Mem.BaseRegNum = BaseRegNum; |
| Res.Mem.OffsetIsReg = OffsetIsReg; |
| Res.Mem.Offset = Offset; |
| Res.Mem.OffsetRegNum = OffsetRegNum; |
| Res.Mem.OffsetRegShifted = OffsetRegShifted; |
| Res.Mem.ShiftType = ShiftType; |
| Res.Mem.ShiftAmount = ShiftAmount; |
| Res.Mem.Preindexed = Preindexed; |
| Res.Mem.Postindexed = Postindexed; |
| Res.Mem.Negative = Negative; |
| Res.Mem.Writeback = Writeback; |
| return Res; |
| } |
| }; |
| |
| } // end anonymous namespace. |
| |
| /// Try to parse a register name. The token must be an Identifier when called, |
| /// and if it is a register name a Reg operand is created, the token is eaten |
| /// and false is returned. Else true is returned and no token is eaten. |
| /// TODO this is likely to change to allow different register types and or to |
| /// parse for a specific register type. |
| bool ARMAsmParser::MaybeParseRegister(ARMOperand &Op, bool ParseWriteBack) { |
| const AsmToken &Tok = Parser.getTok(); |
| assert(Tok.is(AsmToken::Identifier) && "Token is not an Identifier"); |
| |
| // FIXME: Validate register for the current architecture; we have to do |
| // validation later, so maybe there is no need for this here. |
| int RegNum; |
| |
| RegNum = MatchRegisterName(Tok.getString()); |
| if (RegNum == -1) |
| return true; |
| Parser.Lex(); // Eat identifier token. |
| |
| bool Writeback = false; |
| if (ParseWriteBack) { |
| const AsmToken &ExclaimTok = Parser.getTok(); |
| if (ExclaimTok.is(AsmToken::Exclaim)) { |
| Writeback = true; |
| Parser.Lex(); // Eat exclaim token |
| } |
| } |
| |
| Op = ARMOperand::CreateReg(RegNum, Writeback); |
| |
| return false; |
| } |
| |
| /// Parse a register list, return false if successful else return true or an |
| /// error. The first token must be a '{' when called. |
| bool ARMAsmParser::ParseRegisterList(ARMOperand &Op) { |
| assert(Parser.getTok().is(AsmToken::LCurly) && |
| "Token is not an Left Curly Brace"); |
| Parser.Lex(); // Eat left curly brace token. |
| |
| const AsmToken &RegTok = Parser.getTok(); |
| SMLoc RegLoc = RegTok.getLoc(); |
| if (RegTok.isNot(AsmToken::Identifier)) |
| return Error(RegLoc, "register expected"); |
| int RegNum = MatchRegisterName(RegTok.getString()); |
| if (RegNum == -1) |
| return Error(RegLoc, "register expected"); |
| Parser.Lex(); // Eat identifier token. |
| unsigned RegList = 1 << RegNum; |
| |
| int HighRegNum = RegNum; |
| // TODO ranges like "{Rn-Rm}" |
| while (Parser.getTok().is(AsmToken::Comma)) { |
| Parser.Lex(); // Eat comma token. |
| |
| const AsmToken &RegTok = Parser.getTok(); |
| SMLoc RegLoc = RegTok.getLoc(); |
| if (RegTok.isNot(AsmToken::Identifier)) |
| return Error(RegLoc, "register expected"); |
| int RegNum = MatchRegisterName(RegTok.getString()); |
| if (RegNum == -1) |
| return Error(RegLoc, "register expected"); |
| |
| if (RegList & (1 << RegNum)) |
| Warning(RegLoc, "register duplicated in register list"); |
| else if (RegNum <= HighRegNum) |
| Warning(RegLoc, "register not in ascending order in register list"); |
| RegList |= 1 << RegNum; |
| HighRegNum = RegNum; |
| |
| Parser.Lex(); // Eat identifier token. |
| } |
| const AsmToken &RCurlyTok = Parser.getTok(); |
| if (RCurlyTok.isNot(AsmToken::RCurly)) |
| return Error(RCurlyTok.getLoc(), "'}' expected"); |
| Parser.Lex(); // Eat left curly brace token. |
| |
| return false; |
| } |
| |
| /// Parse an arm memory expression, return false if successful else return true |
| /// or an error. The first token must be a '[' when called. |
| /// TODO Only preindexing and postindexing addressing are started, unindexed |
| /// with option, etc are still to do. |
| bool ARMAsmParser::ParseMemory(ARMOperand &Op) { |
| assert(Parser.getTok().is(AsmToken::LBrac) && |
| "Token is not an Left Bracket"); |
| Parser.Lex(); // Eat left bracket token. |
| |
| const AsmToken &BaseRegTok = Parser.getTok(); |
| if (BaseRegTok.isNot(AsmToken::Identifier)) |
| return Error(BaseRegTok.getLoc(), "register expected"); |
| if (MaybeParseRegister(Op, false)) |
| return Error(BaseRegTok.getLoc(), "register expected"); |
| int BaseRegNum = Op.getReg(); |
| |
| bool Preindexed = false; |
| bool Postindexed = false; |
| bool OffsetIsReg = false; |
| bool Negative = false; |
| bool Writeback = false; |
| |
| // First look for preindexed address forms, that is after the "[Rn" we now |
| // have to see if the next token is a comma. |
| const AsmToken &Tok = Parser.getTok(); |
| if (Tok.is(AsmToken::Comma)) { |
| Preindexed = true; |
| Parser.Lex(); // Eat comma token. |
| int OffsetRegNum; |
| bool OffsetRegShifted; |
| enum ShiftType ShiftType; |
| const MCExpr *ShiftAmount; |
| const MCExpr *Offset; |
| if(ParseMemoryOffsetReg(Negative, OffsetRegShifted, ShiftType, ShiftAmount, |
| Offset, OffsetIsReg, OffsetRegNum)) |
| return true; |
| const AsmToken &RBracTok = Parser.getTok(); |
| if (RBracTok.isNot(AsmToken::RBrac)) |
| return Error(RBracTok.getLoc(), "']' expected"); |
| Parser.Lex(); // Eat right bracket token. |
| |
| const AsmToken &ExclaimTok = Parser.getTok(); |
| if (ExclaimTok.is(AsmToken::Exclaim)) { |
| Writeback = true; |
| Parser.Lex(); // Eat exclaim token |
| } |
| Op = ARMOperand::CreateMem(BaseRegNum, OffsetIsReg, Offset, OffsetRegNum, |
| OffsetRegShifted, ShiftType, ShiftAmount, |
| Preindexed, Postindexed, Negative, Writeback); |
| return false; |
| } |
| // The "[Rn" we have so far was not followed by a comma. |
| else if (Tok.is(AsmToken::RBrac)) { |
| // This is a post indexing addressing forms, that is a ']' follows after |
| // the "[Rn". |
| Postindexed = true; |
| Writeback = true; |
| Parser.Lex(); // Eat right bracket token. |
| |
| int OffsetRegNum = 0; |
| bool OffsetRegShifted = false; |
| enum ShiftType ShiftType; |
| const MCExpr *ShiftAmount; |
| const MCExpr *Offset; |
| |
| const AsmToken &NextTok = Parser.getTok(); |
| if (NextTok.isNot(AsmToken::EndOfStatement)) { |
| if (NextTok.isNot(AsmToken::Comma)) |
| return Error(NextTok.getLoc(), "',' expected"); |
| Parser.Lex(); // Eat comma token. |
| if(ParseMemoryOffsetReg(Negative, OffsetRegShifted, ShiftType, |
| ShiftAmount, Offset, OffsetIsReg, OffsetRegNum)) |
| return true; |
| } |
| |
| Op = ARMOperand::CreateMem(BaseRegNum, OffsetIsReg, Offset, OffsetRegNum, |
| OffsetRegShifted, ShiftType, ShiftAmount, |
| Preindexed, Postindexed, Negative, Writeback); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /// Parse the offset of a memory operand after we have seen "[Rn," or "[Rn]," |
| /// we will parse the following (were +/- means that a plus or minus is |
| /// optional): |
| /// +/-Rm |
| /// +/-Rm, shift |
| /// #offset |
| /// we return false on success or an error otherwise. |
| bool ARMAsmParser::ParseMemoryOffsetReg(bool &Negative, |
| bool &OffsetRegShifted, |
| enum ShiftType &ShiftType, |
| const MCExpr *&ShiftAmount, |
| const MCExpr *&Offset, |
| bool &OffsetIsReg, |
| int &OffsetRegNum) { |
| ARMOperand Op; |
| Negative = false; |
| OffsetRegShifted = false; |
| OffsetIsReg = false; |
| OffsetRegNum = -1; |
| const AsmToken &NextTok = Parser.getTok(); |
| if (NextTok.is(AsmToken::Plus)) |
| Parser.Lex(); // Eat plus token. |
| else if (NextTok.is(AsmToken::Minus)) { |
| Negative = true; |
| Parser.Lex(); // Eat minus token |
| } |
| // See if there is a register following the "[Rn," or "[Rn]," we have so far. |
| const AsmToken &OffsetRegTok = Parser.getTok(); |
| if (OffsetRegTok.is(AsmToken::Identifier)) { |
| OffsetIsReg = !MaybeParseRegister(Op, false); |
| if (OffsetIsReg) |
| OffsetRegNum = Op.getReg(); |
| } |
| // If we parsed a register as the offset then their can be a shift after that |
| if (OffsetRegNum != -1) { |
| // Look for a comma then a shift |
| const AsmToken &Tok = Parser.getTok(); |
| if (Tok.is(AsmToken::Comma)) { |
| Parser.Lex(); // Eat comma token. |
| |
| const AsmToken &Tok = Parser.getTok(); |
| if (ParseShift(ShiftType, ShiftAmount)) |
| return Error(Tok.getLoc(), "shift expected"); |
| OffsetRegShifted = true; |
| } |
| } |
| else { // the "[Rn," or "[Rn,]" we have so far was not followed by "Rm" |
| // Look for #offset following the "[Rn," or "[Rn]," |
| const AsmToken &HashTok = Parser.getTok(); |
| if (HashTok.isNot(AsmToken::Hash)) |
| return Error(HashTok.getLoc(), "'#' expected"); |
| Parser.Lex(); // Eat hash token. |
| |
| if (getParser().ParseExpression(Offset)) |
| return true; |
| } |
| return false; |
| } |
| |
| /// ParseShift as one of these two: |
| /// ( lsl | lsr | asr | ror ) , # shift_amount |
| /// rrx |
| /// and returns true if it parses a shift otherwise it returns false. |
| bool ARMAsmParser::ParseShift(ShiftType &St, const MCExpr *&ShiftAmount) { |
| const AsmToken &Tok = Parser.getTok(); |
| if (Tok.isNot(AsmToken::Identifier)) |
| return true; |
| const StringRef &ShiftName = Tok.getString(); |
| if (ShiftName == "lsl" || ShiftName == "LSL") |
| St = Lsl; |
| else if (ShiftName == "lsr" || ShiftName == "LSR") |
| St = Lsr; |
| else if (ShiftName == "asr" || ShiftName == "ASR") |
| St = Asr; |
| else if (ShiftName == "ror" || ShiftName == "ROR") |
| St = Ror; |
| else if (ShiftName == "rrx" || ShiftName == "RRX") |
| St = Rrx; |
| else |
| return true; |
| Parser.Lex(); // Eat shift type token. |
| |
| // Rrx stands alone. |
| if (St == Rrx) |
| return false; |
| |
| // Otherwise, there must be a '#' and a shift amount. |
| const AsmToken &HashTok = Parser.getTok(); |
| if (HashTok.isNot(AsmToken::Hash)) |
| return Error(HashTok.getLoc(), "'#' expected"); |
| Parser.Lex(); // Eat hash token. |
| |
| if (getParser().ParseExpression(ShiftAmount)) |
| return true; |
| |
| return false; |
| } |
| |
| /// A hack to allow some testing, to be replaced by a real table gen version. |
| int ARMAsmParser::MatchRegisterName(const StringRef &Name) { |
| if (Name == "r0" || Name == "R0") |
| return 0; |
| else if (Name == "r1" || Name == "R1") |
| return 1; |
| else if (Name == "r2" || Name == "R2") |
| return 2; |
| else if (Name == "r3" || Name == "R3") |
| return 3; |
| else if (Name == "r3" || Name == "R3") |
| return 3; |
| else if (Name == "r4" || Name == "R4") |
| return 4; |
| else if (Name == "r5" || Name == "R5") |
| return 5; |
| else if (Name == "r6" || Name == "R6") |
| return 6; |
| else if (Name == "r7" || Name == "R7") |
| return 7; |
| else if (Name == "r8" || Name == "R8") |
| return 8; |
| else if (Name == "r9" || Name == "R9") |
| return 9; |
| else if (Name == "r10" || Name == "R10") |
| return 10; |
| else if (Name == "r11" || Name == "R11" || Name == "fp") |
| return 11; |
| else if (Name == "r12" || Name == "R12" || Name == "ip") |
| return 12; |
| else if (Name == "r13" || Name == "R13" || Name == "sp") |
| return 13; |
| else if (Name == "r14" || Name == "R14" || Name == "lr") |
| return 14; |
| else if (Name == "r15" || Name == "R15" || Name == "pc") |
| return 15; |
| return -1; |
| } |
| |
| /// A hack to allow some testing, to be replaced by a real table gen version. |
| bool ARMAsmParser:: |
| MatchInstruction(const SmallVectorImpl<MCParsedAsmOperand*> &Operands, |
| MCInst &Inst) { |
| ARMOperand &Op0 = *(ARMOperand*)Operands[0]; |
| assert(Op0.Kind == ARMOperand::Token && "First operand not a Token"); |
| const StringRef &Mnemonic = Op0.getToken(); |
| if (Mnemonic == "add" || |
| Mnemonic == "stmfd" || |
| Mnemonic == "str" || |
| Mnemonic == "ldmfd" || |
| Mnemonic == "ldr" || |
| Mnemonic == "mov" || |
| Mnemonic == "sub" || |
| Mnemonic == "bl" || |
| Mnemonic == "push" || |
| Mnemonic == "blx" || |
| Mnemonic == "pop") { |
| // Hard-coded to a valid instruction, till we have a real matcher. |
| Inst = MCInst(); |
| Inst.setOpcode(ARM::MOVr); |
| Inst.addOperand(MCOperand::CreateReg(2)); |
| Inst.addOperand(MCOperand::CreateReg(2)); |
| Inst.addOperand(MCOperand::CreateImm(0)); |
| Inst.addOperand(MCOperand::CreateImm(0)); |
| Inst.addOperand(MCOperand::CreateReg(0)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /// Parse a arm instruction operand. For now this parses the operand regardless |
| /// of the mnemonic. |
| bool ARMAsmParser::ParseOperand(ARMOperand &Op) { |
| switch (getLexer().getKind()) { |
| case AsmToken::Identifier: |
| if (!MaybeParseRegister(Op, true)) |
| return false; |
| // This was not a register so parse other operands that start with an |
| // identifier (like labels) as expressions and create them as immediates. |
| const MCExpr *IdVal; |
| if (getParser().ParseExpression(IdVal)) |
| return true; |
| Op = ARMOperand::CreateImm(IdVal); |
| return false; |
| case AsmToken::LBrac: |
| return ParseMemory(Op); |
| case AsmToken::LCurly: |
| return ParseRegisterList(Op); |
| case AsmToken::Hash: |
| // #42 -> immediate. |
| // TODO: ":lower16:" and ":upper16:" modifiers after # before immediate |
| Parser.Lex(); |
| const MCExpr *ImmVal; |
| if (getParser().ParseExpression(ImmVal)) |
| return true; |
| Op = ARMOperand::CreateImm(ImmVal); |
| return false; |
| default: |
| return Error(Parser.getTok().getLoc(), "unexpected token in operand"); |
| } |
| } |
| |
| /// Parse an arm instruction mnemonic followed by its operands. |
| bool ARMAsmParser::ParseInstruction(const StringRef &Name, SMLoc NameLoc, |
| SmallVectorImpl<MCParsedAsmOperand*> &Operands) { |
| Operands.push_back(new ARMOperand(ARMOperand::CreateToken(Name))); |
| |
| SMLoc Loc = Parser.getTok().getLoc(); |
| if (getLexer().isNot(AsmToken::EndOfStatement)) { |
| |
| // Read the first operand. |
| ARMOperand Op; |
| if (ParseOperand(Op)) return true; |
| Operands.push_back(new ARMOperand(Op)); |
| |
| while (getLexer().is(AsmToken::Comma)) { |
| Parser.Lex(); // Eat the comma. |
| |
| // Parse and remember the operand. |
| if (ParseOperand(Op)) return true; |
| Operands.push_back(new ARMOperand(Op)); |
| } |
| } |
| return false; |
| } |
| |
| /// ParseDirective parses the arm specific directives |
| bool ARMAsmParser::ParseDirective(AsmToken DirectiveID) { |
| StringRef IDVal = DirectiveID.getIdentifier(); |
| if (IDVal == ".word") |
| return ParseDirectiveWord(4, DirectiveID.getLoc()); |
| else if (IDVal == ".thumb") |
| return ParseDirectiveThumb(DirectiveID.getLoc()); |
| else if (IDVal == ".thumb_func") |
| return ParseDirectiveThumbFunc(DirectiveID.getLoc()); |
| else if (IDVal == ".code") |
| return ParseDirectiveCode(DirectiveID.getLoc()); |
| else if (IDVal == ".syntax") |
| return ParseDirectiveSyntax(DirectiveID.getLoc()); |
| return true; |
| } |
| |
| /// ParseDirectiveWord |
| /// ::= .word [ expression (, expression)* ] |
| bool ARMAsmParser::ParseDirectiveWord(unsigned Size, SMLoc L) { |
| if (getLexer().isNot(AsmToken::EndOfStatement)) { |
| for (;;) { |
| const MCExpr *Value; |
| if (getParser().ParseExpression(Value)) |
| return true; |
| |
| getParser().getStreamer().EmitValue(Value, Size, 0/*addrspace*/); |
| |
| if (getLexer().is(AsmToken::EndOfStatement)) |
| break; |
| |
| // FIXME: Improve diagnostic. |
| if (getLexer().isNot(AsmToken::Comma)) |
| return Error(L, "unexpected token in directive"); |
| Parser.Lex(); |
| } |
| } |
| |
| Parser.Lex(); |
| return false; |
| } |
| |
| /// ParseDirectiveThumb |
| /// ::= .thumb |
| bool ARMAsmParser::ParseDirectiveThumb(SMLoc L) { |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return Error(L, "unexpected token in directive"); |
| Parser.Lex(); |
| |
| // TODO: set thumb mode |
| // TODO: tell the MC streamer the mode |
| // getParser().getStreamer().Emit???(); |
| return false; |
| } |
| |
| /// ParseDirectiveThumbFunc |
| /// ::= .thumbfunc symbol_name |
| bool ARMAsmParser::ParseDirectiveThumbFunc(SMLoc L) { |
| const AsmToken &Tok = Parser.getTok(); |
| if (Tok.isNot(AsmToken::Identifier) && Tok.isNot(AsmToken::String)) |
| return Error(L, "unexpected token in .syntax directive"); |
| StringRef ATTRIBUTE_UNUSED SymbolName = Parser.getTok().getIdentifier(); |
| Parser.Lex(); // Consume the identifier token. |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return Error(L, "unexpected token in directive"); |
| Parser.Lex(); |
| |
| // TODO: mark symbol as a thumb symbol |
| // getParser().getStreamer().Emit???(); |
| return false; |
| } |
| |
| /// ParseDirectiveSyntax |
| /// ::= .syntax unified | divided |
| bool ARMAsmParser::ParseDirectiveSyntax(SMLoc L) { |
| const AsmToken &Tok = Parser.getTok(); |
| if (Tok.isNot(AsmToken::Identifier)) |
| return Error(L, "unexpected token in .syntax directive"); |
| const StringRef &Mode = Tok.getString(); |
| bool unified_syntax; |
| if (Mode == "unified" || Mode == "UNIFIED") { |
| Parser.Lex(); |
| unified_syntax = true; |
| } |
| else if (Mode == "divided" || Mode == "DIVIDED") { |
| Parser.Lex(); |
| unified_syntax = false; |
| } |
| else |
| return Error(L, "unrecognized syntax mode in .syntax directive"); |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return Error(Parser.getTok().getLoc(), "unexpected token in directive"); |
| Parser.Lex(); |
| |
| // TODO tell the MC streamer the mode |
| // getParser().getStreamer().Emit???(); |
| return false; |
| } |
| |
| /// ParseDirectiveCode |
| /// ::= .code 16 | 32 |
| bool ARMAsmParser::ParseDirectiveCode(SMLoc L) { |
| const AsmToken &Tok = Parser.getTok(); |
| if (Tok.isNot(AsmToken::Integer)) |
| return Error(L, "unexpected token in .code directive"); |
| int64_t Val = Parser.getTok().getIntVal(); |
| bool thumb_mode; |
| if (Val == 16) { |
| Parser.Lex(); |
| thumb_mode = true; |
| } |
| else if (Val == 32) { |
| Parser.Lex(); |
| thumb_mode = false; |
| } |
| else |
| return Error(L, "invalid operand to .code directive"); |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) |
| return Error(Parser.getTok().getLoc(), "unexpected token in directive"); |
| Parser.Lex(); |
| |
| // TODO tell the MC streamer the mode |
| // getParser().getStreamer().Emit???(); |
| return false; |
| } |
| |
| /// Force static initialization. |
| extern "C" void LLVMInitializeARMAsmParser() { |
| RegisterAsmParser<ARMAsmParser> X(TheARMTarget); |
| RegisterAsmParser<ARMAsmParser> Y(TheThumbTarget); |
| } |