| //===- XtensaAsmParser.cpp - Parse Xtensa assembly to MCInst instructions -===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "MCTargetDesc/XtensaMCTargetDesc.h" |
| #include "TargetInfo/XtensaTargetInfo.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCExpr.h" |
| #include "llvm/MC/MCInst.h" |
| #include "llvm/MC/MCInstrInfo.h" |
| #include "llvm/MC/MCParser/MCAsmLexer.h" |
| #include "llvm/MC/MCParser/MCParsedAsmOperand.h" |
| #include "llvm/MC/MCParser/MCTargetAsmParser.h" |
| #include "llvm/MC/MCRegisterInfo.h" |
| #include "llvm/MC/MCStreamer.h" |
| #include "llvm/MC/MCSubtargetInfo.h" |
| #include "llvm/MC/TargetRegistry.h" |
| #include "llvm/Support/Casting.h" |
| |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "xtensa-asm-parser" |
| |
| struct XtensaOperand; |
| |
| class XtensaAsmParser : public MCTargetAsmParser { |
| |
| SMLoc getLoc() const { return getParser().getTok().getLoc(); } |
| |
| // Override MCTargetAsmParser. |
| bool ParseDirective(AsmToken DirectiveID) override; |
| bool parseRegister(MCRegister &RegNo, |
| SMLoc &StartLoc, SMLoc &EndLoc) override; |
| bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, |
| SMLoc NameLoc, OperandVector &Operands) override; |
| bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, |
| OperandVector &Operands, MCStreamer &Out, |
| uint64_t &ErrorInfo, |
| bool MatchingInlineAsm) override; |
| unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, |
| unsigned Kind) override; |
| |
| // Auto-generated instruction matching functions |
| #define GET_ASSEMBLER_HEADER |
| #include "XtensaGenAsmMatcher.inc" |
| |
| OperandMatchResultTy parseImmediate(OperandVector &Operands); |
| OperandMatchResultTy parseRegister(OperandVector &Operands, |
| bool AllowParens = false, bool SR = false); |
| OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands); |
| bool parseOperand(OperandVector &Operands, StringRef Mnemonic, |
| bool SR = false); |
| bool ParseInstructionWithSR(ParseInstructionInfo &Info, StringRef Name, |
| SMLoc NameLoc, OperandVector &Operands); |
| OperandMatchResultTy tryParseRegister(MCRegister &RegNo, SMLoc &StartLoc, |
| SMLoc &EndLoc) override { |
| return MatchOperand_NoMatch; |
| } |
| OperandMatchResultTy parsePCRelTarget(OperandVector &Operands); |
| |
| public: |
| enum XtensaMatchResultTy { |
| Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, |
| #define GET_OPERAND_DIAGNOSTIC_TYPES |
| #include "XtensaGenAsmMatcher.inc" |
| #undef GET_OPERAND_DIAGNOSTIC_TYPES |
| }; |
| |
| XtensaAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, |
| const MCInstrInfo &MII, const MCTargetOptions &Options) |
| : MCTargetAsmParser(Options, STI, MII) { |
| setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); |
| } |
| }; |
| |
| // Return true if Expr is in the range [MinValue, MaxValue]. |
| static bool inRange(const MCExpr *Expr, int64_t MinValue, int64_t MaxValue) { |
| if (auto *CE = dyn_cast<MCConstantExpr>(Expr)) { |
| int64_t Value = CE->getValue(); |
| return Value >= MinValue && Value <= MaxValue; |
| } |
| return false; |
| } |
| |
| struct XtensaOperand : public MCParsedAsmOperand { |
| |
| enum KindTy { |
| Token, |
| Register, |
| Immediate, |
| } Kind; |
| |
| struct RegOp { |
| unsigned RegNum; |
| }; |
| |
| struct ImmOp { |
| const MCExpr *Val; |
| }; |
| |
| SMLoc StartLoc, EndLoc; |
| union { |
| StringRef Tok; |
| RegOp Reg; |
| ImmOp Imm; |
| }; |
| |
| XtensaOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {} |
| |
| public: |
| XtensaOperand(const XtensaOperand &o) : MCParsedAsmOperand() { |
| Kind = o.Kind; |
| StartLoc = o.StartLoc; |
| EndLoc = o.EndLoc; |
| switch (Kind) { |
| case Register: |
| Reg = o.Reg; |
| break; |
| case Immediate: |
| Imm = o.Imm; |
| break; |
| case Token: |
| Tok = o.Tok; |
| break; |
| } |
| } |
| |
| bool isToken() const override { return Kind == Token; } |
| bool isReg() const override { return Kind == Register; } |
| bool isImm() const override { return Kind == Immediate; } |
| bool isMem() const override { return false; } |
| |
| bool isImm(int64_t MinValue, int64_t MaxValue) const { |
| return Kind == Immediate && inRange(getImm(), MinValue, MaxValue); |
| } |
| |
| bool isImm8() const { return isImm(-128, 127); } |
| |
| bool isImm8_sh8() const { |
| return isImm(-32768, 32512) && |
| ((cast<MCConstantExpr>(getImm())->getValue() & 0xFF) == 0); |
| } |
| |
| bool isImm12() const { return isImm(-2048, 2047); } |
| |
| bool isImm12m() const { return isImm(-2048, 2047); } |
| |
| bool isOffset4m32() const { |
| return isImm(0, 60) && |
| ((cast<MCConstantExpr>(getImm())->getValue() & 0x3) == 0); |
| } |
| |
| bool isOffset8m8() const { return isImm(0, 255); } |
| |
| bool isOffset8m16() const { |
| return isImm(0, 510) && |
| ((cast<MCConstantExpr>(getImm())->getValue() & 0x1) == 0); |
| } |
| |
| bool isOffset8m32() const { |
| return isImm(0, 1020) && |
| ((cast<MCConstantExpr>(getImm())->getValue() & 0x3) == 0); |
| } |
| |
| bool isUimm4() const { return isImm(0, 15); } |
| |
| bool isUimm5() const { return isImm(0, 31); } |
| |
| bool isImm8n_7() const { return isImm(-8, 7); } |
| |
| bool isShimm1_31() const { return isImm(1, 31); } |
| |
| bool isImm16_31() const { return isImm(16, 31); } |
| |
| bool isImm1_16() const { return isImm(1, 16); } |
| |
| bool isB4const() const { |
| if (Kind != Immediate) |
| return false; |
| if (auto *CE = dyn_cast<MCConstantExpr>(getImm())) { |
| int64_t Value = CE->getValue(); |
| switch (Value) { |
| case -1: |
| case 1: |
| case 2: |
| case 3: |
| case 4: |
| case 5: |
| case 6: |
| case 7: |
| case 8: |
| case 10: |
| case 12: |
| case 16: |
| case 32: |
| case 64: |
| case 128: |
| case 256: |
| return true; |
| default: |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| bool isB4constu() const { |
| if (Kind != Immediate) |
| return false; |
| if (auto *CE = dyn_cast<MCConstantExpr>(getImm())) { |
| int64_t Value = CE->getValue(); |
| switch (Value) { |
| case 32768: |
| case 65536: |
| case 2: |
| case 3: |
| case 4: |
| case 5: |
| case 6: |
| case 7: |
| case 8: |
| case 10: |
| case 12: |
| case 16: |
| case 32: |
| case 64: |
| case 128: |
| case 256: |
| return true; |
| default: |
| return false; |
| } |
| } |
| return false; |
| } |
| |
| /// getStartLoc - Gets location of the first token of this operand |
| SMLoc getStartLoc() const override { return StartLoc; } |
| /// getEndLoc - Gets location of the last token of this operand |
| SMLoc getEndLoc() const override { return EndLoc; } |
| |
| unsigned getReg() const override { |
| assert(Kind == Register && "Invalid type access!"); |
| return Reg.RegNum; |
| } |
| |
| const MCExpr *getImm() const { |
| assert(Kind == Immediate && "Invalid type access!"); |
| return Imm.Val; |
| } |
| |
| StringRef getToken() const { |
| assert(Kind == Token && "Invalid type access!"); |
| return Tok; |
| } |
| |
| void print(raw_ostream &OS) const override { |
| switch (Kind) { |
| case Immediate: |
| OS << *getImm(); |
| break; |
| case Register: |
| OS << "<register x"; |
| OS << getReg() << ">"; |
| break; |
| case Token: |
| OS << "'" << getToken() << "'"; |
| break; |
| } |
| } |
| |
| static std::unique_ptr<XtensaOperand> createToken(StringRef Str, SMLoc S) { |
| auto Op = std::make_unique<XtensaOperand>(Token); |
| Op->Tok = Str; |
| Op->StartLoc = S; |
| Op->EndLoc = S; |
| return Op; |
| } |
| |
| static std::unique_ptr<XtensaOperand> createReg(unsigned RegNo, SMLoc S, |
| SMLoc E) { |
| auto Op = std::make_unique<XtensaOperand>(Register); |
| Op->Reg.RegNum = RegNo; |
| Op->StartLoc = S; |
| Op->EndLoc = E; |
| return Op; |
| } |
| |
| static std::unique_ptr<XtensaOperand> createImm(const MCExpr *Val, SMLoc S, |
| SMLoc E) { |
| auto Op = std::make_unique<XtensaOperand>(Immediate); |
| Op->Imm.Val = Val; |
| Op->StartLoc = S; |
| Op->EndLoc = E; |
| return Op; |
| } |
| |
| void addExpr(MCInst &Inst, const MCExpr *Expr) const { |
| assert(Expr && "Expr shouldn't be null!"); |
| int64_t Imm = 0; |
| bool IsConstant = false; |
| |
| if (auto *CE = dyn_cast<MCConstantExpr>(Expr)) { |
| IsConstant = true; |
| Imm = CE->getValue(); |
| } |
| |
| if (IsConstant) |
| Inst.addOperand(MCOperand::createImm(Imm)); |
| else |
| Inst.addOperand(MCOperand::createExpr(Expr)); |
| } |
| |
| // Used by the TableGen Code |
| void addRegOperands(MCInst &Inst, unsigned N) const { |
| assert(N == 1 && "Invalid number of operands!"); |
| Inst.addOperand(MCOperand::createReg(getReg())); |
| } |
| |
| void addImmOperands(MCInst &Inst, unsigned N) const { |
| assert(N == 1 && "Invalid number of operands!"); |
| addExpr(Inst, getImm()); |
| } |
| }; |
| |
| #define GET_REGISTER_MATCHER |
| #define GET_MATCHER_IMPLEMENTATION |
| #include "XtensaGenAsmMatcher.inc" |
| |
| unsigned XtensaAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, |
| unsigned Kind) { |
| return Match_InvalidOperand; |
| } |
| |
| static SMLoc RefineErrorLoc(const SMLoc Loc, const OperandVector &Operands, |
| uint64_t ErrorInfo) { |
| if (ErrorInfo != ~0ULL && ErrorInfo < Operands.size()) { |
| SMLoc ErrorLoc = Operands[ErrorInfo]->getStartLoc(); |
| if (ErrorLoc == SMLoc()) |
| return Loc; |
| return ErrorLoc; |
| } |
| return Loc; |
| } |
| |
| bool XtensaAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, |
| OperandVector &Operands, |
| MCStreamer &Out, |
| uint64_t &ErrorInfo, |
| bool MatchingInlineAsm) { |
| MCInst Inst; |
| auto Result = |
| MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); |
| |
| switch (Result) { |
| default: |
| break; |
| case Match_Success: |
| Inst.setLoc(IDLoc); |
| Out.emitInstruction(Inst, getSTI()); |
| return false; |
| case Match_MissingFeature: |
| return Error(IDLoc, "instruction use requires an option to be enabled"); |
| case Match_MnemonicFail: |
| return Error(IDLoc, "unrecognized instruction mnemonic"); |
| case Match_InvalidOperand: { |
| SMLoc ErrorLoc = IDLoc; |
| if (ErrorInfo != ~0U) { |
| if (ErrorInfo >= Operands.size()) |
| return Error(ErrorLoc, "too few operands for instruction"); |
| |
| ErrorLoc = ((XtensaOperand &)*Operands[ErrorInfo]).getStartLoc(); |
| if (ErrorLoc == SMLoc()) |
| ErrorLoc = IDLoc; |
| } |
| return Error(ErrorLoc, "invalid operand for instruction"); |
| } |
| case Match_InvalidImm8: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [-128, 127]"); |
| case Match_InvalidImm8_sh8: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [-32768, 32512], first 8 bits " |
| "should be zero"); |
| case Match_InvalidB4const: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected b4const immediate"); |
| case Match_InvalidB4constu: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected b4constu immediate"); |
| case Match_InvalidImm12: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [-2048, 2047]"); |
| case Match_InvalidImm12m: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [-2048, 2047]"); |
| case Match_InvalidImm1_16: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [1, 16]"); |
| case Match_InvalidShimm1_31: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [1, 31]"); |
| case Match_InvalidUimm4: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [0, 15]"); |
| case Match_InvalidUimm5: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [0, 31]"); |
| case Match_InvalidOffset8m8: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [0, 255]"); |
| case Match_InvalidOffset8m16: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [0, 510], first bit " |
| "should be zero"); |
| case Match_InvalidOffset8m32: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [0, 1020], first 2 bits " |
| "should be zero"); |
| case Match_InvalidOffset4m32: |
| return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), |
| "expected immediate in range [0, 60], first 2 bits " |
| "should be zero"); |
| } |
| |
| report_fatal_error("Unknown match type detected!"); |
| } |
| |
| OperandMatchResultTy |
| XtensaAsmParser::parsePCRelTarget(OperandVector &Operands) { |
| MCAsmParser &Parser = getParser(); |
| LLVM_DEBUG(dbgs() << "parsePCRelTarget\n"); |
| |
| SMLoc S = getLexer().getLoc(); |
| |
| // Expressions are acceptable |
| const MCExpr *Expr = nullptr; |
| if (Parser.parseExpression(Expr)) { |
| // We have no way of knowing if a symbol was consumed so we must ParseFail |
| return MatchOperand_ParseFail; |
| } |
| |
| // Currently not support constants |
| if (Expr->getKind() == MCExpr::ExprKind::Constant) { |
| Error(getLoc(), "unknown operand"); |
| return MatchOperand_ParseFail; |
| } |
| |
| Operands.push_back(XtensaOperand::createImm(Expr, S, getLexer().getLoc())); |
| return MatchOperand_Success; |
| } |
| |
| bool XtensaAsmParser::parseRegister(MCRegister &RegNo, SMLoc &StartLoc, |
| SMLoc &EndLoc) { |
| const AsmToken &Tok = getParser().getTok(); |
| StartLoc = Tok.getLoc(); |
| EndLoc = Tok.getEndLoc(); |
| RegNo = 0; |
| StringRef Name = getLexer().getTok().getIdentifier(); |
| |
| if (!MatchRegisterName(Name) && !MatchRegisterAltName(Name)) { |
| getParser().Lex(); // Eat identifier token. |
| return false; |
| } |
| |
| return Error(StartLoc, "invalid register name"); |
| } |
| |
| OperandMatchResultTy XtensaAsmParser::parseRegister(OperandVector &Operands, |
| bool AllowParens, bool SR) { |
| SMLoc FirstS = getLoc(); |
| bool HadParens = false; |
| AsmToken Buf[2]; |
| StringRef RegName; |
| |
| // If this a parenthesised register name is allowed, parse it atomically |
| if (AllowParens && getLexer().is(AsmToken::LParen)) { |
| size_t ReadCount = getLexer().peekTokens(Buf); |
| if (ReadCount == 2 && Buf[1].getKind() == AsmToken::RParen) { |
| if ((Buf[0].getKind() == AsmToken::Integer) && (!SR)) |
| return MatchOperand_NoMatch; |
| HadParens = true; |
| getParser().Lex(); // Eat '(' |
| } |
| } |
| |
| unsigned RegNo = 0; |
| |
| switch (getLexer().getKind()) { |
| default: |
| return MatchOperand_NoMatch; |
| case AsmToken::Integer: |
| if (!SR) |
| return MatchOperand_NoMatch; |
| RegName = StringRef(std::to_string(getLexer().getTok().getIntVal())); |
| RegNo = MatchRegisterName(RegName); |
| if (RegNo == 0) |
| RegNo = MatchRegisterAltName(RegName); |
| break; |
| case AsmToken::Identifier: |
| RegName = getLexer().getTok().getIdentifier(); |
| RegNo = MatchRegisterName(RegName); |
| if (RegNo == 0) |
| RegNo = MatchRegisterAltName(RegName); |
| break; |
| } |
| |
| if (RegNo == 0) { |
| if (HadParens) |
| getLexer().UnLex(Buf[0]); |
| return MatchOperand_NoMatch; |
| } |
| if (HadParens) |
| Operands.push_back(XtensaOperand::createToken("(", FirstS)); |
| SMLoc S = getLoc(); |
| SMLoc E = getParser().getTok().getEndLoc(); |
| getLexer().Lex(); |
| Operands.push_back(XtensaOperand::createReg(RegNo, S, E)); |
| |
| if (HadParens) { |
| getParser().Lex(); // Eat ')' |
| Operands.push_back(XtensaOperand::createToken(")", getLoc())); |
| } |
| |
| return MatchOperand_Success; |
| } |
| |
| OperandMatchResultTy XtensaAsmParser::parseImmediate(OperandVector &Operands) { |
| SMLoc S = getLoc(); |
| SMLoc E; |
| const MCExpr *Res; |
| |
| switch (getLexer().getKind()) { |
| default: |
| return MatchOperand_NoMatch; |
| case AsmToken::LParen: |
| case AsmToken::Minus: |
| case AsmToken::Plus: |
| case AsmToken::Tilde: |
| case AsmToken::Integer: |
| case AsmToken::String: |
| if (getParser().parseExpression(Res)) |
| return MatchOperand_ParseFail; |
| break; |
| case AsmToken::Identifier: { |
| StringRef Identifier; |
| if (getParser().parseIdentifier(Identifier)) |
| return MatchOperand_ParseFail; |
| |
| MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier); |
| Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); |
| break; |
| } |
| case AsmToken::Percent: |
| return parseOperandWithModifier(Operands); |
| } |
| |
| E = SMLoc::getFromPointer(S.getPointer() - 1); |
| Operands.push_back(XtensaOperand::createImm(Res, S, E)); |
| return MatchOperand_Success; |
| } |
| |
| OperandMatchResultTy |
| XtensaAsmParser::parseOperandWithModifier(OperandVector &Operands) { |
| return MatchOperand_ParseFail; |
| } |
| |
| /// Looks at a token type and creates the relevant operand |
| /// from this information, adding to Operands. |
| /// If operand was parsed, returns false, else true. |
| bool XtensaAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic, |
| bool SR) { |
| // Check if the current operand has a custom associated parser, if so, try to |
| // custom parse the operand, or fallback to the general approach. |
| OperandMatchResultTy ResTy = MatchOperandParserImpl(Operands, Mnemonic); |
| if (ResTy == MatchOperand_Success) |
| return false; |
| |
| // If there wasn't a custom match, try the generic matcher below. Otherwise, |
| // there was a match, but an error occurred, in which case, just return that |
| // the operand parsing failed. |
| if (ResTy == MatchOperand_ParseFail) |
| return true; |
| |
| // Attempt to parse token as register |
| if (parseRegister(Operands, true, SR) == MatchOperand_Success) |
| return false; |
| |
| // Attempt to parse token as an immediate |
| if (parseImmediate(Operands) == MatchOperand_Success) { |
| return false; |
| } |
| |
| // Finally we have exhausted all options and must declare defeat. |
| Error(getLoc(), "unknown operand"); |
| return true; |
| } |
| |
| bool XtensaAsmParser::ParseInstructionWithSR(ParseInstructionInfo &Info, |
| StringRef Name, SMLoc NameLoc, |
| OperandVector &Operands) { |
| if ((Name.startswith("wsr.") || Name.startswith("rsr.") || |
| Name.startswith("xsr.")) && |
| (Name.size() > 4)) { |
| // Parse case when instruction name is concatenated with SR register |
| // name, like "wsr.sar a1" |
| |
| // First operand is token for instruction |
| Operands.push_back(XtensaOperand::createToken(Name.take_front(3), NameLoc)); |
| |
| StringRef RegName = Name.drop_front(4); |
| unsigned RegNo = MatchRegisterName(RegName); |
| |
| if (RegNo == 0) |
| RegNo = MatchRegisterAltName(RegName); |
| |
| if (RegNo == 0) { |
| Error(NameLoc, "invalid register name"); |
| return true; |
| } |
| |
| // Parse operand |
| if (parseOperand(Operands, Name)) |
| return true; |
| |
| SMLoc S = getLoc(); |
| SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); |
| Operands.push_back(XtensaOperand::createReg(RegNo, S, E)); |
| } else { |
| // First operand is token for instruction |
| Operands.push_back(XtensaOperand::createToken(Name, NameLoc)); |
| |
| // Parse first operand |
| if (parseOperand(Operands, Name)) |
| return true; |
| |
| if (!getLexer().is(AsmToken::Comma)) { |
| SMLoc Loc = getLexer().getLoc(); |
| getParser().eatToEndOfStatement(); |
| return Error(Loc, "unexpected token"); |
| } |
| |
| getLexer().Lex(); |
| |
| // Parse second operand |
| if (parseOperand(Operands, Name, true)) |
| return true; |
| } |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) { |
| SMLoc Loc = getLexer().getLoc(); |
| getParser().eatToEndOfStatement(); |
| return Error(Loc, "unexpected token"); |
| } |
| |
| getParser().Lex(); // Consume the EndOfStatement. |
| return false; |
| } |
| |
| bool XtensaAsmParser::ParseInstruction(ParseInstructionInfo &Info, |
| StringRef Name, SMLoc NameLoc, |
| OperandVector &Operands) { |
| if (Name.startswith("wsr") || Name.startswith("rsr") || |
| Name.startswith("xsr")) { |
| return ParseInstructionWithSR(Info, Name, NameLoc, Operands); |
| } |
| |
| // First operand is token for instruction |
| Operands.push_back(XtensaOperand::createToken(Name, NameLoc)); |
| |
| // If there are no more operands, then finish |
| if (getLexer().is(AsmToken::EndOfStatement)) |
| return false; |
| |
| // Parse first operand |
| if (parseOperand(Operands, Name)) |
| return true; |
| |
| // Parse until end of statement, consuming commas between operands |
| while (getLexer().is(AsmToken::Comma)) { |
| // Consume comma token |
| getLexer().Lex(); |
| |
| // Parse next operand |
| if (parseOperand(Operands, Name)) |
| return true; |
| } |
| |
| if (getLexer().isNot(AsmToken::EndOfStatement)) { |
| SMLoc Loc = getLexer().getLoc(); |
| getParser().eatToEndOfStatement(); |
| return Error(Loc, "unexpected token"); |
| } |
| |
| getParser().Lex(); // Consume the EndOfStatement. |
| return false; |
| } |
| |
| bool XtensaAsmParser::ParseDirective(AsmToken DirectiveID) { return true; } |
| |
| // Force static initialization. |
| extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaAsmParser() { |
| RegisterMCAsmParser<XtensaAsmParser> X(getTheXtensaTarget()); |
| } |