//===-- AMDGPUAsmParser.cpp - Parse SI asm 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 "AMDKernelCodeT.h" #include "MCTargetDesc/AMDGPUMCTargetDesc.h" #include "MCTargetDesc/AMDGPUTargetStreamer.h" #include "SIDefines.h" #include "Utils/AMDGPUBaseInfo.h" #include "Utils/AMDKernelCodeTUtils.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.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/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/MCSymbolELF.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ELF.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" // FIXME ODR: Move this to some common place for AsmParser and InstPrinter namespace llvm { namespace AMDGPU { namespace SendMsg { // This must be in sync with llvm::AMDGPU::SendMsg::Id enum members. static const char* const IdSymbolic[] = { nullptr, "MSG_INTERRUPT", "MSG_GS", "MSG_GS_DONE", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "MSG_SYSMSG" }; // These two must be in sync with llvm::AMDGPU::SendMsg::Op enum members. static const char* const OpSysSymbolic[] = { nullptr, "SYSMSG_OP_ECC_ERR_INTERRUPT", "SYSMSG_OP_REG_RD", "SYSMSG_OP_HOST_TRAP_ACK", "SYSMSG_OP_TTRACE_PC" }; static const char* const OpGsSymbolic[] = { "GS_OP_NOP", "GS_OP_CUT", "GS_OP_EMIT", "GS_OP_EMIT_CUT" }; } // namespace SendMsg } // namespace AMDGPU } // namespace llvm using namespace llvm; namespace { struct OptionalOperand; enum RegisterKind { IS_UNKNOWN, IS_VGPR, IS_SGPR, IS_TTMP, IS_SPECIAL }; class AMDGPUOperand : public MCParsedAsmOperand { enum KindTy { Token, Immediate, Register, Expression } Kind; SMLoc StartLoc, EndLoc; public: AMDGPUOperand(enum KindTy K) : MCParsedAsmOperand(), Kind(K) {} MCContext *Ctx; typedef std::unique_ptr Ptr; enum ImmTy { ImmTyNone, ImmTyGDS, ImmTyOffen, ImmTyIdxen, ImmTyAddr64, ImmTyOffset, ImmTyOffset0, ImmTyOffset1, ImmTyGLC, ImmTySLC, ImmTyTFE, ImmTyClampSI, ImmTyOModSI, ImmTyDppCtrl, ImmTyDppRowMask, ImmTyDppBankMask, ImmTyDppBoundCtrl, ImmTySdwaSel, ImmTySdwaDstUnused, ImmTyDMask, ImmTyUNorm, ImmTyDA, ImmTyR128, ImmTyLWE, ImmTyHwreg, ImmTySendMsg, }; struct TokOp { const char *Data; unsigned Length; }; struct ImmOp { bool IsFPImm; ImmTy Type; int64_t Val; int Modifiers; }; struct RegOp { unsigned RegNo; int Modifiers; const MCRegisterInfo *TRI; const MCSubtargetInfo *STI; bool IsForcedVOP3; }; union { TokOp Tok; ImmOp Imm; RegOp Reg; const MCExpr *Expr; }; void addImmOperands(MCInst &Inst, unsigned N, bool ApplyModifiers = true) const { if (Imm.Type == ImmTyNone && ApplyModifiers && Imm.Modifiers != 0) { // Apply modifiers to immediate value int64_t Val = Imm.Val; bool Negate = Imm.Modifiers & 0x1; bool Abs = Imm.Modifiers & 0x2; if (Imm.IsFPImm) { APFloat F(BitsToFloat(Val)); if (Abs) { F.clearSign(); } if (Negate) { F.changeSign(); } Val = F.bitcastToAPInt().getZExtValue(); } else { Val = Abs ? std::abs(Val) : Val; Val = Negate ? -Val : Val; } Inst.addOperand(MCOperand::createImm(Val)); } else { Inst.addOperand(MCOperand::createImm(getImm())); } } StringRef getToken() const { return StringRef(Tok.Data, Tok.Length); } void addRegOperands(MCInst &Inst, unsigned N) const { Inst.addOperand(MCOperand::createReg(AMDGPU::getMCReg(getReg(), *Reg.STI))); } void addRegOrImmOperands(MCInst &Inst, unsigned N) const { if (isRegKind()) addRegOperands(Inst, N); else addImmOperands(Inst, N); } void addRegOrImmWithInputModsOperands(MCInst &Inst, unsigned N) const { if (isRegKind()) { Inst.addOperand(MCOperand::createImm(Reg.Modifiers)); addRegOperands(Inst, N); } else { Inst.addOperand(MCOperand::createImm(Imm.Modifiers)); addImmOperands(Inst, N, false); } } void addSoppBrTargetOperands(MCInst &Inst, unsigned N) const { if (isImm()) addImmOperands(Inst, N); else { assert(isExpr()); Inst.addOperand(MCOperand::createExpr(Expr)); } } bool isToken() const override { return Kind == Token; } bool isImm() const override { return Kind == Immediate; } bool isInlinableImm() const { if (!isImm() || Imm.Type != AMDGPUOperand::ImmTyNone /* Only plain immediates are inlinable (e.g. "clamp" attribute is not) */ ) return false; // TODO: We should avoid using host float here. It would be better to // check the float bit values which is what a few other places do. // We've had bot failures before due to weird NaN support on mips hosts. const float F = BitsToFloat(Imm.Val); // TODO: Add 1/(2*pi) for VI return (Imm.Val <= 64 && Imm.Val >= -16) || (F == 0.0 || F == 0.5 || F == -0.5 || F == 1.0 || F == -1.0 || F == 2.0 || F == -2.0 || F == 4.0 || F == -4.0); } int64_t getImm() const { return Imm.Val; } enum ImmTy getImmTy() const { assert(isImm()); return Imm.Type; } bool isRegKind() const { return Kind == Register; } bool isReg() const override { return Kind == Register && Reg.Modifiers == 0; } bool isRegOrImmWithInputMods() const { return Kind == Register || isInlinableImm(); } bool isImmTy(ImmTy ImmT) const { return isImm() && Imm.Type == ImmT; } bool isClampSI() const { return isImmTy(ImmTyClampSI); } bool isOModSI() const { return isImmTy(ImmTyOModSI); } bool isImmModifier() const { return Kind == Immediate && Imm.Type != ImmTyNone; } bool isDMask() const { return isImmTy(ImmTyDMask); } bool isUNorm() const { return isImmTy(ImmTyUNorm); } bool isDA() const { return isImmTy(ImmTyDA); } bool isR128() const { return isImmTy(ImmTyUNorm); } bool isLWE() const { return isImmTy(ImmTyLWE); } bool isMod() const { return isClampSI() || isOModSI(); } bool isOffen() const { return isImmTy(ImmTyOffen); } bool isIdxen() const { return isImmTy(ImmTyIdxen); } bool isAddr64() const { return isImmTy(ImmTyAddr64); } bool isOffset() const { return isImmTy(ImmTyOffset) && isUInt<16>(getImm()); } bool isOffset0() const { return isImmTy(ImmTyOffset0) && isUInt<16>(getImm()); } bool isOffset1() const { return isImmTy(ImmTyOffset1) && isUInt<8>(getImm()); } bool isGDS() const { return isImmTy(ImmTyGDS); } bool isGLC() const { return isImmTy(ImmTyGLC); } bool isSLC() const { return isImmTy(ImmTySLC); } bool isTFE() const { return isImmTy(ImmTyTFE); } bool isBankMask() const { return isImmTy(ImmTyDppBankMask); } bool isRowMask() const { return isImmTy(ImmTyDppRowMask); } bool isBoundCtrl() const { return isImmTy(ImmTyDppBoundCtrl); } bool isSDWASel() const { return isImmTy(ImmTySdwaSel); } bool isSDWADstUnused() const { return isImmTy(ImmTySdwaDstUnused); } void setModifiers(unsigned Mods) { assert(isReg() || (isImm() && Imm.Modifiers == 0)); if (isReg()) Reg.Modifiers = Mods; else Imm.Modifiers = Mods; } bool hasModifiers() const { assert(isRegKind() || isImm()); return isRegKind() ? Reg.Modifiers != 0 : Imm.Modifiers != 0; } unsigned getReg() const override { return Reg.RegNo; } bool isRegOrImm() const { return isReg() || isImm(); } bool isRegClass(unsigned RCID) const { return isReg() && Reg.TRI->getRegClass(RCID).contains(getReg()); } bool isSCSrc32() const { return isInlinableImm() || isRegClass(AMDGPU::SReg_32RegClassID); } bool isSCSrc64() const { return isInlinableImm() || isRegClass(AMDGPU::SReg_64RegClassID); } bool isSSrc32() const { return isImm() || isSCSrc32(); } bool isSSrc64() const { // TODO: Find out how SALU supports extension of 32-bit literals to 64 bits. // See isVSrc64(). return isImm() || isSCSrc64(); } bool isVCSrc32() const { return isInlinableImm() || isRegClass(AMDGPU::VS_32RegClassID); } bool isVCSrc64() const { return isInlinableImm() || isRegClass(AMDGPU::VS_64RegClassID); } bool isVSrc32() const { return isImm() || isVCSrc32(); } bool isVSrc64() const { // TODO: Check if the 64-bit value (coming from assembly source) can be // narrowed to 32 bits (in the instruction stream). That require knowledge // of instruction type (unsigned/signed, floating or "untyped"/B64), // see [AMD GCN3 ISA 6.3.1]. // TODO: How 64-bit values are formed from 32-bit literals in _B64 insns? return isImm() || isVCSrc64(); } bool isMem() const override { return false; } bool isExpr() const { return Kind == Expression; } bool isSoppBrTarget() const { return isExpr() || isImm(); } SMLoc getStartLoc() const override { return StartLoc; } SMLoc getEndLoc() const override { return EndLoc; } void printImmTy(raw_ostream& OS, ImmTy Type) const { switch (Type) { case ImmTyNone: OS << "None"; break; case ImmTyGDS: OS << "GDS"; break; case ImmTyOffen: OS << "Offen"; break; case ImmTyIdxen: OS << "Idxen"; break; case ImmTyAddr64: OS << "Addr64"; break; case ImmTyOffset: OS << "Offset"; break; case ImmTyOffset0: OS << "Offset0"; break; case ImmTyOffset1: OS << "Offset1"; break; case ImmTyGLC: OS << "GLC"; break; case ImmTySLC: OS << "SLC"; break; case ImmTyTFE: OS << "TFE"; break; case ImmTyClampSI: OS << "ClampSI"; break; case ImmTyOModSI: OS << "OModSI"; break; case ImmTyDppCtrl: OS << "DppCtrl"; break; case ImmTyDppRowMask: OS << "DppRowMask"; break; case ImmTyDppBankMask: OS << "DppBankMask"; break; case ImmTyDppBoundCtrl: OS << "DppBoundCtrl"; break; case ImmTySdwaSel: OS << "SdwaSel"; break; case ImmTySdwaDstUnused: OS << "SdwaDstUnused"; break; case ImmTyDMask: OS << "DMask"; break; case ImmTyUNorm: OS << "UNorm"; break; case ImmTyDA: OS << "DA"; break; case ImmTyR128: OS << "R128"; break; case ImmTyLWE: OS << "LWE"; break; case ImmTyHwreg: OS << "Hwreg"; break; case ImmTySendMsg: OS << "SendMsg"; break; } } void print(raw_ostream &OS) const override { switch (Kind) { case Register: OS << "'; break; case Immediate: OS << '<' << getImm(); if (getImmTy() != ImmTyNone) { OS << " type: "; printImmTy(OS, getImmTy()); } OS << " mods: " << Imm.Modifiers << '>'; break; case Token: OS << '\'' << getToken() << '\''; break; case Expression: OS << "'; break; } } static AMDGPUOperand::Ptr CreateImm(int64_t Val, SMLoc Loc, enum ImmTy Type = ImmTyNone, bool IsFPImm = false) { auto Op = llvm::make_unique(Immediate); Op->Imm.Val = Val; Op->Imm.IsFPImm = IsFPImm; Op->Imm.Type = Type; Op->Imm.Modifiers = 0; Op->StartLoc = Loc; Op->EndLoc = Loc; return Op; } static AMDGPUOperand::Ptr CreateToken(StringRef Str, SMLoc Loc, bool HasExplicitEncodingSize = true) { auto Res = llvm::make_unique(Token); Res->Tok.Data = Str.data(); Res->Tok.Length = Str.size(); Res->StartLoc = Loc; Res->EndLoc = Loc; return Res; } static AMDGPUOperand::Ptr CreateReg(unsigned RegNo, SMLoc S, SMLoc E, const MCRegisterInfo *TRI, const MCSubtargetInfo *STI, bool ForceVOP3) { auto Op = llvm::make_unique(Register); Op->Reg.RegNo = RegNo; Op->Reg.TRI = TRI; Op->Reg.STI = STI; Op->Reg.Modifiers = 0; Op->Reg.IsForcedVOP3 = ForceVOP3; Op->StartLoc = S; Op->EndLoc = E; return Op; } static AMDGPUOperand::Ptr CreateExpr(const class MCExpr *Expr, SMLoc S) { auto Op = llvm::make_unique(Expression); Op->Expr = Expr; Op->StartLoc = S; Op->EndLoc = S; return Op; } bool isSWaitCnt() const; bool isHwreg() const; bool isSendMsg() const; bool isMubufOffset() const; bool isSMRDOffset() const; bool isSMRDLiteralOffset() const; bool isDPPCtrl() const; }; class AMDGPUAsmParser : public MCTargetAsmParser { const MCInstrInfo &MII; MCAsmParser &Parser; unsigned ForcedEncodingSize; bool isSI() const { return AMDGPU::isSI(getSTI()); } bool isCI() const { return AMDGPU::isCI(getSTI()); } bool isVI() const { return AMDGPU::isVI(getSTI()); } bool hasSGPR102_SGPR103() const { return !isVI(); } /// @name Auto-generated Match Functions /// { #define GET_ASSEMBLER_HEADER #include "AMDGPUGenAsmMatcher.inc" /// } private: bool ParseDirectiveMajorMinor(uint32_t &Major, uint32_t &Minor); bool ParseDirectiveHSACodeObjectVersion(); bool ParseDirectiveHSACodeObjectISA(); bool ParseAMDKernelCodeTValue(StringRef ID, amd_kernel_code_t &Header); bool ParseDirectiveAMDKernelCodeT(); bool ParseSectionDirectiveHSAText(); bool subtargetHasRegister(const MCRegisterInfo &MRI, unsigned RegNo) const; bool ParseDirectiveAMDGPUHsaKernel(); bool ParseDirectiveAMDGPUHsaModuleGlobal(); bool ParseDirectiveAMDGPUHsaProgramGlobal(); bool ParseSectionDirectiveHSADataGlobalAgent(); bool ParseSectionDirectiveHSADataGlobalProgram(); bool ParseSectionDirectiveHSARodataReadonlyAgent(); bool AddNextRegisterToList(unsigned& Reg, unsigned& RegWidth, RegisterKind RegKind, unsigned Reg1, unsigned RegNum); bool ParseAMDGPURegister(RegisterKind& RegKind, unsigned& Reg, unsigned& RegNum, unsigned& RegWidth); void cvtMubufImpl(MCInst &Inst, const OperandVector &Operands, bool IsAtomic, bool IsAtomicReturn); public: enum AMDGPUMatchResultTy { Match_PreferE32 = FIRST_TARGET_MATCH_RESULT_TY }; AMDGPUAsmParser(const MCSubtargetInfo &STI, MCAsmParser &_Parser, const MCInstrInfo &MII, const MCTargetOptions &Options) : MCTargetAsmParser(Options, STI), MII(MII), Parser(_Parser), ForcedEncodingSize(0) { MCAsmParserExtension::Initialize(Parser); if (getSTI().getFeatureBits().none()) { // Set default features. copySTI().ToggleFeature("SOUTHERN_ISLANDS"); } setAvailableFeatures(ComputeAvailableFeatures(getSTI().getFeatureBits())); } AMDGPUTargetStreamer &getTargetStreamer() { MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); return static_cast(TS); } unsigned getForcedEncodingSize() const { return ForcedEncodingSize; } void setForcedEncodingSize(unsigned Size) { ForcedEncodingSize = Size; } bool isForcedVOP3() const { return ForcedEncodingSize == 64; } std::unique_ptr parseRegister(); bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; unsigned checkTargetMatchPredicate(MCInst &Inst) override; unsigned validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) override; bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, OperandVector &Operands, MCStreamer &Out, uint64_t &ErrorInfo, bool MatchingInlineAsm) override; bool ParseDirective(AsmToken DirectiveID) override; OperandMatchResultTy parseOperand(OperandVector &Operands, StringRef Mnemonic); bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) override; OperandMatchResultTy parseIntWithPrefix(const char *Prefix, int64_t &Int); OperandMatchResultTy parseIntWithPrefix(const char *Prefix, OperandVector &Operands, enum AMDGPUOperand::ImmTy ImmTy = AMDGPUOperand::ImmTyNone, bool (*ConvertResult)(int64_t&) = 0); OperandMatchResultTy parseNamedBit(const char *Name, OperandVector &Operands, enum AMDGPUOperand::ImmTy ImmTy = AMDGPUOperand::ImmTyNone); OperandMatchResultTy parseStringWithPrefix(const char *Prefix, StringRef &Value); OperandMatchResultTy parseImm(OperandVector &Operands); OperandMatchResultTy parseRegOrImm(OperandVector &Operands); OperandMatchResultTy parseRegOrImmWithInputMods(OperandVector &Operands); void cvtDSOffset01(MCInst &Inst, const OperandVector &Operands); void cvtDS(MCInst &Inst, const OperandVector &Operands); bool parseCnt(int64_t &IntVal); OperandMatchResultTy parseSWaitCntOps(OperandVector &Operands); bool parseHwregOperand(int64_t &HwRegCode, int64_t &Offset, int64_t &Width, bool &IsIdentifier); OperandMatchResultTy parseHwreg(OperandVector &Operands); private: struct OperandInfoTy { int64_t Id; bool IsSymbolic; OperandInfoTy(int64_t Id_) : Id(Id_), IsSymbolic(false) { } }; bool parseSendMsg(OperandInfoTy &Msg, OperandInfoTy &Operation, int64_t &StreamId); public: OperandMatchResultTy parseOptionalOperand(OperandVector &Operands); OperandMatchResultTy parseSendMsgOp(OperandVector &Operands); OperandMatchResultTy parseSOppBrTarget(OperandVector &Operands); AMDGPUOperand::Ptr defaultHwreg() const; void cvtMubuf(MCInst &Inst, const OperandVector &Operands) { cvtMubufImpl(Inst, Operands, false, false); } void cvtMubufAtomic(MCInst &Inst, const OperandVector &Operands) { cvtMubufImpl(Inst, Operands, true, false); } void cvtMubufAtomicReturn(MCInst &Inst, const OperandVector &Operands) { cvtMubufImpl(Inst, Operands, true, true); } AMDGPUOperand::Ptr defaultMubufOffset() const; AMDGPUOperand::Ptr defaultGLC() const; AMDGPUOperand::Ptr defaultSLC() const; AMDGPUOperand::Ptr defaultTFE() const; AMDGPUOperand::Ptr defaultDMask() const; AMDGPUOperand::Ptr defaultUNorm() const; AMDGPUOperand::Ptr defaultDA() const; AMDGPUOperand::Ptr defaultR128() const; AMDGPUOperand::Ptr defaultLWE() const; AMDGPUOperand::Ptr defaultSMRDOffset() const; AMDGPUOperand::Ptr defaultSMRDLiteralOffset() const; AMDGPUOperand::Ptr defaultClampSI() const; AMDGPUOperand::Ptr defaultOModSI() const; OperandMatchResultTy parseOModOperand(OperandVector &Operands); void cvtId(MCInst &Inst, const OperandVector &Operands); void cvtVOP3_2_mod(MCInst &Inst, const OperandVector &Operands); void cvtVOP3(MCInst &Inst, const OperandVector &Operands); void cvtMIMG(MCInst &Inst, const OperandVector &Operands); void cvtMIMGAtomic(MCInst &Inst, const OperandVector &Operands); OperandMatchResultTy parseDPPCtrl(OperandVector &Operands); AMDGPUOperand::Ptr defaultRowMask() const; AMDGPUOperand::Ptr defaultBankMask() const; AMDGPUOperand::Ptr defaultBoundCtrl() const; void cvtDPP(MCInst &Inst, const OperandVector &Operands); OperandMatchResultTy parseSDWASel(OperandVector &Operands); OperandMatchResultTy parseSDWADstUnused(OperandVector &Operands); AMDGPUOperand::Ptr defaultSDWASel() const; AMDGPUOperand::Ptr defaultSDWADstUnused() const; }; struct OptionalOperand { const char *Name; AMDGPUOperand::ImmTy Type; bool IsBit; bool (*ConvertResult)(int64_t&); }; } static int getRegClass(RegisterKind Is, unsigned RegWidth) { if (Is == IS_VGPR) { switch (RegWidth) { default: return -1; case 1: return AMDGPU::VGPR_32RegClassID; case 2: return AMDGPU::VReg_64RegClassID; case 3: return AMDGPU::VReg_96RegClassID; case 4: return AMDGPU::VReg_128RegClassID; case 8: return AMDGPU::VReg_256RegClassID; case 16: return AMDGPU::VReg_512RegClassID; } } else if (Is == IS_TTMP) { switch (RegWidth) { default: return -1; case 1: return AMDGPU::TTMP_32RegClassID; case 2: return AMDGPU::TTMP_64RegClassID; case 4: return AMDGPU::TTMP_128RegClassID; } } else if (Is == IS_SGPR) { switch (RegWidth) { default: return -1; case 1: return AMDGPU::SGPR_32RegClassID; case 2: return AMDGPU::SGPR_64RegClassID; case 4: return AMDGPU::SGPR_128RegClassID; case 8: return AMDGPU::SReg_256RegClassID; case 16: return AMDGPU::SReg_512RegClassID; } } return -1; } static unsigned getSpecialRegForName(StringRef RegName) { return StringSwitch(RegName) .Case("exec", AMDGPU::EXEC) .Case("vcc", AMDGPU::VCC) .Case("flat_scratch", AMDGPU::FLAT_SCR) .Case("m0", AMDGPU::M0) .Case("scc", AMDGPU::SCC) .Case("tba", AMDGPU::TBA) .Case("tma", AMDGPU::TMA) .Case("flat_scratch_lo", AMDGPU::FLAT_SCR_LO) .Case("flat_scratch_hi", AMDGPU::FLAT_SCR_HI) .Case("vcc_lo", AMDGPU::VCC_LO) .Case("vcc_hi", AMDGPU::VCC_HI) .Case("exec_lo", AMDGPU::EXEC_LO) .Case("exec_hi", AMDGPU::EXEC_HI) .Case("tma_lo", AMDGPU::TMA_LO) .Case("tma_hi", AMDGPU::TMA_HI) .Case("tba_lo", AMDGPU::TBA_LO) .Case("tba_hi", AMDGPU::TBA_HI) .Default(0); } bool AMDGPUAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) { auto R = parseRegister(); if (!R) return true; assert(R->isReg()); RegNo = R->getReg(); StartLoc = R->getStartLoc(); EndLoc = R->getEndLoc(); return false; } bool AMDGPUAsmParser::AddNextRegisterToList(unsigned& Reg, unsigned& RegWidth, RegisterKind RegKind, unsigned Reg1, unsigned RegNum) { switch (RegKind) { case IS_SPECIAL: if (Reg == AMDGPU::EXEC_LO && Reg1 == AMDGPU::EXEC_HI) { Reg = AMDGPU::EXEC; RegWidth = 2; return true; } if (Reg == AMDGPU::FLAT_SCR_LO && Reg1 == AMDGPU::FLAT_SCR_HI) { Reg = AMDGPU::FLAT_SCR; RegWidth = 2; return true; } if (Reg == AMDGPU::VCC_LO && Reg1 == AMDGPU::VCC_HI) { Reg = AMDGPU::VCC; RegWidth = 2; return true; } if (Reg == AMDGPU::TBA_LO && Reg1 == AMDGPU::TBA_HI) { Reg = AMDGPU::TBA; RegWidth = 2; return true; } if (Reg == AMDGPU::TMA_LO && Reg1 == AMDGPU::TMA_HI) { Reg = AMDGPU::TMA; RegWidth = 2; return true; } return false; case IS_VGPR: case IS_SGPR: case IS_TTMP: if (Reg1 != Reg + RegWidth) { return false; } RegWidth++; return true; default: assert(false); return false; } } bool AMDGPUAsmParser::ParseAMDGPURegister(RegisterKind& RegKind, unsigned& Reg, unsigned& RegNum, unsigned& RegWidth) { const MCRegisterInfo *TRI = getContext().getRegisterInfo(); if (getLexer().is(AsmToken::Identifier)) { StringRef RegName = Parser.getTok().getString(); if ((Reg = getSpecialRegForName(RegName))) { Parser.Lex(); RegKind = IS_SPECIAL; } else { unsigned RegNumIndex = 0; if (RegName[0] == 'v') { RegNumIndex = 1; RegKind = IS_VGPR; } else if (RegName[0] == 's') { RegNumIndex = 1; RegKind = IS_SGPR; } else if (RegName.startswith("ttmp")) { RegNumIndex = strlen("ttmp"); RegKind = IS_TTMP; } else { return false; } if (RegName.size() > RegNumIndex) { // Single 32-bit register: vXX. if (RegName.substr(RegNumIndex).getAsInteger(10, RegNum)) { return false; } Parser.Lex(); RegWidth = 1; } else { // Range of registers: v[XX:YY]. Parser.Lex(); int64_t RegLo, RegHi; if (getLexer().isNot(AsmToken::LBrac)) { return false; } Parser.Lex(); if (getParser().parseAbsoluteExpression(RegLo)) { return false; } if (getLexer().isNot(AsmToken::Colon)) { return false; } Parser.Lex(); if (getParser().parseAbsoluteExpression(RegHi)) { return false; } if (getLexer().isNot(AsmToken::RBrac)) { return false; } Parser.Lex(); RegNum = (unsigned) RegLo; RegWidth = (RegHi - RegLo) + 1; } } } else if (getLexer().is(AsmToken::LBrac)) { // List of consecutive registers: [s0,s1,s2,s3] Parser.Lex(); if (!ParseAMDGPURegister(RegKind, Reg, RegNum, RegWidth)) { return false; } if (RegWidth != 1) { return false; } RegisterKind RegKind1; unsigned Reg1, RegNum1, RegWidth1; do { if (getLexer().is(AsmToken::Comma)) { Parser.Lex(); } else if (getLexer().is(AsmToken::RBrac)) { Parser.Lex(); break; } else if (ParseAMDGPURegister(RegKind1, Reg1, RegNum1, RegWidth1)) { if (RegWidth1 != 1) { return false; } if (RegKind1 != RegKind) { return false; } if (!AddNextRegisterToList(Reg, RegWidth, RegKind1, Reg1, RegNum1)) { return false; } } else { return false; } } while (true); } else { return false; } switch (RegKind) { case IS_SPECIAL: RegNum = 0; RegWidth = 1; break; case IS_VGPR: case IS_SGPR: case IS_TTMP: { unsigned Size = 1; if (RegKind == IS_SGPR || RegKind == IS_TTMP) { // SGPR and TTMP registers must be are aligned. Max required alignment is 4 dwords. Size = std::min(RegWidth, 4u); } if (RegNum % Size != 0) { return false; } RegNum = RegNum / Size; int RCID = getRegClass(RegKind, RegWidth); if (RCID == -1) { return false; } const MCRegisterClass RC = TRI->getRegClass(RCID); if (RegNum >= RC.getNumRegs()) { return false; } Reg = RC.getRegister(RegNum); break; } default: assert(false); return false; } if (!subtargetHasRegister(*TRI, Reg)) { return false; } return true; } std::unique_ptr AMDGPUAsmParser::parseRegister() { const auto &Tok = Parser.getTok(); SMLoc StartLoc = Tok.getLoc(); SMLoc EndLoc = Tok.getEndLoc(); const MCRegisterInfo *TRI = getContext().getRegisterInfo(); RegisterKind RegKind; unsigned Reg, RegNum, RegWidth; if (!ParseAMDGPURegister(RegKind, Reg, RegNum, RegWidth)) { return nullptr; } return AMDGPUOperand::CreateReg(Reg, StartLoc, EndLoc, TRI, &getSTI(), false); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseImm(OperandVector &Operands) { bool Minus = false; if (getLexer().getKind() == AsmToken::Minus) { Minus = true; Parser.Lex(); } SMLoc S = Parser.getTok().getLoc(); switch(getLexer().getKind()) { case AsmToken::Integer: { int64_t IntVal; if (getParser().parseAbsoluteExpression(IntVal)) return MatchOperand_ParseFail; if (!isInt<32>(IntVal) && !isUInt<32>(IntVal)) { Error(S, "invalid immediate: only 32-bit values are legal"); return MatchOperand_ParseFail; } if (Minus) IntVal *= -1; Operands.push_back(AMDGPUOperand::CreateImm(IntVal, S)); return MatchOperand_Success; } case AsmToken::Real: { // FIXME: We should emit an error if a double precisions floating-point // value is used. I'm not sure the best way to detect this. int64_t IntVal; if (getParser().parseAbsoluteExpression(IntVal)) return MatchOperand_ParseFail; APFloat F((float)BitsToDouble(IntVal)); if (Minus) F.changeSign(); Operands.push_back( AMDGPUOperand::CreateImm(F.bitcastToAPInt().getZExtValue(), S, AMDGPUOperand::ImmTyNone, true)); return MatchOperand_Success; } default: return Minus ? MatchOperand_ParseFail : MatchOperand_NoMatch; } } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseRegOrImm(OperandVector &Operands) { auto res = parseImm(Operands); if (res != MatchOperand_NoMatch) { return res; } if (auto R = parseRegister()) { assert(R->isReg()); R->Reg.IsForcedVOP3 = isForcedVOP3(); Operands.push_back(std::move(R)); return MatchOperand_Success; } return MatchOperand_ParseFail; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseRegOrImmWithInputMods(OperandVector &Operands) { // XXX: During parsing we can't determine if minus sign means // negate-modifier or negative immediate value. // By default we suppose it is modifier. bool Negate = false, Abs = false, Abs2 = false; if (getLexer().getKind()== AsmToken::Minus) { Parser.Lex(); Negate = true; } if (getLexer().getKind() == AsmToken::Identifier && Parser.getTok().getString() == "abs") { Parser.Lex(); Abs2 = true; if (getLexer().isNot(AsmToken::LParen)) { Error(Parser.getTok().getLoc(), "expected left paren after abs"); return MatchOperand_ParseFail; } Parser.Lex(); } if (getLexer().getKind() == AsmToken::Pipe) { if (Abs2) { Error(Parser.getTok().getLoc(), "expected register or immediate"); return MatchOperand_ParseFail; } Parser.Lex(); Abs = true; } auto Res = parseRegOrImm(Operands); if (Res != MatchOperand_Success) { return Res; } unsigned Modifiers = 0; if (Negate) { Modifiers |= 0x1; } if (Abs) { if (getLexer().getKind() != AsmToken::Pipe) { Error(Parser.getTok().getLoc(), "expected vertical bar"); return MatchOperand_ParseFail; } Parser.Lex(); Modifiers |= 0x2; } if (Abs2) { if (getLexer().isNot(AsmToken::RParen)) { Error(Parser.getTok().getLoc(), "expected closing parentheses"); return MatchOperand_ParseFail; } Parser.Lex(); Modifiers |= 0x2; } if (Modifiers) { AMDGPUOperand &Op = static_cast(*Operands.back()); Op.setModifiers(Modifiers); } return MatchOperand_Success; } unsigned AMDGPUAsmParser::checkTargetMatchPredicate(MCInst &Inst) { uint64_t TSFlags = MII.get(Inst.getOpcode()).TSFlags; if ((getForcedEncodingSize() == 32 && (TSFlags & SIInstrFlags::VOP3)) || (getForcedEncodingSize() == 64 && !(TSFlags & SIInstrFlags::VOP3))) return Match_InvalidOperand; if ((TSFlags & SIInstrFlags::VOP3) && (TSFlags & SIInstrFlags::VOPAsmPrefer32Bit) && getForcedEncodingSize() != 64) return Match_PreferE32; return Match_Success; } bool AMDGPUAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, OperandVector &Operands, MCStreamer &Out, uint64_t &ErrorInfo, bool MatchingInlineAsm) { MCInst Inst; 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 not supported on this GPU"); case Match_MnemonicFail: return Error(IDLoc, "unrecognized instruction mnemonic"); case Match_InvalidOperand: { SMLoc ErrorLoc = IDLoc; if (ErrorInfo != ~0ULL) { if (ErrorInfo >= Operands.size()) { return Error(IDLoc, "too few operands for instruction"); } ErrorLoc = ((AMDGPUOperand &)*Operands[ErrorInfo]).getStartLoc(); if (ErrorLoc == SMLoc()) ErrorLoc = IDLoc; } return Error(ErrorLoc, "invalid operand for instruction"); } case Match_PreferE32: return Error(IDLoc, "internal error: instruction without _e64 suffix " "should be encoded as e32"); } llvm_unreachable("Implement any new match types added!"); } bool AMDGPUAsmParser::ParseDirectiveMajorMinor(uint32_t &Major, uint32_t &Minor) { if (getLexer().isNot(AsmToken::Integer)) return TokError("invalid major version"); Major = getLexer().getTok().getIntVal(); Lex(); if (getLexer().isNot(AsmToken::Comma)) return TokError("minor version number required, comma expected"); Lex(); if (getLexer().isNot(AsmToken::Integer)) return TokError("invalid minor version"); Minor = getLexer().getTok().getIntVal(); Lex(); return false; } bool AMDGPUAsmParser::ParseDirectiveHSACodeObjectVersion() { uint32_t Major; uint32_t Minor; if (ParseDirectiveMajorMinor(Major, Minor)) return true; getTargetStreamer().EmitDirectiveHSACodeObjectVersion(Major, Minor); return false; } bool AMDGPUAsmParser::ParseDirectiveHSACodeObjectISA() { uint32_t Major; uint32_t Minor; uint32_t Stepping; StringRef VendorName; StringRef ArchName; // If this directive has no arguments, then use the ISA version for the // targeted GPU. if (getLexer().is(AsmToken::EndOfStatement)) { AMDGPU::IsaVersion Isa = AMDGPU::getIsaVersion(getSTI().getFeatureBits()); getTargetStreamer().EmitDirectiveHSACodeObjectISA(Isa.Major, Isa.Minor, Isa.Stepping, "AMD", "AMDGPU"); return false; } if (ParseDirectiveMajorMinor(Major, Minor)) return true; if (getLexer().isNot(AsmToken::Comma)) return TokError("stepping version number required, comma expected"); Lex(); if (getLexer().isNot(AsmToken::Integer)) return TokError("invalid stepping version"); Stepping = getLexer().getTok().getIntVal(); Lex(); if (getLexer().isNot(AsmToken::Comma)) return TokError("vendor name required, comma expected"); Lex(); if (getLexer().isNot(AsmToken::String)) return TokError("invalid vendor name"); VendorName = getLexer().getTok().getStringContents(); Lex(); if (getLexer().isNot(AsmToken::Comma)) return TokError("arch name required, comma expected"); Lex(); if (getLexer().isNot(AsmToken::String)) return TokError("invalid arch name"); ArchName = getLexer().getTok().getStringContents(); Lex(); getTargetStreamer().EmitDirectiveHSACodeObjectISA(Major, Minor, Stepping, VendorName, ArchName); return false; } bool AMDGPUAsmParser::ParseAMDKernelCodeTValue(StringRef ID, amd_kernel_code_t &Header) { SmallString<40> ErrStr; raw_svector_ostream Err(ErrStr); if (!parseAmdKernelCodeField(ID, getLexer(), Header, Err)) { return TokError(Err.str()); } Lex(); return false; } bool AMDGPUAsmParser::ParseDirectiveAMDKernelCodeT() { amd_kernel_code_t Header; AMDGPU::initDefaultAMDKernelCodeT(Header, getSTI().getFeatureBits()); while (true) { if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("amd_kernel_code_t values must begin on a new line"); // Lex EndOfStatement. This is in a while loop, because lexing a comment // will set the current token to EndOfStatement. while(getLexer().is(AsmToken::EndOfStatement)) Lex(); if (getLexer().isNot(AsmToken::Identifier)) return TokError("expected value identifier or .end_amd_kernel_code_t"); StringRef ID = getLexer().getTok().getIdentifier(); Lex(); if (ID == ".end_amd_kernel_code_t") break; if (ParseAMDKernelCodeTValue(ID, Header)) return true; } getTargetStreamer().EmitAMDKernelCodeT(Header); return false; } bool AMDGPUAsmParser::ParseSectionDirectiveHSAText() { getParser().getStreamer().SwitchSection( AMDGPU::getHSATextSection(getContext())); return false; } bool AMDGPUAsmParser::ParseDirectiveAMDGPUHsaKernel() { if (getLexer().isNot(AsmToken::Identifier)) return TokError("expected symbol name"); StringRef KernelName = Parser.getTok().getString(); getTargetStreamer().EmitAMDGPUSymbolType(KernelName, ELF::STT_AMDGPU_HSA_KERNEL); Lex(); return false; } bool AMDGPUAsmParser::ParseDirectiveAMDGPUHsaModuleGlobal() { if (getLexer().isNot(AsmToken::Identifier)) return TokError("expected symbol name"); StringRef GlobalName = Parser.getTok().getIdentifier(); getTargetStreamer().EmitAMDGPUHsaModuleScopeGlobal(GlobalName); Lex(); return false; } bool AMDGPUAsmParser::ParseDirectiveAMDGPUHsaProgramGlobal() { if (getLexer().isNot(AsmToken::Identifier)) return TokError("expected symbol name"); StringRef GlobalName = Parser.getTok().getIdentifier(); getTargetStreamer().EmitAMDGPUHsaProgramScopeGlobal(GlobalName); Lex(); return false; } bool AMDGPUAsmParser::ParseSectionDirectiveHSADataGlobalAgent() { getParser().getStreamer().SwitchSection( AMDGPU::getHSADataGlobalAgentSection(getContext())); return false; } bool AMDGPUAsmParser::ParseSectionDirectiveHSADataGlobalProgram() { getParser().getStreamer().SwitchSection( AMDGPU::getHSADataGlobalProgramSection(getContext())); return false; } bool AMDGPUAsmParser::ParseSectionDirectiveHSARodataReadonlyAgent() { getParser().getStreamer().SwitchSection( AMDGPU::getHSARodataReadonlyAgentSection(getContext())); return false; } bool AMDGPUAsmParser::ParseDirective(AsmToken DirectiveID) { StringRef IDVal = DirectiveID.getString(); if (IDVal == ".hsa_code_object_version") return ParseDirectiveHSACodeObjectVersion(); if (IDVal == ".hsa_code_object_isa") return ParseDirectiveHSACodeObjectISA(); if (IDVal == ".amd_kernel_code_t") return ParseDirectiveAMDKernelCodeT(); if (IDVal == ".hsatext") return ParseSectionDirectiveHSAText(); if (IDVal == ".amdgpu_hsa_kernel") return ParseDirectiveAMDGPUHsaKernel(); if (IDVal == ".amdgpu_hsa_module_global") return ParseDirectiveAMDGPUHsaModuleGlobal(); if (IDVal == ".amdgpu_hsa_program_global") return ParseDirectiveAMDGPUHsaProgramGlobal(); if (IDVal == ".hsadata_global_agent") return ParseSectionDirectiveHSADataGlobalAgent(); if (IDVal == ".hsadata_global_program") return ParseSectionDirectiveHSADataGlobalProgram(); if (IDVal == ".hsarodata_readonly_agent") return ParseSectionDirectiveHSARodataReadonlyAgent(); return true; } bool AMDGPUAsmParser::subtargetHasRegister(const MCRegisterInfo &MRI, unsigned RegNo) const { if (isCI()) return true; if (isSI()) { // No flat_scr switch (RegNo) { case AMDGPU::FLAT_SCR: case AMDGPU::FLAT_SCR_LO: case AMDGPU::FLAT_SCR_HI: return false; default: return true; } } // VI only has 102 SGPRs, so make sure we aren't trying to use the 2 more that // SI/CI have. for (MCRegAliasIterator R(AMDGPU::SGPR102_SGPR103, &MRI, true); R.isValid(); ++R) { if (*R == RegNo) return false; } return true; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) { // Try to parse with a custom parser OperandMatchResultTy ResTy = MatchOperandParserImpl(Operands, Mnemonic); // If we successfully parsed the operand or if there as an error parsing, // we are done. // // If we are parsing after we reach EndOfStatement then this means we // are appending default values to the Operands list. This is only done // by custom parser, so we shouldn't continue on to the generic parsing. if (ResTy == MatchOperand_Success || ResTy == MatchOperand_ParseFail || getLexer().is(AsmToken::EndOfStatement)) return ResTy; ResTy = parseRegOrImm(Operands); if (ResTy == MatchOperand_Success) return ResTy; if (getLexer().getKind() == AsmToken::Identifier) { const auto &Tok = Parser.getTok(); Operands.push_back(AMDGPUOperand::CreateToken(Tok.getString(), Tok.getLoc())); Parser.Lex(); return MatchOperand_Success; } return MatchOperand_NoMatch; } bool AMDGPUAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, OperandVector &Operands) { // Clear any forced encodings from the previous instruction. setForcedEncodingSize(0); if (Name.endswith("_e64")) setForcedEncodingSize(64); else if (Name.endswith("_e32")) setForcedEncodingSize(32); // Add the instruction mnemonic Operands.push_back(AMDGPUOperand::CreateToken(Name, NameLoc)); if (Name.endswith("_e64")) { Name = Name.substr(0, Name.size() - 4); } if (Name.endswith("_e32")) { Name = Name.substr(0, Name.size() - 4); } while (!getLexer().is(AsmToken::EndOfStatement)) { AMDGPUAsmParser::OperandMatchResultTy Res = parseOperand(Operands, Name); // Eat the comma or space if there is one. if (getLexer().is(AsmToken::Comma)) Parser.Lex(); switch (Res) { case MatchOperand_Success: break; case MatchOperand_ParseFail: Error(getLexer().getLoc(), "failed parsing operand."); while (!getLexer().is(AsmToken::EndOfStatement)) { Parser.Lex(); } return true; case MatchOperand_NoMatch: Error(getLexer().getLoc(), "not a valid operand."); while (!getLexer().is(AsmToken::EndOfStatement)) { Parser.Lex(); } return true; } } return false; } //===----------------------------------------------------------------------===// // Utility functions //===----------------------------------------------------------------------===// AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseIntWithPrefix(const char *Prefix, int64_t &Int) { switch(getLexer().getKind()) { default: return MatchOperand_NoMatch; case AsmToken::Identifier: { StringRef Name = Parser.getTok().getString(); if (!Name.equals(Prefix)) { return MatchOperand_NoMatch; } Parser.Lex(); if (getLexer().isNot(AsmToken::Colon)) return MatchOperand_ParseFail; Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return MatchOperand_ParseFail; if (getParser().parseAbsoluteExpression(Int)) return MatchOperand_ParseFail; break; } } return MatchOperand_Success; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseIntWithPrefix(const char *Prefix, OperandVector &Operands, enum AMDGPUOperand::ImmTy ImmTy, bool (*ConvertResult)(int64_t&)) { SMLoc S = Parser.getTok().getLoc(); int64_t Value = 0; AMDGPUAsmParser::OperandMatchResultTy Res = parseIntWithPrefix(Prefix, Value); if (Res != MatchOperand_Success) return Res; if (ConvertResult && !ConvertResult(Value)) { return MatchOperand_ParseFail; } Operands.push_back(AMDGPUOperand::CreateImm(Value, S, ImmTy)); return MatchOperand_Success; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseNamedBit(const char *Name, OperandVector &Operands, enum AMDGPUOperand::ImmTy ImmTy) { int64_t Bit = 0; SMLoc S = Parser.getTok().getLoc(); // We are at the end of the statement, and this is a default argument, so // use a default value. if (getLexer().isNot(AsmToken::EndOfStatement)) { switch(getLexer().getKind()) { case AsmToken::Identifier: { StringRef Tok = Parser.getTok().getString(); if (Tok == Name) { Bit = 1; Parser.Lex(); } else if (Tok.startswith("no") && Tok.endswith(Name)) { Bit = 0; Parser.Lex(); } else { return MatchOperand_NoMatch; } break; } default: return MatchOperand_NoMatch; } } Operands.push_back(AMDGPUOperand::CreateImm(Bit, S, ImmTy)); return MatchOperand_Success; } typedef std::map OptionalImmIndexMap; void addOptionalImmOperand(MCInst& Inst, const OperandVector& Operands, OptionalImmIndexMap& OptionalIdx, enum AMDGPUOperand::ImmTy ImmT, int64_t Default = 0) { auto i = OptionalIdx.find(ImmT); if (i != OptionalIdx.end()) { unsigned Idx = i->second; ((AMDGPUOperand &)*Operands[Idx]).addImmOperands(Inst, 1); } else { Inst.addOperand(MCOperand::createImm(Default)); } } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseStringWithPrefix(const char *Prefix, StringRef &Value) { if (getLexer().isNot(AsmToken::Identifier)) { return MatchOperand_NoMatch; } StringRef Tok = Parser.getTok().getString(); if (Tok != Prefix) { return MatchOperand_NoMatch; } Parser.Lex(); if (getLexer().isNot(AsmToken::Colon)) { return MatchOperand_ParseFail; } Parser.Lex(); if (getLexer().isNot(AsmToken::Identifier)) { return MatchOperand_ParseFail; } Value = Parser.getTok().getString(); return MatchOperand_Success; } //===----------------------------------------------------------------------===// // ds //===----------------------------------------------------------------------===// void AMDGPUAsmParser::cvtDSOffset01(MCInst &Inst, const OperandVector &Operands) { OptionalImmIndexMap OptionalIdx; for (unsigned i = 1, e = Operands.size(); i != e; ++i) { AMDGPUOperand &Op = ((AMDGPUOperand &)*Operands[i]); // Add the register arguments if (Op.isReg()) { Op.addRegOperands(Inst, 1); continue; } // Handle optional arguments OptionalIdx[Op.getImmTy()] = i; } addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyOffset0); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyOffset1); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyGDS); Inst.addOperand(MCOperand::createReg(AMDGPU::M0)); // m0 } void AMDGPUAsmParser::cvtDS(MCInst &Inst, const OperandVector &Operands) { std::map OptionalIdx; bool GDSOnly = false; for (unsigned i = 1, e = Operands.size(); i != e; ++i) { AMDGPUOperand &Op = ((AMDGPUOperand &)*Operands[i]); // Add the register arguments if (Op.isReg()) { Op.addRegOperands(Inst, 1); continue; } if (Op.isToken() && Op.getToken() == "gds") { GDSOnly = true; continue; } // Handle optional arguments OptionalIdx[Op.getImmTy()] = i; } addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyOffset); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyGDS); if (!GDSOnly) { addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyGDS); } Inst.addOperand(MCOperand::createReg(AMDGPU::M0)); // m0 } //===----------------------------------------------------------------------===// // s_waitcnt //===----------------------------------------------------------------------===// bool AMDGPUAsmParser::parseCnt(int64_t &IntVal) { StringRef CntName = Parser.getTok().getString(); int64_t CntVal; Parser.Lex(); if (getLexer().isNot(AsmToken::LParen)) return true; Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return true; if (getParser().parseAbsoluteExpression(CntVal)) return true; if (getLexer().isNot(AsmToken::RParen)) return true; Parser.Lex(); if (getLexer().is(AsmToken::Amp) || getLexer().is(AsmToken::Comma)) Parser.Lex(); int CntShift; int CntMask; if (CntName == "vmcnt") { CntMask = 0xf; CntShift = 0; } else if (CntName == "expcnt") { CntMask = 0x7; CntShift = 4; } else if (CntName == "lgkmcnt") { CntMask = 0xf; CntShift = 8; } else { return true; } IntVal &= ~(CntMask << CntShift); IntVal |= (CntVal << CntShift); return false; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseSWaitCntOps(OperandVector &Operands) { // Disable all counters by default. // vmcnt [3:0] // expcnt [6:4] // lgkmcnt [11:8] int64_t CntVal = 0xf7f; SMLoc S = Parser.getTok().getLoc(); switch(getLexer().getKind()) { default: return MatchOperand_ParseFail; case AsmToken::Integer: // The operand can be an integer value. if (getParser().parseAbsoluteExpression(CntVal)) return MatchOperand_ParseFail; break; case AsmToken::Identifier: do { if (parseCnt(CntVal)) return MatchOperand_ParseFail; } while(getLexer().isNot(AsmToken::EndOfStatement)); break; } Operands.push_back(AMDGPUOperand::CreateImm(CntVal, S)); return MatchOperand_Success; } bool AMDGPUAsmParser::parseHwregOperand(int64_t &HwRegCode, int64_t &Offset, int64_t &Width, bool &IsIdentifier) { if (Parser.getTok().getString() != "hwreg") return true; Parser.Lex(); if (getLexer().isNot(AsmToken::LParen)) return true; Parser.Lex(); if (getLexer().is(AsmToken::Identifier)) { IsIdentifier = true; HwRegCode = StringSwitch(Parser.getTok().getString()) .Case("HW_REG_MODE" , 1) .Case("HW_REG_STATUS" , 2) .Case("HW_REG_TRAPSTS" , 3) .Case("HW_REG_HW_ID" , 4) .Case("HW_REG_GPR_ALLOC", 5) .Case("HW_REG_LDS_ALLOC", 6) .Case("HW_REG_IB_STS" , 7) .Default(-1); Parser.Lex(); } else { IsIdentifier = false; if (getLexer().isNot(AsmToken::Integer)) return true; if (getParser().parseAbsoluteExpression(HwRegCode)) return true; } if (getLexer().is(AsmToken::RParen)) { Parser.Lex(); return false; } // optional params if (getLexer().isNot(AsmToken::Comma)) return true; Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return true; if (getParser().parseAbsoluteExpression(Offset)) return true; if (getLexer().isNot(AsmToken::Comma)) return true; Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return true; if (getParser().parseAbsoluteExpression(Width)) return true; if (getLexer().isNot(AsmToken::RParen)) return true; Parser.Lex(); return false; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseHwreg(OperandVector &Operands) { int64_t Imm16Val = 0; SMLoc S = Parser.getTok().getLoc(); switch(getLexer().getKind()) { default: return MatchOperand_NoMatch; case AsmToken::Integer: // The operand can be an integer value. if (getParser().parseAbsoluteExpression(Imm16Val)) return MatchOperand_ParseFail; if (!isInt<16>(Imm16Val) && !isUInt<16>(Imm16Val)) { Error(S, "invalid immediate: only 16-bit values are legal"); // Do not return error code, but create an imm operand anyway and proceed // to the next operand, if any. That avoids unneccessary error messages. } break; case AsmToken::Identifier: { bool IsIdentifier = false; int64_t HwRegCode = -1; int64_t Offset = 0; // default int64_t Width = 32; // default if (parseHwregOperand(HwRegCode, Offset, Width, IsIdentifier)) return MatchOperand_ParseFail; // HwRegCode (6) [5:0] // Offset (5) [10:6] // WidthMinusOne (5) [15:11] if (HwRegCode < 0 || HwRegCode > 63) { if (IsIdentifier) Error(S, "invalid symbolic name of hardware register"); else Error(S, "invalid code of hardware register: only 6-bit values are legal"); } if (Offset < 0 || Offset > 31) Error(S, "invalid bit offset: only 5-bit values are legal"); if (Width < 1 || Width > 32) Error(S, "invalid bitfield width: only values from 1 to 32 are legal"); Imm16Val = HwRegCode | (Offset << 6) | ((Width-1) << 11); } break; } Operands.push_back(AMDGPUOperand::CreateImm(Imm16Val, S, AMDGPUOperand::ImmTyHwreg)); return MatchOperand_Success; } bool AMDGPUOperand::isSWaitCnt() const { return isImm(); } bool AMDGPUOperand::isHwreg() const { return isImmTy(ImmTyHwreg); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultHwreg() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyHwreg); } bool AMDGPUAsmParser::parseSendMsg(OperandInfoTy &Msg, OperandInfoTy &Operation, int64_t &StreamId) { using namespace llvm::AMDGPU::SendMsg; if (Parser.getTok().getString() != "sendmsg") return true; Parser.Lex(); if (getLexer().isNot(AsmToken::LParen)) return true; Parser.Lex(); if (getLexer().is(AsmToken::Identifier)) { Msg.IsSymbolic = true; Msg.Id = ID_UNKNOWN_; const std::string tok = Parser.getTok().getString(); for (int i = ID_GAPS_FIRST_; i < ID_GAPS_LAST_; ++i) { switch(i) { default: continue; // Omit gaps. case ID_INTERRUPT: case ID_GS: case ID_GS_DONE: case ID_SYSMSG: break; } if (tok == IdSymbolic[i]) { Msg.Id = i; break; } } Parser.Lex(); } else { Msg.IsSymbolic = false; if (getLexer().isNot(AsmToken::Integer)) return true; if (getParser().parseAbsoluteExpression(Msg.Id)) return true; if (getLexer().is(AsmToken::Integer)) if (getParser().parseAbsoluteExpression(Msg.Id)) Msg.Id = ID_UNKNOWN_; } if (Msg.Id == ID_UNKNOWN_) // Don't know how to parse the rest. return false; if (!(Msg.Id == ID_GS || Msg.Id == ID_GS_DONE || Msg.Id == ID_SYSMSG)) { if (getLexer().isNot(AsmToken::RParen)) return true; Parser.Lex(); return false; } if (getLexer().isNot(AsmToken::Comma)) return true; Parser.Lex(); assert(Msg.Id == ID_GS || Msg.Id == ID_GS_DONE || Msg.Id == ID_SYSMSG); Operation.Id = ID_UNKNOWN_; if (getLexer().is(AsmToken::Identifier)) { Operation.IsSymbolic = true; const char* const *S = (Msg.Id == ID_SYSMSG) ? OpSysSymbolic : OpGsSymbolic; const int F = (Msg.Id == ID_SYSMSG) ? OP_SYS_FIRST_ : OP_GS_FIRST_; const int L = (Msg.Id == ID_SYSMSG) ? OP_SYS_LAST_ : OP_GS_LAST_; const std::string Tok = Parser.getTok().getString(); for (int i = F; i < L; ++i) { if (Tok == S[i]) { Operation.Id = i; break; } } Parser.Lex(); } else { Operation.IsSymbolic = false; if (getLexer().isNot(AsmToken::Integer)) return true; if (getParser().parseAbsoluteExpression(Operation.Id)) return true; } if ((Msg.Id == ID_GS || Msg.Id == ID_GS_DONE) && Operation.Id != OP_GS_NOP) { // Stream id is optional. if (getLexer().is(AsmToken::RParen)) { Parser.Lex(); return false; } if (getLexer().isNot(AsmToken::Comma)) return true; Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return true; if (getParser().parseAbsoluteExpression(StreamId)) return true; } if (getLexer().isNot(AsmToken::RParen)) return true; Parser.Lex(); return false; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseSendMsgOp(OperandVector &Operands) { using namespace llvm::AMDGPU::SendMsg; int64_t Imm16Val = 0; SMLoc S = Parser.getTok().getLoc(); switch(getLexer().getKind()) { default: return MatchOperand_NoMatch; case AsmToken::Integer: // The operand can be an integer value. if (getParser().parseAbsoluteExpression(Imm16Val)) return MatchOperand_NoMatch; if (!isInt<16>(Imm16Val) && !isUInt<16>(Imm16Val)) { Error(S, "invalid immediate: only 16-bit values are legal"); // Do not return error code, but create an imm operand anyway and proceed // to the next operand, if any. That avoids unneccessary error messages. } break; case AsmToken::Identifier: { OperandInfoTy Msg(ID_UNKNOWN_); OperandInfoTy Operation(OP_UNKNOWN_); int64_t StreamId = STREAM_ID_DEFAULT; if (parseSendMsg(Msg, Operation, StreamId)) return MatchOperand_NoMatch; do { // Validate and encode message ID. if (! ((ID_INTERRUPT <= Msg.Id && Msg.Id <= ID_GS_DONE) || Msg.Id == ID_SYSMSG)) { if (Msg.IsSymbolic) Error(S, "invalid/unsupported symbolic name of message"); else Error(S, "invalid/unsupported code of message"); break; } Imm16Val = Msg.Id; // Validate and encode operation ID. if (Msg.Id == ID_GS || Msg.Id == ID_GS_DONE) { if (! (OP_GS_FIRST_ <= Operation.Id && Operation.Id < OP_GS_LAST_)) { if (Operation.IsSymbolic) Error(S, "invalid symbolic name of GS_OP"); else Error(S, "invalid code of GS_OP: only 2-bit values are legal"); break; } if (Operation.Id == OP_GS_NOP && Msg.Id != ID_GS_DONE) { Error(S, "invalid GS_OP: NOP is for GS_DONE only"); break; } Imm16Val |= (Operation.Id << OP_SHIFT_); } if (Msg.Id == ID_SYSMSG) { if (! (OP_SYS_FIRST_ <= Operation.Id && Operation.Id < OP_SYS_LAST_)) { if (Operation.IsSymbolic) Error(S, "invalid/unsupported symbolic name of SYSMSG_OP"); else Error(S, "invalid/unsupported code of SYSMSG_OP"); break; } Imm16Val |= (Operation.Id << OP_SHIFT_); } // Validate and encode stream ID. if ((Msg.Id == ID_GS || Msg.Id == ID_GS_DONE) && Operation.Id != OP_GS_NOP) { if (! (STREAM_ID_FIRST_ <= StreamId && StreamId < STREAM_ID_LAST_)) { Error(S, "invalid stream id: only 2-bit values are legal"); break; } Imm16Val |= (StreamId << STREAM_ID_SHIFT_); } } while (0); } break; } Operands.push_back(AMDGPUOperand::CreateImm(Imm16Val, S, AMDGPUOperand::ImmTySendMsg)); return MatchOperand_Success; } bool AMDGPUOperand::isSendMsg() const { return isImmTy(ImmTySendMsg); } //===----------------------------------------------------------------------===// // sopp branch targets //===----------------------------------------------------------------------===// AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseSOppBrTarget(OperandVector &Operands) { SMLoc S = Parser.getTok().getLoc(); switch (getLexer().getKind()) { default: return MatchOperand_ParseFail; case AsmToken::Integer: { int64_t Imm; if (getParser().parseAbsoluteExpression(Imm)) return MatchOperand_ParseFail; Operands.push_back(AMDGPUOperand::CreateImm(Imm, S)); return MatchOperand_Success; } case AsmToken::Identifier: Operands.push_back(AMDGPUOperand::CreateExpr( MCSymbolRefExpr::create(getContext().getOrCreateSymbol( Parser.getTok().getString()), getContext()), S)); Parser.Lex(); return MatchOperand_Success; } } //===----------------------------------------------------------------------===// // mubuf //===----------------------------------------------------------------------===// bool AMDGPUOperand::isMubufOffset() const { return isImmTy(ImmTyOffset) && isUInt<12>(getImm()); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultMubufOffset() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyOffset); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultGLC() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyGLC); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultSLC() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTySLC); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultTFE() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyTFE); } void AMDGPUAsmParser::cvtMubufImpl(MCInst &Inst, const OperandVector &Operands, bool IsAtomic, bool IsAtomicReturn) { OptionalImmIndexMap OptionalIdx; assert(IsAtomicReturn ? IsAtomic : true); for (unsigned i = 1, e = Operands.size(); i != e; ++i) { AMDGPUOperand &Op = ((AMDGPUOperand &)*Operands[i]); // Add the register arguments if (Op.isReg()) { Op.addRegOperands(Inst, 1); continue; } // Handle the case where soffset is an immediate if (Op.isImm() && Op.getImmTy() == AMDGPUOperand::ImmTyNone) { Op.addImmOperands(Inst, 1); continue; } // Handle tokens like 'offen' which are sometimes hard-coded into the // asm string. There are no MCInst operands for these. if (Op.isToken()) { continue; } assert(Op.isImm()); // Handle optional arguments OptionalIdx[Op.getImmTy()] = i; } // Copy $vdata_in operand and insert as $vdata for MUBUF_Atomic RTN insns. if (IsAtomicReturn) { MCInst::iterator I = Inst.begin(); // $vdata_in is always at the beginning. Inst.insert(I, *I); } addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyOffset); if (!IsAtomic) { // glc is hard-coded. addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyGLC); } addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTySLC); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyTFE); } //===----------------------------------------------------------------------===// // mimg //===----------------------------------------------------------------------===// void AMDGPUAsmParser::cvtMIMG(MCInst &Inst, const OperandVector &Operands) { unsigned I = 1; const MCInstrDesc &Desc = MII.get(Inst.getOpcode()); for (unsigned J = 0; J < Desc.getNumDefs(); ++J) { ((AMDGPUOperand &)*Operands[I++]).addRegOperands(Inst, 1); } OptionalImmIndexMap OptionalIdx; for (unsigned E = Operands.size(); I != E; ++I) { AMDGPUOperand &Op = ((AMDGPUOperand &)*Operands[I]); // Add the register arguments if (Op.isRegOrImm()) { Op.addRegOrImmOperands(Inst, 1); continue; } else if (Op.isImmModifier()) { OptionalIdx[Op.getImmTy()] = I; } else { assert(false); } } addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyDMask); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyUNorm); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyGLC); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyDA); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyR128); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyTFE); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyLWE); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTySLC); } void AMDGPUAsmParser::cvtMIMGAtomic(MCInst &Inst, const OperandVector &Operands) { unsigned I = 1; const MCInstrDesc &Desc = MII.get(Inst.getOpcode()); for (unsigned J = 0; J < Desc.getNumDefs(); ++J) { ((AMDGPUOperand &)*Operands[I++]).addRegOperands(Inst, 1); } // Add src, same as dst ((AMDGPUOperand &)*Operands[I]).addRegOperands(Inst, 1); OptionalImmIndexMap OptionalIdx; for (unsigned E = Operands.size(); I != E; ++I) { AMDGPUOperand &Op = ((AMDGPUOperand &)*Operands[I]); // Add the register arguments if (Op.isRegOrImm()) { Op.addRegOrImmOperands(Inst, 1); continue; } else if (Op.isImmModifier()) { OptionalIdx[Op.getImmTy()] = I; } else { assert(false); } } addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyDMask); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyUNorm); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyGLC); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyDA); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyR128); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyTFE); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyLWE); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTySLC); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultDMask() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyDMask); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultUNorm() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyUNorm); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultDA() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyDA); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultR128() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyR128); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultLWE() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyLWE); } //===----------------------------------------------------------------------===// // smrd //===----------------------------------------------------------------------===// bool AMDGPUOperand::isSMRDOffset() const { // FIXME: Support 20-bit offsets on VI. We need to to pass subtarget // information here. return isImm() && isUInt<8>(getImm()); } bool AMDGPUOperand::isSMRDLiteralOffset() const { // 32-bit literals are only supported on CI and we only want to use them // when the offset is > 8-bits. return isImm() && !isUInt<8>(getImm()) && isUInt<32>(getImm()); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultSMRDOffset() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyOffset); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultSMRDLiteralOffset() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyOffset); } //===----------------------------------------------------------------------===// // vop3 //===----------------------------------------------------------------------===// static bool ConvertOmodMul(int64_t &Mul) { if (Mul != 1 && Mul != 2 && Mul != 4) return false; Mul >>= 1; return true; } static bool ConvertOmodDiv(int64_t &Div) { if (Div == 1) { Div = 0; return true; } if (Div == 2) { Div = 3; return true; } return false; } static bool ConvertBoundCtrl(int64_t &BoundCtrl) { if (BoundCtrl == 0) { BoundCtrl = 1; return true; } else if (BoundCtrl == -1) { BoundCtrl = 0; return true; } return false; } // Note: the order in this table matches the order of operands in AsmString. static const OptionalOperand AMDGPUOptionalOperandTable[] = { {"offen", AMDGPUOperand::ImmTyOffen, true, nullptr}, {"idxen", AMDGPUOperand::ImmTyIdxen, true, nullptr}, {"addr64", AMDGPUOperand::ImmTyAddr64, true, nullptr}, {"offset0", AMDGPUOperand::ImmTyOffset0, false, nullptr}, {"offset1", AMDGPUOperand::ImmTyOffset1, false, nullptr}, {"gds", AMDGPUOperand::ImmTyGDS, true, nullptr}, {"offset", AMDGPUOperand::ImmTyOffset, false, nullptr}, {"glc", AMDGPUOperand::ImmTyGLC, true, nullptr}, {"slc", AMDGPUOperand::ImmTySLC, true, nullptr}, {"tfe", AMDGPUOperand::ImmTyTFE, true, nullptr}, {"clamp", AMDGPUOperand::ImmTyClampSI, true, nullptr}, {"omod", AMDGPUOperand::ImmTyOModSI, false, ConvertOmodMul}, {"unorm", AMDGPUOperand::ImmTyUNorm, true, nullptr}, {"da", AMDGPUOperand::ImmTyDA, true, nullptr}, {"r128", AMDGPUOperand::ImmTyR128, true, nullptr}, {"lwe", AMDGPUOperand::ImmTyLWE, true, nullptr}, {"dmask", AMDGPUOperand::ImmTyDMask, false, nullptr}, {"row_mask", AMDGPUOperand::ImmTyDppRowMask, false, nullptr}, {"bank_mask", AMDGPUOperand::ImmTyDppBankMask, false, nullptr}, {"bound_ctrl", AMDGPUOperand::ImmTyDppBoundCtrl, false, ConvertBoundCtrl}, {"sdwa_sel", AMDGPUOperand::ImmTySdwaSel, false, nullptr}, {"dst_unused", AMDGPUOperand::ImmTySdwaDstUnused, false, nullptr}, }; AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseOptionalOperand(OperandVector &Operands) { OperandMatchResultTy res; for (const OptionalOperand &Op : AMDGPUOptionalOperandTable) { // try to parse any optional operand here if (Op.IsBit) { res = parseNamedBit(Op.Name, Operands, Op.Type); } else if (Op.Type == AMDGPUOperand::ImmTyOModSI) { res = parseOModOperand(Operands); } else if (Op.Type == AMDGPUOperand::ImmTySdwaSel) { res = parseSDWASel(Operands); } else if (Op.Type == AMDGPUOperand::ImmTySdwaDstUnused) { res = parseSDWADstUnused(Operands); } else { res = parseIntWithPrefix(Op.Name, Operands, Op.Type, Op.ConvertResult); } if (res != MatchOperand_NoMatch) { return res; } } return MatchOperand_NoMatch; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseOModOperand(OperandVector &Operands) { StringRef Name = Parser.getTok().getString(); if (Name == "mul") { return parseIntWithPrefix("mul", Operands, AMDGPUOperand::ImmTyOModSI, ConvertOmodMul); } else if (Name == "div") { return parseIntWithPrefix("div", Operands, AMDGPUOperand::ImmTyOModSI, ConvertOmodDiv); } else { return MatchOperand_NoMatch; } } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultClampSI() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyClampSI); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultOModSI() const { return AMDGPUOperand::CreateImm(1, SMLoc(), AMDGPUOperand::ImmTyOModSI); } void AMDGPUAsmParser::cvtId(MCInst &Inst, const OperandVector &Operands) { unsigned I = 1; const MCInstrDesc &Desc = MII.get(Inst.getOpcode()); for (unsigned J = 0; J < Desc.getNumDefs(); ++J) { ((AMDGPUOperand &)*Operands[I++]).addRegOperands(Inst, 1); } for (unsigned E = Operands.size(); I != E; ++I) ((AMDGPUOperand &)*Operands[I]).addRegOrImmOperands(Inst, 1); } void AMDGPUAsmParser::cvtVOP3_2_mod(MCInst &Inst, const OperandVector &Operands) { uint64_t TSFlags = MII.get(Inst.getOpcode()).TSFlags; if (TSFlags & SIInstrFlags::VOP3) { cvtVOP3(Inst, Operands); } else { cvtId(Inst, Operands); } } void AMDGPUAsmParser::cvtVOP3(MCInst &Inst, const OperandVector &Operands) { OptionalImmIndexMap OptionalIdx; unsigned I = 1; const MCInstrDesc &Desc = MII.get(Inst.getOpcode()); for (unsigned J = 0; J < Desc.getNumDefs(); ++J) { ((AMDGPUOperand &)*Operands[I++]).addRegOperands(Inst, 1); } for (unsigned E = Operands.size(); I != E; ++I) { AMDGPUOperand &Op = ((AMDGPUOperand &)*Operands[I]); if (Op.isRegOrImmWithInputMods()) { Op.addRegOrImmWithInputModsOperands(Inst, 2); } else if (Op.isImm()) { OptionalIdx[Op.getImmTy()] = I; } else { assert(false); } } addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyClampSI); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyOModSI); } //===----------------------------------------------------------------------===// // dpp //===----------------------------------------------------------------------===// bool AMDGPUOperand::isDPPCtrl() const { bool result = isImm() && getImmTy() == ImmTyDppCtrl && isUInt<9>(getImm()); if (result) { int64_t Imm = getImm(); return ((Imm >= 0x000) && (Imm <= 0x0ff)) || ((Imm >= 0x101) && (Imm <= 0x10f)) || ((Imm >= 0x111) && (Imm <= 0x11f)) || ((Imm >= 0x121) && (Imm <= 0x12f)) || (Imm == 0x130) || (Imm == 0x134) || (Imm == 0x138) || (Imm == 0x13c) || (Imm == 0x140) || (Imm == 0x141) || (Imm == 0x142) || (Imm == 0x143); } return false; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseDPPCtrl(OperandVector &Operands) { SMLoc S = Parser.getTok().getLoc(); StringRef Prefix; int64_t Int; if (getLexer().getKind() == AsmToken::Identifier) { Prefix = Parser.getTok().getString(); } else { return MatchOperand_NoMatch; } if (Prefix == "row_mirror") { Int = 0x140; } else if (Prefix == "row_half_mirror") { Int = 0x141; } else { // Check to prevent parseDPPCtrlOps from eating invalid tokens if (Prefix != "quad_perm" && Prefix != "row_shl" && Prefix != "row_shr" && Prefix != "row_ror" && Prefix != "wave_shl" && Prefix != "wave_rol" && Prefix != "wave_shr" && Prefix != "wave_ror" && Prefix != "row_bcast") { return MatchOperand_NoMatch; } Parser.Lex(); if (getLexer().isNot(AsmToken::Colon)) return MatchOperand_ParseFail; if (Prefix == "quad_perm") { // quad_perm:[%d,%d,%d,%d] Parser.Lex(); if (getLexer().isNot(AsmToken::LBrac)) return MatchOperand_ParseFail; Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return MatchOperand_ParseFail; Int = getLexer().getTok().getIntVal(); Parser.Lex(); if (getLexer().isNot(AsmToken::Comma)) return MatchOperand_ParseFail; Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return MatchOperand_ParseFail; Int += (getLexer().getTok().getIntVal() << 2); Parser.Lex(); if (getLexer().isNot(AsmToken::Comma)) return MatchOperand_ParseFail; Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return MatchOperand_ParseFail; Int += (getLexer().getTok().getIntVal() << 4); Parser.Lex(); if (getLexer().isNot(AsmToken::Comma)) return MatchOperand_ParseFail; Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return MatchOperand_ParseFail; Int += (getLexer().getTok().getIntVal() << 6); Parser.Lex(); if (getLexer().isNot(AsmToken::RBrac)) return MatchOperand_ParseFail; } else { // sel:%d Parser.Lex(); if (getLexer().isNot(AsmToken::Integer)) return MatchOperand_ParseFail; Int = getLexer().getTok().getIntVal(); if (Prefix == "row_shl") { Int |= 0x100; } else if (Prefix == "row_shr") { Int |= 0x110; } else if (Prefix == "row_ror") { Int |= 0x120; } else if (Prefix == "wave_shl") { Int = 0x130; } else if (Prefix == "wave_rol") { Int = 0x134; } else if (Prefix == "wave_shr") { Int = 0x138; } else if (Prefix == "wave_ror") { Int = 0x13C; } else if (Prefix == "row_bcast") { if (Int == 15) { Int = 0x142; } else if (Int == 31) { Int = 0x143; } } else { return MatchOperand_ParseFail; } } } Parser.Lex(); // eat last token Operands.push_back(AMDGPUOperand::CreateImm(Int, S, AMDGPUOperand::ImmTyDppCtrl)); return MatchOperand_Success; } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultRowMask() const { return AMDGPUOperand::CreateImm(0xf, SMLoc(), AMDGPUOperand::ImmTyDppRowMask); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultBankMask() const { return AMDGPUOperand::CreateImm(0xf, SMLoc(), AMDGPUOperand::ImmTyDppBankMask); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultBoundCtrl() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTyDppBoundCtrl); } void AMDGPUAsmParser::cvtDPP(MCInst &Inst, const OperandVector &Operands) { OptionalImmIndexMap OptionalIdx; unsigned I = 1; const MCInstrDesc &Desc = MII.get(Inst.getOpcode()); for (unsigned J = 0; J < Desc.getNumDefs(); ++J) { ((AMDGPUOperand &)*Operands[I++]).addRegOperands(Inst, 1); } for (unsigned E = Operands.size(); I != E; ++I) { AMDGPUOperand &Op = ((AMDGPUOperand &)*Operands[I]); // Add the register arguments if (Op.isRegOrImmWithInputMods()) { // We convert only instructions with modifiers Op.addRegOrImmWithInputModsOperands(Inst, 2); } else if (Op.isDPPCtrl()) { Op.addImmOperands(Inst, 1); } else if (Op.isImm()) { // Handle optional arguments OptionalIdx[Op.getImmTy()] = I; } else { llvm_unreachable("Invalid operand type"); } } // ToDo: fix default values for row_mask and bank_mask addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyDppRowMask, 0xf); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyDppBankMask, 0xf); addOptionalImmOperand(Inst, Operands, OptionalIdx, AMDGPUOperand::ImmTyDppBoundCtrl); } //===----------------------------------------------------------------------===// // sdwa //===----------------------------------------------------------------------===// AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseSDWASel(OperandVector &Operands) { SMLoc S = Parser.getTok().getLoc(); StringRef Value; AMDGPUAsmParser::OperandMatchResultTy res; res = parseStringWithPrefix("dst_sel", Value); if (res == MatchOperand_ParseFail) { return MatchOperand_ParseFail; } else if (res == MatchOperand_NoMatch) { res = parseStringWithPrefix("src0_sel", Value); if (res == MatchOperand_ParseFail) { return MatchOperand_ParseFail; } else if (res == MatchOperand_NoMatch) { res = parseStringWithPrefix("src1_sel", Value); if (res != MatchOperand_Success) { return res; } } } int64_t Int; Int = StringSwitch(Value) .Case("BYTE_0", 0) .Case("BYTE_1", 1) .Case("BYTE_2", 2) .Case("BYTE_3", 3) .Case("WORD_0", 4) .Case("WORD_1", 5) .Case("DWORD", 6) .Default(0xffffffff); Parser.Lex(); // eat last token if (Int == 0xffffffff) { return MatchOperand_ParseFail; } Operands.push_back(AMDGPUOperand::CreateImm(Int, S, AMDGPUOperand::ImmTySdwaSel)); return MatchOperand_Success; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseSDWADstUnused(OperandVector &Operands) { SMLoc S = Parser.getTok().getLoc(); StringRef Value; AMDGPUAsmParser::OperandMatchResultTy res; res = parseStringWithPrefix("dst_unused", Value); if (res != MatchOperand_Success) { return res; } int64_t Int; Int = StringSwitch(Value) .Case("UNUSED_PAD", 0) .Case("UNUSED_SEXT", 1) .Case("UNUSED_PRESERVE", 2) .Default(0xffffffff); Parser.Lex(); // eat last token if (Int == 0xffffffff) { return MatchOperand_ParseFail; } Operands.push_back(AMDGPUOperand::CreateImm(Int, S, AMDGPUOperand::ImmTySdwaDstUnused)); return MatchOperand_Success; } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultSDWASel() const { return AMDGPUOperand::CreateImm(6, SMLoc(), AMDGPUOperand::ImmTySdwaSel); } AMDGPUOperand::Ptr AMDGPUAsmParser::defaultSDWADstUnused() const { return AMDGPUOperand::CreateImm(0, SMLoc(), AMDGPUOperand::ImmTySdwaDstUnused); } /// Force static initialization. extern "C" void LLVMInitializeAMDGPUAsmParser() { RegisterMCAsmParser A(TheAMDGPUTarget); RegisterMCAsmParser B(TheGCNTarget); } #define GET_REGISTER_MATCHER #define GET_MATCHER_IMPLEMENTATION #include "AMDGPUGenAsmMatcher.inc" // This fuction should be defined after auto-generated include so that we have // MatchClassKind enum defined unsigned AMDGPUAsmParser::validateTargetOperandClass(MCParsedAsmOperand &Op, unsigned Kind) { // Tokens like "glc" would be parsed as immediate operands in ParseOperand(). // But MatchInstructionImpl() expects to meet token and fails to validate // operand. This method checks if we are given immediate operand but expect to // get corresponding token. AMDGPUOperand &Operand = (AMDGPUOperand&)Op; switch (Kind) { case MCK_addr64: return Operand.isAddr64() ? Match_Success : Match_InvalidOperand; case MCK_gds: return Operand.isGDS() ? Match_Success : Match_InvalidOperand; case MCK_glc: return Operand.isGLC() ? Match_Success : Match_InvalidOperand; case MCK_idxen: return Operand.isIdxen() ? Match_Success : Match_InvalidOperand; case MCK_offen: return Operand.isOffen() ? Match_Success : Match_InvalidOperand; default: return Match_InvalidOperand; } }