| //===- MSP430AsmParser.cpp - Parse MSP430 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 "MSP430.h" | |
| #include "MSP430RegisterInfo.h" | |
| #include "MCTargetDesc/MSP430MCTargetDesc.h" | |
| #include "TargetInfo/MSP430TargetInfo.h" | |
| #include "llvm/ADT/APInt.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCExpr.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCInstBuilder.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/MCStreamer.h" | |
| #include "llvm/MC/MCSubtargetInfo.h" | |
| #include "llvm/MC/MCSymbol.h" | |
| #include "llvm/MC/MCValue.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/MathExtras.h" | |
| #define DEBUG_TYPE "msp430-asm-parser" | |
| using namespace llvm; | |
| namespace { | |
| /// Parses MSP430 assembly from a stream. | |
| class MSP430AsmParser : public MCTargetAsmParser { | |
| const MCSubtargetInfo &STI; | |
| MCAsmParser &Parser; | |
| const MCRegisterInfo *MRI; | |
| 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; | |
| bool ParseDirectiveRefSym(AsmToken DirectiveID); | |
| unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, | |
| unsigned Kind) override; | |
| bool parseJccInstruction(ParseInstructionInfo &Info, StringRef Name, | |
| SMLoc NameLoc, OperandVector &Operands); | |
| bool ParseOperand(OperandVector &Operands); | |
| bool ParseLiteralValues(unsigned Size, SMLoc L); | |
| MCAsmParser &getParser() const { return Parser; } | |
| MCAsmLexer &getLexer() const { return Parser.getLexer(); } | |
| /// @name Auto-generated Matcher Functions | |
| /// { | |
| #define GET_ASSEMBLER_HEADER | |
| #include "MSP430GenAsmMatcher.inc" | |
| /// } | |
| public: | |
| MSP430AsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, | |
| const MCInstrInfo &MII, const MCTargetOptions &Options) | |
| : MCTargetAsmParser(Options, STI, MII), STI(STI), Parser(Parser) { | |
| MCAsmParserExtension::Initialize(Parser); | |
| MRI = getContext().getRegisterInfo(); | |
| setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); | |
| } | |
| }; | |
| /// A parsed MSP430 assembly operand. | |
| class MSP430Operand : public MCParsedAsmOperand { | |
| typedef MCParsedAsmOperand Base; | |
| enum KindTy { | |
| k_Imm, | |
| k_Reg, | |
| k_Tok, | |
| k_Mem, | |
| k_IndReg, | |
| k_PostIndReg | |
| } Kind; | |
| struct Memory { | |
| unsigned Reg; | |
| const MCExpr *Offset; | |
| }; | |
| union { | |
| const MCExpr *Imm; | |
| unsigned Reg; | |
| StringRef Tok; | |
| Memory Mem; | |
| }; | |
| SMLoc Start, End; | |
| public: | |
| MSP430Operand(StringRef Tok, SMLoc const &S) | |
| : Kind(k_Tok), Tok(Tok), Start(S), End(S) {} | |
| MSP430Operand(KindTy Kind, unsigned Reg, SMLoc const &S, SMLoc const &E) | |
| : Kind(Kind), Reg(Reg), Start(S), End(E) {} | |
| MSP430Operand(MCExpr const *Imm, SMLoc const &S, SMLoc const &E) | |
| : Kind(k_Imm), Imm(Imm), Start(S), End(E) {} | |
| MSP430Operand(unsigned Reg, MCExpr const *Expr, SMLoc const &S, | |
| SMLoc const &E) | |
| : Kind(k_Mem), Mem({Reg, Expr}), Start(S), End(E) {} | |
| void addRegOperands(MCInst &Inst, unsigned N) const { | |
| assert((Kind == k_Reg || Kind == k_IndReg || Kind == k_PostIndReg) && | |
| "Unexpected operand kind"); | |
| assert(N == 1 && "Invalid number of operands!"); | |
| Inst.addOperand(MCOperand::createReg(Reg)); | |
| } | |
| void addExprOperand(MCInst &Inst, const MCExpr *Expr) const { | |
| // Add as immediate when possible | |
| if (!Expr) | |
| Inst.addOperand(MCOperand::createImm(0)); | |
| else if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Expr)) | |
| Inst.addOperand(MCOperand::createImm(CE->getValue())); | |
| else | |
| Inst.addOperand(MCOperand::createExpr(Expr)); | |
| } | |
| void addImmOperands(MCInst &Inst, unsigned N) const { | |
| assert(Kind == k_Imm && "Unexpected operand kind"); | |
| assert(N == 1 && "Invalid number of operands!"); | |
| addExprOperand(Inst, Imm); | |
| } | |
| void addMemOperands(MCInst &Inst, unsigned N) const { | |
| assert(Kind == k_Mem && "Unexpected operand kind"); | |
| assert(N == 2 && "Invalid number of operands"); | |
| Inst.addOperand(MCOperand::createReg(Mem.Reg)); | |
| addExprOperand(Inst, Mem.Offset); | |
| } | |
| bool isReg() const override { return Kind == k_Reg; } | |
| bool isImm() const override { return Kind == k_Imm; } | |
| bool isToken() const override { return Kind == k_Tok; } | |
| bool isMem() const override { return Kind == k_Mem; } | |
| bool isIndReg() const { return Kind == k_IndReg; } | |
| bool isPostIndReg() const { return Kind == k_PostIndReg; } | |
| bool isCGImm() const { | |
| if (Kind != k_Imm) | |
| return false; | |
| int64_t Val; | |
| if (!Imm->evaluateAsAbsolute(Val)) | |
| return false; | |
| if (Val == 0 || Val == 1 || Val == 2 || Val == 4 || Val == 8 || Val == -1) | |
| return true; | |
| return false; | |
| } | |
| StringRef getToken() const { | |
| assert(Kind == k_Tok && "Invalid access!"); | |
| return Tok; | |
| } | |
| unsigned getReg() const override { | |
| assert(Kind == k_Reg && "Invalid access!"); | |
| return Reg; | |
| } | |
| void setReg(unsigned RegNo) { | |
| assert(Kind == k_Reg && "Invalid access!"); | |
| Reg = RegNo; | |
| } | |
| static std::unique_ptr<MSP430Operand> CreateToken(StringRef Str, SMLoc S) { | |
| return std::make_unique<MSP430Operand>(Str, S); | |
| } | |
| static std::unique_ptr<MSP430Operand> CreateReg(unsigned RegNum, SMLoc S, | |
| SMLoc E) { | |
| return std::make_unique<MSP430Operand>(k_Reg, RegNum, S, E); | |
| } | |
| static std::unique_ptr<MSP430Operand> CreateImm(const MCExpr *Val, SMLoc S, | |
| SMLoc E) { | |
| return std::make_unique<MSP430Operand>(Val, S, E); | |
| } | |
| static std::unique_ptr<MSP430Operand> CreateMem(unsigned RegNum, | |
| const MCExpr *Val, | |
| SMLoc S, SMLoc E) { | |
| return std::make_unique<MSP430Operand>(RegNum, Val, S, E); | |
| } | |
| static std::unique_ptr<MSP430Operand> CreateIndReg(unsigned RegNum, SMLoc S, | |
| SMLoc E) { | |
| return std::make_unique<MSP430Operand>(k_IndReg, RegNum, S, E); | |
| } | |
| static std::unique_ptr<MSP430Operand> CreatePostIndReg(unsigned RegNum, SMLoc S, | |
| SMLoc E) { | |
| return std::make_unique<MSP430Operand>(k_PostIndReg, RegNum, S, E); | |
| } | |
| SMLoc getStartLoc() const override { return Start; } | |
| SMLoc getEndLoc() const override { return End; } | |
| void print(raw_ostream &O) const override { | |
| switch (Kind) { | |
| case k_Tok: | |
| O << "Token " << Tok; | |
| break; | |
| case k_Reg: | |
| O << "Register " << Reg; | |
| break; | |
| case k_Imm: | |
| O << "Immediate " << *Imm; | |
| break; | |
| case k_Mem: | |
| O << "Memory "; | |
| O << *Mem.Offset << "(" << Reg << ")"; | |
| break; | |
| case k_IndReg: | |
| O << "RegInd " << Reg; | |
| break; | |
| case k_PostIndReg: | |
| O << "PostInc " << Reg; | |
| break; | |
| } | |
| } | |
| }; | |
| } // end anonymous namespace | |
| bool MSP430AsmParser::MatchAndEmitInstruction(SMLoc Loc, unsigned &Opcode, | |
| OperandVector &Operands, | |
| MCStreamer &Out, | |
| uint64_t &ErrorInfo, | |
| bool MatchingInlineAsm) { | |
| MCInst Inst; | |
| unsigned MatchResult = | |
| MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); | |
| switch (MatchResult) { | |
| case Match_Success: | |
| Inst.setLoc(Loc); | |
| Out.emitInstruction(Inst, STI); | |
| return false; | |
| case Match_MnemonicFail: | |
| return Error(Loc, "invalid instruction mnemonic"); | |
| case Match_InvalidOperand: { | |
| SMLoc ErrorLoc = Loc; | |
| if (ErrorInfo != ~0U) { | |
| if (ErrorInfo >= Operands.size()) | |
| return Error(ErrorLoc, "too few operands for instruction"); | |
| ErrorLoc = ((MSP430Operand &)*Operands[ErrorInfo]).getStartLoc(); | |
| if (ErrorLoc == SMLoc()) | |
| ErrorLoc = Loc; | |
| } | |
| return Error(ErrorLoc, "invalid operand for instruction"); | |
| } | |
| default: | |
| return true; | |
| } | |
| } | |
| // Auto-generated by TableGen | |
| static unsigned MatchRegisterName(StringRef Name); | |
| static unsigned MatchRegisterAltName(StringRef Name); | |
| bool MSP430AsmParser::parseRegister(MCRegister &RegNo, SMLoc &StartLoc, | |
| SMLoc &EndLoc) { | |
| switch (tryParseRegister(RegNo, StartLoc, EndLoc)) { | |
| case MatchOperand_ParseFail: | |
| return Error(StartLoc, "invalid register name"); | |
| case MatchOperand_Success: | |
| return false; | |
| case MatchOperand_NoMatch: | |
| return true; | |
| } | |
| llvm_unreachable("unknown match result type"); | |
| } | |
| OperandMatchResultTy MSP430AsmParser::tryParseRegister(MCRegister &RegNo, | |
| SMLoc &StartLoc, | |
| SMLoc &EndLoc) { | |
| if (getLexer().getKind() == AsmToken::Identifier) { | |
| auto Name = getLexer().getTok().getIdentifier().lower(); | |
| RegNo = MatchRegisterName(Name); | |
| if (RegNo == MSP430::NoRegister) { | |
| RegNo = MatchRegisterAltName(Name); | |
| if (RegNo == MSP430::NoRegister) | |
| return MatchOperand_NoMatch; | |
| } | |
| AsmToken const &T = getParser().getTok(); | |
| StartLoc = T.getLoc(); | |
| EndLoc = T.getEndLoc(); | |
| getLexer().Lex(); // eat register token | |
| return MatchOperand_Success; | |
| } | |
| return MatchOperand_ParseFail; | |
| } | |
| bool MSP430AsmParser::parseJccInstruction(ParseInstructionInfo &Info, | |
| StringRef Name, SMLoc NameLoc, | |
| OperandVector &Operands) { | |
| if (!Name.startswith_insensitive("j")) | |
| return true; | |
| auto CC = Name.drop_front().lower(); | |
| unsigned CondCode; | |
| if (CC == "ne" || CC == "nz") | |
| CondCode = MSP430CC::COND_NE; | |
| else if (CC == "eq" || CC == "z") | |
| CondCode = MSP430CC::COND_E; | |
| else if (CC == "lo" || CC == "nc") | |
| CondCode = MSP430CC::COND_LO; | |
| else if (CC == "hs" || CC == "c") | |
| CondCode = MSP430CC::COND_HS; | |
| else if (CC == "n") | |
| CondCode = MSP430CC::COND_N; | |
| else if (CC == "ge") | |
| CondCode = MSP430CC::COND_GE; | |
| else if (CC == "l") | |
| CondCode = MSP430CC::COND_L; | |
| else if (CC == "mp") | |
| CondCode = MSP430CC::COND_NONE; | |
| else | |
| return Error(NameLoc, "unknown instruction"); | |
| if (CondCode == (unsigned)MSP430CC::COND_NONE) | |
| Operands.push_back(MSP430Operand::CreateToken("jmp", NameLoc)); | |
| else { | |
| Operands.push_back(MSP430Operand::CreateToken("j", NameLoc)); | |
| const MCExpr *CCode = MCConstantExpr::create(CondCode, getContext()); | |
| Operands.push_back(MSP430Operand::CreateImm(CCode, SMLoc(), SMLoc())); | |
| } | |
| // Skip optional '$' sign. | |
| if (getLexer().getKind() == AsmToken::Dollar) | |
| getLexer().Lex(); // Eat '$' | |
| const MCExpr *Val; | |
| SMLoc ExprLoc = getLexer().getLoc(); | |
| if (getParser().parseExpression(Val)) | |
| return Error(ExprLoc, "expected expression operand"); | |
| int64_t Res; | |
| if (Val->evaluateAsAbsolute(Res)) | |
| if (Res < -512 || Res > 511) | |
| return Error(ExprLoc, "invalid jump offset"); | |
| Operands.push_back(MSP430Operand::CreateImm(Val, ExprLoc, | |
| getLexer().getLoc())); | |
| if (getLexer().isNot(AsmToken::EndOfStatement)) { | |
| SMLoc Loc = getLexer().getLoc(); | |
| getParser().eatToEndOfStatement(); | |
| return Error(Loc, "unexpected token"); | |
| } | |
| getParser().Lex(); // Consume the EndOfStatement. | |
| return false; | |
| } | |
| bool MSP430AsmParser::ParseInstruction(ParseInstructionInfo &Info, | |
| StringRef Name, SMLoc NameLoc, | |
| OperandVector &Operands) { | |
| // Drop .w suffix | |
| if (Name.endswith_insensitive(".w")) | |
| Name = Name.drop_back(2); | |
| if (!parseJccInstruction(Info, Name, NameLoc, Operands)) | |
| return false; | |
| // First operand is instruction mnemonic | |
| Operands.push_back(MSP430Operand::CreateToken(Name, NameLoc)); | |
| // If there are no more operands, then finish | |
| if (getLexer().is(AsmToken::EndOfStatement)) | |
| return false; | |
| // Parse first operand | |
| if (ParseOperand(Operands)) | |
| return true; | |
| // Parse second operand if any | |
| if (getLexer().is(AsmToken::Comma)) { | |
| getLexer().Lex(); // Eat ',' | |
| if (ParseOperand(Operands)) | |
| 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 MSP430AsmParser::ParseDirectiveRefSym(AsmToken DirectiveID) { | |
| StringRef Name; | |
| if (getParser().parseIdentifier(Name)) | |
| return TokError("expected identifier in directive"); | |
| MCSymbol *Sym = getContext().getOrCreateSymbol(Name); | |
| getStreamer().emitSymbolAttribute(Sym, MCSA_Global); | |
| return false; | |
| } | |
| bool MSP430AsmParser::ParseDirective(AsmToken DirectiveID) { | |
| StringRef IDVal = DirectiveID.getIdentifier(); | |
| if (IDVal.lower() == ".long") { | |
| ParseLiteralValues(4, DirectiveID.getLoc()); | |
| } else if (IDVal.lower() == ".word" || IDVal.lower() == ".short") { | |
| ParseLiteralValues(2, DirectiveID.getLoc()); | |
| } else if (IDVal.lower() == ".byte") { | |
| ParseLiteralValues(1, DirectiveID.getLoc()); | |
| } else if (IDVal.lower() == ".refsym") { | |
| return ParseDirectiveRefSym(DirectiveID); | |
| } | |
| return true; | |
| } | |
| bool MSP430AsmParser::ParseOperand(OperandVector &Operands) { | |
| switch (getLexer().getKind()) { | |
| default: return true; | |
| case AsmToken::Identifier: { | |
| // try rN | |
| MCRegister RegNo; | |
| SMLoc StartLoc, EndLoc; | |
| if (!parseRegister(RegNo, StartLoc, EndLoc)) { | |
| Operands.push_back(MSP430Operand::CreateReg(RegNo, StartLoc, EndLoc)); | |
| return false; | |
| } | |
| [[fallthrough]]; | |
| } | |
| case AsmToken::Integer: | |
| case AsmToken::Plus: | |
| case AsmToken::Minus: { | |
| SMLoc StartLoc = getParser().getTok().getLoc(); | |
| const MCExpr *Val; | |
| // Try constexpr[(rN)] | |
| if (!getParser().parseExpression(Val)) { | |
| MCRegister RegNo = MSP430::PC; | |
| SMLoc EndLoc = getParser().getTok().getLoc(); | |
| // Try (rN) | |
| if (getLexer().getKind() == AsmToken::LParen) { | |
| getLexer().Lex(); // Eat '(' | |
| SMLoc RegStartLoc; | |
| if (parseRegister(RegNo, RegStartLoc, EndLoc)) | |
| return true; | |
| if (getLexer().getKind() != AsmToken::RParen) | |
| return true; | |
| EndLoc = getParser().getTok().getEndLoc(); | |
| getLexer().Lex(); // Eat ')' | |
| } | |
| Operands.push_back(MSP430Operand::CreateMem(RegNo, Val, StartLoc, | |
| EndLoc)); | |
| return false; | |
| } | |
| return true; | |
| } | |
| case AsmToken::Amp: { | |
| // Try &constexpr | |
| SMLoc StartLoc = getParser().getTok().getLoc(); | |
| getLexer().Lex(); // Eat '&' | |
| const MCExpr *Val; | |
| if (!getParser().parseExpression(Val)) { | |
| SMLoc EndLoc = getParser().getTok().getLoc(); | |
| Operands.push_back(MSP430Operand::CreateMem(MSP430::SR, Val, StartLoc, | |
| EndLoc)); | |
| return false; | |
| } | |
| return true; | |
| } | |
| case AsmToken::At: { | |
| // Try @rN[+] | |
| SMLoc StartLoc = getParser().getTok().getLoc(); | |
| getLexer().Lex(); // Eat '@' | |
| MCRegister RegNo; | |
| SMLoc RegStartLoc, EndLoc; | |
| if (parseRegister(RegNo, RegStartLoc, EndLoc)) | |
| return true; | |
| if (getLexer().getKind() == AsmToken::Plus) { | |
| Operands.push_back(MSP430Operand::CreatePostIndReg(RegNo, StartLoc, EndLoc)); | |
| getLexer().Lex(); // Eat '+' | |
| return false; | |
| } | |
| if (Operands.size() > 1) // Emulate @rd in destination position as 0(rd) | |
| Operands.push_back(MSP430Operand::CreateMem(RegNo, | |
| MCConstantExpr::create(0, getContext()), StartLoc, EndLoc)); | |
| else | |
| Operands.push_back(MSP430Operand::CreateIndReg(RegNo, StartLoc, EndLoc)); | |
| return false; | |
| } | |
| case AsmToken::Hash: | |
| // Try #constexpr | |
| SMLoc StartLoc = getParser().getTok().getLoc(); | |
| getLexer().Lex(); // Eat '#' | |
| const MCExpr *Val; | |
| if (!getParser().parseExpression(Val)) { | |
| SMLoc EndLoc = getParser().getTok().getLoc(); | |
| Operands.push_back(MSP430Operand::CreateImm(Val, StartLoc, EndLoc)); | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| bool MSP430AsmParser::ParseLiteralValues(unsigned Size, SMLoc L) { | |
| auto parseOne = [&]() -> bool { | |
| const MCExpr *Value; | |
| if (getParser().parseExpression(Value)) | |
| return true; | |
| getParser().getStreamer().emitValue(Value, Size, L); | |
| return false; | |
| }; | |
| return (parseMany(parseOne)); | |
| } | |
| extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeMSP430AsmParser() { | |
| RegisterMCAsmParser<MSP430AsmParser> X(getTheMSP430Target()); | |
| } | |
| #define GET_REGISTER_MATCHER | |
| #define GET_MATCHER_IMPLEMENTATION | |
| #include "MSP430GenAsmMatcher.inc" | |
| static unsigned convertGR16ToGR8(unsigned Reg) { | |
| switch (Reg) { | |
| default: | |
| llvm_unreachable("Unknown GR16 register"); | |
| case MSP430::PC: return MSP430::PCB; | |
| case MSP430::SP: return MSP430::SPB; | |
| case MSP430::SR: return MSP430::SRB; | |
| case MSP430::CG: return MSP430::CGB; | |
| case MSP430::R4: return MSP430::R4B; | |
| case MSP430::R5: return MSP430::R5B; | |
| case MSP430::R6: return MSP430::R6B; | |
| case MSP430::R7: return MSP430::R7B; | |
| case MSP430::R8: return MSP430::R8B; | |
| case MSP430::R9: return MSP430::R9B; | |
| case MSP430::R10: return MSP430::R10B; | |
| case MSP430::R11: return MSP430::R11B; | |
| case MSP430::R12: return MSP430::R12B; | |
| case MSP430::R13: return MSP430::R13B; | |
| case MSP430::R14: return MSP430::R14B; | |
| case MSP430::R15: return MSP430::R15B; | |
| } | |
| } | |
| unsigned MSP430AsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, | |
| unsigned Kind) { | |
| MSP430Operand &Op = static_cast<MSP430Operand &>(AsmOp); | |
| if (!Op.isReg()) | |
| return Match_InvalidOperand; | |
| unsigned Reg = Op.getReg(); | |
| bool isGR16 = | |
| MSP430MCRegisterClasses[MSP430::GR16RegClassID].contains(Reg); | |
| if (isGR16 && (Kind == MCK_GR8)) { | |
| Op.setReg(convertGR16ToGR8(Reg)); | |
| return Match_Success; | |
| } | |
| return Match_InvalidOperand; | |
| } |