| //===-- BPFAsmParser.cpp - Parse BPF assembly to MCInst instructions --===// | |
| // | |
| // 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/BPFMCTargetDesc.h" | |
| #include "TargetInfo/BPFTargetInfo.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; | |
| namespace { | |
| struct BPFOperand; | |
| class BPFAsmParser : public MCTargetAsmParser { | |
| SMLoc getLoc() const { return getParser().getTok().getLoc(); } | |
| bool PreMatchCheck(OperandVector &Operands); | |
| bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, | |
| OperandVector &Operands, MCStreamer &Out, | |
| uint64_t &ErrorInfo, | |
| bool MatchingInlineAsm) override; | |
| bool parseRegister(MCRegister &RegNo, SMLoc &StartLoc, | |
| SMLoc &EndLoc) override; | |
| OperandMatchResultTy tryParseRegister(MCRegister &RegNo, SMLoc &StartLoc, | |
| SMLoc &EndLoc) override; | |
| bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, | |
| SMLoc NameLoc, OperandVector &Operands) override; | |
| bool ParseDirective(AsmToken DirectiveID) override; | |
| // "=" is used as assignment operator for assembly statment, so can't be used | |
| // for symbol assignment. | |
| bool equalIsAsmAssignment() override { return false; } | |
| // "*" is used for dereferencing memory that it will be the start of | |
| // statement. | |
| bool starIsStartOfStatement() override { return true; } | |
| #define GET_ASSEMBLER_HEADER | |
| #include "BPFGenAsmMatcher.inc" | |
| OperandMatchResultTy parseImmediate(OperandVector &Operands); | |
| OperandMatchResultTy parseRegister(OperandVector &Operands); | |
| OperandMatchResultTy parseOperandAsOperator(OperandVector &Operands); | |
| public: | |
| enum BPFMatchResultTy { | |
| Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, | |
| #define GET_OPERAND_DIAGNOSTIC_TYPES | |
| #include "BPFGenAsmMatcher.inc" | |
| #undef GET_OPERAND_DIAGNOSTIC_TYPES | |
| }; | |
| BPFAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, | |
| const MCInstrInfo &MII, const MCTargetOptions &Options) | |
| : MCTargetAsmParser(Options, STI, MII) { | |
| setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); | |
| } | |
| }; | |
| /// BPFOperand - Instances of this class represent a parsed machine | |
| /// instruction | |
| struct BPFOperand : 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; | |
| }; | |
| BPFOperand(KindTy K) : Kind(K) {} | |
| public: | |
| BPFOperand(const BPFOperand &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 isConstantImm() const { | |
| return isImm() && isa<MCConstantExpr>(getImm()); | |
| } | |
| int64_t getConstantImm() const { | |
| const MCExpr *Val = getImm(); | |
| return static_cast<const MCConstantExpr *>(Val)->getValue(); | |
| } | |
| bool isSImm12() const { | |
| return (isConstantImm() && isInt<12>(getConstantImm())); | |
| } | |
| /// 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; | |
| } | |
| } | |
| void addExpr(MCInst &Inst, const MCExpr *Expr) const { | |
| assert(Expr && "Expr shouldn't be null!"); | |
| if (auto *CE = dyn_cast<MCConstantExpr>(Expr)) | |
| Inst.addOperand(MCOperand::createImm(CE->getValue())); | |
| 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()); | |
| } | |
| static std::unique_ptr<BPFOperand> createToken(StringRef Str, SMLoc S) { | |
| auto Op = std::make_unique<BPFOperand>(Token); | |
| Op->Tok = Str; | |
| Op->StartLoc = S; | |
| Op->EndLoc = S; | |
| return Op; | |
| } | |
| static std::unique_ptr<BPFOperand> createReg(unsigned RegNo, SMLoc S, | |
| SMLoc E) { | |
| auto Op = std::make_unique<BPFOperand>(Register); | |
| Op->Reg.RegNum = RegNo; | |
| Op->StartLoc = S; | |
| Op->EndLoc = E; | |
| return Op; | |
| } | |
| static std::unique_ptr<BPFOperand> createImm(const MCExpr *Val, SMLoc S, | |
| SMLoc E) { | |
| auto Op = std::make_unique<BPFOperand>(Immediate); | |
| Op->Imm.Val = Val; | |
| Op->StartLoc = S; | |
| Op->EndLoc = E; | |
| return Op; | |
| } | |
| // Identifiers that can be used at the start of a statment. | |
| static bool isValidIdAtStart(StringRef Name) { | |
| return StringSwitch<bool>(Name.lower()) | |
| .Case("if", true) | |
| .Case("call", true) | |
| .Case("goto", true) | |
| .Case("*", true) | |
| .Case("exit", true) | |
| .Case("lock", true) | |
| .Case("ld_pseudo", true) | |
| .Default(false); | |
| } | |
| // Identifiers that can be used in the middle of a statment. | |
| static bool isValidIdInMiddle(StringRef Name) { | |
| return StringSwitch<bool>(Name.lower()) | |
| .Case("u64", true) | |
| .Case("u32", true) | |
| .Case("u16", true) | |
| .Case("u8", true) | |
| .Case("be64", true) | |
| .Case("be32", true) | |
| .Case("be16", true) | |
| .Case("le64", true) | |
| .Case("le32", true) | |
| .Case("le16", true) | |
| .Case("goto", true) | |
| .Case("ll", true) | |
| .Case("skb", true) | |
| .Case("s", true) | |
| .Default(false); | |
| } | |
| }; | |
| } // end anonymous namespace. | |
| #define GET_REGISTER_MATCHER | |
| #define GET_MATCHER_IMPLEMENTATION | |
| #include "BPFGenAsmMatcher.inc" | |
| bool BPFAsmParser::PreMatchCheck(OperandVector &Operands) { | |
| if (Operands.size() == 4) { | |
| // check "reg1 = -reg2" and "reg1 = be16/be32/be64/le16/le32/le64 reg2", | |
| // reg1 must be the same as reg2 | |
| BPFOperand &Op0 = (BPFOperand &)*Operands[0]; | |
| BPFOperand &Op1 = (BPFOperand &)*Operands[1]; | |
| BPFOperand &Op2 = (BPFOperand &)*Operands[2]; | |
| BPFOperand &Op3 = (BPFOperand &)*Operands[3]; | |
| if (Op0.isReg() && Op1.isToken() && Op2.isToken() && Op3.isReg() | |
| && Op1.getToken() == "=" | |
| && (Op2.getToken() == "-" || Op2.getToken() == "be16" | |
| || Op2.getToken() == "be32" || Op2.getToken() == "be64" | |
| || Op2.getToken() == "le16" || Op2.getToken() == "le32" | |
| || Op2.getToken() == "le64") | |
| && Op0.getReg() != Op3.getReg()) | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool BPFAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, | |
| OperandVector &Operands, | |
| MCStreamer &Out, uint64_t &ErrorInfo, | |
| bool MatchingInlineAsm) { | |
| MCInst Inst; | |
| SMLoc ErrorLoc; | |
| if (PreMatchCheck(Operands)) | |
| return Error(IDLoc, "additional inst constraint not met"); | |
| switch (MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm)) { | |
| 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: | |
| ErrorLoc = IDLoc; | |
| if (ErrorInfo != ~0U) { | |
| if (ErrorInfo >= Operands.size()) | |
| return Error(ErrorLoc, "too few operands for instruction"); | |
| ErrorLoc = ((BPFOperand &)*Operands[ErrorInfo]).getStartLoc(); | |
| if (ErrorLoc == SMLoc()) | |
| ErrorLoc = IDLoc; | |
| } | |
| return Error(ErrorLoc, "invalid operand for instruction"); | |
| } | |
| llvm_unreachable("Unknown match type detected!"); | |
| } | |
| bool BPFAsmParser::parseRegister(MCRegister &RegNo, SMLoc &StartLoc, | |
| SMLoc &EndLoc) { | |
| if (tryParseRegister(RegNo, StartLoc, EndLoc) != MatchOperand_Success) | |
| return Error(StartLoc, "invalid register name"); | |
| return false; | |
| } | |
| OperandMatchResultTy BPFAsmParser::tryParseRegister(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)) { | |
| getParser().Lex(); // Eat identifier token. | |
| return MatchOperand_Success; | |
| } | |
| return MatchOperand_NoMatch; | |
| } | |
| OperandMatchResultTy | |
| BPFAsmParser::parseOperandAsOperator(OperandVector &Operands) { | |
| SMLoc S = getLoc(); | |
| if (getLexer().getKind() == AsmToken::Identifier) { | |
| StringRef Name = getLexer().getTok().getIdentifier(); | |
| if (BPFOperand::isValidIdInMiddle(Name)) { | |
| getLexer().Lex(); | |
| Operands.push_back(BPFOperand::createToken(Name, S)); | |
| return MatchOperand_Success; | |
| } | |
| return MatchOperand_NoMatch; | |
| } | |
| switch (getLexer().getKind()) { | |
| case AsmToken::Minus: | |
| case AsmToken::Plus: { | |
| if (getLexer().peekTok().is(AsmToken::Integer)) | |
| return MatchOperand_NoMatch; | |
| [[fallthrough]]; | |
| } | |
| case AsmToken::Equal: | |
| case AsmToken::Greater: | |
| case AsmToken::Less: | |
| case AsmToken::Pipe: | |
| case AsmToken::Star: | |
| case AsmToken::LParen: | |
| case AsmToken::RParen: | |
| case AsmToken::LBrac: | |
| case AsmToken::RBrac: | |
| case AsmToken::Slash: | |
| case AsmToken::Amp: | |
| case AsmToken::Percent: | |
| case AsmToken::Caret: { | |
| StringRef Name = getLexer().getTok().getString(); | |
| getLexer().Lex(); | |
| Operands.push_back(BPFOperand::createToken(Name, S)); | |
| return MatchOperand_Success; | |
| } | |
| case AsmToken::EqualEqual: | |
| case AsmToken::ExclaimEqual: | |
| case AsmToken::GreaterEqual: | |
| case AsmToken::GreaterGreater: | |
| case AsmToken::LessEqual: | |
| case AsmToken::LessLess: { | |
| Operands.push_back(BPFOperand::createToken( | |
| getLexer().getTok().getString().substr(0, 1), S)); | |
| Operands.push_back(BPFOperand::createToken( | |
| getLexer().getTok().getString().substr(1, 1), S)); | |
| getLexer().Lex(); | |
| return MatchOperand_Success; | |
| } | |
| default: | |
| break; | |
| } | |
| return MatchOperand_NoMatch; | |
| } | |
| OperandMatchResultTy BPFAsmParser::parseRegister(OperandVector &Operands) { | |
| SMLoc S = getLoc(); | |
| SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); | |
| switch (getLexer().getKind()) { | |
| default: | |
| return MatchOperand_NoMatch; | |
| case AsmToken::Identifier: | |
| StringRef Name = getLexer().getTok().getIdentifier(); | |
| unsigned RegNo = MatchRegisterName(Name); | |
| if (RegNo == 0) | |
| return MatchOperand_NoMatch; | |
| getLexer().Lex(); | |
| Operands.push_back(BPFOperand::createReg(RegNo, S, E)); | |
| } | |
| return MatchOperand_Success; | |
| } | |
| OperandMatchResultTy BPFAsmParser::parseImmediate(OperandVector &Operands) { | |
| switch (getLexer().getKind()) { | |
| default: | |
| return MatchOperand_NoMatch; | |
| case AsmToken::LParen: | |
| case AsmToken::Minus: | |
| case AsmToken::Plus: | |
| case AsmToken::Integer: | |
| case AsmToken::String: | |
| case AsmToken::Identifier: | |
| break; | |
| } | |
| const MCExpr *IdVal; | |
| SMLoc S = getLoc(); | |
| if (getParser().parseExpression(IdVal)) | |
| return MatchOperand_ParseFail; | |
| SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); | |
| Operands.push_back(BPFOperand::createImm(IdVal, S, E)); | |
| return MatchOperand_Success; | |
| } | |
| /// ParseInstruction - Parse an BPF instruction which is in BPF verifier | |
| /// format. | |
| bool BPFAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, | |
| SMLoc NameLoc, OperandVector &Operands) { | |
| // The first operand could be either register or actually an operator. | |
| unsigned RegNo = MatchRegisterName(Name); | |
| if (RegNo != 0) { | |
| SMLoc E = SMLoc::getFromPointer(NameLoc.getPointer() - 1); | |
| Operands.push_back(BPFOperand::createReg(RegNo, NameLoc, E)); | |
| } else if (BPFOperand::isValidIdAtStart (Name)) | |
| Operands.push_back(BPFOperand::createToken(Name, NameLoc)); | |
| else | |
| return Error(NameLoc, "invalid register/token name"); | |
| while (!getLexer().is(AsmToken::EndOfStatement)) { | |
| // Attempt to parse token as operator | |
| if (parseOperandAsOperator(Operands) == MatchOperand_Success) | |
| continue; | |
| // Attempt to parse token as register | |
| if (parseRegister(Operands) == MatchOperand_Success) | |
| continue; | |
| // Attempt to parse token as an immediate | |
| if (parseImmediate(Operands) != MatchOperand_Success) { | |
| SMLoc Loc = getLexer().getLoc(); | |
| return Error(Loc, "unexpected token"); | |
| } | |
| } | |
| if (getLexer().isNot(AsmToken::EndOfStatement)) { | |
| SMLoc Loc = getLexer().getLoc(); | |
| getParser().eatToEndOfStatement(); | |
| return Error(Loc, "unexpected token"); | |
| } | |
| // Consume the EndOfStatement. | |
| getParser().Lex(); | |
| return false; | |
| } | |
| bool BPFAsmParser::ParseDirective(AsmToken DirectiveID) { return true; } | |
| extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeBPFAsmParser() { | |
| RegisterMCAsmParser<BPFAsmParser> X(getTheBPFTarget()); | |
| RegisterMCAsmParser<BPFAsmParser> Y(getTheBPFleTarget()); | |
| RegisterMCAsmParser<BPFAsmParser> Z(getTheBPFbeTarget()); | |
| } |