Currently, an invalid asm insn, either in an asm file or
in an inline asm format, might be silently dropped. This patch
fixed two places where this may happen by
signaling the error so user knows what goes wrong.
The following is an example to demonstrate error messages:
-bash-4.2$ cat t.c
int test(void *ctx) {
#if defined(NO_ERROR)
asm volatile("r0 = *(u16 *)skb[%0]" : : "i"(2));
#elif defined(ERROR_1)
asm volatile("r20 = *(u16 *)skb[%0]" : : "i"(2));
#elif defined(ERROR_2)
asm volatile("r0 = *(u16 *)(r1 + ?)" : :);
#endif
return 0;
}
-bash-4.2$ cat run.sh
for macro in NO_ERROR ERROR_1 ERROR_2; do
echo "===== compile for macro" $macro
clang -D${macro} -O2 -target bpf -emit-llvm -S t.c
echo "==llc=="
llc -march=bpf -filetype=obj t.ll
done
-bash-4.2$ ./run.sh
===== compile for macro NO_ERROR
==llc==
===== compile for macro ERROR_1
==llc==
<inline asm>:1:2: error: invalid register/token name
r20 = *(u16 *)skb[2]
^
note: !srcloc = 135
===== compile for macro ERROR_2
==llc==
<inline asm>:1:21: error: unexpected token
r0 = *(u16 *)(r1 + ?)
^
note: !srcloc = 210
-bash-4.2$
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Yonghong Song <yhs@fb.com>
llvm-svn: 329849
501 lines
14 KiB
C++
501 lines
14 KiB
C++
//===-- BPFAsmParser.cpp - Parse BPF 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 "MCTargetDesc/BPFMCTargetDesc.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/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/Support/Casting.h"
|
|
#include "llvm/Support/TargetRegistry.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(unsigned &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) : MCParsedAsmOperand(), 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() && dyn_cast<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 = 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 = 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 = 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(unsigned &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 false;
|
|
}
|
|
|
|
return Error(StartLoc, "invalid register name");
|
|
}
|
|
|
|
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;
|
|
}
|
|
// Fall through.
|
|
|
|
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" void LLVMInitializeBPFAsmParser() {
|
|
RegisterMCAsmParser<BPFAsmParser> X(getTheBPFTarget());
|
|
RegisterMCAsmParser<BPFAsmParser> Y(getTheBPFleTarget());
|
|
RegisterMCAsmParser<BPFAsmParser> Z(getTheBPFbeTarget());
|
|
}
|