//===-- 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 "MCTargetDesc/AMDGPUMCTargetDesc.h" #include "MCTargetDesc/AMDGPUTargetStreamer.h" #include "Utils/AMDGPUBaseInfo.h" #include "AMDKernelCodeT.h" #include "SIDefines.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.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/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/MCTargetAsmParser.h" #include "llvm/Support/ELF.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Debug.h" using namespace llvm; namespace { struct OptionalOperand; class AMDGPUOperand : public MCParsedAsmOperand { enum KindTy { Token, Immediate, Register, Expression } Kind; SMLoc StartLoc, EndLoc; public: AMDGPUOperand(enum KindTy K) : MCParsedAsmOperand(), Kind(K) {} MCContext *Ctx; enum ImmTy { ImmTyNone, ImmTyDSOffset0, ImmTyDSOffset1, ImmTyGDS, ImmTyOffset, ImmTyGLC, ImmTySLC, ImmTyTFE, ImmTyClamp, ImmTyOMod }; struct TokOp { const char *Data; unsigned Length; }; struct ImmOp { bool IsFPImm; ImmTy Type; int64_t Val; }; 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) const { 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 (isReg()) addRegOperands(Inst, N); else addImmOperands(Inst, N); } void addRegWithInputModsOperands(MCInst &Inst, unsigned N) const { Inst.addOperand(MCOperand::createImm( Reg.Modifiers == -1 ? 0 : Reg.Modifiers)); addRegOperands(Inst, N); } void addSoppBrTargetOperands(MCInst &Inst, unsigned N) const { if (isImm()) addImmOperands(Inst, N); else { assert(isExpr()); Inst.addOperand(MCOperand::createExpr(Expr)); } } bool defaultTokenHasSuffix() const { StringRef Token(Tok.Data, Tok.Length); return Token.endswith("_e32") || Token.endswith("_e64"); } bool isToken() const override { return Kind == Token; } bool isImm() const override { return Kind == Immediate; } bool isInlineImm() const { float F = BitsToFloat(Imm.Val); // TODO: Add 0.5pi for VI return isImm() && ((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)); } bool isDSOffset0() const { assert(isImm()); return Imm.Type == ImmTyDSOffset0; } bool isDSOffset1() const { assert(isImm()); return Imm.Type == ImmTyDSOffset1; } 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 == -1; } bool isRegWithInputMods() const { return Kind == Register && (Reg.IsForcedVOP3 || Reg.Modifiers != -1); } void setModifiers(unsigned Mods) { assert(isReg()); Reg.Modifiers = Mods; } bool hasModifiers() const { assert(isRegKind()); return Reg.Modifiers != -1; } unsigned getReg() const override { return Reg.RegNo; } bool isRegOrImm() const { return isReg() || isImm(); } bool isRegClass(unsigned RCID) const { return Reg.TRI->getRegClass(RCID).contains(getReg()); } bool isSCSrc32() const { return isInlineImm() || (isReg() && isRegClass(AMDGPU::SReg_32RegClassID)); } bool isSSrc32() const { return isImm() || (isReg() && isRegClass(AMDGPU::SReg_32RegClassID)); } bool isSSrc64() const { return isImm() || isInlineImm() || (isReg() && isRegClass(AMDGPU::SReg_64RegClassID)); } bool isSCSrc64() const { return (isReg() && isRegClass(AMDGPU::SReg_64RegClassID)) || isInlineImm(); } bool isVCSrc32() const { return isInlineImm() || (isReg() && isRegClass(AMDGPU::VS_32RegClassID)); } bool isVCSrc64() const { return isInlineImm() || (isReg() && isRegClass(AMDGPU::VS_64RegClassID)); } bool isVSrc32() const { return isImm() || (isReg() && isRegClass(AMDGPU::VS_32RegClassID)); } bool isVSrc64() const { return isImm() || (isReg() && isRegClass(AMDGPU::VS_64RegClassID)); } 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 print(raw_ostream &OS) const override { switch (Kind) { case Register: OS << "'; break; case Immediate: OS << getImm(); break; case Token: OS << '\'' << getToken() << '\''; break; case Expression: OS << "'; break; } } static std::unique_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->StartLoc = Loc; Op->EndLoc = Loc; return Op; } static std::unique_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 std::unique_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 = -1; Op->Reg.IsForcedVOP3 = ForceVOP3; Op->StartLoc = S; Op->EndLoc = E; return Op; } static std::unique_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 isDSOffset() const; bool isDSOffset01() const; bool isSWaitCnt() const; bool isMubufOffset() const; bool isSMRDOffset() const; bool isSMRDLiteralOffset() 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(); public: 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; } bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; unsigned checkTargetMatchPredicate(MCInst &Inst) 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, int64_t Default = 0); OperandMatchResultTy parseIntWithPrefix(const char *Prefix, OperandVector &Operands, enum AMDGPUOperand::ImmTy ImmTy = AMDGPUOperand::ImmTyNone); OperandMatchResultTy parseNamedBit(const char *Name, OperandVector &Operands, enum AMDGPUOperand::ImmTy ImmTy = AMDGPUOperand::ImmTyNone); OperandMatchResultTy parseOptionalOps( const ArrayRef &OptionalOps, OperandVector &Operands); void cvtDSOffset01(MCInst &Inst, const OperandVector &Operands); void cvtDS(MCInst &Inst, const OperandVector &Operands); OperandMatchResultTy parseDSOptionalOps(OperandVector &Operands); OperandMatchResultTy parseDSOff01OptionalOps(OperandVector &Operands); OperandMatchResultTy parseDSOffsetOptional(OperandVector &Operands); bool parseCnt(int64_t &IntVal); OperandMatchResultTy parseSWaitCntOps(OperandVector &Operands); OperandMatchResultTy parseSOppBrTarget(OperandVector &Operands); OperandMatchResultTy parseFlatOptionalOps(OperandVector &Operands); OperandMatchResultTy parseFlatAtomicOptionalOps(OperandVector &Operands); void cvtFlat(MCInst &Inst, const OperandVector &Operands); void cvtMubuf(MCInst &Inst, const OperandVector &Operands); OperandMatchResultTy parseOffset(OperandVector &Operands); OperandMatchResultTy parseMubufOptionalOps(OperandVector &Operands); OperandMatchResultTy parseGLC(OperandVector &Operands); OperandMatchResultTy parseSLC(OperandVector &Operands); OperandMatchResultTy parseTFE(OperandVector &Operands); OperandMatchResultTy parseDMask(OperandVector &Operands); OperandMatchResultTy parseUNorm(OperandVector &Operands); OperandMatchResultTy parseR128(OperandVector &Operands); void cvtVOP3(MCInst &Inst, const OperandVector &Operands); OperandMatchResultTy parseVOP3OptionalOps(OperandVector &Operands); }; struct OptionalOperand { const char *Name; AMDGPUOperand::ImmTy Type; bool IsBit; int64_t Default; bool (*ConvertResult)(int64_t&); }; } static int getRegClass(bool IsVgpr, unsigned RegWidth) { if (IsVgpr) { 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; } } switch (RegWidth) { default: return -1; case 1: return AMDGPU::SGPR_32RegClassID; case 2: return AMDGPU::SGPR_64RegClassID; case 4: return AMDGPU::SReg_128RegClassID; case 8: return AMDGPU::SReg_256RegClassID; case 16: return AMDGPU::SReg_512RegClassID; } } static unsigned getRegForName(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("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) .Default(0); } bool AMDGPUAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) { const AsmToken Tok = Parser.getTok(); StartLoc = Tok.getLoc(); EndLoc = Tok.getEndLoc(); const MCRegisterInfo *TRI = getContext().getRegisterInfo(); StringRef RegName = Tok.getString(); RegNo = getRegForName(RegName); if (RegNo) { Parser.Lex(); return !subtargetHasRegister(*TRI, RegNo); } // Match vgprs and sgprs if (RegName[0] != 's' && RegName[0] != 'v') return true; bool IsVgpr = RegName[0] == 'v'; unsigned RegWidth; unsigned RegIndexInClass; if (RegName.size() > 1) { // We have a 32-bit register RegWidth = 1; if (RegName.substr(1).getAsInteger(10, RegIndexInClass)) return true; Parser.Lex(); } else { // We have a register greater than 32-bits. int64_t RegLo, RegHi; Parser.Lex(); if (getLexer().isNot(AsmToken::LBrac)) return true; Parser.Lex(); if (getParser().parseAbsoluteExpression(RegLo)) return true; if (getLexer().isNot(AsmToken::Colon)) return true; Parser.Lex(); if (getParser().parseAbsoluteExpression(RegHi)) return true; if (getLexer().isNot(AsmToken::RBrac)) return true; Parser.Lex(); RegWidth = (RegHi - RegLo) + 1; if (IsVgpr) { // VGPR registers aren't aligned. RegIndexInClass = RegLo; } else { // SGPR registers are aligned. Max alignment is 4 dwords. unsigned Size = std::min(RegWidth, 4u); if (RegLo % Size != 0) return true; RegIndexInClass = RegLo / Size; } } int RCID = getRegClass(IsVgpr, RegWidth); if (RCID == -1) return true; const MCRegisterClass RC = TRI->getRegClass(RCID); if (RegIndexInClass >= RC.getNumRegs()) return true; RegNo = RC.getRegister(RegIndexInClass); return !subtargetHasRegister(*TRI, RegNo); } 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()) { if (isForcedVOP3()) { // If 64-bit encoding has been forced we can end up with no // clamp or omod operands if none of the registers have modifiers, // so we need to add these to the operand list. AMDGPUOperand &LastOp = ((AMDGPUOperand &)*Operands[Operands.size() - 1]); if (LastOp.isRegKind() || (LastOp.isImm() && LastOp.getImmTy() != AMDGPUOperand::ImmTyNone)) { SMLoc S = Parser.getTok().getLoc(); Operands.push_back(AMDGPUOperand::CreateImm(0, S, AMDGPUOperand::ImmTyClamp)); Operands.push_back(AMDGPUOperand::CreateImm(0, S, AMDGPUOperand::ImmTyOMod)); bool Res = MatchAndEmitInstruction(IDLoc, Opcode, Operands, Out, ErrorInfo, MatchingInlineAsm); if (!Res) return Res; } } 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) { if (getLexer().isNot(AsmToken::Equal)) return TokError("expected '='"); Lex(); if (getLexer().isNot(AsmToken::Integer)) return TokError("amd_kernel_code_t values must be integers"); uint64_t Value = getLexer().getTok().getIntVal(); Lex(); if (ID == "kernel_code_version_major") Header.amd_kernel_code_version_major = Value; else if (ID == "kernel_code_version_minor") Header.amd_kernel_code_version_minor = Value; else if (ID == "machine_kind") Header.amd_machine_kind = Value; else if (ID == "machine_version_major") Header.amd_machine_version_major = Value; else if (ID == "machine_version_minor") Header.amd_machine_version_minor = Value; else if (ID == "machine_version_stepping") Header.amd_machine_version_stepping = Value; else if (ID == "kernel_code_entry_byte_offset") Header.kernel_code_entry_byte_offset = Value; else if (ID == "kernel_code_prefetch_byte_size") Header.kernel_code_prefetch_byte_size = Value; else if (ID == "max_scratch_backing_memory_byte_size") Header.max_scratch_backing_memory_byte_size = Value; else if (ID == "compute_pgm_rsrc1_vgprs") Header.compute_pgm_resource_registers |= S_00B848_VGPRS(Value); else if (ID == "compute_pgm_rsrc1_sgprs") Header.compute_pgm_resource_registers |= S_00B848_SGPRS(Value); else if (ID == "compute_pgm_rsrc1_priority") Header.compute_pgm_resource_registers |= S_00B848_PRIORITY(Value); else if (ID == "compute_pgm_rsrc1_float_mode") Header.compute_pgm_resource_registers |= S_00B848_FLOAT_MODE(Value); else if (ID == "compute_pgm_rsrc1_priv") Header.compute_pgm_resource_registers |= S_00B848_PRIV(Value); else if (ID == "compute_pgm_rsrc1_dx10_clamp") Header.compute_pgm_resource_registers |= S_00B848_DX10_CLAMP(Value); else if (ID == "compute_pgm_rsrc1_debug_mode") Header.compute_pgm_resource_registers |= S_00B848_DEBUG_MODE(Value); else if (ID == "compute_pgm_rsrc1_ieee_mode") Header.compute_pgm_resource_registers |= S_00B848_IEEE_MODE(Value); else if (ID == "compute_pgm_rsrc2_scratch_en") Header.compute_pgm_resource_registers |= (S_00B84C_SCRATCH_EN(Value) << 32); else if (ID == "compute_pgm_rsrc2_user_sgpr") Header.compute_pgm_resource_registers |= (S_00B84C_USER_SGPR(Value) << 32); else if (ID == "compute_pgm_rsrc2_tgid_x_en") Header.compute_pgm_resource_registers |= (S_00B84C_TGID_X_EN(Value) << 32); else if (ID == "compute_pgm_rsrc2_tgid_y_en") Header.compute_pgm_resource_registers |= (S_00B84C_TGID_Y_EN(Value) << 32); else if (ID == "compute_pgm_rsrc2_tgid_z_en") Header.compute_pgm_resource_registers |= (S_00B84C_TGID_Z_EN(Value) << 32); else if (ID == "compute_pgm_rsrc2_tg_size_en") Header.compute_pgm_resource_registers |= (S_00B84C_TG_SIZE_EN(Value) << 32); else if (ID == "compute_pgm_rsrc2_tidig_comp_cnt") Header.compute_pgm_resource_registers |= (S_00B84C_TIDIG_COMP_CNT(Value) << 32); else if (ID == "compute_pgm_rsrc2_excp_en_msb") Header.compute_pgm_resource_registers |= (S_00B84C_EXCP_EN_MSB(Value) << 32); else if (ID == "compute_pgm_rsrc2_lds_size") Header.compute_pgm_resource_registers |= (S_00B84C_LDS_SIZE(Value) << 32); else if (ID == "compute_pgm_rsrc2_excp_en") Header.compute_pgm_resource_registers |= (S_00B84C_EXCP_EN(Value) << 32); else if (ID == "compute_pgm_resource_registers") Header.compute_pgm_resource_registers = Value; else if (ID == "enable_sgpr_private_segment_buffer") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_PRIVATE_SEGMENT_BUFFER_SHIFT); else if (ID == "enable_sgpr_dispatch_ptr") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_DISPATCH_PTR_SHIFT); else if (ID == "enable_sgpr_queue_ptr") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_QUEUE_PTR_SHIFT); else if (ID == "enable_sgpr_kernarg_segment_ptr") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_KERNARG_SEGMENT_PTR_SHIFT); else if (ID == "enable_sgpr_dispatch_id") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_DISPATCH_ID_SHIFT); else if (ID == "enable_sgpr_flat_scratch_init") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_FLAT_SCRATCH_INIT_SHIFT); else if (ID == "enable_sgpr_private_segment_size") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_PRIVATE_SEGMENT_SIZE_SHIFT); else if (ID == "enable_sgpr_grid_workgroup_count_x") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_GRID_WORKGROUP_COUNT_X_SHIFT); else if (ID == "enable_sgpr_grid_workgroup_count_y") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_GRID_WORKGROUP_COUNT_Y_SHIFT); else if (ID == "enable_sgpr_grid_workgroup_count_z") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_SGPR_GRID_WORKGROUP_COUNT_Z_SHIFT); else if (ID == "enable_ordered_append_gds") Header.code_properties |= (Value << AMD_CODE_PROPERTY_ENABLE_ORDERED_APPEND_GDS_SHIFT); else if (ID == "private_element_size") Header.code_properties |= (Value << AMD_CODE_PROPERTY_PRIVATE_ELEMENT_SIZE_SHIFT); else if (ID == "is_ptr64") Header.code_properties |= (Value << AMD_CODE_PROPERTY_IS_PTR64_SHIFT); else if (ID == "is_dynamic_callstack") Header.code_properties |= (Value << AMD_CODE_PROPERTY_IS_DYNAMIC_CALLSTACK_SHIFT); else if (ID == "is_debug_enabled") Header.code_properties |= (Value << AMD_CODE_PROPERTY_IS_DEBUG_SUPPORTED_SHIFT); else if (ID == "is_xnack_enabled") Header.code_properties |= (Value << AMD_CODE_PROPERTY_IS_XNACK_SUPPORTED_SHIFT); else if (ID == "workitem_private_segment_byte_size") Header.workitem_private_segment_byte_size = Value; else if (ID == "workgroup_group_segment_byte_size") Header.workgroup_group_segment_byte_size = Value; else if (ID == "gds_segment_byte_size") Header.gds_segment_byte_size = Value; else if (ID == "kernarg_segment_byte_size") Header.kernarg_segment_byte_size = Value; else if (ID == "workgroup_fbarrier_count") Header.workgroup_fbarrier_count = Value; else if (ID == "wavefront_sgpr_count") Header.wavefront_sgpr_count = Value; else if (ID == "workitem_vgpr_count") Header.workitem_vgpr_count = Value; else if (ID == "reserved_vgpr_first") Header.reserved_vgpr_first = Value; else if (ID == "reserved_vgpr_count") Header.reserved_vgpr_count = Value; else if (ID == "reserved_sgpr_first") Header.reserved_sgpr_first = Value; else if (ID == "reserved_sgpr_count") Header.reserved_sgpr_count = Value; else if (ID == "debug_wavefront_private_segment_offset_sgpr") Header.debug_wavefront_private_segment_offset_sgpr = Value; else if (ID == "debug_private_segment_buffer_sgpr") Header.debug_private_segment_buffer_sgpr = Value; else if (ID == "kernarg_segment_alignment") Header.kernarg_segment_alignment = Value; else if (ID == "group_segment_alignment") Header.group_segment_alignment = Value; else if (ID == "private_segment_alignment") Header.private_segment_alignment = Value; else if (ID == "wavefront_size") Header.wavefront_size = Value; else if (ID == "call_convention") Header.call_convention = Value; else if (ID == "runtime_loader_kernel_symbol") Header.runtime_loader_kernel_symbol = Value; else return TokError("amd_kernel_code_t value not recognized."); 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" || IDVal == ".text") 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; } static bool operandsHaveModifiers(const OperandVector &Operands) { for (unsigned i = 0, e = Operands.size(); i != e; ++i) { const AMDGPUOperand &Op = ((AMDGPUOperand&)*Operands[i]); if (Op.isRegKind() && Op.hasModifiers()) return true; if (Op.isImm() && (Op.getImmTy() == AMDGPUOperand::ImmTyOMod || Op.getImmTy() == AMDGPUOperand::ImmTyClamp)) return true; } return false; } 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; bool Negate = false, Abs = false; if (getLexer().getKind()== AsmToken::Minus) { Parser.Lex(); Negate = true; } if (getLexer().getKind() == AsmToken::Pipe) { Parser.Lex(); Abs = true; } switch(getLexer().getKind()) { case AsmToken::Integer: { SMLoc S = Parser.getTok().getLoc(); 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 (Negate) 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. SMLoc S = Parser.getTok().getLoc(); int64_t IntVal; if (getParser().parseAbsoluteExpression(IntVal)) return MatchOperand_ParseFail; APFloat F((float)BitsToDouble(IntVal)); if (Negate) F.changeSign(); Operands.push_back( AMDGPUOperand::CreateImm(F.bitcastToAPInt().getZExtValue(), S)); return MatchOperand_Success; } case AsmToken::Identifier: { SMLoc S, E; unsigned RegNo; if (!ParseRegister(RegNo, S, E)) { bool HasModifiers = operandsHaveModifiers(Operands); unsigned Modifiers = 0; if (Negate) Modifiers |= 0x1; if (Abs) { if (getLexer().getKind() != AsmToken::Pipe) return MatchOperand_ParseFail; Parser.Lex(); Modifiers |= 0x2; } if (Modifiers && !HasModifiers) { // We are adding a modifier to src1 or src2 and previous sources // don't have modifiers, so we need to go back and empty modifers // for each previous source. for (unsigned PrevRegIdx = Operands.size() - 1; PrevRegIdx > 1; --PrevRegIdx) { AMDGPUOperand &RegOp = ((AMDGPUOperand&)*Operands[PrevRegIdx]); RegOp.setModifiers(0); } } Operands.push_back(AMDGPUOperand::CreateReg( RegNo, S, E, getContext().getRegisterInfo(), &getSTI(), isForcedVOP3())); if (HasModifiers || Modifiers) { AMDGPUOperand &RegOp = ((AMDGPUOperand&)*Operands[Operands.size() - 1]); RegOp.setModifiers(Modifiers); } } else { Operands.push_back(AMDGPUOperand::CreateToken(Parser.getTok().getString(), S)); Parser.Lex(); } return MatchOperand_Success; } default: 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)); 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: return Error(getLexer().getLoc(), "failed parsing operand."); case MatchOperand_NoMatch: return Error(getLexer().getLoc(), "not a valid operand."); } } // Once we reach end of statement, continue parsing so we can add default // values for optional arguments. AMDGPUAsmParser::OperandMatchResultTy Res; while ((Res = parseOperand(Operands, Name)) != MatchOperand_NoMatch) { if (Res != MatchOperand_Success) return Error(getLexer().getLoc(), "failed parsing operand."); } return false; } //===----------------------------------------------------------------------===// // Utility functions //===----------------------------------------------------------------------===// AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseIntWithPrefix(const char *Prefix, int64_t &Int, int64_t Default) { // We are at the end of the statement, and this is a default argument, so // use a default value. if (getLexer().is(AsmToken::EndOfStatement)) { Int = Default; return MatchOperand_Success; } switch(getLexer().getKind()) { default: return MatchOperand_NoMatch; case AsmToken::Identifier: { StringRef OffsetName = Parser.getTok().getString(); if (!OffsetName.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) { SMLoc S = Parser.getTok().getLoc(); int64_t Offset = 0; AMDGPUAsmParser::OperandMatchResultTy Res = parseIntWithPrefix(Prefix, Offset); if (Res != MatchOperand_Success) return Res; Operands.push_back(AMDGPUOperand::CreateImm(Offset, 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; } static bool operandsHasOptionalOp(const OperandVector &Operands, const OptionalOperand &OOp) { for (unsigned i = 0; i < Operands.size(); i++) { const AMDGPUOperand &ParsedOp = ((const AMDGPUOperand &)*Operands[i]); if ((ParsedOp.isImm() && ParsedOp.getImmTy() == OOp.Type) || (ParsedOp.isToken() && ParsedOp.getToken() == OOp.Name)) return true; } return false; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseOptionalOps(const ArrayRef &OptionalOps, OperandVector &Operands) { SMLoc S = Parser.getTok().getLoc(); for (const OptionalOperand &Op : OptionalOps) { if (operandsHasOptionalOp(Operands, Op)) continue; AMDGPUAsmParser::OperandMatchResultTy Res; int64_t Value; if (Op.IsBit) { Res = parseNamedBit(Op.Name, Operands, Op.Type); if (Res == MatchOperand_NoMatch) continue; return Res; } Res = parseIntWithPrefix(Op.Name, Value, Op.Default); if (Res == MatchOperand_NoMatch) continue; if (Res != MatchOperand_Success) return Res; if (Op.ConvertResult && !Op.ConvertResult(Value)) { return MatchOperand_ParseFail; } Operands.push_back(AMDGPUOperand::CreateImm(Value, S, Op.Type)); return MatchOperand_Success; } return MatchOperand_NoMatch; } //===----------------------------------------------------------------------===// // ds //===----------------------------------------------------------------------===// static const OptionalOperand DSOptionalOps [] = { {"offset", AMDGPUOperand::ImmTyOffset, false, 0, nullptr}, {"gds", AMDGPUOperand::ImmTyGDS, true, 0, nullptr} }; static const OptionalOperand DSOptionalOpsOff01 [] = { {"offset0", AMDGPUOperand::ImmTyDSOffset0, false, 0, nullptr}, {"offset1", AMDGPUOperand::ImmTyDSOffset1, false, 0, nullptr}, {"gds", AMDGPUOperand::ImmTyGDS, true, 0, nullptr} }; AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseDSOptionalOps(OperandVector &Operands) { return parseOptionalOps(DSOptionalOps, Operands); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseDSOff01OptionalOps(OperandVector &Operands) { return parseOptionalOps(DSOptionalOpsOff01, Operands); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseDSOffsetOptional(OperandVector &Operands) { SMLoc S = Parser.getTok().getLoc(); AMDGPUAsmParser::OperandMatchResultTy Res = parseIntWithPrefix("offset", Operands, AMDGPUOperand::ImmTyOffset); if (Res == MatchOperand_NoMatch) { Operands.push_back(AMDGPUOperand::CreateImm(0, S, AMDGPUOperand::ImmTyOffset)); Res = MatchOperand_Success; } return Res; } bool AMDGPUOperand::isDSOffset() const { return isImm() && isUInt<16>(getImm()); } bool AMDGPUOperand::isDSOffset01() const { return isImm() && isUInt<8>(getImm()); } void AMDGPUAsmParser::cvtDSOffset01(MCInst &Inst, const OperandVector &Operands) { std::map 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; } unsigned Offset0Idx = OptionalIdx[AMDGPUOperand::ImmTyDSOffset0]; unsigned Offset1Idx = OptionalIdx[AMDGPUOperand::ImmTyDSOffset1]; unsigned GDSIdx = OptionalIdx[AMDGPUOperand::ImmTyGDS]; ((AMDGPUOperand &)*Operands[Offset0Idx]).addImmOperands(Inst, 1); // offset0 ((AMDGPUOperand &)*Operands[Offset1Idx]).addImmOperands(Inst, 1); // offset1 ((AMDGPUOperand &)*Operands[GDSIdx]).addImmOperands(Inst, 1); // gds 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; } unsigned OffsetIdx = OptionalIdx[AMDGPUOperand::ImmTyOffset]; ((AMDGPUOperand &)*Operands[OffsetIdx]).addImmOperands(Inst, 1); // offset if (!GDSOnly) { unsigned GDSIdx = OptionalIdx[AMDGPUOperand::ImmTyGDS]; ((AMDGPUOperand &)*Operands[GDSIdx]).addImmOperands(Inst, 1); // gds } 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 = 0x7; 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 [10:8] int64_t CntVal = 0x77f; 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 AMDGPUOperand::isSWaitCnt() const { return isImm(); } //===----------------------------------------------------------------------===// // 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; } } //===----------------------------------------------------------------------===// // flat //===----------------------------------------------------------------------===// static const OptionalOperand FlatOptionalOps [] = { {"glc", AMDGPUOperand::ImmTyGLC, true, 0, nullptr}, {"slc", AMDGPUOperand::ImmTySLC, true, 0, nullptr}, {"tfe", AMDGPUOperand::ImmTyTFE, true, 0, nullptr} }; static const OptionalOperand FlatAtomicOptionalOps [] = { {"slc", AMDGPUOperand::ImmTySLC, true, 0, nullptr}, {"tfe", AMDGPUOperand::ImmTyTFE, true, 0, nullptr} }; AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseFlatOptionalOps(OperandVector &Operands) { return parseOptionalOps(FlatOptionalOps, Operands); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseFlatAtomicOptionalOps(OperandVector &Operands) { return parseOptionalOps(FlatAtomicOptionalOps, Operands); } void AMDGPUAsmParser::cvtFlat(MCInst &Inst, const OperandVector &Operands) { std::map 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 'glc' token which is sometimes hard-coded into the // asm string. There are no MCInst operands for these. if (Op.isToken()) continue; // Handle optional arguments OptionalIdx[Op.getImmTy()] = i; } // flat atomic instructions don't have a glc argument. if (OptionalIdx.count(AMDGPUOperand::ImmTyGLC)) { unsigned GLCIdx = OptionalIdx[AMDGPUOperand::ImmTyGLC]; ((AMDGPUOperand &)*Operands[GLCIdx]).addImmOperands(Inst, 1); } unsigned SLCIdx = OptionalIdx[AMDGPUOperand::ImmTySLC]; unsigned TFEIdx = OptionalIdx[AMDGPUOperand::ImmTyTFE]; ((AMDGPUOperand &)*Operands[SLCIdx]).addImmOperands(Inst, 1); ((AMDGPUOperand &)*Operands[TFEIdx]).addImmOperands(Inst, 1); } //===----------------------------------------------------------------------===// // mubuf //===----------------------------------------------------------------------===// static const OptionalOperand MubufOptionalOps [] = { {"offset", AMDGPUOperand::ImmTyOffset, false, 0, nullptr}, {"glc", AMDGPUOperand::ImmTyGLC, true, 0, nullptr}, {"slc", AMDGPUOperand::ImmTySLC, true, 0, nullptr}, {"tfe", AMDGPUOperand::ImmTyTFE, true, 0, nullptr} }; AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseMubufOptionalOps(OperandVector &Operands) { return parseOptionalOps(MubufOptionalOps, Operands); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseOffset(OperandVector &Operands) { return parseIntWithPrefix("offset", Operands); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseGLC(OperandVector &Operands) { return parseNamedBit("glc", Operands); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseSLC(OperandVector &Operands) { return parseNamedBit("slc", Operands); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseTFE(OperandVector &Operands) { return parseNamedBit("tfe", Operands); } bool AMDGPUOperand::isMubufOffset() const { return isImm() && isUInt<12>(getImm()); } void AMDGPUAsmParser::cvtMubuf(MCInst &Inst, const OperandVector &Operands) { std::map 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 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; } assert(OptionalIdx.size() == 4); unsigned OffsetIdx = OptionalIdx[AMDGPUOperand::ImmTyOffset]; unsigned GLCIdx = OptionalIdx[AMDGPUOperand::ImmTyGLC]; unsigned SLCIdx = OptionalIdx[AMDGPUOperand::ImmTySLC]; unsigned TFEIdx = OptionalIdx[AMDGPUOperand::ImmTyTFE]; ((AMDGPUOperand &)*Operands[OffsetIdx]).addImmOperands(Inst, 1); ((AMDGPUOperand &)*Operands[GLCIdx]).addImmOperands(Inst, 1); ((AMDGPUOperand &)*Operands[SLCIdx]).addImmOperands(Inst, 1); ((AMDGPUOperand &)*Operands[TFEIdx]).addImmOperands(Inst, 1); } //===----------------------------------------------------------------------===// // mimg //===----------------------------------------------------------------------===// AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseDMask(OperandVector &Operands) { return parseIntWithPrefix("dmask", Operands); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseUNorm(OperandVector &Operands) { return parseNamedBit("unorm", Operands); } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseR128(OperandVector &Operands) { return parseNamedBit("r128", Operands); } //===----------------------------------------------------------------------===// // 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()); } //===----------------------------------------------------------------------===// // 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 const OptionalOperand VOP3OptionalOps [] = { {"clamp", AMDGPUOperand::ImmTyClamp, true, 0, nullptr}, {"mul", AMDGPUOperand::ImmTyOMod, false, 1, ConvertOmodMul}, {"div", AMDGPUOperand::ImmTyOMod, false, 1, ConvertOmodDiv}, }; static bool isVOP3(OperandVector &Operands) { if (operandsHaveModifiers(Operands)) return true; AMDGPUOperand &DstOp = ((AMDGPUOperand&)*Operands[1]); if (DstOp.isReg() && DstOp.isRegClass(AMDGPU::SGPR_64RegClassID)) return true; if (Operands.size() >= 5) return true; if (Operands.size() > 3) { AMDGPUOperand &Src1Op = ((AMDGPUOperand&)*Operands[3]); if (Src1Op.getReg() && (Src1Op.isRegClass(AMDGPU::SReg_32RegClassID) || Src1Op.isRegClass(AMDGPU::SReg_64RegClassID))) return true; } return false; } AMDGPUAsmParser::OperandMatchResultTy AMDGPUAsmParser::parseVOP3OptionalOps(OperandVector &Operands) { // The value returned by this function may change after parsing // an operand so store the original value here. bool HasModifiers = operandsHaveModifiers(Operands); bool IsVOP3 = isVOP3(Operands); if (HasModifiers || IsVOP3 || getLexer().isNot(AsmToken::EndOfStatement) || getForcedEncodingSize() == 64) { AMDGPUAsmParser::OperandMatchResultTy Res = parseOptionalOps(VOP3OptionalOps, Operands); if (!HasModifiers && Res == MatchOperand_Success) { // We have added a modifier operation, so we need to make sure all // previous register operands have modifiers for (unsigned i = 2, e = Operands.size(); i != e; ++i) { AMDGPUOperand &Op = ((AMDGPUOperand&)*Operands[i]); if (Op.isReg()) Op.setModifiers(0); } } return Res; } return MatchOperand_NoMatch; } void AMDGPUAsmParser::cvtVOP3(MCInst &Inst, const OperandVector &Operands) { unsigned i = 1; const MCInstrDesc &Desc = MII.get(Inst.getOpcode()); if (Desc.getNumDefs() > 0) { ((AMDGPUOperand &)*Operands[i++]).addRegOperands(Inst, 1); } std::map OptionalIdx; if (operandsHaveModifiers(Operands)) { for (unsigned e = Operands.size(); i != e; ++i) { AMDGPUOperand &Op = ((AMDGPUOperand &)*Operands[i]); if (Op.isRegWithInputMods()) { ((AMDGPUOperand &)*Operands[i]).addRegWithInputModsOperands(Inst, 2); continue; } OptionalIdx[Op.getImmTy()] = i; } unsigned ClampIdx = OptionalIdx[AMDGPUOperand::ImmTyClamp]; unsigned OModIdx = OptionalIdx[AMDGPUOperand::ImmTyOMod]; ((AMDGPUOperand &)*Operands[ClampIdx]).addImmOperands(Inst, 1); ((AMDGPUOperand &)*Operands[OModIdx]).addImmOperands(Inst, 1); } else { for (unsigned e = Operands.size(); i != e; ++i) ((AMDGPUOperand &)*Operands[i]).addRegOrImmOperands(Inst, 1); } } /// Force static initialization. extern "C" void LLVMInitializeAMDGPUAsmParser() { RegisterMCAsmParser A(TheAMDGPUTarget); RegisterMCAsmParser B(TheGCNTarget); } #define GET_REGISTER_MATCHER #define GET_MATCHER_IMPLEMENTATION #include "AMDGPUGenAsmMatcher.inc"