//===- 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(); } 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(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(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(getImm())->getValue() & 0x3) == 0); } bool isOffset8m8() const { return isImm(0, 255); } bool isOffset8m16() const { return isImm(0, 510) && ((cast(getImm())->getValue() & 0x1) == 0); } bool isOffset8m32() const { return isImm(0, 1020) && ((cast(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(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(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 << ""; break; case Token: OS << "'" << getToken() << "'"; break; } } static std::unique_ptr createToken(StringRef Str, SMLoc S) { auto Op = std::make_unique(Token); Op->Tok = Str; Op->StartLoc = S; Op->EndLoc = S; return Op; } static std::unique_ptr createReg(unsigned RegNo, SMLoc S, SMLoc E) { auto Op = std::make_unique(Register); Op->Reg.RegNum = RegNo; Op->StartLoc = S; Op->EndLoc = E; return Op; } static std::unique_ptr createImm(const MCExpr *Val, SMLoc S, SMLoc E) { auto Op = std::make_unique(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(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 (!parseOptionalToken(AsmToken::Comma)) { SMLoc Loc = getLexer().getLoc(); getParser().eatToEndOfStatement(); return Error(Loc, "unexpected token"); } // 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 (parseOptionalToken(AsmToken::Comma)) 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; } // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeXtensaAsmParser() { RegisterMCAsmParser X(getTheXtensaTarget()); }