//===-- VEAsmParser.cpp - Parse VE 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/VEMCExpr.h" #include "MCTargetDesc/VEMCTargetDesc.h" #include "TargetInfo/VETargetInfo.h" #include "VE.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.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/MCAsmParser.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/MCSymbol.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace llvm; #define DEBUG_TYPE "ve-asmparser" namespace { class VEOperand; class VEAsmParser : public MCTargetAsmParser { MCAsmParser &Parser; /// @name Auto-generated Match Functions /// { #define GET_ASSEMBLER_HEADER #include "VEGenAsmMatcher.inc" /// } // public interface of the MCTargetAsmParser. 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; int parseRegisterName(unsigned (*matchFn)(StringRef)); OperandMatchResultTy tryParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) override; bool ParseDirective(AsmToken DirectiveID) override; // Custom parse functions for VE specific operands. OperandMatchResultTy parseMEMOperand(OperandVector &Operands); OperandMatchResultTy parseOperand(OperandVector &Operands, StringRef Name); OperandMatchResultTy parseVEAsmOperand(std::unique_ptr &Operand); public: VEAsmParser(const MCSubtargetInfo &sti, MCAsmParser &parser, const MCInstrInfo &MII, const MCTargetOptions &Options) : MCTargetAsmParser(Options, sti, MII), Parser(parser) { // Initialize the set of available features. setAvailableFeatures(ComputeAvailableFeatures(getSTI().getFeatureBits())); } }; } // end anonymous namespace namespace { /// VEOperand - Instances of this class represent a parsed VE machine /// instruction. class VEOperand : public MCParsedAsmOperand { private: enum KindTy { k_Token, k_Register, k_Immediate, // SX-Aurora ASX form is disp(index, base). k_MemoryRegRegImm, // base=reg, index=reg, disp=imm k_MemoryRegImmImm, // base=reg, index=imm, disp=imm k_MemoryZeroRegImm, // base=0, index=reg, disp=imm k_MemoryZeroImmImm, // base=0, index=imm, disp=imm } Kind; SMLoc StartLoc, EndLoc; struct Token { const char *Data; unsigned Length; }; struct RegOp { unsigned RegNum; }; struct ImmOp { const MCExpr *Val; }; struct MemOp { unsigned Base; unsigned IndexReg; const MCExpr *Index; const MCExpr *Offset; }; union { struct Token Tok; struct RegOp Reg; struct ImmOp Imm; struct MemOp Mem; }; public: VEOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {} bool isToken() const override { return Kind == k_Token; } bool isReg() const override { return Kind == k_Register; } bool isImm() const override { return Kind == k_Immediate; } bool isMem() const override { return isMEMrri() || isMEMrii() || isMEMzri() || isMEMzii() || isMEMri() || isMEMzi(); } bool isMEMrri() const { return Kind == k_MemoryRegRegImm; } bool isMEMrii() const { return Kind == k_MemoryRegImmImm; } bool isMEMzri() const { return Kind == k_MemoryZeroRegImm; } bool isMEMzii() const { return Kind == k_MemoryZeroImmImm; } // isMEMri and isMEMzi will be implemented later. bool isMEMri() const { return false; } bool isMEMzi() const { return false; } bool isSImm7() { if (!isImm()) return false; // Constant case if (const MCConstantExpr *ConstExpr = dyn_cast(Imm.Val)) { int64_t Value = ConstExpr->getValue(); return isInt<7>(Value); } return false; } StringRef getToken() const { assert(Kind == k_Token && "Invalid access!"); return StringRef(Tok.Data, Tok.Length); } unsigned getReg() const override { assert((Kind == k_Register) && "Invalid access!"); return Reg.RegNum; } const MCExpr *getImm() const { assert((Kind == k_Immediate) && "Invalid access!"); return Imm.Val; } unsigned getMemBase() const { assert((Kind == k_MemoryRegRegImm || Kind == k_MemoryRegImmImm) && "Invalid access!"); return Mem.Base; } unsigned getMemIndexReg() const { assert((Kind == k_MemoryRegRegImm || Kind == k_MemoryZeroRegImm) && "Invalid access!"); return Mem.IndexReg; } const MCExpr *getMemIndex() const { assert((Kind == k_MemoryRegImmImm || Kind == k_MemoryZeroImmImm) && "Invalid access!"); return Mem.Index; } const MCExpr *getMemOffset() const { assert((Kind == k_MemoryRegRegImm || Kind == k_MemoryRegImmImm || Kind == k_MemoryZeroImmImm || Kind == k_MemoryZeroRegImm) && "Invalid access!"); return Mem.Offset; } void setMemOffset(const MCExpr *off) { assert((Kind == k_MemoryRegRegImm || Kind == k_MemoryRegImmImm || Kind == k_MemoryZeroImmImm || Kind == k_MemoryZeroRegImm) && "Invalid access!"); Mem.Offset = off; } /// getStartLoc - Get the location of the first token of this operand. SMLoc getStartLoc() const override { return StartLoc; } /// getEndLoc - Get the location of the last token of this operand. SMLoc getEndLoc() const override { return EndLoc; } void print(raw_ostream &OS) const override { switch (Kind) { case k_Token: OS << "Token: " << getToken() << "\n"; break; case k_Register: OS << "Reg: #" << getReg() << "\n"; break; case k_Immediate: OS << "Imm: " << getImm() << "\n"; break; case k_MemoryRegRegImm: assert(getMemOffset() != nullptr); OS << "Mem: #" << getMemBase() << "+#" << getMemIndexReg() << "+" << *getMemOffset() << "\n"; break; case k_MemoryRegImmImm: assert(getMemIndex() != nullptr && getMemOffset() != nullptr); OS << "Mem: #" << getMemBase() << "+" << *getMemIndex() << "+" << *getMemOffset() << "\n"; break; case k_MemoryZeroRegImm: assert(getMemOffset() != nullptr); OS << "Mem: 0+#" << getMemIndexReg() << "+" << *getMemOffset() << "\n"; break; case k_MemoryZeroImmImm: assert(getMemIndex() != nullptr && getMemOffset() != nullptr); OS << "Mem: 0+" << *getMemIndex() << "+" << *getMemOffset() << "\n"; break; } } 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!"); const MCExpr *Expr = getImm(); addExpr(Inst, Expr); } void addSImm7Operands(MCInst &Inst, unsigned N) const { addImmOperands(Inst, N); } void addExpr(MCInst &Inst, const MCExpr *Expr) const { // Add as immediate when possible. Null MCExpr = 0. if (!Expr) Inst.addOperand(MCOperand::createImm(0)); else if (const MCConstantExpr *CE = dyn_cast(Expr)) Inst.addOperand(MCOperand::createImm(CE->getValue())); else Inst.addOperand(MCOperand::createExpr(Expr)); } void addMEMrriOperands(MCInst &Inst, unsigned N) const { assert(N == 3 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createReg(getMemBase())); Inst.addOperand(MCOperand::createReg(getMemIndexReg())); addExpr(Inst, getMemOffset()); } void addMEMriiOperands(MCInst &Inst, unsigned N) const { assert(N == 3 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createReg(getMemBase())); addExpr(Inst, getMemIndex()); addExpr(Inst, getMemOffset()); } void addMEMzriOperands(MCInst &Inst, unsigned N) const { assert(N == 3 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createImm(0)); Inst.addOperand(MCOperand::createReg(getMemIndexReg())); addExpr(Inst, getMemOffset()); } void addMEMziiOperands(MCInst &Inst, unsigned N) const { assert(N == 3 && "Invalid number of operands!"); Inst.addOperand(MCOperand::createImm(0)); addExpr(Inst, getMemIndex()); addExpr(Inst, getMemOffset()); } void addMEMriOperands(MCInst &Inst, unsigned N) const { // FIXME: implement } void addMEMziOperands(MCInst &Inst, unsigned N) const { // FIXME: implement } static std::unique_ptr CreateToken(StringRef Str, SMLoc S) { auto Op = std::make_unique(k_Token); Op->Tok.Data = Str.data(); Op->Tok.Length = Str.size(); Op->StartLoc = S; Op->EndLoc = S; return Op; } static std::unique_ptr CreateReg(unsigned RegNum, SMLoc S, SMLoc E) { auto Op = std::make_unique(k_Register); Op->Reg.RegNum = RegNum; 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(k_Immediate); Op->Imm.Val = Val; Op->StartLoc = S; Op->EndLoc = E; return Op; } static std::unique_ptr MorphToMEMrri(unsigned Base, unsigned Index, std::unique_ptr Op) { const MCExpr *Imm = Op->getImm(); Op->Kind = k_MemoryRegRegImm; Op->Mem.Base = Base; Op->Mem.IndexReg = Index; Op->Mem.Index = nullptr; Op->Mem.Offset = Imm; return Op; } static std::unique_ptr MorphToMEMrii(unsigned Base, const MCExpr *Index, std::unique_ptr Op) { const MCExpr *Imm = Op->getImm(); Op->Kind = k_MemoryRegImmImm; Op->Mem.Base = Base; Op->Mem.IndexReg = 0; Op->Mem.Index = Index; Op->Mem.Offset = Imm; return Op; } static std::unique_ptr MorphToMEMzri(unsigned Index, std::unique_ptr Op) { const MCExpr *Imm = Op->getImm(); Op->Kind = k_MemoryZeroRegImm; Op->Mem.Base = 0; Op->Mem.IndexReg = Index; Op->Mem.Index = nullptr; Op->Mem.Offset = Imm; return Op; } static std::unique_ptr MorphToMEMzii(const MCExpr *Index, std::unique_ptr Op) { const MCExpr *Imm = Op->getImm(); Op->Kind = k_MemoryZeroImmImm; Op->Mem.Base = 0; Op->Mem.IndexReg = 0; Op->Mem.Index = Index; Op->Mem.Offset = Imm; return Op; } }; } // end anonymous namespace bool VEAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, 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(IDLoc); Out.emitInstruction(Inst, getSTI()); return false; case Match_MissingFeature: return Error(IDLoc, "instruction requires a CPU feature not currently enabled"); case Match_InvalidOperand: { SMLoc ErrorLoc = IDLoc; if (ErrorInfo != ~0ULL) { if (ErrorInfo >= Operands.size()) return Error(IDLoc, "too few operands for instruction"); ErrorLoc = ((VEOperand &)*Operands[ErrorInfo]).getStartLoc(); if (ErrorLoc == SMLoc()) ErrorLoc = IDLoc; } return Error(ErrorLoc, "invalid operand for instruction"); } case Match_MnemonicFail: return Error(IDLoc, "invalid instruction mnemonic"); } llvm_unreachable("Implement any new match types added!"); } bool VEAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) { if (tryParseRegister(RegNo, StartLoc, EndLoc) != MatchOperand_Success) return Error(StartLoc, "invalid register name"); return false; } /// Parses a register name using a given matching function. /// Checks for lowercase or uppercase if necessary. int VEAsmParser::parseRegisterName(unsigned (*matchFn)(StringRef)) { StringRef Name = Parser.getTok().getString(); int RegNum = matchFn(Name); // GCC supports case insensitive register names. All of the VE registers // are all lower case. if (RegNum == VE::NoRegister) { RegNum = matchFn(Name.lower()); } return RegNum; } /// Maps from the set of all register names to a register number. /// \note Generated by TableGen. static unsigned MatchRegisterName(StringRef Name); /// Maps from the set of all alternative registernames to a register number. /// \note Generated by TableGen. static unsigned MatchRegisterAltName(StringRef Name); OperandMatchResultTy VEAsmParser::tryParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) { const AsmToken Tok = Parser.getTok(); StartLoc = Tok.getLoc(); EndLoc = Tok.getEndLoc(); RegNo = 0; if (getLexer().getKind() != AsmToken::Percent) return MatchOperand_NoMatch; Parser.Lex(); RegNo = parseRegisterName(&MatchRegisterName); if (RegNo == VE::NoRegister) RegNo = parseRegisterName(&MatchRegisterAltName); if (RegNo != VE::NoRegister) { Parser.Lex(); return MatchOperand_Success; } getLexer().UnLex(Tok); return MatchOperand_NoMatch; } bool VEAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) { // First operand in MCInst is instruction mnemonic. Operands.push_back(VEOperand::CreateToken(Name, NameLoc)); if (getLexer().isNot(AsmToken::EndOfStatement)) { // Read the first operand. if (parseOperand(Operands, Name) != MatchOperand_Success) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token"); } while (getLexer().is(AsmToken::Comma)) { Parser.Lex(); // Eat the comma. // Parse and remember the operand. if (parseOperand(Operands, Name) != MatchOperand_Success) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token"); } } } if (getLexer().isNot(AsmToken::EndOfStatement)) { SMLoc Loc = getLexer().getLoc(); return Error(Loc, "unexpected token"); } Parser.Lex(); // Consume the EndOfStatement. return false; } bool VEAsmParser::ParseDirective(AsmToken DirectiveID) { // Let the MC layer to handle other directives. return true; } OperandMatchResultTy VEAsmParser::parseMEMOperand(OperandVector &Operands) { LLVM_DEBUG(dbgs() << "parseMEMOperand\n"); const AsmToken &Tok = Parser.getTok(); SMLoc S = Tok.getLoc(); SMLoc E = Tok.getEndLoc(); // Parse ASX format // disp // disp(, base) // disp(index) // disp(index, base) // (, base) // (index) // (index, base) std::unique_ptr Offset; switch (getLexer().getKind()) { default: return MatchOperand_NoMatch; case AsmToken::Minus: case AsmToken::Integer: case AsmToken::Dot: { const MCExpr *EVal; if (!getParser().parseExpression(EVal, E)) Offset = VEOperand::CreateImm(EVal, S, E); else return MatchOperand_NoMatch; break; } case AsmToken::LParen: // empty disp (= 0) Offset = VEOperand::CreateImm(MCConstantExpr::create(0, getContext()), S, E); break; } switch (getLexer().getKind()) { default: return MatchOperand_ParseFail; case AsmToken::EndOfStatement: Operands.push_back(VEOperand::MorphToMEMzii( MCConstantExpr::create(0, getContext()), std::move(Offset))); return MatchOperand_Success; case AsmToken::LParen: Parser.Lex(); // Eat the ( break; } const MCExpr *IndexValue = nullptr; unsigned IndexReg = 0; switch (getLexer().getKind()) { default: if (ParseRegister(IndexReg, S, E)) return MatchOperand_ParseFail; break; case AsmToken::Minus: case AsmToken::Integer: case AsmToken::Dot: if (getParser().parseExpression(IndexValue, E)) return MatchOperand_ParseFail; break; case AsmToken::Comma: // empty index IndexValue = MCConstantExpr::create(0, getContext()); break; } switch (getLexer().getKind()) { default: return MatchOperand_ParseFail; case AsmToken::RParen: Parser.Lex(); // Eat the ) Operands.push_back( IndexValue ? VEOperand::MorphToMEMzii(IndexValue, std::move(Offset)) : VEOperand::MorphToMEMzri(IndexReg, std::move(Offset))); return MatchOperand_Success; case AsmToken::Comma: Parser.Lex(); // Eat the , break; } unsigned BaseReg = 0; if (ParseRegister(BaseReg, S, E)) return MatchOperand_ParseFail; if (!Parser.getTok().is(AsmToken::RParen)) return MatchOperand_ParseFail; Parser.Lex(); // Eat the ) Operands.push_back( IndexValue ? VEOperand::MorphToMEMrii(BaseReg, IndexValue, std::move(Offset)) : VEOperand::MorphToMEMrri(BaseReg, IndexReg, std::move(Offset))); return MatchOperand_Success; } OperandMatchResultTy VEAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) { LLVM_DEBUG(dbgs() << "parseOperand\n"); OperandMatchResultTy ResTy = MatchOperandParserImpl(Operands, Mnemonic); // 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_Success || ResTy == MatchOperand_ParseFail) return ResTy; switch (getLexer().getKind()) { case AsmToken::LParen: // FIXME: Parsing "(" + %vreg + ", " + %vreg + ")" // FALLTHROUGH default: { std::unique_ptr Op; ResTy = parseVEAsmOperand(Op); if (ResTy != MatchOperand_Success || !Op) return MatchOperand_ParseFail; // Push the parsed operand into the list of operands Operands.push_back(std::move(Op)); if (!Parser.getTok().is(AsmToken::LParen)) break; // FIXME: Parsing %vec-reg + "(" + %sclar-reg/number + ")" break; } } return MatchOperand_Success; } OperandMatchResultTy VEAsmParser::parseVEAsmOperand(std::unique_ptr &Op) { LLVM_DEBUG(dbgs() << "parseVEAsmOperand\n"); SMLoc S = Parser.getTok().getLoc(); SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); const MCExpr *EVal; Op = nullptr; switch (getLexer().getKind()) { default: break; case AsmToken::Percent: unsigned RegNo; if (tryParseRegister(RegNo, S, E) == MatchOperand_Success) Op = VEOperand::CreateReg(RegNo, S, E); break; case AsmToken::Minus: case AsmToken::Integer: case AsmToken::Dot: if (!getParser().parseExpression(EVal, E)) Op = VEOperand::CreateImm(EVal, S, E); break; case AsmToken::Identifier: { StringRef Identifier; if (!getParser().parseIdentifier(Identifier)) { E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier); const MCExpr *Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); Op = VEOperand::CreateImm(Res, S, E); } break; } } return (Op) ? MatchOperand_Success : MatchOperand_ParseFail; } // Force static initialization. extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeVEAsmParser() { RegisterMCAsmParser A(getTheVETarget()); } #define GET_REGISTER_MATCHER #define GET_MATCHER_IMPLEMENTATION #include "VEGenAsmMatcher.inc"