[llvm-branch-commits] [llvm] [3/7][PISA] Add PISA register file, instruction set, and MC layer (PR #214107)
Michal Paszkowski via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Aug 4 17:51:27 PDT 2026
https://github.com/michalpaszkowski created https://github.com/llvm/llvm-project/pull/214107
Add the PISA register file and register-bank descriptions, the instruction
formats and definitions, and the MC/assembly layer (instruction printer,
register and code encoders, target streamer, MC target description and the
supporting enums), replacing the stub descriptions from
the initial target skeleton.
This provides the instruction-set description and assembly-emission
foundation. The GlobalISel lowering and instruction selection that consume it
are added in the following changes.
>From 5650cd1f8d730e00925eca81dc2396e25aa2e6ce Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Mon, 3 Aug 2026 03:04:59 -0700
Subject: [PATCH 1/2] Add PISA register file, instruction set, and MC layer
Add the PISA register file and register-bank descriptions, the instruction
formats and definitions, and the MC/assembly layer (instruction printer,
register and code encoders, target streamer, MC target description and the
supporting enums), replacing the stub descriptions from
the initial target skeleton.
This provides the instruction-set description and assembly-emission
foundation. The GlobalISel lowering and instruction selection that consume it
are added in the following changes.
---
llvm/include/llvm/IR/CallingConv.h | 4 +
llvm/include/llvm/MC/MCInst.h | 20 +
.../llvm/Target/PISA/MnemonicOperand.td | 344 +++
llvm/lib/MC/MCInst.cpp | 2 +
llvm/lib/Target/PISA/CMakeLists.txt | 14 +-
.../Target/PISA/MCTargetDesc/CMakeLists.txt | 5 +
.../Target/PISA/MCTargetDesc/PISABaseInfo.h | 44 +
.../lib/Target/PISA/MCTargetDesc/PISAEnum.cpp | 19 +
llvm/lib/Target/PISA/MCTargetDesc/PISAEnum.h | 65 +
.../PISA/MCTargetDesc/PISAInstPrinter.cpp | 405 ++-
.../PISA/MCTargetDesc/PISAInstPrinter.h | 86 +-
.../Target/PISA/MCTargetDesc/PISAMCExpr.cpp | 31 +
.../lib/Target/PISA/MCTargetDesc/PISAMCExpr.h | 54 +
.../PISA/MCTargetDesc/PISAMCTargetDesc.cpp | 27 +-
.../PISA/MCTargetDesc/PISAMCTargetDesc.h | 26 +-
.../PISA/MCTargetDesc/PISARegEncoder.cpp | 183 ++
.../Target/PISA/MCTargetDesc/PISARegEncoder.h | 73 +
.../PISA/MCTargetDesc/PISATargetStreamer.cpp | 572 ++++
.../PISA/MCTargetDesc/PISATargetStreamer.h | 286 ++
llvm/lib/Target/PISA/PISA.td | 22 +-
llvm/lib/Target/PISA/PISACacheCtrlMMRA.h | 92 +
llvm/lib/Target/PISA/PISACombine.td | 1075 +++++++
llvm/lib/Target/PISA/PISADefines.h | 52 +
llvm/lib/Target/PISA/PISAGenericOpcodes.td | 99 +
llvm/lib/Target/PISA/PISAHelpers.h | 38 +
llvm/lib/Target/PISA/PISAInstrFormats.td | 834 ++++++
llvm/lib/Target/PISA/PISAInstrInfo.cpp | 449 ++-
llvm/lib/Target/PISA/PISAInstrInfo.h | 32 +-
llvm/lib/Target/PISA/PISAInstrInfo.td | 2522 ++++++++++++++++-
llvm/lib/Target/PISA/PISAMCInstLower.cpp | 134 +
llvm/lib/Target/PISA/PISAMCInstLower.h | 103 +
llvm/lib/Target/PISA/PISARegManager.cpp | 62 +
llvm/lib/Target/PISA/PISARegManager.h | 47 +
llvm/lib/Target/PISA/PISARegisterBanks.td | 15 +
llvm/lib/Target/PISA/PISARegisterInfo.cpp | 272 +-
llvm/lib/Target/PISA/PISARegisterInfo.h | 55 +
llvm/lib/Target/PISA/PISARegisterInfo.td | 260 +-
llvm/lib/Target/PISA/PISAUtils.cpp | 52 +
llvm/lib/Target/PISA/PISAUtils.h | 36 +
39 files changed, 8448 insertions(+), 63 deletions(-)
create mode 100644 llvm/include/llvm/Target/PISA/MnemonicOperand.td
create mode 100644 llvm/lib/Target/PISA/MCTargetDesc/PISABaseInfo.h
create mode 100644 llvm/lib/Target/PISA/MCTargetDesc/PISAEnum.cpp
create mode 100644 llvm/lib/Target/PISA/MCTargetDesc/PISAEnum.h
create mode 100644 llvm/lib/Target/PISA/MCTargetDesc/PISAMCExpr.cpp
create mode 100644 llvm/lib/Target/PISA/MCTargetDesc/PISAMCExpr.h
create mode 100644 llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.cpp
create mode 100644 llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.h
create mode 100644 llvm/lib/Target/PISA/MCTargetDesc/PISATargetStreamer.cpp
create mode 100644 llvm/lib/Target/PISA/MCTargetDesc/PISATargetStreamer.h
create mode 100644 llvm/lib/Target/PISA/PISACacheCtrlMMRA.h
create mode 100644 llvm/lib/Target/PISA/PISACombine.td
create mode 100644 llvm/lib/Target/PISA/PISADefines.h
create mode 100644 llvm/lib/Target/PISA/PISAGenericOpcodes.td
create mode 100644 llvm/lib/Target/PISA/PISAHelpers.h
create mode 100644 llvm/lib/Target/PISA/PISAInstrFormats.td
create mode 100644 llvm/lib/Target/PISA/PISAMCInstLower.cpp
create mode 100644 llvm/lib/Target/PISA/PISAMCInstLower.h
create mode 100644 llvm/lib/Target/PISA/PISARegManager.cpp
create mode 100644 llvm/lib/Target/PISA/PISARegManager.h
create mode 100644 llvm/lib/Target/PISA/PISARegisterBanks.td
create mode 100644 llvm/lib/Target/PISA/PISAUtils.cpp
create mode 100644 llvm/lib/Target/PISA/PISAUtils.h
diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h
index 249c512dda532..7992dac627a76 100644
--- a/llvm/include/llvm/IR/CallingConv.h
+++ b/llvm/include/llvm/IR/CallingConv.h
@@ -297,6 +297,9 @@ namespace CallingConv {
/// stateless compartment.
CHERIoT_LibraryCall = 127,
+ /// Used for PISA kernel functions.
+ PISA_KERNEL = 183,
+
/// The highest possible ID. Must be some 2^k - 1.
MaxID = 1023
};
@@ -323,6 +326,7 @@ constexpr bool isCallableCC(CallingConv::ID CC) {
case CallingConv::AMDGPU_PS:
case CallingConv::AMDGPU_VS:
case CallingConv::SPIR_KERNEL:
+ case CallingConv::PISA_KERNEL:
case CallingConv::PTX_Kernel:
return false;
default:
diff --git a/llvm/include/llvm/MC/MCInst.h b/llvm/include/llvm/MC/MCInst.h
index b0db6b8408da5..adba42f4ac1fa 100644
--- a/llvm/include/llvm/MC/MCInst.h
+++ b/llvm/include/llvm/MC/MCInst.h
@@ -42,6 +42,7 @@ class MCOperand {
kInvalid, ///< Uninitialized.
kRegister, ///< Register operand.
kImmediate, ///< Immediate operand.
+ kHFPImmediate, ///< Half-floating-point immediate operand.
kSFPImmediate, ///< Single-floating-point immediate operand.
kDFPImmediate, ///< Double-Floating-point immediate operand.
kExpr, ///< Relocatable immediate operand.
@@ -52,6 +53,7 @@ class MCOperand {
union {
unsigned RegVal;
int64_t ImmVal;
+ uint16_t HFPImmVal;
uint32_t SFPImmVal;
uint64_t FPImmVal;
const MCExpr *ExprVal;
@@ -64,6 +66,7 @@ class MCOperand {
bool isValid() const { return Kind != kInvalid; }
bool isReg() const { return Kind == kRegister; }
bool isImm() const { return Kind == kImmediate; }
+ bool isHFPImm() const { return Kind == kHFPImmediate; }
bool isSFPImm() const { return Kind == kSFPImmediate; }
bool isDFPImm() const { return Kind == kDFPImmediate; }
bool isExpr() const { return Kind == kExpr; }
@@ -91,6 +94,16 @@ class MCOperand {
ImmVal = Val;
}
+ uint16_t getHFPImm() const {
+ assert(isHFPImm() && "This is not an HFP immediate");
+ return HFPImmVal;
+ }
+
+ void setHFPImm(uint16_t Val) {
+ assert(isHFPImm() && "This is not an HFP immediate");
+ HFPImmVal = Val;
+ }
+
uint32_t getSFPImm() const {
assert(isSFPImm() && "This is not an SFP immediate");
return SFPImmVal;
@@ -149,6 +162,13 @@ class MCOperand {
return Op;
}
+ static MCOperand createHFPImm(uint16_t Val) {
+ MCOperand Op;
+ Op.Kind = kHFPImmediate;
+ Op.HFPImmVal = Val;
+ return Op;
+ }
+
static MCOperand createSFPImm(uint32_t Val) {
MCOperand Op;
Op.Kind = kSFPImmediate;
diff --git a/llvm/include/llvm/Target/PISA/MnemonicOperand.td b/llvm/include/llvm/Target/PISA/MnemonicOperand.td
new file mode 100644
index 0000000000000..ecdfe71282df5
--- /dev/null
+++ b/llvm/include/llvm/Target/PISA/MnemonicOperand.td
@@ -0,0 +1,344 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// DAG operator used for dag representation of a condition flag: (cflag FlagName value)
+// These are used in EnumOptionOpnd to represent certain
+def cflag;
+
+// Function to find index of a item from a list of string.
+// Note that tablegen does not allow recursive definition.
+class getIndexFromList<list<string> slist, string item> {
+ int Idx = !cond(!and(!ge(!size(slist),1), !eq(slist[0],item)) : 0,
+ !and(!ge(!size(slist),2), !eq(slist[1],item)) : 1,
+ !and(!ge(!size(slist),3), !eq(slist[2],item)) : 2,
+ !and(!ge(!size(slist),4), !eq(slist[3],item)) : 3,
+ !and(!ge(!size(slist),5), !eq(slist[4],item)) : 4,
+ !and(!ge(!size(slist),6), !eq(slist[5],item)) : 5,
+ !and(!ge(!size(slist),7), !eq(slist[6],item)) : 6,
+ !and(!ge(!size(slist),8), !eq(slist[7],item)) : 7,
+ !and(!ge(!size(slist),9), !eq(slist[8],item)) : 8,
+ !and(!ge(!size(slist),10),!eq(slist[9],item)) : 9,
+ !and(!ge(!size(slist),11),!eq(slist[10],item)) : 10,
+ !and(!ge(!size(slist),12),!eq(slist[11],item)) : 11,
+ !and(!ge(!size(slist),13),!eq(slist[12],item)) : 12,
+ !and(!ge(!size(slist),14),!eq(slist[13],item)) : 13,
+ !and(!ge(!size(slist),15),!eq(slist[14],item)) : 14,
+ !and(!ge(!size(slist),16),!eq(slist[15],item)) : 15,
+ true : -1);
+}
+
+// Flags are encoded from msb - 1 - numflags to lsb
+class getShiftedFlag<list<string> cFlagNames, string flagName> {
+ defvar LsbIdx = !sub(!sub(!size(cFlagNames), 1), getIndexFromList<cFlagNames, flagName>.Idx);
+ bits<16> enc = !shl(1, LsbIdx);
+}
+
+// Get bits enabled for all flags in daglist regardless of the value.
+// e.g. if flag f1 is positioned at 2^2 and f2 in 2^0,
+// [(cflag "f1", true), (cflag "f2", false)] -> {... 0, 1, 0, 1}
+class EncodeCFlagMask<list<string> cFlagNames, list<dag> cFlags> {
+ bits<16> enc = !foldl(0, cFlags, acc, fdag,
+ !or(getShiftedFlag<cFlagNames, !getdagarg<string>(fdag, "name")>.enc, acc));
+}
+
+// Get bits enables for all flags in daglist if the value is true.
+// e.g. if flag f1 is positioned at 2^2 and f2 in 2^0,
+// [(cflag "f1", true), (cflag "f2", false)] -> {... 0, 1, 0, 0}
+class EncodeCFlag<list<string> cFlagNames, list<dag> cFlags> {
+ bits<16> enc = !foldl(0, cFlags, acc, fdag,
+ !or(!and(!getdagarg<bit>(fdag, "val"),
+ getShiftedFlag<cFlagNames, !getdagarg<string>(fdag, "name")>.enc),
+ acc));
+}
+
+
+// ----- Definitions for EnumOptions.
+// Base definitions for operands that have a value selected from a
+// set of limited options.
+
+// Convert list of flag bits into a single accumulated value in bits<16>
+class EncodeCFlagFromList<list<bit> bitList> {
+ bits<16> enc = !foldl(0, bitList, acc, flag, !or(!shl(acc, 1), flag));
+}
+
+// Enum to list all type of EnumOptions
+def EnumOptionClass: GenericEnum {
+ let FilterClass = "EnumOptionClassEntry";
+ let NameField = "EnumName";
+}
+
+// Lookup name string for enum types
+def EnumOptionStrTable : GenericTable {
+ let FilterClass = "EnumOptionClassEntry";
+ let CppTypeName = "EnumOptionStrEntry";
+ let Fields = ["Type", "Str"];
+ string TypeOf_Type = "EnumOptionClass";
+ let PrimaryKey = ["Type"];
+ let PrimaryKeyName = "lookupEnumOptionStr";
+}
+
+class EnumOptionClassEntry<string name> {
+ EnumOptionClassEntry Type = !cast<EnumOptionClassEntry>(NAME);
+ string EnumName = "EO_"#name;
+ string Str = name;
+}
+
+// Base class used to define per option enum entries
+class EnumOptionEntry<EnumOptionClassEntry ClassID, string name, int val, string enumPrefix="", string optStr=!tolower(name), string enumName="", list<bit> cFlags=[]> {
+ EnumOptionClassEntry OptClass = ClassID;
+ EnumOptionEntry EnumValue = !cast<EnumOptionEntry>(NAME);
+ string OptStr = optStr;
+
+ // Capitalize the name and add prefix for enum type
+ string EnumName = !cond(!not(!empty(enumName)) : enumName,
+ !empty(enumPrefix) : !toupper(name),
+ true : enumPrefix#"_"#!toupper(name));
+
+ // Need "bits<bit_size> Value" in all inheriting classes
+ bits<32> Value = val;
+
+ // Flags to classify the enum values
+ bits<16> CFlagEnc = EncodeCFlagFromList<cFlags>.enc;
+}
+
+// Store EnumOption related info
+class EnumOptionInfo<EnumOptionClassEntry OptID, ValueType vt, string enumPrefix,
+ bit isMandatory, int defaultVal,
+ Operand opnd, list<string> cFlagNames> {
+ EnumOptionClassEntry OptClassID = OptID;
+ ValueType VT = vt;
+ string EnumPrefix = enumPrefix;
+ bit IsMandatory = isMandatory;
+ int DefaultVal = defaultVal;
+ Operand Opnd = opnd;
+ list<string> CFlagNames = cFlagNames;
+}
+
+class EnumOptionEntryDef<EnumOptionInfo info, string name, int val, string optStr=!tolower(name), string enumName="">
+ : EnumOptionEntry<info.OptClassID, name, val, info.EnumPrefix, optStr, enumName>;
+
+def EnumOptionTable : GenericTable {
+ let FilterClass = "EnumOptionEntry";
+ let Fields = ["OptClass", "Value", "OptStr", "CFlagEnc"];
+ string TypeOf_OptClass = "EnumOptionClass";
+ // FIXME: Is there a way to get EnumName printed in the table
+ // rather than int literals?
+ let PrimaryKey = ["OptClass", "Value"];
+ let PrimaryKeyName = "lookupEnumOptionByValue";
+}
+
+def lookupEnumOptionByOptStr : SearchIndex {
+ let Table = EnumOptionTable;
+ let Key = ["OptClass", "OptStr"];
+}
+
+def lookupEnumOptionsWithClass : SearchIndex {
+ let Table = EnumOptionTable;
+ let Key = ["OptClass"];
+}
+
+// Base for defining enum and lookup tables
+multiclass EnumTypeDef<string EnumEntryClassName=NAME#"Entry", string enumPrefix=""> {
+ // Define enum definition
+ // User must define a entry class named as EnumEntryClassName by inheriting EnumOptionEntry
+ def NAME : GenericEnum {
+ let FilterClass = EnumEntryClassName;
+ let NameField = "EnumName";
+ let ValueField = "Value";
+ string EnumPrefix = enumPrefix;
+ }
+}
+
+class EnumOptionOpndPMC<string name,
+ string optClassStr,
+ bit isMandatory = false,
+ int defaultVal = 0,
+ bits<16> CFlagMask = 0,
+ bits<16> CFlagCond = 0> : AsmOperandClass {
+ let Name = name;
+ let ParserMethod = "parseEnumOptionOpnd("#optClassStr#","#isMandatory#","#defaultVal#")";
+ let ParserMethod = "parseEnumOptionOpnd("#optClassStr#","#isMandatory#","#CFlagMask#","#CFlagCond#","#defaultVal#")";
+ let PredicateMethod = "isEnumOptionOpnd("#optClassStr#")";
+ let RenderMethod = "addEnumOptionOpnd("#optClassStr#")";
+}
+
+class EnumOptionOpnd<ValueType vt,
+ string opndName,
+ EnumOptionClassEntry optClassID,
+ bit isMandatory = false,
+ int defaultVal = 0> : Operand<vt> {
+ // Mask that need to filter relevant flags
+ bits<16> CFlagMask = 0;
+ // Matched value after masking
+ bits<16> CFlagCond = 0;
+ let OperandType = "OPERAND_IMMEDIATE";
+ let PrintMethod = "printEnumOption("#optClassID.EnumName#","#isMandatory#","#defaultVal#")";
+ let ParserMatchClass = EnumOptionOpndPMC<opndName, optClassID.EnumName, isMandatory, defaultVal, CFlagMask, CFlagCond>;
+}
+
+multiclass EnumOptionOpndDef<
+ ValueType vt,
+ string enumPrefix,
+ bit isMandatory = false,
+ int defaultVal = 0,
+ list<string> cFlagNames = [],
+ string EnumEntryClassName = NAME#"Entry"> {
+ // Define enum type representing the enum class
+ defvar TypeName = NAME#"ClassID";
+ def TypeName : EnumOptionClassEntry<NAME>;
+ defvar optClassID = !cast<EnumOptionClassEntry>(TypeName);
+ defm NAME : EnumTypeDef<EnumEntryClassName, enumPrefix>;
+
+ defvar OpndName = NAME#"Opnd";
+ def OpndName
+ : EnumOptionOpnd<vt, EnumEntryClassName#"Opnd", optClassID, isMandatory, defaultVal> ;
+ def NAME#"EnumOptionInfo"
+ : EnumOptionInfo<optClassID, vt, enumPrefix,
+ isMandatory, defaultVal,
+ !cast<Operand>(OpndName), cFlagNames>;
+}
+
+class EnumOptionIDEntry;
+
+def EnumOptionID : GenericEnum {
+ let FilterClass = "EnumOptionIDEntry";
+}
+
+class EnumOptionTableEntry<string optstr>{
+ EnumOptionIDEntry OptID = !cast<EnumOptionIDEntry>(NAME);
+ string OptStr = optstr;
+}
+
+class EnumOptionOpndParserMatchClass<string OptID> : AsmOperandClass {
+ let Name = NAME;
+ let ParserMethod = "parseEnumOptionOpnd("#OptID#")";
+ let PredicateMethod = "isEnumOptionOpnd("#OptID#")";
+ let RenderMethod = "addEnumOptionOpnd("#OptID#")";
+}
+
+class EnumOptionOpndWithDefaultOps<
+ string OptID, EnumOptionOpndParserMatchClass pmc, bit isMandatory = false,
+ int defaultVal = 0> :
+ OperandWithDefaultOps<i32, (ops(i32 defaultVal))> {
+ let OperandType = "OPERAND_IMMEDIATE";
+ let MIOperandInfo = (ops i32imm);
+ let PrintMethod = "printEnumOption("#OptID#","#isMandatory#","#defaultVal#")";
+ let ParserMatchClass = pmc;
+}
+
+multiclass EnumOptionOpndWithDefaultOpsDef<
+ string enumPrefix, bit isMandatory = false, int defaultVal = 0,
+ string EnumEntryClassName = NAME#"Entry"> {
+ defvar TypeName = NAME#"ClassID";
+ def TypeName : EnumOptionClassEntry<NAME>;
+ defvar optClassID = !cast<EnumOptionClassEntry>(TypeName);
+ defm NAME : EnumTypeDef<EnumEntryClassName, enumPrefix>;
+
+ defvar OptID = "EO_"#NAME;
+ defvar OpndName = NAME#"Opnd";
+
+ def NAME#"OpndPMC" : EnumOptionOpndParserMatchClass<OptID> {
+ let ParserMethod = "parseEnumOptionOpnd("#OptID#","#isMandatory#",0,0,"#defaultVal#")";
+ }
+
+ def OpndName : EnumOptionOpndWithDefaultOps<
+ OptID, !cast<EnumOptionOpndParserMatchClass>(NAME#"OpndPMC"), isMandatory,
+ defaultVal>;
+}
+
+// Selects subset of given enum types to be used as an operand type.
+// cFlags are condition flags used to confine enum values to ones
+// that matches given flag name and values.
+// e.g. [(cflag "MyFlag1", true), (cflag "MyFlag2", false)] can be given
+// to only select enum values with MyFlag1 = true and MyFlag2 = false.
+class EnumOptionSubsetOpnd<EnumOptionInfo EOInfo, list<dag> cFlags>
+ : EnumOptionOpnd<EOInfo.VT, NAME#"Opnd", EOInfo.OptClassID, EOInfo.IsMandatory, EOInfo.DefaultVal> {
+ // This is used to mask only relevant flag bits this operand needs to match
+ let CFlagMask = EncodeCFlagMask<EOInfo.CFlagNames, cFlags>.enc;
+ // Flags values to be matched after masking.
+ let CFlagCond = EncodeCFlag<EOInfo.CFlagNames, cFlags>.enc;
+}
+
+// EnableFlag: Tells which bool value is valid for the option
+// 1: Accept only disabled, 2: Accept only enabled, 3: Accept both
+class BoolOptionOpndParserMatchClass<string OptID, int EnableFlag=3> : AsmOperandClass {
+ let Name = NAME;
+ let ParserMethod = "parseBoolOptionOpnd("#OptID#", "#EnableFlag#")";
+ let PredicateMethod = "isBoolOptionOpnd("#OptID#","#EnableFlag#")";
+ let RenderMethod = "addBoolOptionOpnd("#OptID#")";
+}
+
+class BoolOptionOpnd<string OptID, BoolOptionOpndParserMatchClass pmc> : Operand<i16> {
+ let OperandType = "OPERAND_IMMEDIATE";
+
+ // Pass optString as name
+ let PrintMethod = "printBoolOptionOpnd("#OptID#")";
+ let ParserMatchClass = pmc;
+}
+
+class BoolOptionOpndWithDefaultOps<string OptID, BoolOptionOpndParserMatchClass pmc, bit defaultVal = 0> : OperandWithDefaultOps<i16, (ops(i16 defaultVal))> {
+ let OperandType = "OPERAND_IMMEDIATE";
+
+ // Pass optString as name
+ let PrintMethod = "printBoolOptionOpnd("#OptID#")";
+ let ParserMatchClass = pmc;
+}
+
+// Assign IDs for each bool options
+def BoolOptionID : GenericEnum {
+ let FilterClass = "BoolOptionIDEntry";
+}
+
+// Table with option id to option string mapping
+def BoolOptionTable : GenericTable {
+ let FilterClass = "BoolOptionTableEntry";
+ let Fields = ["OptID", "OptStr"];
+ string TypeOf_OptID = "BoolOptionID";
+ let PrimaryKey = ["OptID"];
+ let PrimaryKeyName = "lookupBoolOptionByID";
+}
+
+def lookupBoolOptionByOptStr : SearchIndex {
+ let Table = BoolOptionTable;
+ let Key = ["OptStr"];
+}
+
+class BoolOptionIDEntry;
+
+class BoolOptionTableEntry<string optstr>{
+ BoolOptionIDEntry OptID = !cast<BoolOptionIDEntry>(NAME);
+ string OptStr = optstr;
+}
+
+// Dummy entry to ensure BoolOptionTable is never empty
+def BO_DUMMY_ : BoolOptionIDEntry, BoolOptionTableEntry<"">;
+
+multiclass DefBoolOptionOpndType<string OptID, int EnableFlag = 3> {
+ defvar PMCName = NAME#"OpndPMC";
+ def PMCName : BoolOptionOpndParserMatchClass<OptID, EnableFlag>;
+ def NAME#"Opnd" : BoolOptionOpnd<OptID, !cast<BoolOptionOpndParserMatchClass>(PMCName)>;
+}
+
+// DisabledOpnd type does not accept option but creates MIR operand with false value
+// EnabledOpnd mandates optional field to be present and create true operand
+multiclass BoolOptionOpndDef<string OptStr=!tolower(NAME), bit DefineDerivedOpnds = false> {
+ def "BO_"#NAME : BoolOptionIDEntry, BoolOptionTableEntry<OptStr>;
+ defvar OptID = "BO_"#NAME;
+ defm NAME : DefBoolOptionOpndType<OptID, 3>;
+ if DefineDerivedOpnds then {
+ defm NAME#"Disabled" : DefBoolOptionOpndType<OptID, 1>;
+ defm NAME#"Enabled" : DefBoolOptionOpndType<OptID, 2>;
+ }
+}
+
+multiclass BoolOptionOpndWithDefaultOpsDef<string OptStr=!tolower(NAME), bit defaultVal = 0> {
+ def "BO_"#NAME : BoolOptionIDEntry, BoolOptionTableEntry<OptStr>;
+ defvar OptID = "BO_"#NAME;
+ def NAME#"OpndPMC" : BoolOptionOpndParserMatchClass<OptID>;
+ def NAME#"Opnd" : BoolOptionOpndWithDefaultOps<OptID, !cast<BoolOptionOpndParserMatchClass>(NAME#"OpndPMC"), defaultVal>;
+}
diff --git a/llvm/lib/MC/MCInst.cpp b/llvm/lib/MC/MCInst.cpp
index f29cb48a16ece..7684f872c90fd 100644
--- a/llvm/lib/MC/MCInst.cpp
+++ b/llvm/lib/MC/MCInst.cpp
@@ -32,6 +32,8 @@ void MCOperand::print(raw_ostream &OS, const MCContext *Ctx) const {
OS << getReg().id();
} else if (isImm())
OS << "Imm:" << getImm();
+ else if (isHFPImm())
+ OS << "HFPImm:" << getHFPImm();
else if (isSFPImm())
OS << "SFPImm:" << bit_cast<float>(getSFPImm());
else if (isDFPImm())
diff --git a/llvm/lib/Target/PISA/CMakeLists.txt b/llvm/lib/Target/PISA/CMakeLists.txt
index fafef2a3c3fe8..5c3bfe6ec02bf 100644
--- a/llvm/lib/Target/PISA/CMakeLists.txt
+++ b/llvm/lib/Target/PISA/CMakeLists.txt
@@ -1,36 +1,46 @@
add_llvm_component_group(PISA)
set(LLVM_TARGET_DEFINITIONS PISA.td)
-
tablegen(LLVM PISAGenAsmWriter.inc -gen-asm-writer)
tablegen(LLVM PISAGenInstrInfo.inc -gen-instr-info)
+tablegen(LLVM PISAGenMCCodeEmitter.inc -gen-emitter)
tablegen(LLVM PISAGenRegisterInfo.inc -gen-register-info)
tablegen(LLVM PISAGenSubtargetInfo.inc -gen-subtarget)
+tablegen(LLVM PISAGenSearchableTables.inc -gen-searchable-tables)
add_public_tablegen_target(PISACommonTableGen)
add_llvm_target(PISACodeGen
PISAInstrInfo.cpp
+ PISAMCInstLower.cpp
+ PISARegManager.cpp
PISARegisterInfo.cpp
PISASubtarget.cpp
PISATargetMachine.cpp
+ PISAUtils.cpp
LINK_COMPONENTS
Analysis
+ AsmPrinter
CodeGen
CodeGenTypes
Core
+ Demangle
+ GlobalISel
MC
+ Passes
+ Scalar
SelectionDAG
Support
Target
TargetParser
+ TransformUtils
PISADesc
PISAInfo
ADD_TO_COMPONENT
PISA
- )
+)
add_subdirectory(MCTargetDesc)
add_subdirectory(TargetInfo)
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/PISA/MCTargetDesc/CMakeLists.txt
index 1e7639e93d484..80a21ad6c6aa2 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/CMakeLists.txt
+++ b/llvm/lib/Target/PISA/MCTargetDesc/CMakeLists.txt
@@ -1,7 +1,11 @@
add_llvm_component_library(LLVMPISADesc
+ PISAEnum.cpp
PISAMCAsmInfo.cpp
PISAMCTargetDesc.cpp
+ PISATargetStreamer.cpp
PISAInstPrinter.cpp
+ PISAMCExpr.cpp
+ PISARegEncoder.cpp
LINK_COMPONENTS
CodeGenTypes
@@ -14,3 +18,4 @@ add_llvm_component_library(LLVMPISADesc
ADD_TO_COMPONENT
PISA
)
+
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISABaseInfo.h b/llvm/lib/Target/PISA/MCTargetDesc/PISABaseInfo.h
new file mode 100644
index 0000000000000..a396a106f28e7
--- /dev/null
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISABaseInfo.h
@@ -0,0 +1,44 @@
+//===-- PISABaseInfo.h - Top level PISA definitions -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISABASEINFO_H
+#define LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISABASEINFO_H
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace llvm {
+
+// Return a string representation of the operands from startIndex onwards.
+// Templated to allow both MachineInstr and MCInst to use the same logic.
+template <class InstType>
+std::string getPISAStringOperand(const InstType &MI, unsigned StartIndex) {
+ std::string S; // Iteratively append to this string.
+
+ const unsigned NumOps = MI.getNumOperands();
+ bool IsFinished = false;
+ for (unsigned I = StartIndex; I < NumOps && !IsFinished; ++I) {
+ const auto &Op = MI.getOperand(I);
+ if (!Op.isImm()) // Stop if we hit a register operand.
+ break;
+ assert((Op.getImm() >> 32) == 0 && "Imm operand should be i32 word");
+ const uint32_t Imm = Op.getImm(); // Each i32 word is up to 4 characters.
+ for (unsigned ShiftAmount = 0; ShiftAmount < 32; ShiftAmount += 8) {
+ char C = (Imm >> ShiftAmount) & 0xff;
+ if (C == 0) { // Stop if we hit a null-terminator character.
+ IsFinished = true;
+ break;
+ }
+ S += C; // Otherwise, append the character to the result string.
+ }
+ }
+ return S;
+}
+} // namespace llvm
+#endif // LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISABASEINFO_H
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAEnum.cpp b/llvm/lib/Target/PISA/MCTargetDesc/PISAEnum.cpp
new file mode 100644
index 0000000000000..4dd7d019bb66f
--- /dev/null
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAEnum.cpp
@@ -0,0 +1,19 @@
+//===-- PISAEnum.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MCTargetDesc/PISAEnum.h"
+
+namespace llvm {
+namespace PISA {
+
+#define GET_BoolOptionTable_IMPL
+#define GET_EnumOptionTable_IMPL
+#include "PISAGenSearchableTables.inc"
+
+} // namespace PISA
+} // namespace llvm
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAEnum.h b/llvm/lib/Target/PISA/MCTargetDesc/PISAEnum.h
new file mode 100644
index 0000000000000..77c47b724c27c
--- /dev/null
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAEnum.h
@@ -0,0 +1,65 @@
+//===-- PISAEnum.h --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAENUM_H
+#define LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAENUM_H
+
+#include "MCTargetDesc/PISAMCExpr.h"
+#include "MCTargetDesc/PISAMCTargetDesc.h"
+#include "MCTargetDesc/PISARegEncoder.h"
+#include "MCTargetDesc/PISATargetStreamer.h"
+#include "PISADefines.h"
+#include "PISAMCInstLower.h"
+#include "TargetInfo/PISATargetInfo.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringTable.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCParser/AsmLexer.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCValue.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm::PISA;
+
+namespace llvm {
+namespace PISA {
+
+#define GET_BoolOptionID_DECL
+#define GET_EnumOptionClass_DECL
+#define GET_LoadCacheControl_DECL
+#define GET_StoreCacheControl_DECL
+#define GET_AtomicCacheControl_DECL
+#include "PISAGenSearchableTables.inc"
+
+struct BoolOptionTableEntry {
+ BoolOptionID OptID;
+ StringTable::Offset OptStr;
+};
+
+struct EnumOptionEntry {
+ EnumOptionClass OptClass;
+ unsigned Value;
+ StringTable::Offset OptStr;
+ unsigned CFlags;
+};
+
+#define GET_BoolOptionTable_DECL
+#define GET_EnumOptionTable_DECL
+#include "PISAGenSearchableTables.inc"
+
+} // namespace PISA
+} // namespace llvm
+#endif // LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAENUM_H
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.cpp b/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.cpp
index f16a2e1177fc8..3d39eed23963c 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.cpp
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.cpp
@@ -7,29 +7,412 @@
//===----------------------------------------------------------------------===//
#include "PISAInstPrinter.h"
+#include "MCTargetDesc/PISARegEncoder.h"
+#include "PISA.h"
+#include "PISABaseInfo.h"
+#include "PISAEnum.h"
+#include "PISAInstrInfo.h"
+#include "PISAMCExpr.h"
+#include "PISAMCInstLower.h"
+#include "PISAUtils.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/CodeGen/Register.h"
+#include "llvm/IR/PISAIntrinsicUtils.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
-#include "llvm/Support/raw_ostream.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCSymbol.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormattedStream.h"
using namespace llvm;
+using namespace llvm::PISA;
-#include "PISAGenAsmWriter.inc"
+#define DEBUG_TYPE "asm-printer"
-void PISAInstPrinter::printRegName(raw_ostream &OS, MCRegister Reg) {
- OS << getRegisterName(Reg);
-}
+static cl::opt<bool>
+ AlwaysPrintDefaultValue("pisa-always-print-default-value",
+ cl::desc("Always print default value"),
+ cl::init(false), cl::ReallyHidden);
+
+// Include the auto-generated portion of the assembly writer.
+#include "PISAGenAsmWriter.inc"
void PISAInstPrinter::printInst(const MCInst *MI, uint64_t Address,
StringRef Annot, const MCSubtargetInfo &STI,
raw_ostream &OS) {
+ this->STI = &STI;
+
printInstruction(MI, Address, OS);
+
printAnnotation(OS, Annot);
}
-void PISAInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
+void PISAInstPrinter::printRegName(raw_ostream &OS, MCRegister Reg) {
+ if (MCRegister::isPhysicalRegister(Reg)) {
+ OS << getRegisterName(Reg);
+ } else {
+ auto [Prefix, Idx] = RegEncoder::decodeVirtualRegister(Reg);
+ OS << Prefix << Idx;
+ }
+}
+
+void PISAInstPrinter::printImm1Opnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ OS << formatImm(MCOp.getImm());
+}
+
+void PISAInstPrinter::printImm8Opnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ OS << formatImm(MCOp.getImm());
+}
+
+void PISAInstPrinter::printImm16Opnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ OS << formatImm(MCOp.getImm());
+}
+
+void PISAInstPrinter::printImm32Opnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ OS << formatImm(MCOp.getImm());
+}
+
+void PISAInstPrinter::printImm64Opnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ OS << formatImm(MCOp.getImm());
+}
+
+void PISAInstPrinter::printImm128Opnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ OS << formatImm(MCOp.getImm());
+}
+
+void PISAInstPrinter::printFpImm16Opnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ OS << format_hex(MCOp.getHFPImm(), /*Width=*/6, /*Upper=*/true);
+}
+
+void PISAInstPrinter::printFpImm32Opnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ OS << format_hex(MCOp.getSFPImm(), /*Width=*/10, /*Upper=*/true);
+}
+
+void PISAInstPrinter::printFpImm64Opnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ OS << format_hex(MCOp.getDFPImm(), /*Width=*/18, /*Upper=*/true);
+}
+
+void PISAInstPrinter::printRegOpnd(unsigned RCID, const MCInst *MCI,
+ unsigned OpNo, raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ printRegName(OS, MCOp.getReg());
+ printSwizzle(MCI, OpNo, OS);
+}
+
+void PISAInstPrinter::printBrTargetOpnd(const MCInst *MCI, uint64_t Address,
+ unsigned OpNo, raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ MAI.printExpr(OS, *MCOp.getExpr());
+}
+
+void PISAInstPrinter::printLocalVariableOpnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ assert(PISAMCInstLower::isVariableRef(*MCI, OpNo));
+ printOperand(MCI, OpNo, OS);
+}
+
+void PISAInstPrinter::printGlobalVariableOpnd(const MCInst *MCI,
+ uint64_t Address, unsigned OpNo,
+ raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ MAI.printExpr(OS, *MCOp.getExpr());
+}
+
+void PISAInstPrinter::printMemScopeOpnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS) {
+ static const char *MemScopeStrs[] = {
+ ".system", // pisa::MemoryScope::system
+ ".gpu", // pisa::MemoryScope::gpu
+ ".workgroup", // pisa::MemoryScope::workgroup
+ ".subgroup", // pisa::MemoryScope::subgroup
+ };
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ const unsigned Scope = MCOp.getImm();
+ assert(Scope <= pisa::MemoryScope::subgroup && "Invalid memory scope");
+ OS << MemScopeStrs[Scope];
+}
+
+void PISAInstPrinter::printSwizzle(const MCInst *MI, unsigned OpNo,
raw_ostream &O) {
- const MCOperand &Op = MI->getOperand(OpNo);
- if (Op.isReg())
- printRegName(O, Op.getReg());
- else if (Op.isImm())
- O << Op.getImm();
+ auto Swizzle = PISAMCInstLower::getSwizzle(*MI, OpNo);
+ swizzleRepr(O, static_cast<unsigned>(Swizzle));
+}
+
+void PISAInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O, const char *Modifier) {
+ assert((Modifier == 0 || Modifier[0] == 0) && "No modifiers supported");
+ if (PISAMCInstLower::isVariableRef(*MI, OpNo)) {
+ assert(MI->getOperand(OpNo).isImm() && "expecting imm operand");
+ unsigned int FrameIndex = MI->getOperand(OpNo).getImm();
+ O << "@R" << FrameIndex;
+ } else if (OpNo < MI->getNumOperands()) {
+ const MCOperand &Op = MI->getOperand(OpNo);
+ if (Op.isReg()) {
+ printRegName(O, Op.getReg());
+ printSwizzle(MI, OpNo, O);
+ } else if (Op.isImm())
+ O << formatImm((int64_t)Op.getImm());
+ else if (Op.isSFPImm())
+ printFpImm32Opnd(MI, OpNo, O);
+ else if (Op.isDFPImm())
+ printFpImm64Opnd(MI, OpNo, O);
+ else if (Op.isExpr())
+ MAI.printExpr(O, *Op.getExpr());
+ else
+ llvm_unreachable("Unexpected operand type");
+ }
+}
+
+void PISAInstPrinter::negateRepr(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ int64_t DoNegate = MI->getOperand(OpNo).getImm();
+ if (DoNegate)
+ O << "!";
+}
+
+void PISAInstPrinter::swizzleRepr(raw_ostream &O, unsigned SwizzleVal) {
+ switch (static_cast<Swizzle>(SwizzleVal)) {
+ case Swizzle::X:
+ O << ".x";
+ break;
+ case Swizzle::Y:
+ O << ".y";
+ break;
+ case Swizzle::Z:
+ O << ".z";
+ break;
+ case Swizzle::W:
+ O << ".w";
+ break;
+ case Swizzle::XYZW:
+ O << ".xyzw";
+ break;
+ case Swizzle::XY:
+ O << ".xy";
+ break;
+ case Swizzle::ZW:
+ O << ".zw";
+ break;
+ case Swizzle::NONE:
+ break;
+ }
+}
+
+void PISAInstPrinter::printFunctionCallTargetOpnd(const MCInst *MI,
+ unsigned OpNo,
+ raw_ostream &O) {
+ printOperand(MI, OpNo, O);
+
+ // print function args if any
+ O << " (";
+ for (unsigned ArgIdx = OpNo + 1; ArgIdx < MI->getNumOperands(); ++ArgIdx) {
+ printOperand(MI, ArgIdx, O);
+ if (ArgIdx + 1 < MI->getNumOperands())
+ O << ", ";
+ }
+ O << ");";
+}
+
+void PISAInstPrinter::printAddrOffsetImm(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ int64_t Val = MI->getOperand(OpNo).getImm();
+ // print imm offset only when it's not zero
+ if (Val > 0) {
+ O << " + " << formatImm(Val);
+ } else if (Val < 0) {
+ if (Val == std::numeric_limits<int64_t>::min())
+ O << " - " << llvm::format("%" PRIu64, Val);
+ else
+ O << " - " << formatImm(-Val);
+ }
+}
+
+void PISAInstPrinter::printBfnOpcode(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ int64_t Val = MI->getOperand(OpNo).getImm();
+ O << format_hex(Val & 0xFF, 4);
+}
+
+void PISAInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ const unsigned NumOps = MI->getNumOperands();
+ unsigned StrStartIndex = OpNo;
+ while (StrStartIndex < NumOps) {
+ if (MI->getOperand(StrStartIndex).isReg())
+ break;
+
+ std::string Str = getPISAStringOperand(*MI, OpNo);
+ if (StrStartIndex != OpNo)
+ O << ' '; // Add a space if we're starting a new string/argument.
+ O << '"';
+ for (char C : Str) {
+ if (C == '"')
+ O.write('\\'); // Escape " characters (might break for complex UTF-8).
+ O.write(C);
+ }
+ O << '"';
+
+ unsigned NumOpsInString = (Str.size() / 4) + 1;
+ StrStartIndex += NumOpsInString;
+ }
+}
+
+void PISAInstPrinter::printMemOperand(const MCInst *MI, int OpNo,
+ raw_ostream &OS,
+ const char * /*Modifier*/) {
+ // [ Base OP Offset ]
+ OS << "[";
+ printOperand(MI, OpNo, OS);
+ const MCOperand &OffsetOp = MI->getOperand(OpNo + 1);
+ if (OffsetOp.isImm()) {
+ auto Val = OffsetOp.getImm();
+ if (Val != 0) {
+ if (Val > 0)
+ OS << " + " << formatImm(Val);
+ else if (Val == std::numeric_limits<int64_t>::min())
+ OS << " - " << llvm::format("%" PRIu64, Val);
+ else
+ OS << " - " << formatImm(-Val);
+ }
+ } else {
+ assert(OffsetOp.isReg() && "Register expected");
+ OS << " + ";
+ printRegName(OS, OffsetOp.getReg());
+ printSwizzle(MI, (OpNo + 1), OS);
+ }
+ OS << "]";
+}
+
+void PISAInstPrinter::printMemSeqOperand(StringRef Pattern, const MCInst *MI,
+ int OpNo, raw_ostream &OS,
+ const char * /* Modifier */) {
+ OS << "[";
+ for (char C : Pattern) {
+ switch (C) {
+ case '{':
+ OS << "{";
+ break;
+ case '}':
+ OS << "}";
+ break;
+ case ',':
+ OS << ", ";
+ break;
+ default:
+ printOperand(MI, OpNo++, OS);
+ break;
+ }
+ }
+ OS << "]";
+}
+
+void PISAInstPrinter::printParamMemOperand(const MCInst *MCI, int OpNo,
+ raw_ostream &OS) {
+ const MCOperand &ArgIdxOp = MCI->getOperand(OpNo);
+ const MCOperand &OffsetOp = MCI->getOperand(OpNo + 1);
+ assert(ArgIdxOp.isImm());
+
+ // Check for an extra expression operand carrying the kernel argument name.
+ unsigned NameOpIdx = OpNo + 2;
+ if (NameOpIdx < MCI->getNumOperands() &&
+ MCI->getOperand(NameOpIdx).isExpr()) {
+ const auto *SRE =
+ cast<MCSymbolRefExpr>(MCI->getOperand(NameOpIdx).getExpr());
+ OS << "[%" << SRE->getSymbol().getName();
+ } else {
+ OS << "[%arg" << ArgIdxOp.getImm();
+ }
+
+ if (OffsetOp.isImm()) {
+ int64_t Offset = OffsetOp.getImm();
+ if (Offset != 0)
+ OS << llvm::format("%+" PRId64, Offset);
+ } else {
+ assert(OffsetOp.isReg() && "Register expected");
+ OS << " + ";
+ printRegName(OS, OffsetOp.getReg());
+ printSwizzle(MCI, (OpNo + 1), OS);
+ }
+ OS << "]";
+}
+
+void PISAInstPrinter::printSymbolName(raw_ostream &OS, StringRef Name,
+ const MCAsmInfo *MAI) {
+ // This is a modification of MCSymbol::print() that prints non-printable
+ // characters differently.
+ if (!MAI || MAI->isValidUnquotedName(Name)) {
+ OS << Name;
+ return;
+ }
+
+ if (MAI && !MAI->supportsNameQuoting())
+ report_fatal_error("Symbol name with unsupported characters");
+
+ OS << '"';
+ for (char C : Name) {
+ if (C == '\n')
+ OS << "\\n";
+ else if (C == '"')
+ OS << "\\\"";
+ else if (isPrint(C))
+ OS << C;
+ else
+ OS << '\\' << hexdigit(C >> 4) << hexdigit(C & 0x0F);
+ }
+ OS << '"';
+}
+
+void PISAInstPrinter::printEnumOption(EnumOptionClass OptClass,
+ bool IsMandatoryField,
+ unsigned DefaultVal, const MCInst *MCI,
+ unsigned OpNo, raw_ostream &OS) {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+
+ unsigned Val = MCOp.getImm();
+ if (!IsMandatoryField && Val == DefaultVal)
+ return;
+
+ const EnumOptionEntry *Entry = lookupEnumOptionByValue(OptClass, Val);
+ assert(Entry && "Enum value not found from lookup table");
+ if (!Entry)
+ return;
+
+ StringRef OptStrRef = getEnumOptionEntryStr(Entry->OptStr);
+ if (!OptStrRef.empty())
+ OS << "." << OptStrRef;
+}
+
+PISAInstPrinter::printOpndFn
+PISAInstPrinter::printBoolOptionOpnd(PISA::BoolOptionID OptID) {
+ return [=](const MCInst *MCI, unsigned OpNo, raw_ostream &OS) -> void {
+ const MCOperand &MCOp = MCI->getOperand(OpNo);
+ if (MCOp.getImm()) {
+ StringRef OptStr =
+ getBoolOptionTableEntryStr(lookupBoolOptionByID(OptID)->OptStr);
+ OS << "." << OptStr;
+ }
+ };
}
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.h b/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.h
index 11ab2987397bb..9094807ab82ca 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.h
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.h
@@ -9,24 +9,106 @@
#ifndef LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAINSTPRINTER_H
#define LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAINSTPRINTER_H
+#include "MCTargetDesc/PISABaseInfo.h"
+#include "MCTargetDesc/PISAEnum.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/MC/MCInstPrinter.h"
namespace llvm {
-
class PISAInstPrinter : public MCInstPrinter {
+
public:
using MCInstPrinter::MCInstPrinter;
+ typedef std::function<void(const MCInst *, unsigned, raw_ostream &)>
+ printOpndFn;
+ bool printAliasInstr(const MCInst *MI, uint64_t Address, raw_ostream &OS);
+ void printCustomAliasOperand(const MCInst *MI, uint64_t Address,
+ unsigned OpIdx, unsigned PrintMethodIdx,
+ raw_ostream &OS);
void printInst(const MCInst *MI, uint64_t Address, StringRef Annot,
const MCSubtargetInfo &STI, raw_ostream &OS) override;
void printRegName(raw_ostream &OS, MCRegister Reg) override;
- void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O);
+ void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O,
+ const char *Modifier = nullptr);
+ printOpndFn printBoolOptionOpnd(PISA::BoolOptionID OptID);
+ void printEnumOption(PISA::EnumOptionClass OptClass, bool IsMandatoryField,
+ unsigned DefaultVal, const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS);
+
+ printOpndFn printEnumOption(PISA::EnumOptionClass OptClass,
+ bool IsMandatoryField = false,
+ unsigned DefaultVal = 0) {
+ return [=](const MCInst *MCI, unsigned OpNo, raw_ostream &OS) -> void {
+ switch (OptClass) {
+ default:
+ printEnumOption(OptClass, IsMandatoryField, DefaultVal, MCI, OpNo, OS);
+ }
+ };
+ }
+
+ void printImm1Opnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+ void printImm8Opnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+ void printImm16Opnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+ void printImm32Opnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+ void printImm64Opnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+ void printImm128Opnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+ void printFpImm16Opnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+ void printFpImm32Opnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+ void printFpImm64Opnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+
+ void printRegOpnd(unsigned RCID, const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS);
+ auto printRegOpnd(unsigned RCID) {
+ return [=](const MCInst *MCI, unsigned OpNo, raw_ostream &OS) {
+ printRegOpnd(RCID, MCI, OpNo, OS);
+ };
+ }
+
+ void printBrTargetOpnd(const MCInst *MCI, uint64_t Address, unsigned OpNo,
+ raw_ostream &OS);
+ void printLocalVariableOpnd(const MCInst *MCI, unsigned OpNo,
+ raw_ostream &OS);
+ void printGlobalVariableOpnd(const MCInst *MCI, uint64_t Address,
+ unsigned OpNo, raw_ostream &OS);
+
+ void printMemScopeOpnd(const MCInst *MCI, unsigned OpNo, raw_ostream &OS);
+
+ void negateRepr(const MCInst *MI, unsigned OpNo, raw_ostream &O);
+ void printStringImm(const MCInst *MI, unsigned OpNo, raw_ostream &O);
+ void printAddrOffsetImm(const MCInst *MI, unsigned OpNo, raw_ostream &O);
+ void printBfnOpcode(const MCInst *MI, unsigned OpNo, raw_ostream &O);
+ void printMemOperand(const MCInst *MI, int OpNo, raw_ostream &O,
+ const char *Modifier = nullptr);
+ void printMemSeqOperand(StringRef Pattern, const MCInst *MI, int OpNo,
+ raw_ostream &OS, const char *Modifier);
+ auto printMemSeqOperand(StringRef Pattern) {
+ return [=](const MCInst *MI, int OpNo, raw_ostream &OS,
+ const char *Modifier = nullptr) {
+ printMemSeqOperand(Pattern, MI, OpNo, OS, Modifier);
+ };
+ }
+ void printParamMemOperand(const MCInst *MI, int OpNo, raw_ostream &O);
+
+ // print FunctionCallTarget operand (reg or function name) and the sub-sequent
+ // function arguments
+ void printFunctionCallTargetOpnd(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O);
// Autogenerated by tblgen.
std::pair<const char *, uint64_t>
getMnemonic(const MCInst &MI) const override;
void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O);
static const char *getRegisterName(MCRegister Reg);
+ static void printSymbolName(raw_ostream &OS, StringRef Name,
+ const MCAsmInfo *MAI);
+
+private:
+ // Caching STI for member functions to check features.
+ const MCSubtargetInfo *STI = nullptr;
+
+ void printSwizzle(const MCInst *MI, unsigned OpNo, raw_ostream &O);
+ void swizzleRepr(raw_ostream &O, unsigned SwizzleVal);
};
} // namespace llvm
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCExpr.cpp b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCExpr.cpp
new file mode 100644
index 0000000000000..e431b48250c7d
--- /dev/null
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCExpr.cpp
@@ -0,0 +1,31 @@
+//===-- PISAMCExpr.cpp - Handle custom MCExprs ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PISAMCExpr.h"
+#include "PISAInstPrinter.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/MC/MCAssembler.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/Support/Format.h"
+using namespace llvm;
+
+const PISAGlobalAddressMCExpr *
+PISAGlobalAddressMCExpr::create(const MCSymbol &Symbol, MCContext &Ctx) {
+ return new (Ctx) PISAGlobalAddressMCExpr(Symbol);
+}
+
+PISAGlobalAddressMCExpr::PISAGlobalAddressMCExpr(const MCSymbol &Sym)
+ : Symbol(&Sym) {
+ assert(Symbol);
+}
+
+void PISAGlobalAddressMCExpr::printImpl(raw_ostream &OS,
+ const MCAsmInfo *MAI) const {
+ OS << "@";
+ PISAInstPrinter::printSymbolName(OS, Symbol->getName(), MAI);
+}
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCExpr.h b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCExpr.h
new file mode 100644
index 0000000000000..6d195f4da8144
--- /dev/null
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCExpr.h
@@ -0,0 +1,54 @@
+//===-- PISAMCExpr.h ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// PISA special MCTargetExpr class to model floating point immediates
+// Modeled after ARMMCExpr
+
+#ifndef LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAMCEXPR_H
+#define LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAMCEXPR_H
+
+#include "llvm/ADT/APFloat.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCSymbol.h"
+#include <utility>
+
+namespace llvm {
+
+/// MCExpr for Global Address.
+/// The operand is the address to a global variable (.e.g. @foo).
+/// It required a prefix "@" for the symbol name
+class PISAGlobalAddressMCExpr : public MCTargetExpr {
+
+private:
+ const MCSymbol *Symbol;
+ explicit PISAGlobalAddressMCExpr(const MCSymbol &Symbol);
+
+public:
+ static const PISAGlobalAddressMCExpr *create(const MCSymbol &Symbol,
+ MCContext &Ctx);
+
+ const MCSymbol &getSymbol() const { return *Symbol; }
+
+ void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override;
+
+ bool evaluateAsRelocatableImpl(MCValue &Res,
+ const MCAssembler *Asm) const override {
+ return false;
+ }
+
+ void visitUsedExpr(MCStreamer &Streamer) const override {}
+ MCFragment *findAssociatedFragment() const override { return nullptr; }
+
+ static bool classof(const MCExpr *E) {
+ return E->getKind() == MCExpr::Target;
+ }
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAMCEXPR_H
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.cpp b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.cpp
index 5dab8ea1bba59..3ffd668f3d05a 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.cpp
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.cpp
@@ -9,14 +9,19 @@
#include "PISAMCTargetDesc.h"
#include "PISAInstPrinter.h"
#include "PISAMCAsmInfo.h"
+#include "PISATargetStreamer.h"
#include "TargetInfo/PISATargetInfo.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/TargetRegistry.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
#define GET_INSTRINFO_MC_DESC
#define ENABLE_INSTR_PREDICATE_VERIFIER
+#define GET_INSTRINFO_OPERAND_ENUM
+#define GET_INSTRINFO_NAMED_OPS
#include "PISAGenInstrInfo.inc"
#define GET_SUBTARGETINFO_MC_DESC
@@ -41,7 +46,7 @@ static MCRegisterInfo *createPISAMCRegisterInfo(const Triple &TT) {
static MCSubtargetInfo *createPISAMCSubtargetInfo(const Triple &TT,
StringRef CPU, StringRef FS) {
- return createPISAMCSubtargetInfoImpl(TT, CPU, /*TuneCPU=*/CPU, FS);
+ return createPISAMCSubtargetInfoImpl(TT, CPU, /*TuneCPU*/ CPU, FS);
}
static MCInstPrinter *createPISAMCInstPrinter(const Triple &T,
@@ -55,10 +60,18 @@ static MCInstPrinter *createPISAMCInstPrinter(const Triple &T,
// NOLINTNEXTLINE(readability-identifier-naming)
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializePISATargetMC() {
- Target *T = &getThePISATarget();
- RegisterMCAsmInfo<PISAMCAsmInfo> X(*T);
- TargetRegistry::RegisterMCInstrInfo(*T, createPISAMCInstrInfo);
- TargetRegistry::RegisterMCRegInfo(*T, createPISAMCRegisterInfo);
- TargetRegistry::RegisterMCSubtargetInfo(*T, createPISAMCSubtargetInfo);
- TargetRegistry::RegisterMCInstPrinter(*T, createPISAMCInstPrinter);
+ [[maybe_unused]] static bool Initialized = []() {
+ Target *T = &getThePISATarget();
+ RegisterMCAsmInfo<PISAMCAsmInfo> X(*T);
+ TargetRegistry::RegisterMCInstrInfo(*T, createPISAMCInstrInfo);
+ TargetRegistry::RegisterMCRegInfo(*T, createPISAMCRegisterInfo);
+ TargetRegistry::RegisterMCSubtargetInfo(*T, createPISAMCSubtargetInfo);
+ TargetRegistry::RegisterMCInstPrinter(*T, createPISAMCInstPrinter);
+ TargetRegistry::RegisterAsmTargetStreamer(*T, createPISAAsmTargetStreamer);
+ TargetRegistry::RegisterObjectTargetStreamer(
+ *T, createPISAObjectTargetStreamer);
+ TargetRegistry::RegisterNullTargetStreamer(*T,
+ createPISANullTargetStreamer);
+ return true;
+ }();
}
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.h b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.h
index cb47d07cf4565..625d0f16077cf 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.h
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.h
@@ -14,13 +14,37 @@
#include <memory>
namespace llvm {
+class MCAsmBackend;
+class MCCodeEmitter;
+class MCContext;
+class MCInstPrinter;
class MCInstrInfo;
+class MCObjectTargetWriter;
class MCRegisterInfo;
+class MCStreamer;
class MCSubtargetInfo;
+class MCTargetOptions;
+class MCTargetStreamer;
class Target;
+class formatted_raw_ostream;
+
+namespace PISA {
+enum OperandType : unsigned {
+ OPERAND_NEGATE = MCOI::OPERAND_FIRST_TARGET,
+ OPERAND_SWIZZLE,
+};
+} // namespace PISA
+
+MCTargetStreamer *createPISAAsmTargetStreamer(MCStreamer &S,
+ formatted_raw_ostream &OS,
+ MCInstPrinter *InstPrint);
+MCTargetStreamer *createPISAObjectTargetStreamer(MCStreamer &S,
+ const MCSubtargetInfo &STI);
+MCTargetStreamer *createPISANullTargetStreamer(MCStreamer &S);
+
} // namespace llvm
-// Defines symbolic names for PISA registers. This defines a mapping from
+// Defines symbolic names for PISA registers. This defines a mapping from
// register name to register number.
#define GET_REGINFO_ENUM
#include "PISAGenRegisterInfo.inc"
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.cpp b/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.cpp
new file mode 100644
index 0000000000000..25364bda0ab87
--- /dev/null
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.cpp
@@ -0,0 +1,183 @@
+//===-- PISARegEncoder.cpp - Encode PISA virtual registers ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PISARegEncoder.h"
+
+using namespace llvm;
+using namespace PISA;
+
+const char *RegEncoder::getPrefixFromBank(RegBank Bank) {
+
+ const auto NumBanks = static_cast<size_t>(RegBank::NUM_BANK);
+
+ const char *const BankPrefixes[NumBanks] = {
+ "%p", "%b", "%h", "%w", "%d", "%q", "%v2b", "%v2h", "%v2w",
+ "%v2d", "%v3b", "%v3h", "%v3w", "%v3d", "%v4b", "%v4h", "%v4w", "%v4d",
+ "%v5w", "%v6w", "%v7w", "%v8w", "%v16w", "%v32w", "%v64w"};
+
+ auto Index = static_cast<size_t>(Bank);
+ if (Index >= NumBanks)
+ llvm_unreachable("Unknown register bank!");
+
+ return BankPrefixes[Index];
+}
+
+RegEncoder::RegBank RegEncoder::getRegBank(unsigned NumElts, unsigned EltSize) {
+ switch (NumElts) {
+ case 1:
+ switch (EltSize) {
+ case 1:
+ return RegBank::Reg1;
+ case 8:
+ return RegBank::Reg8;
+ case 16:
+ return RegBank::Reg16;
+ case 32:
+ return RegBank::Reg32;
+ case 64:
+ return RegBank::Reg64;
+ case 128:
+ return RegBank::Reg128;
+ default:
+ llvm_unreachable("Unknown element size!");
+ }
+ break;
+ case 2:
+ switch (EltSize) {
+ case 8:
+ return RegBank::RegV2_8;
+ case 16:
+ return RegBank::RegV2_16;
+ case 32:
+ return RegBank::RegV2_32;
+ case 64:
+ return RegBank::RegV2_64;
+ default:
+ llvm_unreachable("Unknown element size!");
+ }
+ break;
+ case 3:
+ switch (EltSize) {
+ case 8:
+ return RegBank::RegV3_8;
+ case 16:
+ return RegBank::RegV3_16;
+ case 32:
+ return RegBank::RegV3_32;
+ case 64:
+ return RegBank::RegV3_64;
+ default:
+ llvm_unreachable("Unknown element size!");
+ }
+ break;
+ case 4:
+ switch (EltSize) {
+ case 8:
+ return RegBank::RegV4_8;
+ case 16:
+ return RegBank::RegV4_16;
+ case 32:
+ return RegBank::RegV4_32;
+ case 64:
+ return RegBank::RegV4_64;
+ default:
+ llvm_unreachable("Unknown element size!");
+ }
+ break;
+ case 5:
+ if (EltSize == 32)
+ return RegBank::RegV5_32;
+ else
+ llvm_unreachable("Unknown element size!");
+ break;
+ case 6:
+ if (EltSize == 32)
+ return RegBank::RegV6_32;
+ else
+ llvm_unreachable("Unknown element size!");
+ break;
+ case 7:
+ if (EltSize == 32)
+ return RegBank::RegV7_32;
+ else
+ llvm_unreachable("Unknown element size!");
+ break;
+ case 8:
+ if (EltSize == 32)
+ return RegBank::RegV8_32;
+ else
+ llvm_unreachable("Unknown element size!");
+ break;
+ case 16:
+ if (EltSize == 32)
+ return RegBank::RegV16_32;
+ else
+ llvm_unreachable("Unknown element size!");
+ break;
+ case 32:
+ if (EltSize == 32)
+ return RegBank::RegV32_32;
+ else
+ llvm_unreachable("Unknown element size!");
+ break;
+ case 64:
+ if (EltSize == 32)
+ return RegBank::RegV64_32;
+ else
+ llvm_unreachable("Unknown element size!");
+ break;
+ default:
+ llvm_unreachable("Unknown number of elements!");
+ return RegBank::NUM_BANK;
+ }
+}
+
+// 3 bits used for type, 5 bits used for bank, 24 bits used for index
+static constexpr unsigned NumTypeBits = 3;
+static constexpr unsigned NumBankBits = 5;
+static constexpr unsigned NumRegBits = 24;
+static_assert(RegEncoder::NUM_TYPE < 5, "need to update encoding");
+static_assert(static_cast<unsigned>(RegEncoder::RegBank::NUM_BANK) < 32,
+ "need to update encoding");
+
+RegEncoder::RegType RegEncoder::getRegType(uint8_t TSFlags) {
+ unsigned TypeBitsMask = (1U << NumTypeBits) - 1;
+ return static_cast<RegEncoder::RegType>(TSFlags & TypeBitsMask);
+}
+
+RegEncoder::RegType RegEncoder::getRegType(const TargetRegisterClass *RC) {
+ return getRegType(RC->TSFlags);
+}
+
+RegEncoder::RegBank RegEncoder::getRegBank(uint8_t TSFlags) {
+ unsigned BankBitsMask = (1U << NumBankBits) - 1;
+ return static_cast<RegEncoder::RegBank>(TSFlags & BankBitsMask);
+}
+
+unsigned RegEncoder::encodeVirtualRegister(unsigned Idx, RegBank Bank,
+ RegType Type) {
+ // Check that NumRegBits is sufficient to encode the register Idx
+ auto RegBank = static_cast<unsigned>(Bank);
+ assert(Idx < (1U << NumRegBits) &&
+ "Register index exceeds virtual register encoding limit");
+ return Register::index2VirtReg((Type << (NumRegBits + NumBankBits)) |
+ RegBank << (NumRegBits) |
+ (Idx & ((1U << NumRegBits) - 1)));
+}
+
+std::pair<const char *, unsigned>
+RegEncoder::decodeVirtualRegister(MCRegister Reg) {
+ auto BankBits = (Reg >> NumRegBits) & ((1U << NumBankBits) - 1);
+ const char *Prefix = getPrefixFromBank(getRegBank(BankBits));
+ unsigned Num = Reg & ((1U << NumRegBits) - 1);
+ return std::make_pair(Prefix, Num);
+}
+
+bool RegEncoder::isVirtualRegNo(unsigned RegNo) {
+ return getRegType(RegNo >> (NumRegBits + NumBankBits)) != NONE;
+}
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.h b/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.h
new file mode 100644
index 0000000000000..a4778cae1ed92
--- /dev/null
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.h
@@ -0,0 +1,73 @@
+//===-- PISARegEncoder.h - Encode PISA virtual registers ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAREGENCODER_H
+#define LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAREGENCODER_H
+
+#include "llvm/CodeGen/TargetRegisterInfo.h"
+
+namespace llvm {
+namespace PISA {
+
+class RegEncoder {
+public:
+ enum RegType {
+ NONE,
+ // Keep in sync with TSFlags in PISARegisterInfo.td
+ REG,
+ PRED,
+ NUM_TYPE
+ };
+
+ enum class RegBank : unsigned {
+ Reg1,
+ Reg8,
+ Reg16,
+ Reg32,
+ Reg64,
+ Reg128,
+ RegV2_8,
+ RegV2_16,
+ RegV2_32,
+ RegV2_64,
+ RegV3_8,
+ RegV3_16,
+ RegV3_32,
+ RegV3_64,
+ RegV4_8,
+ RegV4_16,
+ RegV4_32,
+ RegV4_64,
+ RegV5_32,
+ RegV6_32,
+ RegV7_32,
+ RegV8_32,
+ RegV16_32,
+ RegV32_32,
+ RegV64_32,
+ NUM_BANK
+ };
+
+ static RegBank getRegBank(unsigned NumElts, unsigned EltSize);
+ static const char *getPrefixFromBank(RegBank Bank);
+ static bool isVirtualRegNo(unsigned RegNo);
+ static unsigned encodeVirtualRegister(unsigned Idx, RegBank Bank,
+ RegType Type);
+ static std::pair<const char *, unsigned>
+ decodeVirtualRegister(MCRegister Reg);
+
+protected:
+ static RegEncoder::RegBank getRegBank(uint8_t TSFlags);
+ static RegEncoder::RegType getRegType(uint8_t TSFlags);
+ static RegEncoder::RegType getRegType(const TargetRegisterClass *RC);
+};
+
+} // namespace PISA
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISAREGENCODER_H
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISATargetStreamer.cpp b/llvm/lib/Target/PISA/MCTargetDesc/PISATargetStreamer.cpp
new file mode 100644
index 0000000000000..1443af35549b9
--- /dev/null
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISATargetStreamer.cpp
@@ -0,0 +1,572 @@
+//===-- PISATargetStreamer.cpp - PISATargetStreamer class -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MCTargetDesc/PISATargetStreamer.h"
+#include "MCTargetDesc/PISAMCTargetDesc.h"
+#include "MCTargetDesc/PISARegEncoder.h"
+#include "PISAInstPrinter.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/CodeGenTypes/LowLevelType.h"
+#include "llvm/IR/IRPrintingPasses.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/Alignment.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/PISAAddrSpace.h"
+
+using namespace llvm;
+
+PISA::StorageSpace PISA::mapAddrSpaceToStorageSpace(unsigned AS) {
+ switch (PISAAS::AddressSpace(AS)) {
+ case PISAAS::AddressSpace::PRIVATE:
+ return PISA::StorageSpace::PRIVATE;
+ case PISAAS::AddressSpace::GLOBAL:
+ return PISA::StorageSpace::GLOBAL;
+ case PISAAS::AddressSpace::CONSTANT:
+ return PISA::StorageSpace::CONSTANT;
+ case PISAAS::AddressSpace::SHARED:
+ return PISA::StorageSpace::SHARED;
+ case PISAAS::AddressSpace::GENERIC:
+ return PISA::StorageSpace::GENERIC;
+ }
+ llvm_unreachable("Unexpected address space!");
+}
+
+static StringRef getStorageSpaceRepr(PISA::StorageSpace SS) {
+ switch (SS) {
+ case PISA::StorageSpace::PRIVATE:
+ return "private";
+ case PISA::StorageSpace::GLOBAL:
+ return "global";
+ case PISA::StorageSpace::CONSTANT:
+ return "const";
+ case PISA::StorageSpace::SHARED:
+ return "shared";
+ case PISA::StorageSpace::GENERIC:
+ return "generic";
+ }
+ llvm_unreachable("Invalid storage space!");
+}
+
+static StringRef getParamASRepr(PISAAS::AddressSpace AS) {
+ switch (AS) {
+ case PISAAS::AddressSpace::GLOBAL:
+ return "global";
+ case PISAAS::AddressSpace::CONSTANT:
+ return "const";
+ case PISAAS::AddressSpace::SHARED:
+ return "shared";
+ case PISAAS::AddressSpace::GENERIC:
+ return "generic";
+ default:
+ break;
+ }
+ llvm_unreachable("Invalid parameter address space!");
+}
+
+StringRef PISA::getLinkageTyName(PISA::LinkageTy Linkage) {
+ switch (Linkage) {
+ case DEFAULT:
+ return "default";
+ case EXPORT:
+ return "export";
+ case IMPORT:
+ return "import";
+ }
+ llvm_unreachable("Invalid linkage type!");
+}
+
+static StringRef getCallingConvRepr(CallingConv::ID CC) {
+ switch (CC) {
+ case CallingConv::PISA_KERNEL:
+ return ".kernel";
+ default:
+ return ".function";
+ }
+}
+
+static std::string getKernelAttributeRepr(const KernelAttribute &Attr) {
+
+ DenseMap<PISA::KernelAttributeType, StringRef> AvailableMetadataNodeTypes = {
+ {PISA::KernelAttributeType::REQD_WORK_GROUP_SIZE,
+ ".reqd_work_group_size"},
+ {PISA::KernelAttributeType::VEC_TYPE_HINT, ".vec_type_hint"}};
+
+ std::string Result;
+ auto It = AvailableMetadataNodeTypes.find(Attr.KernelAttrType);
+ if (It != AvailableMetadataNodeTypes.end()) {
+ Result = It->second;
+ }
+
+ switch (Attr.KernelAttrType) {
+ case llvm::PISA::KernelAttributeType::REQD_WORK_GROUP_SIZE: {
+ Result += "(";
+ const auto &Values = std::get<std::vector<uint32_t>>(Attr.KernelAttrValues);
+ if (!Values.empty()) {
+ Result +=
+ std::accumulate(std::next(Values.begin()), Values.end(),
+ std::to_string(Values.front()),
+ [](const std::string &Acc, uint32_t El) {
+ return Acc + std::string(", ") + std::to_string(El);
+ });
+ }
+ Result += ")";
+ } break;
+ case llvm::PISA::KernelAttributeType::VEC_TYPE_HINT: {
+ Result += "(";
+ const auto &Arg = std::get<std::string>(Attr.KernelAttrValues);
+ Result += Arg;
+ Result += ")";
+ } break;
+ }
+ return Result;
+}
+
+static void emitTypeString(uint32_t TypeSizeInBits, uint32_t NumElts,
+ raw_ostream &OS) {
+ assert(NumElts > 0);
+ if (NumElts == 1 && TypeSizeInBits == 1) {
+ OS << ".pred";
+ return;
+ }
+
+ if (NumElts > 1)
+ OS << ".v" << NumElts;
+ OS << "." << TypeSizeInBits << "b";
+}
+
+static void emitTypeString(const LLT &T, raw_ostream &OS) {
+ auto TypeBitSize = T.getScalarType().getSizeInBits();
+ auto NumElts = T.isVector() ? T.getNumElements() : 1;
+ emitTypeString(TypeBitSize, NumElts, OS);
+}
+
+namespace {
+
+class PISATargetAsmStreamer final : public PISATargetStreamer {
+ formatted_raw_ostream &OS;
+ const MCAsmInfo *MAI = nullptr;
+ void printName(raw_ostream &OS, StringRef Name) {
+ PISAInstPrinter::printSymbolName(OS, Name, MAI);
+ }
+
+public:
+ PISATargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS)
+ : PISATargetStreamer(S), OS(OS), MAI(&S.getContext().getAsmInfo()) {}
+ ~PISATargetAsmStreamer() override;
+
+ StringRef emitHeader(const PISA::HeaderDcl &HD) override {
+ if (HD.Version != 0)
+ OS << ".version " << (HD.Version / 100) << "." << (HD.Version % 100)
+ << ";\n";
+ if (!HD.Target.empty())
+ OS << ".target " << HD.Target << ";\n";
+ return HD.Target;
+ }
+
+ void emitGlobalVariable(const PISA::GlobalVariableDcl &GV) override {
+ if (GV.Dcl.Linkage != PISA::LinkageTy::DEFAULT)
+ OS << "." << getLinkageTyName(GV.Dcl.Linkage) << " ";
+
+ // globals must have .const or .global addrspace
+ switch (GV.Dcl.SS) {
+ case PISA::StorageSpace::GLOBAL:
+ OS << ".global ";
+ break;
+ case PISA::StorageSpace::CONSTANT:
+ OS << ".const ";
+ break;
+ default:
+ llvm_unreachable("Invalid storage space for global variables");
+ }
+
+ auto PrintOffset = [](int64_t Val, raw_ostream &OS) {
+ // print imm offset only when it's not zero
+ if (Val > 0) {
+ OS << "+" << format("%" PRId64, Val);
+ } else if (Val < 0) {
+ if (Val == std::numeric_limits<int64_t>::min())
+ OS << "-" << format("%" PRIu64, Val);
+ else
+ OS << "-" << format("%" PRId64, -Val);
+ }
+ };
+
+ OS << " .align " << GV.Dcl.Alignment.value() << " ";
+
+ if (!GV.Dcl.Section.empty())
+ OS << ".section(\"" << GV.Dcl.Section << "\") ";
+
+ if (!GV.Dcl.HostAccessName.empty())
+ OS << ".host_access(\"" << GV.Dcl.HostAccessName << "\") ";
+
+ OS << "@";
+ printName(OS, GV.Dcl.Name);
+
+ if (GV.Init.Initializer.empty()) {
+ OS << "[" << GV.Dcl.Size << "]";
+ } else {
+ OS << " = { ";
+ for (auto [i, X] : llvm::enumerate(GV.Init.Initializer)) {
+ OS << ((i == 0) ? "" : ", ");
+ if (auto Iter = GV.Init.Exprs.find(i); Iter != GV.Init.Exprs.end()) {
+ auto &Entry = Iter->second;
+ if (auto *G = std::get_if<PISA::VariableInit::GlobalExpr>(&Entry)) {
+ OS << "." << X.Type.getSizeInBits() << "b ";
+ OS << "@" << G->Name;
+ PrintOffset(G->Offset, OS);
+ } else if (auto *Z = std::get_if<PISA::VariableInit::Zeros>(&Entry)) {
+ OS << ".zero " << Z->N;
+ } else {
+ llvm_unreachable("unknown construct!");
+ }
+ } else {
+ TypeSize SizeInBits = X.Type.getSizeInBits();
+ OS << "." << SizeInBits << "b ";
+ OS << format_hex(X.Value & maskTrailingOnes<uint64_t>(SizeInBits), 1);
+ }
+ }
+ OS << " }";
+ }
+
+ OS << ";\n";
+ }
+
+ void emitFunctionSignature(const PISA::FunctionSignature &Sig) override {
+ if (Sig.DN.CC != CallingConv::PISA_KERNEL) {
+ switch (Sig.DN.Linkage) {
+ case PISA::LinkageTy::EXPORT:
+ OS << ".export ";
+ break;
+ case PISA::LinkageTy::IMPORT:
+ OS << ".import ";
+ break;
+ default:
+ break;
+ }
+ }
+
+ OS << getCallingConvRepr(Sig.DN.CC) << " ";
+
+ for (auto KernelAttr : Sig.DN.KernelAttrs) {
+ OS << getKernelAttributeRepr(KernelAttr) << " ";
+ }
+
+ if (Sig.DN.CC != CallingConv::PISA_KERNEL) {
+ if (Sig.DN.RetLLT.isValid())
+ emitTypeString(Sig.DN.RetLLT, OS);
+ else
+ OS << "void";
+ OS << " ";
+ }
+
+ OS << "@";
+ printName(OS, Sig.DN.Name);
+
+ OS << "(";
+ if (Sig.DN.CC != CallingConv::PISA_KERNEL) {
+ const char *Sep = "";
+ for (auto &Param : Sig.FunctionParams) {
+ OS << Sep << ".reg ";
+ emitTypeString(Param.Ty, OS);
+ OS << " " << Param.Prefix << Param.Idx;
+ Sep = ", ";
+ }
+ } else {
+ const char *Sep = "";
+ unsigned I = 0;
+ for (auto &Param : Sig.KernelParams) {
+ OS << Sep << ".param[" << Param.Size << "] ";
+ if (Param.hasAlign())
+ OS << ".align(" << Param.Align << ") ";
+ if (Param.hasAS())
+ OS << ".addrspace("
+ << getParamASRepr(static_cast<PISAAS::AddressSpace>(Param.AS))
+ << ") ";
+ if (Param.hasPtrAlign())
+ OS << ".ptr_align(" << Param.PtrAlign << ") ";
+ if (!Param.ArgName.empty())
+ OS << "%" << Param.ArgName;
+ else
+ OS << "%arg" << I;
+ I++;
+ Sep = ", ";
+ }
+ }
+
+ OS << ")";
+
+ OS << "\n";
+ }
+
+ void emitFunctionDeclaration(const PISA::FunctionDeclaration &Dcl) override {
+ assert(Dcl.DN.CC != CallingConv::PISA_KERNEL);
+
+ if (Dcl.DN.Linkage != PISA::LinkageTy::IMPORT) {
+ return;
+ }
+
+ OS << ".import ";
+ OS << getCallingConvRepr(Dcl.DN.CC) << " ";
+
+ if (Dcl.DN.RetLLT.isValid()) {
+ emitTypeString(Dcl.DN.RetLLT, OS);
+ } else
+ OS << "void";
+
+ OS << " @";
+ printName(OS, Dcl.DN.Name);
+
+ OS << "(";
+ const char *Sep = "";
+ for (auto &Param : Dcl.FunctionParams) {
+ OS << Sep << ".reg ";
+ emitTypeString(Param.Ty, OS);
+ Sep = ", ";
+ }
+ OS << ");";
+ }
+
+ void addRegDcl(unsigned NumElts, unsigned EltSize, unsigned RegType,
+ RegNamesRange RegNames, bool MustAdd) override {
+ llvm_unreachable("Never used.");
+ }
+
+ void emitRegDcls(const PISA::RegDcls &Dcls,
+ const PISA::DataTypes &DTs) override {
+ for (auto &[Key, TI] : DTs) {
+ // Skip if 0 registers of this type were declared
+ if (TI.RegStart == TI.RegCounter)
+ continue;
+
+ auto [NumElts, BitWidth, RegType] = Key;
+
+ switch (RegType) {
+ case PISA::RegEncoder::PRED:
+ OS << "\t.pred ";
+ break;
+ case PISA::RegEncoder::REG:
+ OS << "\t.reg ";
+ emitTypeString(BitWidth, NumElts, OS);
+ OS << " ";
+ break;
+ default:
+ llvm_unreachable("unknown reg type!");
+ }
+
+ // Do not use range syntax if only 5 or fewer registers were declared
+ if (TI.RegCounter - TI.RegStart <= 5) {
+ for (unsigned I = TI.RegStart; I < TI.RegCounter; ++I) {
+ if (I != TI.RegStart)
+ OS << ", ";
+ OS << TI.Prefix << I;
+ }
+ OS << ";\n";
+ } else {
+ OS << TI.Prefix << "<" << TI.RegStart << "~" << (TI.RegCounter)
+ << ">;\n";
+ }
+ }
+ }
+
+ void addLocalVariableDecl(const PISA::LocalVariableDcl &V) override {
+ OS << "\t." << getStorageSpaceRepr(V.SS) << " ";
+ OS << ".align " << V.Alignment.value() << " ";
+ OS << "@";
+ // If PISA comes from PISA reader StackIndex won't be set, so we fall
+ // back and use already parsed name.
+ if (V.StackIndex == -1) {
+ OS << V.Name;
+ } else {
+ OS << "R" << V.StackIndex;
+ }
+ OS << '[' << V.Size << "];\n";
+ }
+
+ void emitLocalVariableDcls(const PISA::LocalVariableDcls &Dcls) override {
+ for (auto &V : Dcls.Vars)
+ addLocalVariableDecl(V);
+ }
+
+ void emitFuncBodyStart() override { OS << "{\n"; }
+ void emitFuncBodyEnd() override { OS << "}\n"; }
+
+ bool isAsmStreamer() const override { return true; }
+ int getCurLine() const override {
+ // getLine() in OS is 0 based
+ return OS.getLine() + 1;
+ }
+};
+
+class PISATargetELFStreamer final : public PISATargetStreamer {
+public:
+ PISATargetELFStreamer(MCStreamer &S) : PISATargetStreamer(S) {}
+ ~PISATargetELFStreamer() override;
+
+ StringRef emitHeader(const PISA::HeaderDcl &) override {
+ llvm_unreachable("Not implemented yet!");
+ }
+ void emitGlobalVariable(const PISA::GlobalVariableDcl &) override {
+ llvm_unreachable("Not implemented yet!");
+ }
+ void emitFunctionSignature(const PISA::FunctionSignature &) override {
+ llvm_unreachable("Not implemented yet!");
+ }
+ void emitFunctionDeclaration(const PISA::FunctionDeclaration &) override {
+ llvm_unreachable("Not implemented yet!");
+ }
+ void emitRegDcls(const PISA::RegDcls &, const PISA::DataTypes &) override {
+ llvm_unreachable("Not implemented yet!");
+ }
+ void emitLocalVariableDcls(const PISA::LocalVariableDcls &) override {
+ llvm_unreachable("Not implemented yet!");
+ }
+ void emitFuncBodyStart() override {
+ llvm_unreachable("Not implemented yet!");
+ }
+ void emitFuncBodyEnd() override { llvm_unreachable("Not implemented yet!"); }
+
+ void addRegDcl(unsigned NumElts, unsigned EltSize, unsigned RegType,
+ RegNamesRange RegNames, bool MustAdd = true) override {
+ llvm_unreachable("Not implemented yet!");
+ }
+ void addLocalVariableDecl(const PISA::LocalVariableDcl &) override {
+ llvm_unreachable("Not implemented yet!");
+ }
+};
+
+class PISATargetNullStreamer final : public PISATargetStreamer {
+public:
+ PISATargetNullStreamer(MCStreamer &S) : PISATargetStreamer(S) {}
+ ~PISATargetNullStreamer() override;
+
+ StringRef emitHeader(const PISA::HeaderDcl &) override { return ""; }
+ void emitGlobalVariable(const PISA::GlobalVariableDcl &) override {}
+ void emitFunctionSignature(const PISA::FunctionSignature &) override {}
+ void emitFunctionDeclaration(const PISA::FunctionDeclaration &) override {}
+ void emitRegDcls(const PISA::RegDcls &, const PISA::DataTypes &) override {}
+ void emitLocalVariableDcls(const PISA::LocalVariableDcls &) override {}
+ void emitFuncBodyStart() override {}
+ void emitFuncBodyEnd() override {}
+
+ void addRegDcl(unsigned NumElts, unsigned EltSize, unsigned RegType,
+ RegNamesRange RegNames, bool MustAdd = true) override {}
+ void addLocalVariableDecl(const PISA::LocalVariableDcl &) override {}
+};
+
+} // namespace
+
+PISATargetStreamer::~PISATargetStreamer() = default;
+PISATargetAsmStreamer::~PISATargetAsmStreamer() = default;
+PISATargetELFStreamer::~PISATargetELFStreamer() = default;
+PISATargetNullStreamer::~PISATargetNullStreamer() = default;
+
+MCTargetStreamer *
+llvm::createPISAAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS,
+ MCInstPrinter *InstPrinter) {
+ return new PISATargetAsmStreamer(S, OS);
+}
+
+MCTargetStreamer *
+llvm::createPISAObjectTargetStreamer(MCStreamer &S,
+ const MCSubtargetInfo &STI) {
+ const Triple &TT = STI.getTargetTriple();
+ if (TT.isOSBinFormatELF())
+ return new PISATargetELFStreamer(S);
+ return nullptr;
+}
+
+MCTargetStreamer *llvm::createPISANullTargetStreamer(MCStreamer &S) {
+ return new PISATargetNullStreamer(S);
+}
+
+DataType DataTypes::getTypeFromLLT(LLT Ty) {
+ unsigned NumElts = Ty.isScalar() ? 1 : Ty.getNumElements();
+ unsigned EltSize = Ty.getScalarSizeInBits();
+ unsigned RegType =
+ EltSize == 1 ? PISA::RegEncoder::PRED : PISA::RegEncoder::REG;
+ return DataType{NumElts, EltSize, RegType};
+}
+
+LLT DataTypes::getLLTFromType(const DataType &Ty) {
+ if (Ty.NumElts == 1) {
+ return LLT::integer(Ty.EltSize);
+ }
+ return LLT::vector(ElementCount::getFixed(Ty.NumElts), Ty.EltSize);
+}
+
+std::string DataTypes::getPrefixFromLLT(LLT Ty) {
+ unsigned NumElts = Ty.isScalar() ? 1 : Ty.getNumElements();
+ unsigned EltSize = Ty.getScalarSizeInBits();
+ std::string Prefix = "%";
+
+ const DenseMap<unsigned, StringRef> VectorPrefixes = {
+ {1, ""}, {2, "v2"}, {3, "v3"}, {4, "v4"}, {5, "v5"}, {6, "v6"},
+ {7, "v7"}, {8, "v8"}, {16, "v16"}, {32, "v32"}, {64, "v64"}};
+
+ if (auto It = VectorPrefixes.find(NumElts); It != VectorPrefixes.end())
+ Prefix += It->second;
+ else
+ llvm_unreachable("Unsupported PISA vector size");
+
+ const DenseMap<unsigned, StringRef> ScalarPrefixes = {
+ {1, "p"}, // Predicate
+ {8, "b"}, // Byte
+ {16, "h"}, // Half-word
+ {32, "w"}, // Word
+ {64, "d"}, // Double-word
+ {128, "q"} // Quad-word
+ };
+
+ if (auto It = ScalarPrefixes.find(EltSize); It != ScalarPrefixes.end())
+ Prefix += It->second;
+ else
+ llvm_unreachable("Unsupported PISA scalar size");
+ return Prefix;
+}
+
+DataType DataTypes::getTypeFromPrefix(std::string Prefix) {
+ auto Result =
+ llvm::StringSwitch<std::tuple<unsigned, unsigned, unsigned>>(Prefix)
+ .Case("%p", {1, 1, PISA::RegEncoder::PRED})
+ .Case("%b", {1, 8, PISA::RegEncoder::REG})
+ .Case("%h", {1, 16, PISA::RegEncoder::REG})
+ .Case("%w", {1, 32, PISA::RegEncoder::REG})
+ .Case("%d", {1, 64, PISA::RegEncoder::REG})
+ .Case("%q", {1, 128, PISA::RegEncoder::REG})
+ .Case("%v2b", {2, 8, PISA::RegEncoder::REG})
+ .Case("%v3b", {3, 8, PISA::RegEncoder::REG})
+ .Case("%v4b", {4, 8, PISA::RegEncoder::REG})
+ .Case("%v2h", {2, 16, PISA::RegEncoder::REG})
+ .Case("%v3h", {3, 16, PISA::RegEncoder::REG})
+ .Case("%v4h", {4, 16, PISA::RegEncoder::REG})
+ .Case("%v2w", {2, 32, PISA::RegEncoder::REG})
+ .Case("%v3w", {3, 32, PISA::RegEncoder::REG})
+ .Case("%v4w", {4, 32, PISA::RegEncoder::REG})
+ .Case("%v5w", {5, 32, PISA::RegEncoder::REG})
+ .Case("%v6w", {6, 32, PISA::RegEncoder::REG})
+ .Case("%v7w", {7, 32, PISA::RegEncoder::REG})
+ .Case("%v8w", {8, 32, PISA::RegEncoder::REG})
+ .Case("%v16w", {16, 32, PISA::RegEncoder::REG})
+ .Case("%v32w", {32, 32, PISA::RegEncoder::REG})
+ .Case("%v64w", {64, 32, PISA::RegEncoder::REG})
+ .Case("%v2d", {2, 64, PISA::RegEncoder::REG})
+ .Case("%v3d", {3, 64, PISA::RegEncoder::REG})
+ .Case("%v4d", {4, 64, PISA::RegEncoder::REG})
+ .Default({0, 0, 0});
+
+ if (std::get<0>(Result) == 0)
+ llvm_unreachable("Unknown type prefix");
+
+ auto [NumElts, EltSize, RegType] = Result;
+ return DataType{NumElts, EltSize, RegType};
+}
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISATargetStreamer.h b/llvm/lib/Target/PISA/MCTargetDesc/PISATargetStreamer.h
new file mode 100644
index 0000000000000..2610a83f36615
--- /dev/null
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISATargetStreamer.h
@@ -0,0 +1,286 @@
+//===-- PISATargetStreamer.h - PISA Target Streamer -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISATARGETSTREAMER_H
+#define LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISATARGETSTREAMER_H
+
+#include "PISADefines.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/CodeGenTypes/LowLevelType.h"
+#include "llvm/IR/CallingConv.h"
+#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
+#include "llvm/MC/MCStreamer.h"
+#include <tuple>
+#include <utility>
+#include <variant>
+
+namespace llvm {
+
+namespace PISA {
+
+enum LinkageTy { DEFAULT, EXPORT, IMPORT };
+StringRef getLinkageTyName(LinkageTy Linkage);
+
+enum class StorageSpace : unsigned {
+ GENERIC,
+ GLOBAL,
+ CONSTANT,
+ SHARED,
+ PRIVATE,
+};
+StorageSpace mapAddrSpaceToStorageSpace(unsigned AS);
+
+enum class KernelAttributeType {
+ REQD_WORK_GROUP_SIZE,
+ VEC_TYPE_HINT,
+};
+
+struct KernelAttribute {
+ KernelAttributeType KernelAttrType;
+ std::variant<std::vector<uint32_t>, std::string, std::vector<std::string>,
+ uint32_t>
+ KernelAttrValues;
+};
+
+// PISA function and directive name
+struct FunctionDirectiveAndName {
+ // Calling convention.
+ CallingConv::ID CC;
+ // External linkage.
+ LinkageTy Linkage;
+ // Function name.
+ std::string Name;
+ // An optional return LLT for non-kernel functions.
+ LLT RetLLT;
+ // An optional list of kernel attributes.
+ SmallVector<KernelAttribute> KernelAttrs;
+};
+
+struct FunctionDeclParam {
+ LLT Ty;
+};
+
+struct FunctionDeclaration {
+ FunctionDirectiveAndName DN;
+ SmallVector<FunctionDeclParam> FunctionParams;
+};
+
+struct FunctionParameter {
+ LLT Ty;
+ std::string Prefix;
+ unsigned Idx;
+};
+
+struct KernelParameter {
+ unsigned Size = 0;
+ unsigned Align = 0;
+ unsigned PtrAlign = 0;
+ unsigned AS = ~0;
+ std::string ArgName;
+ std::string TypeName;
+ std::string TypeQualifier;
+
+ bool hasAlign() const { return Align != 0; }
+ bool hasPtrAlign() const { return PtrAlign != 0; }
+ bool hasAS() const { return AS != (unsigned)~0; }
+};
+// PISA header info
+struct HeaderDcl {
+ unsigned Version;
+ SmallString<16> Target;
+};
+// PISA function signature
+struct FunctionSignature {
+ FunctionDirectiveAndName DN;
+ // FIXME: Need to unify kernel and function parameters.
+ SmallVector<FunctionParameter> FunctionParams;
+ SmallVector<KernelParameter> KernelParams;
+ unsigned SourceLine = 0;
+};
+// PISA function register declarations
+struct RegDcls {
+ MapVector<std::tuple</*NumElts=*/unsigned, /*BitWidth=*/unsigned,
+ /*Type=*/unsigned>,
+ std::vector<std::pair</*Prefix=*/std::string, /*Id=*/unsigned>>>
+ Regs;
+};
+
+// PISA register declaration types
+struct DataType {
+ unsigned NumElts;
+ unsigned EltSize;
+ unsigned RegType;
+ DataType(unsigned N, unsigned B, unsigned R)
+ : NumElts(N), EltSize(B), RegType(R) {}
+};
+struct TypeInfo {
+ unsigned RegStart;
+ unsigned RegCounter;
+ LLT Ty;
+ std::string Prefix;
+};
+class DataTypes {
+private:
+ MapVector<std::tuple</*NumElts=*/unsigned, /*BitWidth=*/unsigned,
+ /*Type=*/unsigned>,
+ TypeInfo>
+ TypeInfos;
+
+ std::tuple<unsigned, unsigned, unsigned> tupleDT(DataType DT) {
+ return std::make_tuple(DT.NumElts, DT.EltSize, DT.RegType);
+ }
+
+public:
+ static DataType getTypeFromLLT(LLT Ty);
+ static LLT getLLTFromType(const DataType &Ty);
+ static std::string getPrefixFromLLT(LLT Ty);
+ static DataType getTypeFromPrefix(std::string Prefix);
+
+ auto begin() const { return TypeInfos.begin(); }
+ auto end() const { return TypeInfos.end(); }
+
+ TypeInfo &getInfo(LLT Ty) { return getInfo(getTypeFromLLT(Ty)); }
+ TypeInfo &getInfo(unsigned NumElts, unsigned EltSize, unsigned RegType) {
+ return getInfo(DataType(NumElts, EltSize, RegType));
+ }
+ TypeInfo &getInfo(DataType DT) {
+ auto *It = TypeInfos.find(tupleDT(DT));
+ if (It == TypeInfos.end())
+ llvm_unreachable("Expect that requested DataType is already present in "
+ "the TypeInfos map");
+ return It->second;
+ }
+
+ TypeInfo &emplaceInfo(unsigned NumElts, unsigned EltSize, unsigned RegType) {
+ return emplaceInfo(DataType(NumElts, EltSize, RegType));
+ }
+ TypeInfo &emplaceInfo(LLT Ty) { return emplaceInfo(getTypeFromLLT(Ty)); };
+ TypeInfo &emplaceInfo(DataType DT) {
+ LLT Ty = getLLTFromType(DT);
+ auto [It, Inserted] = TypeInfos.try_emplace(
+ tupleDT(DT), TypeInfo{0, 0, Ty, getPrefixFromLLT(Ty)});
+ return It->second;
+ }
+
+ void insertInfo(unsigned NumElts, unsigned EltSize, unsigned RegType,
+ TypeInfo TI) {
+ insertInfo(DataType(NumElts, EltSize, RegType), std::move(TI));
+ }
+ void insertInfo(DataType DT, TypeInfo TI) {
+ auto [It, Inserted] = TypeInfos.try_emplace(tupleDT(DT), std::move(TI));
+ if (!Inserted)
+ llvm_unreachable("Expect that inserted DataType is not already present "
+ "in the TypeInfos map");
+ }
+
+ // Once all function parameter declarations have been processed, call this
+ // to set all RegStart to the register after the last parameter of that Type
+ // Ex. if last parameter of Type Double-Word is %d3, then RegStart for the
+ // Double-Word TypeInfo should be 4. Note that RegCounter is not changed
+ // (in this example, it should still be 4 for d0-d3).
+ void finalizeFuncParams() {
+ for (auto &[_, TI] : TypeInfos)
+ TI.RegStart = TI.RegCounter;
+ }
+};
+
+struct VariableDcl {
+ // External linkage.
+ LinkageTy Linkage;
+ StorageSpace SS;
+ LLT Type;
+ Align Alignment;
+ // Variable name.
+ std::string Name;
+ uint64_t Size = 0;
+ int StackIndex = -1;
+ std::string Section;
+ std::string HostAccessName;
+};
+
+struct VariableInit {
+ struct InitElement {
+ LLT Type;
+ uint64_t Value;
+ };
+ SmallVector<InitElement> Initializer;
+ struct GlobalExpr {
+ // e.g., "@f+8"
+ std::string Name;
+ int64_t Offset = 0;
+ };
+ struct Zeros {
+ // e.g., .zeros 7
+ uint64_t N = 0;
+ };
+ using SpecialEntry = std::variant<GlobalExpr, Zeros>;
+ // This notes that the index in the initializer is actually the given
+ // global or ".zero" directive rather than the immediate value there.
+ DenseMap<uint64_t, SpecialEntry> Exprs;
+};
+
+// Variable declaration and initialization
+struct VariableDclInit {
+ VariableDcl Dcl;
+ VariableInit Init;
+};
+
+// Local variable declaration in private and shared space
+using LocalVariableDcl = VariableDcl;
+struct LocalVariableDcls {
+ SmallVector<LocalVariableDcl> Vars;
+};
+
+// Global variable declaration in global and const space
+using GlobalVariableDcl = VariableDclInit;
+struct GlobalVariableDcls {
+ SmallVector<GlobalVariableDcl> Vars;
+};
+
+} // namespace PISA
+
+class PISATargetStreamer : public MCTargetStreamer {
+public:
+ PISATargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {}
+ ~PISATargetStreamer() override;
+
+ /// Emit header directives (.version, .target). Returns the resolved target
+ /// CPU string, which may differ from the initial CPU if a .target directive
+ /// was present in the input.
+ virtual StringRef emitHeader(const PISA::HeaderDcl &) = 0;
+ virtual void emitGlobalVariable(const PISA::GlobalVariableDcl &) = 0;
+ virtual void emitFunctionSignature(const PISA::FunctionSignature &) = 0;
+ virtual void emitFunctionDeclaration(const PISA::FunctionDeclaration &) = 0;
+ virtual void emitRegDcls(const PISA::RegDcls &, const PISA::DataTypes &) = 0;
+ virtual void emitLocalVariableDcls(const PISA::LocalVariableDcls &) = 0;
+ virtual void emitFuncBodyStart() = 0;
+ virtual void emitFuncBodyEnd() = 0;
+
+ // Register names stored as <Prefix, Id>
+ using RegName = std::tuple<std::string, unsigned>;
+
+ // Array of register names for immutable APIs.
+ using RegNamesRange = ArrayRef<RegName>;
+
+ // If MustAdd is true, addRegDcl would assert if This reg is already added.
+ virtual void addRegDcl(unsigned NumElts, unsigned EltSize, unsigned RegType,
+ RegNamesRange RegNames, bool MustAdd = true) = 0;
+ virtual void addLocalVariableDecl(const PISA::LocalVariableDcl &) = 0;
+
+ void changeSection(const MCSection *CurSection, MCSection *Section,
+ uint32_t SubSection, raw_ostream &OS) override {}
+
+ // Only Asm stream subclass returns true
+ virtual bool isAsmStreamer() const { return false; }
+ virtual int getCurLine() const { return -1; }
+};
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_MCTARGETDESC_PISATARGETSTREAMER_H
diff --git a/llvm/lib/Target/PISA/PISA.td b/llvm/lib/Target/PISA/PISA.td
index 71277b826162b..0a88485ca6420 100644
--- a/llvm/lib/Target/PISA/PISA.td
+++ b/llvm/lib/Target/PISA/PISA.td
@@ -6,29 +6,37 @@
//
//===----------------------------------------------------------------------===//
+include "llvm/TableGen/SearchableTable.td"
include "llvm/Target/Target.td"
+// Global setting
+def Global {
+ string NS = "PISA";
+}
+
+#define PISA
+
include "PISAFeatures.td"
include "PISARegisterInfo.td"
+include "PISARegisterBanks.td"
include "PISAInstrInfo.td"
+include "PISACombine.td"
-// Map the pointer-like operands of the target-independent pseudo instructions
-// (STACKMAP, PATCHPOINT, ...) onto a concrete PISA register class.
defm : RemapAllTargetPseudoPointerOperands<Reg64b>;
-
def PISAInstrInfo : InstrInfo;
class Proc<string Name, list<SubtargetFeature> Features>
- : Processor<Name, NoItineraries, Features>;
+ : Processor<Name, NoItineraries, Features>;
-// Valid -mcpu values.
-def : Proc<"100", [Feature100]>;
+// valid -mcpu values
+def : Proc<"100", [Feature100]>;
def PISAInstPrinter : AsmWriter {
- string AsmWriterClassName = "InstPrinter";
+ string AsmWriterClassName = "InstPrinter";
bit isMCAsmWriter = 1;
}
+
def PISA : Target {
let InstructionSet = PISAInstrInfo;
let AssemblyWriters = [PISAInstPrinter];
diff --git a/llvm/lib/Target/PISA/PISACacheCtrlMMRA.h b/llvm/lib/Target/PISA/PISACacheCtrlMMRA.h
new file mode 100644
index 0000000000000..e46b25419d83d
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISACacheCtrlMMRA.h
@@ -0,0 +1,92 @@
+//===-- PISACacheCtrlMMRA.h -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_PISACACHECTRLMMRA_H
+#define LLVM_LIB_TARGET_PISA_PISACACHECTRLMMRA_H
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/MemoryModelRelaxationAnnotations.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <optional>
+
+namespace llvm {
+namespace PISA {
+
+inline constexpr StringRef CacheCtrlMMRAPrefix = "pisa.cache.ctrl";
+
+// Reads the cache-ctrl integer from the "pisa.cache.ctrl" MMRA tag on I.
+// Returns std::nullopt if the tag is absent or malformed. If more than
+// one distinct cache-ctrl value is attached (e.g. a passthrough merge of
+// two memory ops with different cache hints by an upstream pass such as
+// the LoadStoreVectorizer), emits a warning through the LLVMContext
+// listing the conflicting values and keeps one of them so the compiler
+// can still produce valid output.
+inline std::optional<unsigned> getCacheCtrlFromMMRA(const Instruction &I) {
+ MMRAMetadata MMRA(I);
+ SmallVector<unsigned, 2> Values;
+ for (const auto &[Prefix, Suffix] : MMRA) {
+ if (Prefix != CacheCtrlMMRAPrefix)
+ continue;
+ unsigned Value;
+ if (Suffix.getAsInteger(10, Value))
+ return std::nullopt;
+ if (!is_contained(Values, Value))
+ Values.push_back(Value);
+ }
+ if (Values.empty())
+ return std::nullopt;
+ if (Values.size() > 1) {
+ SmallString<128> Msg;
+ raw_svector_ostream OS(Msg);
+ OS << "instruction has conflicting pisa.cache.ctrl MMRA tags: {";
+ interleaveComma(Values, OS);
+ OS << "}; keeping " << Values.front();
+ I.getContext().diagnose(
+ DiagnosticInfoGeneric(&I, Twine(StringRef(Msg)), DS_Warning));
+ }
+ return Values.front();
+}
+
+// Sets the "pisa.cache.ctrl" MMRA tag on I to Value. Preserves all
+// other MMRA tags already on I, replacing any prior cache-ctrl tag.
+inline void setCacheCtrlMMRA(Instruction &I, unsigned Value) {
+ LLVMContext &Ctx = I.getContext();
+
+ SmallVector<MMRAMetadata::TagT, 4> Tags;
+ MMRAMetadata Existing(I);
+ for (const auto &Tag : Existing) {
+ if (Tag.first != CacheCtrlMMRAPrefix)
+ Tags.push_back(Tag);
+ }
+
+ SmallString<8> Buf;
+ Tags.emplace_back(CacheCtrlMMRAPrefix, Twine(Value).toStringRef(Buf));
+
+ I.setMetadata(LLVMContext::MD_mmra, MMRAMetadata::getMD(Ctx, Tags));
+}
+
+// Copies the "pisa.cache.ctrl" MMRA tag from From to To if present,
+// preserving all other MMRA tags already on To.
+inline void copyCacheCtrlMMRA(const Instruction &From, Instruction &To) {
+ if (auto Value = getCacheCtrlFromMMRA(From))
+ setCacheCtrlMMRA(To, *Value);
+}
+
+} // namespace PISA
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_PISACACHECTRLMMRA_H
diff --git a/llvm/lib/Target/PISA/PISACombine.td b/llvm/lib/Target/PISA/PISACombine.td
new file mode 100644
index 0000000000000..1cd8fa459fdb6
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISACombine.td
@@ -0,0 +1,1075 @@
+//=- PISACombine.td - Define PISA Combine Rules --------------*- tablegen -*-=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+include "llvm/Target/GlobalISel/Combine.td"
+
+def extra_commute_iconstant_to_rhs : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_SMIN, G_SMAX, G_UMIN, G_UMAX, G_UADDO, G_SADDO,
+ G_UMULO, G_SMULO, G_UMULH, G_SMULH, G_UADDSAT,
+ G_SADDSAT, G_SMULFIX, G_UMULFIX, G_SMULFIXSAT,
+ G_UMULFIXSAT):$root, [{
+ return getIConstantVRegVal(${root}->getOperand(1).getReg(), MRI).has_value() &&
+ !getIConstantVRegVal(${root}->getOperand(2).getReg(), MRI).has_value();
+ }]),
+ (apply [{
+ Observer.changingInstr(*${root});
+ Register LHSReg = ${root}->getOperand(1).getReg();
+ Register RHSReg = ${root}->getOperand(2).getReg();
+ ${root}->getOperand(1).setReg(RHSReg);
+ ${root}->getOperand(2).setReg(LHSReg);
+ Observer.changedInstr(*${root});
+ }])
+>;
+
+def extra_commute_fconstant_to_rhs : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_FMINNUM, G_FMAXNUM, G_FMINNUM_IEEE,
+ G_FMAXNUM_IEEE, G_FMINIMUM, G_FMAXIMUM,
+ G_FADD, G_FMUL):$root, [{
+ return getFConstantVRegValWithLookThrough(${root}->getOperand(1).getReg(), MRI).has_value() &&
+ !getFConstantVRegValWithLookThrough(${root}->getOperand(2).getReg(), MRI).has_value();
+ }]),
+ (apply [{
+ Observer.changingInstr(*${root});
+ Register LHSReg = ${root}->getOperand(1).getReg();
+ Register RHSReg = ${root}->getOperand(2).getReg();
+ ${root}->getOperand(1).setReg(RHSReg);
+ ${root}->getOperand(2).setReg(LHSReg);
+ Observer.changedInstr(*${root});
+ }])
+>;
+
+def extra_commute_intrinsic_iconstant_to_rhs : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_INTRINSIC):$root, [{
+ auto IntrinsicID = cast<GIntrinsic>(*${root}).getIntrinsicID();
+ auto isFloatArg = false;
+ switch (IntrinsicID) {
+ default:
+ return false;
+ case Intrinsic::pisa_fadd:
+ case Intrinsic::pisa_fmul:
+ isFloatArg = true;
+ break;
+ }
+ return isFloatArg ?
+ (getFConstantVRegValWithLookThrough(${root}->getOperand(2).getReg(), MRI).has_value() &&
+ !getFConstantVRegValWithLookThrough(${root}->getOperand(3).getReg(), MRI).has_value()) :
+ (getIConstantVRegVal(${root}->getOperand(2).getReg(), MRI).has_value() &&
+ !getIConstantVRegVal(${root}->getOperand(3).getReg(), MRI).has_value());
+ }]),
+ (apply [{
+ Observer.changingInstr(*${root});
+ Register LHSReg = ${root}->getOperand(2).getReg();
+ Register RHSReg = ${root}->getOperand(3).getReg();
+ ${root}->getOperand(2).setReg(RHSReg);
+ ${root}->getOperand(3).setReg(LHSReg);
+ Observer.changedInstr(*${root});
+ }])
+>;
+
+// AND rd1, rs1, C1
+// AND rd2, rd1, C1 - remove copy
+def extra_remove_redundant_constant : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_AND, G_OR):$root,
+ [{
+ auto C1 = getIConstantVRegValWithLookThrough(${root}->getOperand(2).getReg(), MRI);
+ if (C1.has_value()) {
+ auto *DefMI = getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI);
+ if (DefMI && (${root}->getOpcode() == DefMI->getOpcode())) {
+ auto C2 = getIConstantVRegValWithLookThrough(DefMI->getOperand(2).getReg(), MRI);
+ if (C2.has_value()) {
+ ${matchinfo} = DefMI->getOperand(0).getReg();
+ return C1->Value == C2->Value;
+ }
+ }
+ }
+ return false;
+ }]),
+ (apply
+ [{
+ Helper.replaceSingleDefInstWithReg(*${root}, ${matchinfo});
+ }])
+>;
+
+// G_[F]CONSTANT i16 x
+// G_[F]CONSTANT i16 y
+// G_[F]CONSTANT i16 z
+// G_[F]CONSTANT i16 w
+// G_BUILD_VECTOR x, y, z, w
+// => G_CONSTANT i64 [wzyx]
+// => G_BITCAST
+def build_vector_with_constants : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_BUILD_VECTOR):$root,
+ [{
+ auto Type = MRI.getType(${root}->getOperand(0).getReg());
+ if (Type.isPointerVector())
+ return false;
+ auto Size = Type.getSizeInBits();
+ if ((Size <= 64) && isPowerOf2_32(Size)) {
+ for (unsigned I=1; I < ${root}->getNumOperands(); I++) {
+ auto C = getAnyConstantVRegValWithLookThrough(${root}->getOperand(I).getReg(), MRI);
+ if (!C.has_value()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }]),
+ (apply
+ [{
+ applyBuildVectorWithConstants(*${root});
+ }])
+>;
+
+// i56 = G_LOAD x
+// i32 = G_TRUNC i56
+// => i32 = G_LOAD x
+def truncated_load : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_TRUNC):$root,
+ [{
+ auto *DefMI = getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI);
+ auto Type = MRI.getType(DefMI->getOperand(0).getReg());
+ auto Size = Type.getSizeInBits();
+ auto IsVector = Type.isVector();
+ return !isPowerOf2_32(Size) && !IsVector && (DefMI->getOpcode() == TargetOpcode::G_LOAD);
+ }]),
+ (apply
+ [{
+ applyTruncatedLoad(*${root});
+ }])
+>;
+
+// i24 x = G_TRUNC i32
+// G_STORE i24 x
+// => <4 x i8> y = BITCAST x
+// <3 x i8> z = SHUFFLE_VECTOR y, <0,1,2>
+// G_STORE <3 x i8> z
+def truncated_store : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_STORE):$root,
+ [{
+ return matchTruncatedStore(*${root});
+ }]),
+ (apply
+ [{
+ applyTruncatedStore(*${root});
+ }])
+>;
+
+// i24 = G_LOAD x
+// i32 = G_ZEXT i24
+// =>
+// i32 loadRes = G_LOAD x
+// i32 = AND loadRes, 0x00FFFFFF
+def extended_load : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_ZEXT):$root,
+ [{
+ return matchExtendedLoad(*${root});
+ }]),
+ (apply
+ [{
+ applyExtendedLoad(*${root});
+ }])
+>;
+
+// i24 = G_LOAD x
+// =>
+// %3:_(s24) = G_ZEXTLOAD %0:reg32b(p0)
+// %7:_(s32) = G_CONSTANT i32 2
+// %6:reg32b(p0) = G_PTR_ADD %0:reg32b, %7:_(s32)
+// %9:_(s24) = G_ZEXTLOAD %6:reg32b(p0)
+// %11:_(s24) = G_CONSTANT i24 16
+// %10:_(s24) = G_SHL %9:_, %11:_(s24)
+// %12:_(s24) = G_OR %3:_, %10:_
+// Similar transformation is done for G_STORE.
+def expand_nonpowerof2_load_store : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_LOAD, G_STORE):$root,
+ [{
+ return matchExpandNonPowerOf2LoadStore(*${root});
+ }]),
+ (apply
+ [{
+ applyExpandNonPowerOf2LoadStore(*${root});
+ }])
+>;
+
+// If the value we store resulted from a G_LOAD that the rule above expanded,
+// then we can use the individual load values directly instead of merging into
+// one integer, and then splitting it again. Size modifications between the
+// loads and store (i.e. SEXT/ZEXT/TRUNC) are also supported by
+// truncating/extending the relevant load results (or, in the case of
+// truncation, ignoring some loads entirely)
+//
+// Below is a simple example where we simply load and store an i56:
+//
+// bb.1.entry:
+// %0:reg32b(p0) = functionParameter_32b 0
+// -> %3:_(s56) = G_ZEXTLOAD %0:reg32b(p0) ::
+// (load (s32) from %ir.dst)
+// %4:_(s56) = G_CONSTANT i56 0
+// %7:_(s32) = G_CONSTANT i32 4
+// %6:reg32b(p0) = G_PTR_ADD %0:reg32b, %7:_(s32)
+// -> %9:_(s56) = G_ZEXTLOAD %6:reg32b(p0) ::
+// (load (s16) from %ir.dst + 4, align 4)
+// %11:_(s56) = G_CONSTANT i56 32
+// %10:_(s56) = G_SHL %9:_, %11:_(s56)
+// %12:_(s56) = G_OR %3:_, %10:_
+// %14:_(s32) = G_CONSTANT i32 6
+// %13:reg32b(p0) = G_PTR_ADD %0:reg32b, %14:_(s32)
+// -> %16:_(s56) = G_ZEXTLOAD %13:reg32b(p0) ::
+// (load (s8) from %ir.dst + 6, align 2, basealign 4)
+// %18:_(s56) = G_CONSTANT i56 48
+// %17:_(s56) = G_SHL %16:_, %18:_(s56)
+// %19:_(s56) = G_OR %12:_, %17:_
+// G_STORE %19:_(s56), %0:reg32b(p0) ::
+// (store (s56) into %ir.dst, align 4)
+// ret
+def loadVector_matchinfo : GIDefMatchData<"SmallVector<std::pair<MachineInstr *, unsigned>, 8>">;
+def MI_matchinfo : GIDefMatchData<"MachineInstr *">;
+def simplify_nonpowerof2_load_store_chain : GICombineRule<
+ (defs root:$root, loadVector_matchinfo:$matchinfo, MI_matchinfo:$MI_matchinfo),
+ (match (wip_match_opcode G_STORE):$root,
+ [{
+ return matchSimplifyNonPowerOf2LoadStoreChain(*${root}, ${matchinfo}, ${MI_matchinfo});
+ }]),
+ (apply
+ [{
+ applySimplifyNonPowerOf2LoadStoreChain(*${root}, ${matchinfo}, ${MI_matchinfo});
+ }])
+>;
+
+// i1 C = G_CMP ne i? A, 0
+// i? S = G_SELECT i1 C, i? LHS, i? RHS
+// => S = sel.? LHS, RHS, A
+// ... or ...
+// i1 C = G_CMP eq i? A, 0
+// i? S = G_SELECT i1 C, i? LHS, i? RHS
+// => S = sel.? RHS, LHS, A
+def compare_select_reg : GICombineRule<
+ (defs root:$SelectMI),
+ (match (wip_match_opcode G_SELECT):$SelectMI,
+ [{
+ return matchCompareSelect(${SelectMI});
+ }]),
+ (apply
+ [{
+ applyCompareSelect(${SelectMI});
+ }])
+>;
+
+// arcp G_FDIV %0, %1
+// =>
+// %rcp = arcp G_INTRINSIC intrinsic(@llvm.pisa.frcp), %1
+// %result = arcp G_FMUL %0, %rcp
+def fdiv_to_rcp_fmul : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_FDIV):$root,
+ [{ return matchFDivToRcpFMul(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])
+>;
+
+//%2:registers(s32) = G_SHL %0:reg32b, 2
+//%4:registers(s32) = G_ADD %2:registers, %3:registers
+// => %4:registers(s32) = G_PISA_SMAD %0:reg32b, 4, %3:registers
+def shl_add_to_mad : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_ADD):$root,
+ [{ return matchShlAddToMad(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+
+// i32 %lo = G_TRUNC i32 a 16
+// i32 %shift = G_LSHR i32 a 16
+// %hi = G_TRUNC i32 %shift to i16
+// => %1 = G_BITCAST i32 a to <2 x 16>
+// => %hi = G_EXTRACT_VECTOR_ELT <2 x 16> %1, 1
+// => %lo = G_EXTRACT_VECTOR_ELT <2 x 16> %1, 0
+def trunc_shift_to_vector_extract : GICombineRule<
+ (defs root:$TruncMI),
+ (match (wip_match_opcode G_TRUNC):$TruncMI,
+ [{ return matchTruncatedShift(*${TruncMI}); }]),
+ (apply
+ [{ applyTruncatedShift(*${TruncMI}); }])
+>;
+
+// %5 = G_CONSTANT i16 x
+// %6 = G_CONSTANT i16 y
+// %7 = G_AND %5:_, %6:_
+// %2 = G_TRUNC %7 to i8
+// => %2 = COPY (i8)(x & y)
+def const_and_trunc_to_copy : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_TRUNC):$root,
+ [{
+ auto *AndMI = getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI);
+ if (AndMI->getOpcode() == TargetOpcode::G_AND) {
+ auto C1 = getIConstantVRegValWithLookThrough(AndMI->getOperand(1).getReg(), MRI);
+ auto C2 = getIConstantVRegValWithLookThrough(AndMI->getOperand(2).getReg(), MRI);
+ if (C1.has_value() && C2.has_value()) {
+ auto Value = C1->Value & C2->Value;
+ return (Value.getZExtValue() >> MRI.getType(${root}->getOperand(0).getReg()).getSizeInBits()) == 0;
+ }
+ }
+ return false;
+ }]),
+ (apply
+ [{
+ auto *AndMI = getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI);
+ auto C1 = getIConstantVRegValWithLookThrough(AndMI->getOperand(1).getReg(), MRI);
+ auto C2 = getIConstantVRegValWithLookThrough(AndMI->getOperand(2).getReg(), MRI);
+ auto Value = (C1->Value & C2->Value).trunc(MRI.getType(${root}->getOperand(0).getReg()).getSizeInBits());
+ Helper.replaceInstWithConstant(*${root}, Value);
+ }])
+>;
+
+// G_AND (G_ZEXT x), C -> G_ZEXT (G_AND x, trunc(C)).
+// CodeGenPrepare can form the widened mask, but keeping the mask narrow lets
+// address folding see the original arithmetic shape.
+def zext_and_to_and_zext : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_AND):$root,
+ [{ return matchZExtAndToAndZExt(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// %114:_(<5 x s32>) = G_EXTRACT_SUBVECTOR %113:_(<8 x s32>), 0
+// %65:_(s32) = G_EXTRACT_VECTOR_ELT %114:_(<5 x s32>), %66:_(s32)
+// => %65:_(s32) = G_EXTRACT_VECTOR_ELT %113:_(<8 x s32>), %66:_(s32)
+
+def extract_subvector_to_extract_elt : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_EXTRACT_VECTOR_ELT):$root,
+ [{
+ auto *SubvectorMI = getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI);
+ if (!SubvectorMI) {
+ return false;
+ }
+ if (SubvectorMI->getOpcode() != TargetOpcode::G_EXTRACT_SUBVECTOR) {
+ return false;
+ }
+ if (SubvectorMI->getOperand(2).isReg()){
+ // Skip to avoid needing to generate add instruction for index adjustment.
+ return false;
+ }
+ auto C = getIConstantVRegValWithLookThrough(${root}->getOperand(2).getReg(), MRI);
+ if (!C.has_value()){
+ if (SubvectorMI->getOperand(2).getImm() != 0){
+ // Skip to avoid needing to generate add instruction for index adjustment.
+ return false;
+ }
+ }
+ LLVM_DEBUG(dbgs() << " Success: extract_subvector_to_extract_elt rule matches!\n");
+ return true;
+ }]),
+ (apply
+ [{
+ auto *SubvectorMI = getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI);
+ Register SrcReg = SubvectorMI->getOperand(1).getReg();
+ Register IdxReg = ${root}->getOperand(2).getReg();
+ Register DstReg = ${root}->getOperand(0).getReg();
+ auto C = getIConstantVRegValWithLookThrough(${root}->getOperand(2).getReg(), MRI);
+ if (C.has_value() && SubvectorMI->getOperand(2).isImm()){
+ uint64_t NewIdx = C->Value.getZExtValue() + SubvectorMI->getOperand(2).getImm();
+ B.buildExtractVectorElementConstant(DstReg, SrcReg, NewIdx);
+ }
+ else {
+ B.buildExtractVectorElement(DstReg, SrcReg, IdxReg);
+ }
+ ${root}->eraseFromParent();
+ }])
+>;
+
+// %28:_(<8 x i32>) = G_BUILD_VECTOR %91:_(i32), %92:_(i32), %93:_(i32), %94:_(i32), %95:_(i32), %96:_(i32), %97:_(i32), %98:_(i32)
+// %37:_(<4 x i32>) = G_EXTRACT_SUBVECTOR %28:_(<8 x i32>), 0
+// => %37:_(<4 x i32>) = G_BUILD_VECTOR %91:_(i32), %92:_(i32), %93:_(i32), %94:_(i32)
+def extract_subvector_build_vector_matchdata : GIDefMatchData<"SmallVector<Register, 8>">;
+def extract_subvector_build_vector : GICombineRule<
+ (defs root:$root, extract_subvector_build_vector_matchdata:$matchinfo),
+ (match (wip_match_opcode G_EXTRACT_SUBVECTOR):$root,
+ [{ return matchExtractSubvectorBuildVector(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyExtractSubvectorBuildVector(*${root}, ${matchinfo}); }])
+>;
+
+// %29:_(<64 x s32>) = IMPLICIT_DEF
+// %30:_(<64 x s32>) = G_INSERT_SUBVECTOR %29:_, %7:_(<8 x s32>), 0
+// %31:_(<64 x s32>) = G_INSERT_SUBVECTOR %30:_, %10:_(<8 x s32>), 8
+// %32:_(<64 x s32>) = G_INSERT_SUBVECTOR %31:_, %13:_(<8 x s32>), 16
+// %33:_(<64 x s32>) = G_INSERT_SUBVECTOR %32:_, %16:_(<8 x s32>), 24
+// %34:_(<64 x s32>) = G_INSERT_SUBVECTOR %33:_, %19:_(<8 x s32>), 32
+// %35:_(<64 x s32>) = G_INSERT_SUBVECTOR %34:_, %22:_(<8 x s32>), 40
+// %36:_(<64 x s32>) = G_INSERT_SUBVECTOR %35:_, %25:_(<8 x s32>), 48
+// %37:_(<64 x s32>) = G_INSERT_SUBVECTOR %36:_, %28:_(<8 x s32>), 56
+// %4:_(<32 x s32>) = G_EXTRACT_SUBVECTOR %37:_(<64 x s32>), 32
+// =>
+// %29:_(<32 x s32>) = IMPLICIT_DEF
+// %34:_(<32 x s32>) = G_INSERT_SUBVECTOR %33:_, %19:_(<8 x s32>), 0
+// %35:_(<32 x s32>) = G_INSERT_SUBVECTOR %34:_, %22:_(<8 x s32>), 8
+// %36:_(<32 x s32>) = G_INSERT_SUBVECTOR %35:_, %25:_(<8 x s32>), 16
+// %37:_(<32 x s32>) = G_INSERT_SUBVECTOR %36:_, %28:_(<8 x s32>), 24
+// %4:_(<32 x s32>) = COPY %37:_(<32 x s32>)
+def extract_subvector_partial_matchdata : GIDefMatchData<"SmallVector<MachineInstr *, 8>">;
+def extract_subvector_partial : GICombineRule<
+ (defs root:$root, extract_subvector_partial_matchdata:$matchinfo),
+ (match (wip_match_opcode G_EXTRACT_SUBVECTOR):$root,
+ [{ return matchExtractSubvectorPartial(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyExtractSubvectorPartial(*${root}, ${matchinfo}); }])
+>;
+
+// A G_BUILD_VECTOR whose elements are consecutive lanes of a single wider
+// source vector (extracted via G_UNMERGE_VALUES at lanes base, base+1) is a
+// sub-vector slice of that source. Rewrite it to G_EXTRACT_SUBVECTOR so ISel
+// emits a direct composite sub-register COPY (.xy / .zw) that register
+// coalescing folds, instead of an element-wise gather. This is what lets the
+// .zw high half become copy-free (the .xy low half already coalesced).
+// %70,%71,%72,%73 = G_UNMERGE_VALUES %0(<4 x s16>)
+// %23:_(<2 x s16>) = G_BUILD_VECTOR %72, %73 ; lanes 2,3 of %0
+// => %23:_(<2 x s16>) = G_EXTRACT_SUBVECTOR %0, 2
+def build_vector_from_unmerge_lanes_matchdata : GIDefMatchData<"std::tuple<Register, int64_t>">;
+def build_vector_from_unmerge_lanes : GICombineRule<
+ (defs root:$root, build_vector_from_unmerge_lanes_matchdata:$matchinfo),
+ (match (wip_match_opcode G_BUILD_VECTOR):$root,
+ [{ return matchBuildVectorFromUnmergeLanes(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyBuildVectorFromUnmergeLanes(*${root}, ${matchinfo}); }])
+>;
+
+// A G_BUILD_VECTOR that concatenates whole sub-vectors (each source's lanes
+// appear in order) is a G_CONCAT_VECTORS. Rewrite it so ISel writes each
+// source into its composite sub-register slice (.xy / .zw) directly, instead
+// of an element-wise rebuild.
+// %56,%57 = G_UNMERGE_VALUES %14(<2 x s16>) ; %36,%37 = G_UNMERGE_VALUES %29
+// %2:_(<4 x s16>) = G_BUILD_VECTOR %56, %57, %36, %37
+// => %2:_(<4 x s16>) = G_CONCAT_VECTORS %14, %29
+def build_vector_concat_subvectors_matchdata : GIDefMatchData<"SmallVector<Register, 4>">;
+def build_vector_concat_subvectors : GICombineRule<
+ (defs root:$root, build_vector_concat_subvectors_matchdata:$matchinfo),
+ (match (wip_match_opcode G_BUILD_VECTOR):$root,
+ [{ return matchBuildVectorConcatSubvectors(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyBuildVectorConcatSubvectors(*${root}, ${matchinfo}); }])
+>;
+
+// %6 = G_CONSTANT i32 3
+// %2 = G_BUILD_VECTOR %23, %25, %27, %29, %30
+// %3 = G_EXTRACT_VECTOR_ELT %2, %6
+// => %3 = COPY %29
+def build_vector_extract_to_copy : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_EXTRACT_VECTOR_ELT):$root,
+ [{
+ auto *BuildMI = getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI);
+ if (BuildMI->getOpcode() == TargetOpcode::G_BUILD_VECTOR) {
+ auto C = getIConstantVRegValWithLookThrough(${root}->getOperand(2).getReg(), MRI);
+ return C.has_value();
+ }
+ return false;
+ }]),
+ (apply
+ [{
+ auto [BuildMI, RegIdx] = PISA::getDefIgnoringBitcasts(${root}->getOperand(1).getReg(), MRI);
+ auto C = getIConstantVRegValWithLookThrough(${root}->getOperand(2).getReg(), MRI);
+ auto DstReg = ${root}->getOperand(0).getReg();
+ auto OpndReg = BuildMI->getOperand(C->Value.getZExtValue() + 1).getReg();
+ if (DstReg != OpndReg)
+ B.buildCopy(DstReg, OpndReg);
+ ${root}->eraseFromParent();
+ }])
+>;
+
+// s16 %1 = G_CONSTANT i16 1
+// s32 %2 = G_CONSTANT i32 2
+// s16 = G_SHL %1, %2
+// => s16 = G_CONSTANT i16 4
+// note: occurs when dealing with vectors of i1
+def shifts_of_constants : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_SHL, G_LSHR, G_ASHR):$root,
+ [{
+ auto *Src0MI = getDefIgnoringCopies(${root}->getOperand(1).getReg(), MRI);
+ auto *Src1MI = getDefIgnoringCopies(${root}->getOperand(2).getReg(), MRI);
+ return (Src0MI->getOpcode() == TargetOpcode::G_CONSTANT)
+ && (Src1MI->getOpcode() == TargetOpcode::G_CONSTANT);
+ }]),
+ (apply
+ [{
+ applyShiftOfConstants(${root});
+ }])
+>;
+
+// %5:_(s32) = G_CONSTANT i32 3
+// %4:_(s32) = G_INTRINSIC intrinsic(@llvm.pisa.lane.id)
+// %6:_(s32) = nuw nsw G_SHL %4:_, %5:_(s32)
+// %7:_(s64) = nneg G_ZEXT %6:_(s32)
+// %31:_(s64) = G_CONSTANT i64 2
+// %9:_(s64) = nuw nsw G_SHL %7:_, %31:_(s64)
+// => %4:_(s32) = G_INTRINSIC intrinsic(@llvm.pisa.lane.id)
+// %5:_(s32) = G_CONSTANT i32 5
+// %6:_(s32) = nuw nsw G_SHL %4:_, %5:_(s32)
+// %7:_(s64) = nneg G_ZEXT %6:_(s32)
+def lane_id_shl_chain : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_SHL):$root,
+ [{ return matchLaneIdLeftShiftChain(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// A(16) = G_EXTRACT_VECTOR_ELT ARG(32), 0
+// B(16) = G_EXTRACT_VECTOR_ELT ARG(32), 1
+// C(<2x16>) = G_BUILD_VECTOR A, B
+// D(32) = G_BITCAST C(<2x16>)
+// => D(32) = COPY ARG(32)
+def remove_redundant_moves_pre : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_BITCAST):$root,
+ [{ return matchRedundantMovesPre(*${root}); }]),
+ (apply
+ [{ applyRedundantMovesPre(*${root}); }])
+>;
+// A(<2x16>) = G_BITCAST ARG(32)
+// B(16), C(16) = G_UNMERGE_VALUES A(<2x16)
+// D(<2x16>) = G_BUILD_VECTOR B, C
+// E(32) = G_BITCAST D(<2x16>)
+// => E(32) = COPY ARG(32)
+def remove_redundant_moves_post : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_BITCAST):$root,
+ [{ return matchRedundantMovesPost(*${root}); }]),
+ (apply
+ [{ applyRedundantMovesPost(*${root}); }])
+>;
+
+// %4:_(<32 x s8>) = G_BITCAST %3:_(<4 x s64>)
+// %5:_(<8 x s32>) = G_BITCAST %4:_(<32 x s8>)
+// => %5:_(<8 x s32>) = G_BITCAST %3:_(<4 x s64>)
+def redundant_bitcasts : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_BITCAST):$root,
+ [{
+ auto DstReg = ${root}->getOperand(0).getReg();
+ auto SrcReg = ${root}->getOperand(1).getReg();
+ auto *SrcMI = getDefIgnoringCopies(SrcReg, MRI);
+ // do not remove casts between integers and floating point
+ if (MRI.getType(DstReg).getScalarType().isFloat() != MRI.getType(SrcReg).getScalarType().isFloat())
+ return false;
+ // favor <? x s32> as we can use extract/insert ops
+ return (SrcMI->getOpcode() == TargetOpcode::G_BITCAST)
+ && (MRI.getType(DstReg).getScalarSizeInBits() == 32);
+ }]),
+ (apply
+ [{
+ auto DstReg = ${root}->getOperand(0).getReg();
+ auto SrcReg = ${root}->getOperand(1).getReg();
+ auto *SrcMI = getDefIgnoringCopies(SrcReg, MRI);
+ auto OrigSrcReg = SrcMI->getOperand(1).getReg();
+ if (MRI.getType(DstReg) == MRI.getType(OrigSrcReg))
+ B.buildCopy(DstReg, OrigSrcReg);
+ else
+ B.buildBitcast(DstReg, OrigSrcReg);
+ ${root}->eraseFromParent();
+ }])
+>;
+
+// A(s16) = G_EXTRACT_VECTOR_ELT ARG(<N x s16), 0
+// B(s16) = G_EXTRACT_VECTOR_ELT ARG(<N x s16), 1
+// ...
+// X(s16) = G_EXTRACT_VECTOR_ELT ARG(<N x s16>), N-1
+// Y(<N x s16>) = G_BUILD_VECTOR A, B, ..., X
+// => Y(<N x s16>) = COPY ARG(<N x s16>)
+def extract_all_build_vector : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_BUILD_VECTOR):$root,
+ [{ return matchExtractAllToBuildVector(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyExtractAllToBuildVector(*${root}, ${matchinfo}); }])
+>;
+
+// Fold int2ptr(ptr2int(x)) -> x
+// - LLVM version does not consider difference
+// in size between PTR and integer
+def p2i_to_i2p_fixed: GICombineRule<
+ (defs root:$dst, register_matchinfo:$matchinfo),
+ (match (G_PTRTOINT $int, $ptr),
+ (G_INTTOPTR $dst, $int):$root,
+ [{
+ auto PTy = MRI.getType(${ptr}.getReg());
+ auto TTy = MRI.getType(${int}.getReg());
+ auto DTy = MRI.getType(${dst}.getReg());
+ if (PTy.getScalarSizeInBits() != TTy.getScalarSizeInBits())
+ return false;
+ if (TTy.getScalarSizeInBits() != DTy.getScalarSizeInBits())
+ return false;
+ // A COPY between pointers of different address spaces is illegal
+ if (PTy != DTy)
+ return false;
+ ${matchinfo} = ${ptr}.getReg();
+ return true;
+ }]),
+ (apply
+ [{
+ B.buildCopy(${dst}, ${ptr});
+ ${root}->eraseFromParent();
+ }])
+>;
+
+def or_and_to_bfi : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_OR):$root,
+ [{ return matchOrAndToBfi(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// s32 %0 = G_CONSTANT i32 32
+// s32 %1 = G_SUB i32 %0, %width
+// s32 %2 = G_SUB i32 %1, %offset
+// s32 %3 = G_SHL i32 %data, %2
+// s32 %4 = G_LSHR i32 %3, %1
+// => bfe %data %width %offset
+def shift_sub_to_bfe : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_LSHR, G_ASHR):$root,
+ [{ return matchShiftSubToBfe(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// s32 %maskedwidth = G_AND %width, 63
+// s32 %maskedoffset = G_AND %offset, 31
+// bfe %data %maskedwidth %maskedoffset
+// =>
+// bfe %data %width %offset
+def and_bitfield_to_bitfield : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_INTRINSIC, G_UBFX, G_SBFX):$root,
+ [{ return matchAndBitfieldToBitfield(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// This transforms an unoptimal pattern for fcmp + zext i1->i8
+// %14:_(<2 x s16>) = G_BITCAST %CmpRes:_(s32)
+// %20:_(s16), %21:_(s16) = G_UNMERGE_VALUES %14:_(<2 x s16>)
+// %22:_(s8) = G_TRUNC %20:_(s16)
+// %23:_(s8) = G_TRUNC %21:_(s16)
+// %17:_(<2 x s8>) = G_BUILD_VECTOR %22:_(s8), %23:_(s8)
+// %28:_(s16) = G_CONSTANT i16 257
+// %19:_(<2 x s8>) = G_BITCAST %28:_(s16)
+// %24:_(s16) = G_BITCAST %17:_(<2 x s8>)
+// %25:_(s16) = G_BITCAST %19:_(<2 x s8>)
+// %26:_(s16) = G_AND %24:_, %25:_
+// %res:_(<2 x s8>) = G_BITCAST %26:_(s16)
+// => bfe %1, %CmpRes, 1, 0
+// bfe %2, %CmpRes, 1, 16
+// bfi %3, %1, %2, 8
+// trunc.16.32 %res, %3
+def v2_i1_zext_to_bfe_bfi : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_BITCAST):$root,
+ [{ return matchV2i1ZextToBfeBfi(*${root}, ${matchinfo}); }]),
+ (apply [{ applyV2i1ZextToBfeBfi(*${root}, ${matchinfo}); }])>;
+
+// frcp(sqrt(x)) -> frsqrt(x)
+// sqrt(frcp(x)) -> frsqrt(x)
+// frcp(fabs(sqrt(x))) -> fabs(frsqrt(x))
+def rcp_sqrt_to_rsqrt : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_INTRINSIC, G_FSQRT, G_FDIV):$root,
+ [{ return matchRcpSqrtToRsqrt(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// s32 %floor = G_FFLOOR %x
+// s32 %dst = G_FSUB %x, %floor
+// => s32 %dst = frc %x
+// The plain G_FSUB fold requires contract on both the fsub and the floor. Also
+// matches the pisa.fsub intrinsic form, but only for round.towardzero with no
+// saturation.
+def sub_floor_to_frc : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_FSUB, G_INTRINSIC):$root,
+ [{ return matchSubFloorToFrc(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// sequence repeated multiple times ...
+// %39:_(s8) = G_EXTRACT_VECTOR_ELT %4:_(<32 x s8>), [0..31]
+// %41:_(s32) = G_ZEXT %39:_(s8)
+// %42:_(s32) = nuw nsw G_SHL %41:_, [0,8,16,24]
+// %43:_(s32) = disjoint G_OR %38:_, %42:_
+// followed by ...
+// %157:_(<8 x s32>) = G_BUILD_VECTOR %43:_(s32), ...
+// => %157:_(<8 x s32>) = G_BITCAST %4:_(<32 x s8>)
+def extract_insert_to_bitcast : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_BUILD_VECTOR):$root,
+ [{ return matchExtractInsertToBitcast(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyExtractInsertToBitcast(*${root}, ${matchinfo}); }])
+>;
+
+// %374:_(s16) = G_CONSTANT i16 1
+// %375:_(s16) = G_AND %321:_, %374:_
+// %421:_(s16) = G_CONSTANT i16 0
+// %422:_(s16) = G_CONSTANT i16 1
+// %405:_(s16) = G_PISA_SELECT %375:_, %422:_, %421:_
+// => %374:_(s16) = G_CONSTANT i16 1
+// => %375:_(s16) = G_AND %321:_, %374:_
+// => %405:_(s16) = COPY %375
+def and_select : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_PISA_SELECT):$root,
+ [{ return matchAndSelect(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyAndSelect(*${root}, ${matchinfo}); }])
+>;
+
+// %23:_(<4 x s8>) = G_BITCAST %1:_(s32)
+// %2:_(s8) = G_EXTRACT_VECTOR_ELT %23:_(<4 x s8>), %25:_(s32)
+// %7:_(s32) = G_LSHR %1:_, %6:_(s32)
+// %8:_(s8) = G_TRUNC %7:_(s32)
+// %12:_(s32) = G_LSHR %1:_, %11:_(s32)
+// %13:_(s8) = G_TRUNC %12:_(s32)
+// %24:_(s32) = G_CONSTANT i32 3
+// %18:_(s8) = G_EXTRACT_VECTOR_ELT %23:_(<4 x s8>), %24:_(s32)
+// %19:_(<4 x s8>) = G_BUILD_VECTOR %2:_(s8), %8:_(s8), %13:_(s8), %18:_(s8)
+// => %19:_(<4 x s8>) = G_BITCAST %1:_(s32)
+def extract_build_vector_to_bitcast : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_BUILD_VECTOR):$root,
+ [{ return matchExtractBuildVectorToBitcast(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyExtractBuildVectorToBitcast(*${root}, ${matchinfo}); }])
+>;
+
+// Reduce predicates in s32.
+// %12:_(s1) = G_CONSTANT i1 true
+// %3:_(<3 x s1>) = G_FCMP floatpred(oeq), %0:regv3_32b(<3 x s32>), %1:_
+// %4:_(s1) = G_EXTRACT_VECTOR_ELT %3:_(<3 x s1>), %5:_(s32)
+// %6:_(s1) = G_EXTRACT_VECTOR_ELT %3:_(<3 x s1>), %7:_(s32)
+// %8:_(s1) = G_EXTRACT_VECTOR_ELT %3:_(<3 x s1>), %9:_(s32)
+// %10:_(s1) = G_AND %4:_, %6:_
+// %11:_(s1) = G_AND %10:_, %8:_
+// %13:_(s1) = G_ICMP intpred(eq), %11:_(s1), %12:_
+// => %3:_(<3 x s1>) = G_FCMP floatpred(oeq), %0:regv3_32b(<3 x s32>), %1:_
+// %18:_(<3 x s32>) = G_SEXT %3:_(<3 x s1>)
+// %20:_(s32) = G_CONSTANT i32 0
+// %19:_(s32) = G_EXTRACT_VECTOR_ELT %18:_(<3 x s32>), %20:_(s32)
+// %23:_(s32) = G_CONSTANT i32 1
+// %21:_(s32) = G_EXTRACT_VECTOR_ELT %18:_(<3 x s32>), %23:_(s32)
+// %22:_(s32) = G_AND %19:_, %21:_
+// %26:_(s32) = G_CONSTANT i32 2
+// %24:_(s32) = G_EXTRACT_VECTOR_ELT %18:_(<3 x s32>), %26:_(s32)
+// %25:_(s32) = G_AND %22:_, %24:_
+// %27:_(s32) = G_CONSTANT i32 -1
+// %13:_(s1) = G_ICMP intpred(eq), %25:_(s32), %27:_
+// Supports patterns with sext too:
+// %13:_(s8) = G_CONSTANT i8 -1
+// %3:_(<3 x s1>) = G_FCMP floatpred(oeq), %0:regv3_32b(<3 x s32>), %1:_
+// %4:_(<3 x s8>) = G_SEXT %3:_(<3 x s1>)
+// %5:_(s8) = G_EXTRACT_VECTOR_ELT %4:_(<3 x s8>), %6:_(s32)
+// %7:_(s8) = G_EXTRACT_VECTOR_ELT %4:_(<3 x s8>), %8:_(s32)
+// %9:_(s8) = G_EXTRACT_VECTOR_ELT %4:_(<3 x s8>), %10:_(s32)
+// %11:_(s8) = G_AND %5:_, %7:_
+// %12:_(s8) = G_AND %11:_, %9:_
+// %14:_(s1) = G_ICMP intpred(eq), %12:_(s8), %13:_
+def reduce_predicates : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_AND, G_OR, G_XOR):$root,
+ [{ return matchReducePredicates(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// %23:_(s32) = G_CONSTANT i32 8
+// %17:_(s8) = G_TRUNC %1:reg32b(s32)
+// %24:_(s32) = G_LSHR %1:reg32b, %23:_(s32)
+// %25:_(s8) = G_TRUNC %24:_(s32)
+// %28:_(s8) = G_TRUNC %2:reg32b(s32)
+// %31:_(s32) = G_LSHR %2:reg32b, %23:_(s32)
+// %32:_(s8) = G_TRUNC %31:_(s32)
+///%149:_(<4 x s8>) = G_BUILD_VECTOR %17:_(s8), %25:_(s8), %28:_(s8), %32:_(s8)
+// %141:_(s32) = G_BITCAST %149:_(<4 x s8>)
+// => %A = G_AND %1, 0xFFFF
+// => %B = G_SHL %2, 16
+// => %141 = G_OR %A, %B
+def reg2_matchdata : GIDefMatchData<"Reg2MatchInfo">;
+def build_reg_from_2 : GICombineRule<
+ (defs root:$root, reg2_matchdata:$matchinfo),
+ (match (wip_match_opcode G_BITCAST):$root,
+ [{ return matchBuildRegFrom2(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyBuildRegFrom2(*${root}, ${matchinfo}); }])
+>;
+
+// %9:_(s1) = G_FCMP floatpred(oge), %1:reg32b(s32), %2:reg32b
+// %22:_(s32) = G_CONSTANT i32 0
+// %23:_(s32) = G_CONSTANT i32 -1
+// %10:_(s32) = G_SELECT %9:_(s1), %23:_, %22:_
+// %11:_(s32) = G_CONSTANT i32 -1
+// %12:_(s32) = G_XOR %10:_, %11:_
+// %13:_(s32) = G_CONSTANT i32 -1
+// %6:_(s1) = G_ICMP intpred(eq), %12:_(s32), %13:_
+// G_BRCOND %6:_(s1), %bb.4
+// G_BR %bb.3
+// => %9:_(s1) = G_FCMP floatpred(oge), %1:reg32b(s32), %2:reg32b
+// => G_BRCOND %6:_(s1), %bb.3
+// => G_BR %bb.4
+def opt_fcmp_brcond_by_inverting_cond_matchdata : GIDefMatchData<"std::tuple<MachineInstr *, Register>">;
+def opt_fcmp_brcond_by_inverting_cond : GICombineRule<
+ (defs root:$root, opt_fcmp_brcond_by_inverting_cond_matchdata:$matchinfo),
+ (match (wip_match_opcode G_BR):$root,
+ [{ return matchFCmpInvertedCond(*${root}, ${matchinfo}); }]),
+ (apply [{ applyFCmpInvertedCond(*${root}, ${matchinfo}); }])
+>;
+
+def cmp_and_all_ones : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_ICMP):$root,
+ [{ return matchCmpAndAllOnes(*${root}, VT, ${matchinfo}); }]),
+ (apply
+ [{ applyCmpAndAllOnes(*${root}, ${matchinfo}); }])
+>;
+
+def sink_trunc_matchdata : GIDefMatchData<"std::tuple<Register, int64_t>">;
+def sink_trunc : GICombineRule<
+ (defs root:$root, sink_trunc_matchdata:$matchinfo),
+ (match (wip_match_opcode G_AND, G_OR, G_XOR):$root,
+ [{ return matchSinkTrunc(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applySinkTrunc(*${root}, ${matchinfo}); }])
+>;
+
+def trunc_trunc : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_TRUNC):$root,
+ [{ return matchTruncTrunc(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyTruncTrunc(*${root}, ${matchinfo}); }])
+>;
+
+// %1:registers(s32) = G_ABS %0:reg32b
+// %4:registers(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.pisa.ired),
+// IRedOp::UMAX, %1:registers(s32), %2:reg32b(s32), %3:reg32b(s32)
+// => %1:registers(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.pisa.ired),
+// IRedOp::ABSMAX, %0:registers(s32), %2:reg32b(s32), %3:reg32b(s32)
+def abs_redmax_to_redabsmax : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_INTRINSIC_CONVERGENT):$root,
+ [{ return matchAbsRedMaxToRedAbsMax(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// %9:_(s1) = G_CONSTANT i1 true
+// %8:_(s1) = G_ICMP intpred(eq), %15:_(s32), %22:_
+// %10:_(s1) = G_ICMP intpred(eq), %8:_(s1), %9:_
+// %11:_(s32) = G_SEXT %10:_(s1)
+// => %8:_(s1) = G_ICMP intpred(eq), %15:_(s32), %22:_
+// %11:_(s32) = G_SEXT %8:_(s1)
+// or
+// %12:_(s1) = G_CONSTANT i1 true
+// %11:_(s1) = G_AND %10:_, %35:_
+// %13:_(s1) = G_ICMP intpred(eq), %11:_(s1), %12:_
+// %14:_(s32) = G_SEXT %13:_(s1)
+// => %11:_(s1) = G_AND %10:_, %35:_
+// %14:_(s32) = G_SEXT %11:_(s1)
+def cmp_int1 : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_ICMP):$root,
+ [{ return matchCmpInt1(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// %1:_(i1) = G_TRUNC %0:_(wide)
+// %2:_(N) = G_SELECT %1:_(i1), N 1, N 0
+// => %3:_(wide) = G_AND %0, 1
+// %2:_(N) = G_TRUNC %3
+// Avoids introducing an illegal i1 compare when lowering bool-to-int.
+def select_trunc_one_zero : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_SELECT):$root,
+ [{ return matchSelectTruncOneZero(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// %45:_(s32) = G_CONSTANT i32 -1
+// %60:_(s32) = G_CONSTANT i32 0
+// %46:_(s32) = G_SELECT %10:_(s1), %45:_, %60:_
+// %47:_(s32) = G_SELECT %35:_(s1), %45:_, %60:_
+// %48:_(s32) = G_AND %46:_, %47:_
+// %59:_(s32) = G_CONSTANT i32 31
+// %58:_(s32) = G_SHL %48:_, %59:_(s32)
+// %14:_(s32) = G_ASHR %58:_, %59:_(s32)
+// => %14:_(s32) = G_AND %46:_, %47:_
+def shift_true_false : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_ASHR):$root,
+ [{ return matchShiftTrueFalse(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+// %54:_(s8), %55:_(s8), %56:_(s8), %57:_(s8) = G_UNMERGE_VALUES %21:_(<4 x s8>)
+// %39:_(s16) = G_ANYEXT %54:_(s8)
+// %40:_(s16) = G_ANYEXT %55:_(s8)
+// %41:_(s16) = G_ADD %39:_, %40:_
+// %36:_(s16) = G_ANYEXT %56:_(s8)
+// %37:_(s16) = G_ANYEXT %57:_(s8)
+// %38:_(s16) = G_ADD %36:_, %37:_
+// %35:_(s16) = G_ADD %41:_, %38:_
+// => %40:_(s32) = G_INTRINSIC intrinsic(@llvm.pisa.dp4a.uu), 0, %21, 0x01010101, 0
+// %35:_(s16) = G_TRUNC %40:_(s32)
+def add_i8_reduction : GICombineRule<
+ (defs root:$root, build_fn_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_ADD):$root,
+ [{ return matchAddInt8Reduction(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.applyBuildFn(*${root}, ${matchinfo}); }])>;
+
+
+// %370:_(i16), %371:_(i16) = G_UNMERGE_VALUES %366:_(<2 x i16>)
+// %378:_(f16) = G_BITCAST %370:_(i16)
+// %379:_(f16) = G_BITCAST %371:_(i16)
+// %37:_(<2 x f16>) = G_BUILD_VECTOR %378:_(f16), %379:_(f16)
+// => %37:_(<2 x f16>) = G_BITCAST %366:_(<2 x i16>)
+def unmerge_bitcast_buildvector_to_bitcast : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_BUILD_VECTOR):$root,
+ [{ return matchUnmergeBitcastBuildVectorToBitcast(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyUnmergeBitcastBuildVectorToBitcast(*${root}, ${matchinfo}); }])>;
+
+// G_FENCE ordering_A, scope_X
+// ... pure computation (no memory ops) ...
+// G_FENCE ordering_B, scope_X
+// => coalesce into a single G_FENCE with the merged AtomicOrdering
+// (e.g. release+acquire -> acq_rel, anything+seq_cst -> seq_cst)
+def redundant_fence : GICombineRule<
+ (defs root:$root, MI_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_FENCE):$root,
+ [{ return matchRedundantFence(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyRedundantFence(*${root}, ${matchinfo}); }])
+>;
+
+// G_FENCE ordering, scope_X-addrspace_A
+// ... pure computation (no memory ops) ...
+// G_FENCE ordering, scope_X-addrspace_B
+// => G_FENCE ordering, scope_X-generic
+// Merges adjacent fences with same ordering and same base scope but different
+// address spaces into a single generic fence.
+def merge_adjacent_fences : GICombineRule<
+ (defs root:$root, MI_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_FENCE):$root,
+ [{ return matchMergeAdjacentFences(*${root}, ${matchinfo}); }]),
+ (apply
+ [{ applyMergeAdjacentFences(*${root}, ${matchinfo}); }])
+>;
+
+// p2i_to_i2p do not account for pointer-size or address-space difference
+def pisa_identity_combines : GICombineGroup<
+ !listremove(identity_combines.Rules, [p2i_to_i2p])>;
+// avoid G_SELECT %2:_(i1), -1, 0 => G_SEXT %2:_(i1)
+def pisa_select_combines : GICombineGroup<
+ !listremove(select_combines.Rules, [match_selects])>;
+// avoid G_BUILD_VECTOR => G_CONCAT_VECTOR
+// avoid x,y<dead>=unmerge(z) => x=trunc(z): causes G_TRUNC on wide types
+def pisa_merge_combines : GICombineGroup<
+ !listremove(merge_combines.Rules, [combine_build_unmerge, unmerge_dead_to_trunc])>;
+// avoid G_BUILD_VECTOR(undef,undef) => G_IMPLICIT_DEF
+// avoid G_BITCAST/G_TRUNC(undef) => G_IMPLICIT_DEF: post-legalizer must not
+// introduce G_IMPLICIT_DEF for types the legalizer already scalarized
+// (e.g., <2 x i32> resulting from a bitcast of a legal i64 undef)
+def pisa_undef_combines : GICombineGroup<
+ !listremove(undef_combines.Rules, [propagate_undef_all_ops,
+ unary_undef_to_undef])>;
+// narrow_binop_* are conflicting with hoist_logic_op_with_same_opcode_hands, remove them to avoid loop.
+def pisa_cast_combines: GICombineGroup<
+ !listremove(cast_combines.Rules, [
+ narrow_binop_and,
+ narrow_binop_or,
+ narrow_binop_xor])>;
+
+// Rules removed from both pre- and post-legalizer combiners.
+def pisa_removed_combines : GICombineGroup<[
+ identity_combines, // use pisa_identity_combines
+ select_combines, // use pisa_select_combines
+ cast_combines, // use pisa_cast_combines
+ merge_combines, // use pisa_merge_combines
+ undef_combines, // use pisa_undef_combines
+ combines_for_extload, // prevents post-legalizer re-folding of G_LOAD+G_SEXT → G_SEXTLOAD
+ div_rem_to_divrem, // G_[US]DIVREM not supported
+ intrem_combines, // use [su]div.rem instruction
+ opt_brcond_by_inverting_cond,
+ trunc_shift,
+ fold_binop_into_select, // breaks sext(fcmp) AND patterns needed for bfn matching
+ freeze_combines // push_freeze_to_prevent_poison_from_propagating can push G_FREEZE through G_SELECT creating illegal G_FREEZE(i1) after legalization; exclude entire group and re-add freeze_of_non_undef_non_poison explicitly below
+]>;
+
+// Additional rules removed only from the post-legalizer combiner.
+// - intdiv_combines(sdiv_by_const) emits G_SMULH/G_UMULH
+def pisa_post_removed_combines : GICombineGroup<[
+ commute_shift,
+ intdiv_combines
+]>;
+
+// Fold min/max with an identity-element constant:
+// umin(x, UINT_MAX) -> x, umax(x, 0) -> x
+// smin(x, INT_MAX) -> x, smax(x, INT_MIN) -> x
+// The legalizer introduces such patterns when expanding G_VECREDUCE_U/SMIN/MAX
+// (padding lanes are filled with the identity element).
+def minmax_identity_fold : GICombineRule<
+ (defs root:$root, register_matchinfo:$matchinfo),
+ (match (wip_match_opcode G_UMIN, G_UMAX, G_SMIN, G_SMAX):$root,
+ [{ return matchMinMaxIdentityFold(*${root}, ${matchinfo}); }]),
+ (apply [{ Helper.replaceSingleDefInstWithReg(*${root}, ${matchinfo}); }])
+>;
+
+// Fix G_SHL/G_LSHR/G_ASHR shift amounts to i32 post-legalization.
+// PISA only legalizes shifts with i32 shift amounts ({I16,I32}, {I32,I32},
+// {I64,I32}), but post-legalizer rules such as mul_to_shl may introduce a
+// new shift whose amount has the value type (e.g. i64). This rule truncates
+// or zero-extends such shift amounts back to i32.
+def pisa_fix_shift_amt : GICombineRule<
+ (defs root:$root),
+ (match (wip_match_opcode G_SHL, G_LSHR, G_ASHR):$root,
+ [{ return matchFixIllegalShiftAmt(*${root}); }]),
+ (apply [{ applyFixIllegalShiftAmt(*${root}); }])>;
+
+let CombineAllMethodName = "tryCombineAllImpl" in {
+
+def PISAPreLegalizerCombiner: GICombiner<
+ "PISAPreLegalizerCombinerImpl",
+ !listremove(
+ !listconcat(all_combines.Rules, [
+ pisa_identity_combines, pisa_cast_combines, pisa_select_combines, pisa_merge_combines, pisa_undef_combines,
+ truncated_load, truncated_store, trunc_shift_to_vector_extract,
+ extended_load, simplify_nonpowerof2_load_store_chain, expand_nonpowerof2_load_store,
+ extract_insert_to_bitcast, redundant_bitcasts, remove_redundant_moves_pre,
+ rcp_sqrt_to_rsqrt, sub_floor_to_frc, extract_build_vector_to_bitcast,
+ reduce_predicates, cmp_int1, select_trunc_one_zero,
+ // match_selects must run in the pre-legalizer (after select_trunc_one_zero
+ // so trunc-based patterns take priority) to convert
+ // G_SELECT(i1, true, x) → G_OR(i1, x) via tryFoldBoolSelectToLogic.
+ // After legalization this OR becomes a real G_OR that PISABFNMatcher uses
+ // to produce bfn.0xa8. It is excluded from pisa_select_combines
+ // (post-legalizer only) to prevent select(i1, wide -1, wide 0) → sext(i1).
+ match_selects,
+ lane_id_shl_chain, zext_and_to_and_zext,
+ freeze_of_non_undef_non_poison]),
+ pisa_removed_combines.Rules)>;
+
+def PISAPostLegalizerCombiner: GICombiner<
+ "PISAPostLegalizerCombinerImpl",
+ !listremove(
+ !listconcat(all_combines.Rules, [
+ pisa_identity_combines, pisa_cast_combines, pisa_select_combines, pisa_merge_combines, pisa_undef_combines,
+ extra_commute_iconstant_to_rhs, extra_commute_fconstant_to_rhs, extra_commute_intrinsic_iconstant_to_rhs,
+ extra_remove_redundant_constant, build_vector_with_constants, compare_select_reg,
+ shl_add_to_mad, fdiv_to_rcp_fmul, const_and_trunc_to_copy,
+ build_vector_extract_to_copy, extract_subvector_to_extract_elt, extract_subvector_build_vector, extract_subvector_partial, build_vector_from_unmerge_lanes, build_vector_concat_subvectors, shifts_of_constants,
+ remove_redundant_moves_post, p2i_to_i2p_fixed, extract_all_build_vector, unmerge_bitcast_buildvector_to_bitcast,
+ redundant_bitcasts, or_and_to_bfi, shift_sub_to_bfe, v2_i1_zext_to_bfe_bfi, and_bitfield_to_bitfield,
+ and_select, build_reg_from_2, opt_fcmp_brcond_by_inverting_cond,
+ cmp_and_all_ones, sink_trunc, trunc_trunc, abs_redmax_to_redabsmax,
+ shift_true_false, add_i8_reduction, redundant_fence,
+ freeze_of_non_undef_non_poison, pisa_fix_shift_amt, minmax_identity_fold,
+ merge_adjacent_fences]),
+ !listconcat(pisa_removed_combines.Rules, pisa_post_removed_combines.Rules))>;
+}
diff --git a/llvm/lib/Target/PISA/PISADefines.h b/llvm/lib/Target/PISA/PISADefines.h
new file mode 100644
index 0000000000000..42887a9c86db4
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISADefines.h
@@ -0,0 +1,52 @@
+//===-- PISADefines.h - Common defines for PISA ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_PISADEFINES_H
+#define LLVM_LIB_TARGET_PISA_PISADEFINES_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include <cstdint>
+
+namespace llvm {
+namespace PISA {
+
+enum class Swizzle : unsigned { X, Y, Z, W, XYZW, XY, ZW, NONE };
+constexpr unsigned SimdSize = 32;
+
+// Keep these in sync with PISAInstrInfo.td!
+enum class LdCacheCtrl : unsigned {
+ Default = 0,
+ L1c = 1,
+ L1uc = 2,
+ L1s = 3,
+ L2c = 4,
+ L2uc = 5,
+ L3c = 6,
+ L3uc = 7,
+ ri = 8
+};
+
+enum class StCacheCtrl : unsigned {
+ Default = 0,
+ L1uc = 1,
+ L1wb = 2,
+ L1wt = 3,
+ L1s = 4,
+ L2uc = 5,
+ L2wb = 6,
+ L3uc = 7,
+ L3wb = 8
+};
+
+enum class AtomCacheCtrl : unsigned { Default = 0, uc = 2, L2wb = 4, L3wb = 3 };
+
+} // namespace PISA
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_PISADEFINES_H
diff --git a/llvm/lib/Target/PISA/PISAGenericOpcodes.td b/llvm/lib/Target/PISA/PISAGenericOpcodes.td
new file mode 100644
index 0000000000000..f3579d60d86d3
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAGenericOpcodes.td
@@ -0,0 +1,99 @@
+//===-- PISAGenericOpcodes.td - PISA Generic Opcodes -------*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+class PISAGenericInstruction : GenericInstruction {
+ let Namespace = Global.NS;
+}
+
+def G_PISA_PARAM_SLOT : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins untyped_imm_0:$slot, untyped_imm_0:$offset,
+ variable_ops);
+ let hasSideEffects = false;
+}
+
+// Target-specific generic instruction
+class PISAUnaryInstruction : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins type0:$src1);
+ let hasSideEffects = false;
+}
+class PISABinaryInstruction : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins type0:$src1, type0:$src2);
+ let hasSideEffects = false;
+ let isCommutable = true;
+}
+class PISABinaryInstructionRndSat : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins type0:$src1, type0:$src2, untyped_imm_0:$src3, untyped_imm_0:$src4);
+ let hasSideEffects = false;
+ let isCommutable = false;
+}
+class PISABinaryInstructionNanp : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins type0:$src1, type0:$src2, untyped_imm_0:$nanp);
+ let hasSideEffects = false;
+ let isCommutable = true;
+}
+class PISATernaryInstruction : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins type0:$src1, type0:$src2, type0:$src3);
+ let hasSideEffects = false;
+ let isCommutable = false;
+}
+class PISAReductionInstruction : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins untyped_imm_0:$op, type0:$src1, type1:$src2, type0:$src3);
+ let hasSideEffects = false;
+ let isCommutable = false;
+}
+class PISAReductionInstructionNanp : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins untyped_imm_0:$op, type0:$src1, type1:$src2, type0:$src3, untyped_imm_0:$nanp);
+ let hasSideEffects = false;
+ let isCommutable = false;
+}
+class PISATernaryInstructionRndSat : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins type0:$src1, type0:$src2, type0:$src3, untyped_imm_0:$src4, untyped_imm_0:$src5);
+ let hasSideEffects = false;
+ let isCommutable = false;
+}
+class PISACompareInstruction : PISABinaryInstruction {
+ let isCommutable = false;
+}
+
+
+class PISAConvertInstWithRnd : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins type1:$src1, type2:$src2);
+ let hasSideEffects = false;
+}
+
+class PISACasInstruction : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$dst);
+ let InOperandList = (ins untyped_imm_0:$src1, type0:$src2, type0:$src3, untyped_imm_0:$src4);
+ let hasSideEffects = false;
+ let isCommutable = false;
+ let mayLoad = true;
+ let mayStore = true;
+}
+class PISAFAtomInstruction : PISAGenericInstruction {
+ let OutOperandList = (outs type0:$oldval);
+ let InOperandList = (ins ptype1:$addr, type0:$val);
+ let hasSideEffects = false;
+ let isCommutable = false;
+ let mayLoad = true;
+ let mayStore = true;
+}
+
+// sel
+def G_PISA_SELECT : PISATernaryInstruction;
+def pisa_select : SDNode<"PISAISD::SELECT", SDTSelect>;
+def : GINodeEquiv<G_PISA_SELECT, pisa_select>;
diff --git a/llvm/lib/Target/PISA/PISAHelpers.h b/llvm/lib/Target/PISA/PISAHelpers.h
new file mode 100644
index 0000000000000..95cb1b5aa36f9
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAHelpers.h
@@ -0,0 +1,38 @@
+//===-- PISAHelpers.h - Common helpers for PISA ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_PISAHELPERS_H
+#define LLVM_LIB_TARGET_PISA_PISAHELPERS_H
+
+#include "PISADefines.h"
+
+#include "llvm/Support/ErrorHandling.h"
+
+namespace llvm::PISA {
+
+inline unsigned getSwizzleElemCount(PISA::Swizzle Swizzle) {
+ switch (Swizzle) {
+ case PISA::Swizzle::NONE:
+ return 0;
+ case PISA::Swizzle::X:
+ case PISA::Swizzle::Y:
+ case PISA::Swizzle::Z:
+ case PISA::Swizzle::W:
+ return 1;
+ case PISA::Swizzle::XY:
+ case PISA::Swizzle::ZW:
+ return 2;
+ case PISA::Swizzle::XYZW:
+ return 4;
+ }
+ llvm_unreachable("Unknown swizzle!");
+}
+
+} // namespace llvm::PISA
+
+#endif // LLVM_LIB_TARGET_PISA_PISAHELPERS_H
diff --git a/llvm/lib/Target/PISA/PISAInstrFormats.td b/llvm/lib/Target/PISA/PISAInstrFormats.td
new file mode 100644
index 0000000000000..f1b5a8a2646a6
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAInstrFormats.td
@@ -0,0 +1,834 @@
+//===-- PISAInstrFormats.td - PISA Instruction Formats -----*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+include "PISAGenericOpcodes.td"
+include "llvm/Target/PISA/MnemonicOperand.td"
+
+class PISAAsmOperand<string am, string sig=am> : AsmOperandClass {
+ let Name = am;
+ string Signature = sig;
+ let ParserMethod = "parse"#Signature;
+ let PredicateMethod = "is"#Signature;
+}
+
+// This operand class provides a simple interface for quickly describing
+// the syntax of memory operands that are formed by a sequence
+// For example, specifying "R,i" for the args means that
+// the operand would look something like: [%r0, 5].
+class PISAMemSeqAsmOperand<string am, string args, string uniq> : AsmOperandClass {
+ let Name = am#uniq;
+ let ParserMethod = "parse"#am#"(\""#args#"\")";
+ let PredicateMethod = "is"#am;
+ let RenderMethod = "add"#am#"Operands";
+}
+
+class PISAOperandName<string name> {
+ string pm = "print"#name;
+ AsmOperandClass pmc = PISAAsmOperand<name>;
+ string em = "encode"#name;
+ string dm = "decode"#name;
+}
+
+class PISAAsmRegisterOperandPMC<RegisterClass rc, string sig>
+ : PISAAsmOperand<rc#"Opnd", sig> {
+ let RenderMethod = "add"#Signature;
+}
+
+class PISARegisterOperand<RegisterClass rc>
+ : RegisterOperand<rc> {
+ defvar name = "RegOpnd";
+ defvar RCIDStr = "PISA::"#!cast<string>(rc)#"RegClassID";
+ string Signature = name#"("#RCIDStr#")";
+
+ let PrintMethod = "print"#Signature;
+ let EncoderMethod = "encode"#Signature;
+ let DecoderMethod = "decode"#Signature;
+ let ParserMatchClass = PISAAsmRegisterOperandPMC<rc, Signature>;
+}
+
+class PISALocalVariableOperand
+ : Operand<i32>, PISAOperandName<"LocalVariableOpnd"> {
+ let PrintMethod = pm;
+ let ParserMatchClass = pmc;
+ let EncoderMethod = em;
+ let DecoderMethod = dm;
+}
+
+def FrameIndex : PISALocalVariableOperand;
+
+class PISAImmediateOperand<ValueType ty, string name>
+ : Operand<ty>, PISAOperandName<name> {
+ let OperandType = "OPERAND_IMMEDIATE";
+ let PrintMethod = pm;
+ let ParserMatchClass = pmc;
+ let EncoderMethod = em;
+ let DecoderMethod = dm;
+}
+
+class PISAImmediateRangeOperand<ValueType ty, string name, int min, int max>
+ : PISAImmediateOperand<ty, name>,
+ TImmLeaf<ty, "return Imm >= " # min # " && Imm <= " # max # ";">
+{}
+
+class PISABfnOpcodeOperand<ValueType ty, string name>
+ : PISAImmediateOperand<ty, name> {
+ let PrintMethod = "printBfnOpcode";
+}
+
+
+class PISASymbolOperand<ValueType ty, string name>
+ : Operand<ty>, PISAOperandName<name> {
+ let OperandType = "OPERAND_PCREL";
+ let PrintMethod = pm;
+ let ParserMatchClass = pmc;
+ let EncoderMethod = em;
+ let DecoderMethod = dm;
+}
+
+// Immediate address offset operand for the load/store
+class AddrOffImm<ValueType VT> : Operand<VT>{
+ let OperandType = "OPERAND_IMMEDIATE";
+ let PrintMethod = "printAddrOffsetImm";
+}
+
+def PISAGlobalVariableOpnd : PISASymbolOperand<OtherVT, "GlobalVariableOpnd">;
+
+// function name as operand (addrof)
+def FunctionOpnd : PISASymbolOperand<OtherVT, "FunctionOpnd"> {
+ // print the same as a global variable operand, i.e. "@func"
+ let PrintMethod = "printGlobalVariableOpnd";
+}
+
+class IsQualifier<string Name, string QName> {
+ defvar QIndex = !find(Name, QName#".");
+ defvar QIndexAtEnd = !if(!gt(!size(Name), !size(QName)), !sub(!size(Name), !size(QName)), 0);
+ defvar IsAtEnd = !if(!eq(QIndex, -1), !eq(!substr(Name, QIndexAtEnd, !size(QName)), QName), 0);
+ bit ret = !or(!ne(QIndex, -1), IsAtEnd);
+}
+
+class ExtractVectorIndex<string name> {
+ int ret = !foldl(-1, !range(0, 64), acc, idx,
+ !if(IsQualifier<name, "."#idx>.ret, idx, acc));
+}
+
+class _PISAInst<string Name = "UNKNOWN"> : Instruction {
+ field bits<16> Inst;
+
+ let Namespace = "PISA";
+ let DecoderNamespace = "PISA";
+
+ // !cast<void>(directive)
+ let UseNamedOperandTable = 1;
+}
+
+class PISAInst<dag outs, dag ins, string asmstr, list<dag> pattern = []>
+ : _PISAInst<asmstr> {
+ dag OutOperandList = outs;
+ dag InOperandList = ins;
+ let AsmString = asmstr;
+ let Pattern = pattern;
+}
+
+// pisaVT should be a container class only instead of a ValueType.
+class pisaVT<ValueType vt, string d, RegisterClass rc, bit isHasRC = 1> {
+ ValueType VT = vt;
+ string directive = d;
+ string opcode = !subst(".", "_", !if(vt.isFP, directive, !subst("b", "", directive)));
+ string name = !subst("_", "", !cast<string>(NAME));
+
+ string bitwidth = vt.Size#b;
+ // mdirective specifies directive for memory operations (e.g. .32b for .f type)
+ string mdirective = !if(!and(vt.isFP, !not(vt.isVector)), "."#bitwidth, directive);
+ RegisterClass RC = rc;
+ DAGOperand ImmOpnd;
+ DAGOperand RegOpnd = !if(isHasRC, PISARegisterOperand<rc>, ?);
+}
+
+// Subclasses of pisaIntVT, pisaFloatVT, pisaPtrVT, pisaVectorVT, and pisaVoidVT
+// are inherited from pisaVT.
+// The main goal is to add the functionailty of !isa<subclass>,
+// e.g., !isa<pisaIntVT>(_i16) return 1 to tell that it is an int type.
+class pisaIntVT<ValueType vt, string d, RegisterClass rc> :
+ pisaVT<vt, d, rc> {
+ let ImmOpnd = PISAImmediateOperand <vt, "Imm"#vt.Size#"Opnd">;
+}
+
+class pisaFloatVT<ValueType vt, string d, RegisterClass rc> :
+ pisaVT<vt, d, rc> {
+ let ImmOpnd = PISAImmediateOperand <vt, "FpImm"#vt.Size#"Opnd">;
+}
+
+class pisaPtrVT<ValueType vt, string d, RegisterClass rc> :
+ pisaVT<vt, d, rc>;
+
+class pisaVectorVT<ValueType vt, string d, RegisterClass rc> :
+ pisaVT<vt, d, rc>;
+
+class pisaVoidVT<ValueType vt, string d, RegisterClass rc> :
+ pisaVT<vt, d, rc, 0>;
+
+def p0 : PtrValueType<i64, 0>;
+def p1 : PtrValueType<i64, 1>;
+def p2 : PtrValueType<i64, 2>;
+def p3 : PtrValueType<i32, 3>;
+def p4 : PtrValueType<i32, 4>;
+
+// <ValueType, directive, RegisterClass>;
+def _void : pisaVoidVT<isVoid, "", ? >;
+def _i1 : pisaIntVT<i1, ".1b", Pred >;
+def _i8 : pisaIntVT<i8, ".8b", Reg8b >;
+def _i16 : pisaIntVT<i16, ".16b", Reg16b >;
+def _i32 : pisaIntVT<i32, ".32b", Reg32b >;
+def _i64 : pisaIntVT<i64, ".64b", Reg64b >;
+def _i128 : pisaIntVT<i128, ".128b", Reg128b >;
+def _f16 : pisaFloatVT<f16, ".hf", Reg16b >;
+def _bf16 : pisaFloatVT<bf16, ".bf", Reg16b >;
+def _f32 : pisaFloatVT<f32, ".f", Reg32b >;
+def _f64 : pisaFloatVT<f64, ".df", Reg64b >;
+def _p0 : pisaPtrVT<p0, ".64b", Reg64b >;
+def _p1 : pisaPtrVT<p1, ".64b", Reg64b >;
+def _p2 : pisaPtrVT<p2, ".64b", Reg64b >;
+def _p3 : pisaPtrVT<p3, ".32b", Reg32b >;
+def _p4 : pisaPtrVT<p4, ".32b", Reg32b >;
+def _v2i8 : pisaVectorVT<v2i8, ".v2.8b", RegV2_8b >;
+def _v3i8 : pisaVectorVT<v3i8, ".v3.8b", RegV3_8b >;
+def _v4i8 : pisaVectorVT<v4i8, ".v4.8b", RegV4_8b >;
+def _v2i16 : pisaVectorVT<v2i16, ".v2.16b", RegV2_16b >;
+def _v3i16 : pisaVectorVT<v3i16, ".v3.16b", RegV3_16b >;
+def _v4i16 : pisaVectorVT<v4i16, ".v4.16b", RegV4_16b >;
+def _v2i32 : pisaVectorVT<v2i32, ".v2.32b", RegV2_32b >;
+def _v3i32 : pisaVectorVT<v3i32, ".v3.32b", RegV3_32b >;
+def _v4i32 : pisaVectorVT<v4i32, ".v4.32b", RegV4_32b >;
+def _v5i32 : pisaVectorVT<v5i32, ".v5.32b", RegV5_32b >;
+def _v6i32 : pisaVectorVT<v6i32, ".v6.32b", RegV6_32b >;
+def _v7i32 : pisaVectorVT<v7i32, ".v7.32b", RegV7_32b >;
+def _v8i32 : pisaVectorVT<v8i32, ".v8.32b", RegV8_32b >;
+def _v16i32 : pisaVectorVT<v16i32, ".v16.32b", RegV16_32b >;
+def _v32i32 : pisaVectorVT<v32i32, ".v32.32b", RegV32_32b >;
+def _v64i32 : pisaVectorVT<v64i32, ".v64.32b", RegV64_32b >;
+def _v2i64 : pisaVectorVT<v2i64, ".v2.64b", RegV2_64b >;
+def _v3i64 : pisaVectorVT<v3i64, ".v3.64b", RegV3_64b >;
+def _v4i64 : pisaVectorVT<v4i64, ".v4.64b", RegV4_64b >;
+def _v2f16 : pisaVectorVT<v2f16, ".v2.16b", RegV2_16b >;
+def _v3f16 : pisaVectorVT<v3f16, ".v3.16b", RegV3_16b >;
+def _v4f16 : pisaVectorVT<v4f16, ".v4.16b", RegV4_16b >;
+def _v2bf16 : pisaVectorVT<v2bf16, ".v2.16b", RegV2_16b >;
+def _v3bf16 : pisaVectorVT<v3bf16, ".v3.16b", RegV3_16b >;
+def _v4bf16 : pisaVectorVT<v4bf16, ".v4.16b", RegV4_16b >;
+def _v2f32 : pisaVectorVT<v2f32, ".v2.32b", RegV2_32b >;
+def _v3f32 : pisaVectorVT<v3f32, ".v3.32b", RegV3_32b >;
+def _v4f32 : pisaVectorVT<v4f32, ".v4.32b", RegV4_32b >;
+def _v5f32 : pisaVectorVT<v5f32, ".v5.32b", RegV5_32b >;
+def _v6f32 : pisaVectorVT<v6f32, ".v6.32b", RegV6_32b >;
+def _v7f32 : pisaVectorVT<v7f32, ".v7.32b", RegV7_32b >;
+def _v8f32 : pisaVectorVT<v8f32, ".v8.32b", RegV8_32b >;
+def _v2f64 : pisaVectorVT<v2f64, ".v2.64b", RegV2_64b >;
+def _v3f64 : pisaVectorVT<v3f64, ".v3.64b", RegV3_64b >;
+def _v4f64 : pisaVectorVT<v4f64, ".v4.64b", RegV4_64b >;
+
+class PISAValueTypes {
+ list<pisaVT> Integer = [_i8, _i16, _i32, _i64];
+ list<pisaVT> Float = [_bf16, _f16, _f32, _f64];
+ list<pisaVT> Ptr = [_p0, _p1, _p2, _p3, _p4];
+ list<pisaVT> Vector = [_v2i8, _v2i16, _v2i32, _v2i64,
+ _v3i8, _v3i16, _v3i32, _v3i64,
+ _v4i8, _v4i16, _v4i32, _v4i64,
+ _v8i32];
+ list<pisaVT> LargeVector = [_v5i32, _v6i32, _v7i32, _v16i32, _v32i32, _v64i32];
+ // types that can be used in mov instruction
+ // - register moves are limited to 128 bits
+ list<pisaVT> MovImm = !listconcat(Integer, Float, [_i128]);
+ list<pisaVT> MovReg = !filter(VT, !listconcat(MovImm, Vector), !le(VT.VT.Size, 128));
+ // types that can be used as function parameters or return values
+ list<pisaVT> CallReturn = !listconcat(Integer, Vector, LargeVector);
+ // types that can be used in ld/st instructions
+ list<pisaVT> LoadStore = [_i8, _i16, _i32, _i64,
+ _v2i8, _v2i16, _v2i32, _v2i64,
+ _v3i32, _v3i64,
+ _v4i8, _v4i16, _v4i32, _v4i64,
+ _v8i32];
+ // types that can be used in ld.param instructions
+ list<pisaVT> LoadParam = !listconcat(LoadStore, LargeVector);
+ // types that can be used in atomic load/store
+ list<pisaVT> AtomicLoadStore = [_i16, _i32, _i64, _i128];
+ // types that can be used in lifetime.start
+ list<pisaVT> LifetimeTypes = !listconcat([_i1, _i8, _i16, _i32, _i64, _i128],
+ Vector, LargeVector);
+
+}
+def VTs: PISAValueTypes;
+
+class getVectorType<int numElements, int bitSize> {
+ pisaVT VT = !if(!eq(numElements, 1),
+ !cast<pisaVT>("_i"#bitSize),
+ !cast<pisaVT>("_v"#numElements#"i"#bitSize));
+}
+
+class FenceFlushOp {}
+def evict : FenceFlushOp;
+def inval : FenceFlushOp;
+def clean : FenceFlushOp;
+
+class FenceScope {}
+def subgroup : FenceScope;
+def workgroup : FenceScope;
+def gpu : FenceScope;
+def system : FenceScope;
+
+class AddressSpace<int n, pisaVT vt, string _name = NAME> {
+ int num = n;
+ pisaVT VT = vt;
+ PtrValueType PVT = PtrValueType<vt.VT, n>; // !cast<PtrValueType>("p"#num);
+ DAGOperand RC = vt.RegOpnd;
+ string name = _name;
+ string directive = !if(!empty(name), "", "."#name);
+}
+def private : AddressSpace<4, _i32>;
+def global : AddressSpace<1, _i64>;
+def const : AddressSpace<2, _i64>;
+def shared : AddressSpace<3, _i32>;
+def generic : AddressSpace<0, _i64>;
+def default : AddressSpace<0, _i64, "">;
+// FIXME: Rename address space as storage space.
+// Special storage spaces.
+
+class StoreAddrspacePat<AddressSpace AS, PatFrag pat> :
+ PatFrag<(ops node:$data, node:$ptr), (pat node:$data, node:$ptr)> {
+ let IsStore = true;
+ let IsUnindexed = true;
+ let IsNonExtLoad = true;
+ let AddressSpaces = [ AS.num ];
+}
+
+class LoadAddrspacePat<AddressSpace AS, PatFrag pat> :
+ PatFrag<(ops node:$ptr), (pat node:$ptr)> {
+ let IsLoad = true;
+ let IsUnindexed = true;
+ let IsNonExtLoad = true;
+ let AddressSpaces = [ AS.num ];
+}
+
+
+class UnaryAtomAddrspacePat<AddressSpace Addrspace, PatFrag pat>
+ : PatFrag<(ops node:$ptr), (pat node:$ptr)> {
+ let AddressSpaces = [Addrspace.num];
+ let IsAtomic = 1;
+}
+
+class BinaryAtomAddrspacePat<AddressSpace Addrspace, PatFrag pat>
+ : PatFrag<(ops node:$ptr, node:$other), (pat node:$ptr, node:$other)> {
+ let AddressSpaces = [ Addrspace.num ];
+ let IsAtomic = 1;
+}
+
+// Variant of BinaryAtomAddrspacePat that also checks the result register type
+// in GlobalISel, needed to disambiguate bf16 from f16 (both are 16-bit).
+class BinaryAtomAddrspaceMemVTPat<AddressSpace Addrspace, PatFrag pat,
+ string MemVTCheck>
+ : PatFrag<(ops node:$ptr, node:$other), (pat node:$ptr, node:$other)> {
+ let AddressSpaces = [ Addrspace.num ];
+ let IsAtomic = 1;
+ let GISelPredicateCode = [{ return }] # MemVTCheck # [{; }];
+}
+
+// match cmpxchg instruction
+class TernaryAtomAddrspacePat<AddressSpace Addrspace, SDPatternOperator pat> :
+ PatFrag<(ops node:$ptr, node:$opnd1, node:$opnd2), (pat node:$ptr, node:$opnd1, node:$opnd2)> {
+ let AddressSpaces = [ Addrspace.num ];
+ let IsAtomic = 1;
+ let IsLoad = true;
+ let IsStore = true;
+}
+
+/********************************* PISAInst class hierarchy *********************************/
+// +--------------------------------+
+// | _PISAInst: |
+// | defines Inst, Namespace, |
+// (Layer 1) | DecoderNamespace, |
+// | UseNamedOperandTable |
+// +--------------------------------+
+// | | |
+// +---------------+ | +------------------+
+// v v v
+// +--------------------------+ +--------------------------+ +----------------------------+
+// | PISAInst1: | | PISAInst2: | | PISAInst3: |
+// | defines srcRC, | | defines src0RC, src1RC,| | defines src0RC, src1RC, |
+// | dstRC, | | dstRC, | | src2RC, dstRC, |
+// | AsmString, | | AsmString, | | AsmString, |
+// | OutOperandList | | OutOperandList | | OutOperandList |
+// +--------------------------+ +--------------------------+ +----------------------------+
+// | | |
+// v v v
+// +--------------------------+ +--------------------------+ +----------------------------+
+// | PISAInst_r, | | PISAInst_rr, PISAInst_ri,| | PISAInst_rrr, PISAInst_irr,|
+// | PISAInst_i: | | PISAInst_ir, PISAInst_ii:| | PISAInst_rri, PISAInst_iri,|
+// | defines InOperandList, | | defines InOperandList, | | PISAInst_rir, PISAInst_iir,|
+// | Imm opd | | Imm opd | | PISAInst_rii, PISAInst_irr,|
+// +--------------------------+ +--------------------------+ | PISAInst_iii: |
+// | | | defines InOperandList, |
+// | | | Imm opd |
+// | | +----------------------------+
+// | | |
+// | (Classes below are Layer 4) | |
+// | (x is either r or i) | |
+// v v v
+// +--------------------------+ +--------------------------+ +----------------------------+
+// | UnaryInst_x: | | BinInst_xx: | | TernaryInst_xxx |
+// | defines Pattern | | defines Pattern | | defines Pattern |
+// | | | | | |
+// | See also - | | See also - | | See also - |
+// | FP2IntRndmodeInst_r | | CmpBinaryInstReg_rx | | MadInst_rxx |
+// | Int2FPRndmodeInst_r | | CmpBinaryInstPred_rx | | |
+// | | | | | |
+// +--------------------------+ +--------------------------+ +----------------------------+
+//
+// Description: This is the class hierarchy for PISAInst. Each layer defines different fields
+// to share common code. The normal usage is to inherit a class from Layer 3
+// with desired patterns like rr, ri, etc, and to define its own 'Pattern' for
+// DAG selection. All these classes are for non-memory operations. We need to
+// define another formats for memop.
+//
+
+class PISAInst1<string name, pisaVT dstVT, pisaVT srcVT> : _PISAInst<name> {
+ DAGOperand srcRC = srcVT.RegOpnd;
+ DAGOperand dstRC = dstVT.RegOpnd;
+ let AsmString = !if(!isa<pisaVoidVT>(dstVT),
+ name#" \t$src;",
+ name#" \t$dst, $src;");
+ let OutOperandList = !if(!isa<pisaVoidVT>(dstVT),
+ (outs),
+ (outs dstRC:$dst));
+}
+
+class PISAInst_r<string name, pisaVT dstVT, pisaVT srcVT> : PISAInst1<name, dstVT, srcVT> {
+ let InOperandList = (ins srcRC:$src);
+}
+
+class PISAInst_i<string name, pisaVT dstVT, pisaVT srcVT> : PISAInst1<name, dstVT, srcVT> {
+ SDNode immVal = !if(!isa<pisaFloatVT>(srcVT), fpimm, imm);
+ DAGOperand immOpd = srcVT.ImmOpnd;
+ let InOperandList = (ins immOpd:$src);
+}
+
+class PISAInst2<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT> : _PISAInst<name> {
+ DAGOperand src0RC = src0VT.RegOpnd;
+ DAGOperand src1RC = src1VT.RegOpnd;
+ DAGOperand dstRC = dstVT.RegOpnd;
+ let AsmString = !if(!isa<pisaVoidVT>(dstVT),
+ name#" \t$src0, $src1;",
+ name#" \t$dst, $src0, $src1;");
+ let OutOperandList = !if(!isa<pisaVoidVT>(dstVT),
+ (outs),
+ (outs dstRC:$dst));
+}
+
+class PISAInst_rr<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT> : PISAInst2<name, dstVT, src0VT, src1VT> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1);
+}
+
+class PISAInst_ri<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT> : PISAInst2<name, dstVT, src0VT, src1VT> {
+ SDNode immVal1 = !if(!isa<pisaFloatVT>(src1VT), fpimm, imm);
+ DAGOperand immOpd1 = src1VT.ImmOpnd;
+ let InOperandList = (ins src0RC:$src0, immOpd1:$src1);
+}
+
+class PISAInst_ir<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT> : PISAInst2<name, dstVT, src0VT, src1VT> {
+ SDNode immVal0 = !if(!isa<pisaFloatVT>(src0VT), fpimm, imm);
+ DAGOperand immOpd0 = src0VT.ImmOpnd;
+ let InOperandList = (ins immOpd0:$src0, src1RC:$src1);
+}
+
+class PISAInst_ii<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT> : PISAInst2<name, dstVT, src0VT, src1VT> {
+ SDNode immVal0 = !if(!isa<pisaFloatVT>(src0VT), fpimm, imm);
+ SDNode immVal1 = !if(!isa<pisaFloatVT>(src1VT), fpimm, imm);
+ DAGOperand immOpd0 = src0VT.ImmOpnd;
+ DAGOperand immOpd1 = src1VT.ImmOpnd;
+ let InOperandList = (ins immOpd0:$src0, immOpd1:$src1);
+}
+
+class PISAInst3<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT> : _PISAInst<name> {
+ DAGOperand src0RC = src0VT.RegOpnd;
+ DAGOperand src1RC = src1VT.RegOpnd;
+ DAGOperand src2RC = src2VT.RegOpnd;
+ DAGOperand dstRC = dstVT.RegOpnd;
+ let AsmString = !if(!isa<pisaVoidVT>(dstVT),
+ name#" \t$src0, $src1, $src2;",
+ name#" \t$dst, $src0, $src1, $src2;");
+ let OutOperandList = !if(!isa<pisaVoidVT>(dstVT),
+ (outs),
+ (outs dstRC:$dst));
+}
+
+class PISAInst_rrr<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT> : PISAInst3<name, dstVT, src0VT, src1VT, src2VT> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, src2RC:$src2);
+}
+
+class PISAInst_rri<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT> : PISAInst3<name, dstVT, src0VT, src1VT, src2VT> {
+ SDNode immVal2 = !if(!isa<pisaFloatVT>(src2VT), fpimm, imm);
+ DAGOperand immOpd2 = src2VT.ImmOpnd;
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, immOpd2:$src2);
+}
+
+class PISAInst_rir<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT> : PISAInst3<name, dstVT, src0VT, src1VT, src2VT> {
+ SDNode immVal1 = !if(!isa<pisaFloatVT>(src1VT), fpimm, imm);
+ DAGOperand immOpd1 = src1VT.ImmOpnd;
+ let InOperandList = (ins src0RC:$src0, immOpd1:$src1, src2RC:$src2);
+}
+
+class PISAInst_rii<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT> : PISAInst3<name, dstVT, src0VT, src1VT, src2VT> {
+ SDNode immVal1 = !if(!isa<pisaFloatVT>(src1VT), fpimm, imm);
+ SDNode immVal2 = !if(!isa<pisaFloatVT>(src2VT), fpimm, imm);
+ DAGOperand immOpd1 = src1VT.ImmOpnd;
+ DAGOperand immOpd2 = src2VT.ImmOpnd;
+ let InOperandList = (ins src0RC:$src0, immOpd1:$src1, immOpd2:$src2);
+}
+
+class PISAInst_irr<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT> : PISAInst3<name, dstVT, src0VT, src1VT, src2VT> {
+ SDNode immVal0 = !if(!isa<pisaFloatVT>(src0VT), fpimm, imm);
+ DAGOperand immOpd0 = src0VT.ImmOpnd;
+ let InOperandList = (ins immOpd0:$src0, src1RC:$src1, src2RC:$src2);
+}
+
+class PISAInst_iri<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT> : PISAInst3<name, dstVT, src0VT, src1VT, src2VT> {
+ SDNode immVal0 = !if(!isa<pisaFloatVT>(src0VT), fpimm, imm);
+ SDNode immVal2 = !if(!isa<pisaFloatVT>(src2VT), fpimm, imm);
+ DAGOperand immOpd0 = src0VT.ImmOpnd;
+ DAGOperand immOpd2 = src2VT.ImmOpnd;
+ let InOperandList = (ins immOpd0:$src0, src1RC:$src1, immOpd2:$src2);
+}
+
+class PISAInst_iir<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT> : PISAInst3<name, dstVT, src0VT, src1VT, src2VT> {
+ SDNode immVal0 = !if(!isa<pisaFloatVT>(src0VT), fpimm, imm);
+ SDNode immVal1 = !if(!isa<pisaFloatVT>(src1VT), fpimm, imm);
+ DAGOperand immOpd0 = src0VT.ImmOpnd;
+ DAGOperand immOpd1 = src1VT.ImmOpnd;
+ let InOperandList = (ins immOpd0:$src0, immOpd1:$src1, src2RC:$src2);
+}
+
+class PISAInst_iii<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT> : PISAInst3<name, dstVT, src0VT, src1VT, src2VT> {
+ SDNode immVal0 = !if(!isa<pisaFloatVT>(src0VT), fpimm, imm);
+ SDNode immVal1 = !if(!isa<pisaFloatVT>(src1VT), fpimm, imm);
+ SDNode immVal2 = !if(!isa<pisaFloatVT>(src2VT), fpimm, imm);
+ DAGOperand immOpd0 = src0VT.ImmOpnd;
+ DAGOperand immOpd1 = src1VT.ImmOpnd;
+ DAGOperand immOpd2 = src2VT.ImmOpnd;
+ let InOperandList = (ins immOpd0:$src0, immOpd1:$src1, immOpd2:$src2);
+}
+
+class PISAInst4<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT, pisaVT src3VT> : _PISAInst<name> {
+ DAGOperand src0RC = src0VT.RegOpnd;
+ DAGOperand src1RC = src1VT.RegOpnd;
+ DAGOperand src2RC = src2VT.RegOpnd;
+ DAGOperand src3RC = src3VT.RegOpnd;
+ DAGOperand dstRC = dstVT.RegOpnd;
+ let AsmString = !if(!isa<pisaVoidVT>(dstVT),
+ name#" \t$src0, $src1, $src2, $src3;",
+ name#" \t$dst, $src0, $src1, $src2, $src3;");
+ let OutOperandList = !if(!isa<pisaVoidVT>(dstVT),
+ (outs),
+ (outs dstRC:$dst));
+}
+
+class PISAInst_rrrr<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT, pisaVT src3VT> : PISAInst4<name, dstVT, src0VT, src1VT, src2VT, src3VT> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, src2RC:$src2, src3RC:$src3);
+}
+
+class PISAInst5<string name, pisaVT dstVT, pisaVT src0VT, pisaVT src1VT, pisaVT src2VT, pisaVT src3VT, pisaVT src4VT> : _PISAInst<name> {
+ DAGOperand src0RC = src0VT.RegOpnd;
+ DAGOperand src1RC = src1VT.RegOpnd;
+ DAGOperand src2RC = src2VT.RegOpnd;
+ DAGOperand src3RC = src3VT.RegOpnd;
+ DAGOperand src4RC = src4VT.RegOpnd;
+ DAGOperand dstRC = dstVT.RegOpnd;
+ let AsmString = !if(!isa<pisaVoidVT>(dstVT),
+ name#" \t$src0, $src1, $src2, $src3, $src4;",
+ name#" \t$dst, $src0, $src1, $src2, $src3, $src4;");
+ let OutOperandList = !if(!isa<pisaVoidVT>(dstVT),
+ (outs),
+ (outs dstRC:$dst));
+}
+
+/****************************** End of PISAInst class hierarchy *****************************/
+
+/******************************* PISAMemInst class hierarchy ********************************/
+// +--------------------------------+
+// | _PISAInst: |
+// | defines Inst, Namespace, |
+// (Layer 1) | DecoderNamespace, |
+// | UseNamedOperandTable |
+// +--------------------------------+
+// | | |
+// +---------------+ | +------------------+
+// v v v
+// +--------------------------+ +--------------------------+ +--------------------------------+
+// | PISAStore: | | PISALoad: | | PISABinaryAtom: |
+// | defines InOperandList, | | defines InOperandList, | | PISATernaryAtom: |
+// | OutOperandList,| | OutOperandList,| | defines InOperandList, |
+// | AsmString, | | AsmString, | | OutOperandList, |
+// | Pattern | | Pattern | | AsmString, |
+// | | | | | Pattern |
+// +--------------------------+ +--------------------------+ +--------------------------------+
+//
+//
+// Description: This is the class hierarchy for PISAMemInst. The classes shown above define basic
+// structures of PISAMemInst for load, store and atomic operations, like inputs,
+// outputs, printing formats, patterns, etc. Each memory operation can have different
+// operand types for the addressing mode. We use MemProperty class to define
+// variants of formats for the operand types in a form of PISAMem_xx, where x
+// can be either v(Var), r(Reg), or i(Imm) for the corresponding operand.
+//
+
+def ADDR_rr : ComplexPattern<iPTR, 2, "SelectAddr_rr", [], [], 2>;
+def ADDR_ri : ComplexPattern<iPTR, 2, "SelectAddr_ri", [], [], 1>;
+
+class MEM_rr<AddressSpace AS> : Operand<i64> {
+ let MIOperandInfo = (ops AS.RC, AS.RC);
+ let PrintMethod = "printMemOperand";
+ let ParserMatchClass = PISAAsmOperand<"MemRR">;
+}
+
+class MEM_ri<AddressSpace AS> : Operand<i64> {
+ let MIOperandInfo = (ops AS.RC, i64imm);
+ let PrintMethod = "printMemOperand";
+ let ParserMatchClass = PISAAsmOperand<"MemRI">;
+}
+
+// A special memory operand for the kernel parameter *only*.
+def MEM_ii : Operand<i64>, ComplexPattern<i64, 2, "selectParamSlot_ii", [], []> {
+ let MIOperandInfo = (ops i32imm, i64imm);
+ let PrintMethod = "printParamMemOperand";
+ let ParserMatchClass = PISAAsmOperand<"MemII">;
+}
+
+def MEM_ir : Operand<i64>, ComplexPattern<i64, 2, "selectParamSlot_ir", [], []> {
+ let MIOperandInfo = (ops i32imm, Reg32b);
+ let PrintMethod = "printParamMemOperand";
+ let ParserMatchClass = PISAAsmOperand<"MemIR">;
+}
+
+def gi_ADDR_rr : GIComplexOperandMatcher<s64, "SelectAddr_rr">, GIComplexPatternEquiv<ADDR_rr>;
+def gi_ADDR_ri : GIComplexOperandMatcher<s64, "SelectAddr_ri">, GIComplexPatternEquiv<ADDR_ri>;
+def gi_PARAM_SLOT_ii : GIComplexOperandMatcher<s64, "selectParamSlot_ii">, GIComplexPatternEquiv<MEM_ii>;
+def gi_PARAM_SLOT_ir : GIComplexOperandMatcher<s64, "selectParamSlot_ir">, GIComplexPatternEquiv<MEM_ir>;
+
+class MemProperty<AddressSpace as, dag i, ComplexPattern pattern> {
+ dag Ins = i;
+ ComplexPattern Pattern = pattern;
+ AddressSpace AS = as;
+}
+
+class PISAMem_rr<AddressSpace AS> : MemProperty<AS, (ins MEM_rr<AS>:$addr), ADDR_rr>;
+class PISAMem_ri<AddressSpace AS> : MemProperty<AS, (ins MEM_ri<AS>:$addr), ADDR_ri>;
+
+// atomics syncscope("<target-scope>") definitions
+defvar AtomicScopeControlDefault = 255;
+defm AtomicScopeControl : EnumOptionOpndWithDefaultOpsDef<"AtomicScopeControl", false, AtomicScopeControlDefault>;
+class AtomicScopeControlEntry<string name, string mnemonic, bits<8> val>
+ : EnumOptionEntry<AtomicScopeControlClassID, name, val, "AtomicScopeControl", mnemonic>,
+ GenericEnum {
+ let FilterClass = "AtomicScopeControlEntry";
+}
+multiclass AtomicScopeControlDef<string mnemonic, bits<8> value> {
+ defvar entry = !if(!empty(mnemonic), "NONE", !toupper(!subst(".", "_", mnemonic)));
+ defvar EnumName = AtomicScopeControl.EnumPrefix # "_" # entry;
+ def EnumName : AtomicScopeControlEntry<entry, mnemonic, value>;
+}
+
+// Must be in sync with llvm::pisa::MemoryScope definitions in
+// llvm/include/llvm/IR/PISAIntrinsicUtils.h
+defm NONE : AtomicScopeControlDef<"", AtomicScopeControlDefault>;
+defm SYSTEM : AtomicScopeControlDef<"system", 0>;
+defm GPU : AtomicScopeControlDef<"gpu", 1>;
+defm WORKGROUP : AtomicScopeControlDef<"workgroup", 2>;
+
+// atomics cache control definitions
+defm AtomicCacheControl : EnumOptionOpndWithDefaultOpsDef<"AtomicCacheControl", false, 0>;
+class AtomicCacheControlEntry<string name, string mnemonic, bits<8> val>
+ : EnumOptionEntry<AtomicCacheControlClassID, name, val, "AtomicCacheControl", mnemonic>,
+ GenericEnum {
+ let FilterClass = "AtomicCacheControlEntry";
+}
+multiclass AtomicCacheControlDef<string mnemonic, bits<8> value> {
+ defvar entry = !if(!empty(mnemonic), "NONE", !toupper(!subst(".", "_", mnemonic)));
+ defvar EnumName = AtomicCacheControl.EnumPrefix # "_" # entry;
+ def EnumName : AtomicCacheControlEntry<entry, mnemonic, value>;
+}
+defm NONE : AtomicCacheControlDef<"", 0>;
+defm L1UC_L2UC_L3UC : AtomicCacheControlDef<"uc", 2>;
+defm L1UC_L2UC_L3WB : AtomicCacheControlDef<"L3wb", 3>;
+defm L1UC_L2WB_L3UC : AtomicCacheControlDef<"L2wb", 4>;
+
+// Fence memory ordering definitions (matches llvm::AtomicOrdering)
+defm FenceMemOrder : EnumOptionOpndWithDefaultOpsDef<"FenceMemOrder", false, 6>;
+class FenceMemOrderEntry<string name, string mnemonic, bits<8> val>
+ : EnumOptionEntry<FenceMemOrderClassID, name, val, "FenceMemOrder", mnemonic>,
+ GenericEnum {
+ let FilterClass = "FenceMemOrderEntry";
+}
+multiclass FenceMemOrderDef<string mnemonic, bits<8> value> {
+ defvar entry = !toupper(mnemonic);
+ defvar EnumName = FenceMemOrder.EnumPrefix # "_" # entry;
+ def EnumName : FenceMemOrderEntry<entry, mnemonic, value>;
+}
+defm ACQUIRE : FenceMemOrderDef<"acquire", 4>;
+defm RELEASE : FenceMemOrderDef<"release", 5>;
+defm ACQ_REL : FenceMemOrderDef<"acq_rel", 6>;
+defm SEQ_CST : FenceMemOrderDef<"seq_cst", 7>;
+
+
+// load cache control definitions
+defm LoadCacheControl : EnumOptionOpndWithDefaultOpsDef<"LoadCacheControl", false, 0>;
+class LoadCacheControlEntry<string name, string mnemonic, bits<8> val>
+ : EnumOptionEntry<LoadCacheControlClassID, name, val, "LoadCacheControl", mnemonic>,
+ GenericEnum {
+ let FilterClass = "LoadCacheControlEntry";
+}
+multiclass LoadCacheControlDef<string mnemonic, bits<8> value> {
+ defvar entry = !if(!empty(mnemonic), "NONE", !toupper(!subst(".", "_", mnemonic)));
+ defvar EnumName = LoadCacheControl.EnumPrefix # "_" # entry;
+ def EnumName : LoadCacheControlEntry<entry, mnemonic, value>;
+}
+defm NONE : LoadCacheControlDef<"", 0>;
+defm L1UC_L2UC_L3UC : LoadCacheControlDef<"L1uc.L2uc.L3uc", 2>;
+defm L1UC_L2UC_L3C : LoadCacheControlDef<"L1uc.L2uc.L3c", 3>;
+defm L1UC_L2C_L3UC : LoadCacheControlDef<"L1uc.L2c.L3uc", 4>;
+defm L1UC_L2C_L3C : LoadCacheControlDef<"L1uc.L2c.L3c", 5>;
+defm L1C_L2UC_L3UC : LoadCacheControlDef<"L1c.L2uc.L3uc", 6>;
+defm L1C_L2UC_L3C : LoadCacheControlDef<"L1c.L2uc.L3c", 7>;
+defm L1C_L2C_L3UC : LoadCacheControlDef<"L1c.L2c.L3uc", 8>;
+defm L1C_L2C_L3C : LoadCacheControlDef<"L1c.L2c.L3c", 9>;
+defm L1S_L2UC_L3UC : LoadCacheControlDef<"L1s.L2uc.L3uc", 10>;
+defm L1S_L2UC_L3C : LoadCacheControlDef<"L1s.L2uc.L3c", 11>;
+defm L1S_L2C_L3UC : LoadCacheControlDef<"L1s.L2c.L3uc", 12>;
+defm L1S_L2C_L3C : LoadCacheControlDef<"L1s.L2c.L3c", 13>;
+defm RI : LoadCacheControlDef<"ri", 14>;
+
+// store cache control definitions
+defm StoreCacheControl : EnumOptionOpndWithDefaultOpsDef<"StoreCacheControl", false, 0>;
+class StoreCacheControlEntry<string name, string mnemonic, bits<8> val>
+ : EnumOptionEntry<StoreCacheControlClassID, name, val, "StoreCacheControl", mnemonic>,
+ GenericEnum {
+ let FilterClass = "StoreCacheControlEntry";
+}
+multiclass StoreCacheControlDef<string mnemonic, bits<8> value> {
+ defvar entry = !if(!empty(mnemonic), "NONE", !toupper(!subst(".", "_", mnemonic)));
+ defvar EnumName = StoreCacheControl.EnumPrefix # "_" # entry;
+ def EnumName : StoreCacheControlEntry<entry, mnemonic, value>;
+}
+defm NONE : StoreCacheControlDef<"", 0>;
+defm L1UC_L2UC_L3UC : StoreCacheControlDef<"L1uc.L2uc.L3uc", 2>;
+defm L1UC_L2UC_L3WB : StoreCacheControlDef<"L1uc.L2uc.L3wb", 3>;
+defm L1UC_L2WB_L3UC : StoreCacheControlDef<"L1uc.L2wb.L3uc", 4>;
+defm L1UC_L2WB_L3WB : StoreCacheControlDef<"L1uc.L2wb.L3wb", 5>;
+defm L1WT_L2UC_L3UC : StoreCacheControlDef<"L1wt.L2uc.L3uc", 6>;
+defm L1WT_L2UC_L3WB : StoreCacheControlDef<"L1wt.L2uc.L3wb", 7>;
+defm L1WT_L2WB_L3UC : StoreCacheControlDef<"L1wt.L2wb.L3uc", 8>;
+defm L1WT_L2WB_L3WB : StoreCacheControlDef<"L1wt.L2wb.L3wb", 9>;
+defm L1S_L2UC_L3UC : StoreCacheControlDef<"L1s.L2uc.L3uc", 10>;
+defm L1S_L2UC_L3WB : StoreCacheControlDef<"L1s.L2uc.L3wb", 11>;
+defm L1S_L2WB_L3UC : StoreCacheControlDef<"L1s.L2wb.L3uc", 12>;
+defm L1WB_L2UC_L3UC : StoreCacheControlDef<"L1wb.L2uc.L3uc", 13>;
+defm L1WB_L2WB_L3UC : StoreCacheControlDef<"L1wb.L2wb.L3uc", 14>;
+defm L1WB_L2UC_L3WB : StoreCacheControlDef<"L1wb.L2uc.L3wb", 15>;
+
+
+// load instruction
+class PISALoad<string directive, AddressSpace AS, pisaVT VT, MemProperty M, PatFrag node> : _PISAInst<directive> {
+ DAGOperand dstRC = VT.RegOpnd;
+ dag OutOperandList = (outs dstRC:$dst);
+ dag InOperandList = !con(M.Ins, (ins LoadCacheControlOpnd:$cachehint));
+ string AsmString = directive#"${cachehint}"#VT.mdirective#"\t $dst, $addr;";
+ list<dag> Pattern = [(set VT.VT:$dst, (LoadAddrspacePat<AS, node> M.Pattern:$addr))];
+}
+class PISAAtomicLoad<string directive, AddressSpace AS, pisaVT VT, MemProperty M, PatFrag node> : _PISAInst<directive> {
+ DAGOperand dstRC = VT.RegOpnd;
+ dag OutOperandList = (outs dstRC:$dst);
+ dag InOperandList = !con(M.Ins, (ins AtomicScopeControlOpnd:$scope, AtomicCacheControlOpnd:$cachehint));
+ string AsmString = directive#"${scope}${cachehint}"#VT.mdirective#"\t $dst, $addr;";
+ list<dag> Pattern = [(set VT.VT:$dst, (LoadAddrspacePat<AS, node> M.Pattern:$addr))];
+}
+
+// store instruction
+class PISAStore<string directive, AddressSpace AS, pisaVT VT, MemProperty M, PatFrag node> : _PISAInst<directive> {
+ DAGOperand srcRC = VT.RegOpnd;
+ dag OutOperandList = (outs);
+ dag InOperandList = !con(M.Ins, (ins srcRC:$data, StoreCacheControlOpnd:$cachehint));
+ string AsmString = directive#"${cachehint}"#VT.mdirective#"\t $addr, $data;";
+ list<dag> Pattern = [(set (StoreAddrspacePat<AS, node> VT.VT:$data, M.Pattern:$addr))];
+}
+class PISAAtomicStore<string directive, AddressSpace AS, pisaVT VT, MemProperty M, PatFrag node> : _PISAInst<directive> {
+ DAGOperand srcRC = VT.RegOpnd;
+ dag OutOperandList = (outs);
+ dag InOperandList = !con(M.Ins, (ins srcRC:$data, AtomicScopeControlOpnd:$scope, AtomicCacheControlOpnd:$cachehint));
+ string AsmString = directive#"${scope}${cachehint}"#VT.mdirective#"\t $addr, $data;";
+ list<dag> Pattern = [(set (StoreAddrspacePat<AS, node> VT.VT:$data, M.Pattern:$addr))];
+}
+
+
+// The definitions of $dst, $src0 and $src1 are defined in the PISA SPEC
+// https://intel.github.io/pisa/instructions_memory.html#iatom
+class PISAUnaryAtom<string directive, string op, AddressSpace AS, pisaVT VT,
+ MemProperty M, PatFrag node> : _PISAInst<directive#"."#op#VT.directive> {
+ DAGOperand dstRC = VT.RegOpnd;
+ dag OutOperandList = (outs dstRC:$dst);
+ dag InOperandList = !con(M.Ins, (ins AtomicScopeControlOpnd:$scope, AtomicCacheControlOpnd:$cachehint));
+ string AsmString =
+ directive#"${scope}${cachehint}."#op#VT.directive#"\t $dst, $addr;";
+ list<dag> Pattern = [(set VT.VT:$dst,
+ (UnaryAtomAddrspacePat<AS, node> M.Pattern:$addr))];
+}
+
+class PISABinaryAtom<string directive, string op, AddressSpace AS, pisaVT VT,
+ MemProperty M, PatFrag node> : _PISAInst<directive#"."#op#VT.directive> {
+ DAGOperand dstRC = VT.RegOpnd;
+ DAGOperand srcRC = VT.RegOpnd;
+ dag OutOperandList = (outs dstRC:$dst);
+ dag InOperandList = !con(M.Ins, (ins srcRC:$src0, AtomicScopeControlOpnd:$scope, AtomicCacheControlOpnd:$cachehint));
+ string AsmString = directive#"${scope}${cachehint}."#op#VT.directive#"\t $dst, $addr, $src0;";
+ list<dag> Pattern = [(set VT.VT:$dst,
+ (BinaryAtomAddrspacePat<AS, node> M.Pattern:$addr, VT.VT:$src0))];
+}
+
+// cmpxchg instruction
+class PISATernaryAtom<string directive, string op, AddressSpace AS, pisaVT VT,
+ MemProperty M, SDPatternOperator node> : _PISAInst<directive#"."#op#VT.directive> {
+ DAGOperand dstRC = VT.RegOpnd;
+ DAGOperand srcRC = VT.RegOpnd;
+ dag OutOperandList = (outs dstRC:$dst);
+ dag InOperandList = !con(M.Ins, (ins srcRC:$src0, srcRC:$src1, AtomicScopeControlOpnd:$scope, AtomicCacheControlOpnd:$cachehint));
+ string AsmString = directive#"${scope}${cachehint}."#op#VT.directive#"\t $dst, $addr, $src0, $src1;";
+ list<dag> Pattern = [(set VT.VT:$dst, (TernaryAtomAddrspacePat<AS, node> M.Pattern:$addr, VT.VT:$src0, VT.VT:$src1))];
+}
+
+// Remap to PISA[MemOp]_xx for readability, where x represents either r(Reg), or i(Imm) for the corresponding operand
+class PISALoad_rr <string d, AddressSpace AS, pisaVT VT, PatFrag node> : PISALoad <d, AS, VT, PISAMem_rr<AS>, node>;
+class PISAAtomicLoad_rr <string d, AddressSpace AS, pisaVT VT, PatFrag node> : PISAAtomicLoad <d, AS, VT, PISAMem_rr<AS>, node>;
+class PISAStore_rr <string d, AddressSpace AS, pisaVT VT, PatFrag node> : PISAStore <d, AS, VT, PISAMem_rr<AS>, node>;
+class PISAAtomicStore_rr <string d, AddressSpace AS, pisaVT VT, PatFrag node> : PISAAtomicStore <d, AS, VT, PISAMem_rr<AS>, node>;
+class PISAUnaryAtom_rr<string d, string op, AddressSpace AS, pisaVT VT,
+ PatFrag node>
+ : PISAUnaryAtom<d, op, AS, VT, PISAMem_rr<AS>, node>;
+class PISABinaryAtom_rr <string d, string op, AddressSpace AS, pisaVT VT, PatFrag node>
+ : PISABinaryAtom <d, op, AS, VT, PISAMem_rr<AS>, node>;
+class PISATernaryAtom_rr<string d, string op, AddressSpace AS, pisaVT VT, SDPatternOperator node>
+ : PISATernaryAtom<d, op, AS, VT, PISAMem_rr<AS>, node>;
+
+class PISALoad_ri <string d, AddressSpace AS, pisaVT VT, PatFrag node> : PISALoad <d, AS, VT, PISAMem_ri<AS>, node>;
+class PISAAtomicLoad_ri <string d, AddressSpace AS, pisaVT VT, PatFrag node> : PISAAtomicLoad <d, AS, VT, PISAMem_ri<AS>, node>;
+class PISAStore_ri <string d, AddressSpace AS, pisaVT VT, PatFrag node> : PISAStore <d, AS, VT, PISAMem_ri<AS>, node>;
+class PISAAtomicStore_ri <string d, AddressSpace AS, pisaVT VT, PatFrag node> : PISAAtomicStore <d, AS, VT, PISAMem_ri<AS>, node>;
+class PISAUnaryAtom_ri<string d, string op, AddressSpace AS, pisaVT VT,
+ PatFrag node>
+ : PISAUnaryAtom<d, op, AS, VT, PISAMem_ri<AS>, node>;
+class PISABinaryAtom_ri <string d, string op, AddressSpace AS, pisaVT VT, PatFrag node>
+ : PISABinaryAtom <d, op, AS, VT, PISAMem_ri<AS>, node>;
+class PISATernaryAtom_ri<string d, string op, AddressSpace AS, pisaVT VT, SDPatternOperator node>
+ : PISATernaryAtom<d, op, AS, VT, PISAMem_ri<AS>, node>;
+
+
+/**************************** End of PISAMemInst class hierarchy ****************************/
diff --git a/llvm/lib/Target/PISA/PISAInstrInfo.cpp b/llvm/lib/Target/PISA/PISAInstrInfo.cpp
index c20e1a022db71..d9d219aa1c516 100644
--- a/llvm/lib/Target/PISA/PISAInstrInfo.cpp
+++ b/llvm/lib/Target/PISA/PISAInstrInfo.cpp
@@ -9,11 +9,458 @@
#include "PISAInstrInfo.h"
#include "PISA.h"
#include "PISASubtarget.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DebugLoc.h"
+#include "llvm/Support/ErrorHandling.h"
#define GET_INSTRINFO_CTOR_DTOR
#include "PISAGenInstrInfo.inc"
using namespace llvm;
+using namespace PISA;
PISAInstrInfo::PISAInstrInfo(const PISASubtarget &STI)
- : PISAGenInstrInfo(STI, RI), RI() {}
+ : PISAGenInstrInfo(STI, RI) {}
+
+bool PISAInstrInfo::isNoEmissionInstr(const MachineInstr &MI) const {
+ // Functions are emitted to C-style function signature.
+ // These instructions are not required to be in the output.
+ return isFunctionParamInstr(MI);
+}
+
+bool PISAInstrInfo::isFunctionParamInstr(const MachineInstr &MI) const {
+ switch (MI.getOpcode()) {
+ case PISA::functionParameter_i8:
+ case PISA::functionParameter_i16:
+ case PISA::functionParameter_i32:
+ case PISA::functionParameter_i64:
+ case PISA::functionParameter_v2i8:
+ case PISA::functionParameter_v3i8:
+ case PISA::functionParameter_v4i8:
+ case PISA::functionParameter_v2i16:
+ case PISA::functionParameter_v3i16:
+ case PISA::functionParameter_v4i16:
+ case PISA::functionParameter_v2i32:
+ case PISA::functionParameter_v3i32:
+ case PISA::functionParameter_v4i32:
+ case PISA::functionParameter_v5i32:
+ case PISA::functionParameter_v6i32:
+ case PISA::functionParameter_v7i32:
+ case PISA::functionParameter_v8i32:
+ case PISA::functionParameter_v16i32:
+ case PISA::functionParameter_v32i32:
+ case PISA::functionParameter_v64i32:
+ case PISA::functionParameter_v2i64:
+ case PISA::functionParameter_v3i64:
+ case PISA::functionParameter_v4i64:
+ return true;
+ default:
+ return false;
+ }
+}
+
+namespace llvm {
+namespace PISA {
+static const MachineOperand &getMO(const MachineInstr &MI, PISA::OpName Name) {
+ int16_t Idx = getNamedOperandIdx(MI.getOpcode(), Name);
+ assert(Idx >= 0 && "name not present!");
+
+ return MI.getOperand(Idx);
+}
+} // namespace PISA
+} // namespace llvm
+
+// See description in TargetInstrInfo.h
+bool PISAInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
+ MachineBasicBlock *&TBB,
+ MachineBasicBlock *&FBB,
+ SmallVectorImpl<MachineOperand> &Cond,
+ bool /*AllowModify*/) const {
+ // If the block has no terminators, it just falls into the block after it.
+ MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr();
+ if (I == MBB.end() || !isUnpredicatedTerminator(*I))
+ return false;
+
+ // Get the last instruction in the block.
+ MachineInstr *LastInst = &*I;
+
+ // If there is only one terminator instruction, process it.
+ if (I == MBB.begin() || !isUnpredicatedTerminator(*--I)) {
+ if (LastInst->isUnconditionalBranch()) {
+ if (LastInst->getOpcode() != PISA::gotolabel)
+ return true;
+ TBB = LastInst->getOperand(0).getMBB();
+ return false;
+ }
+ if (LastInst->isConditionalBranch()) {
+ if (LastInst->getOpcode() != PISA::predgoto)
+ return true;
+ // Block ends with fall-through condbranch.
+ TBB = getMO(*LastInst, OpName::label).getMBB();
+ // mod, pred
+ Cond.push_back(LastInst->getOperand(0));
+ Cond.push_back(LastInst->getOperand(1));
+ return false;
+ }
+ return true; // Can't handle indirect branch.
+ }
+
+ // Get the instruction before it if it is a terminator.
+ MachineInstr *SecondLastInst = &*I;
+
+ if (!SecondLastInst->isConditionalBranch() ||
+ !LastInst->isUnconditionalBranch() ||
+ // triple terminator?
+ (I != MBB.begin() && isUnpredicatedTerminator(*--I)))
+ return true;
+
+ if (SecondLastInst->getOpcode() != PISA::predgoto ||
+ LastInst->getOpcode() != PISA::gotolabel)
+ return true;
+
+ TBB = getMO(*SecondLastInst, OpName::label).getMBB();
+ FBB = LastInst->getOperand(0).getMBB();
+
+ // mod, pred
+ Cond.push_back(SecondLastInst->getOperand(0));
+ Cond.push_back(SecondLastInst->getOperand(1));
+
+ return false;
+}
+
+// See description in TargetInstrInfo.h
+unsigned PISAInstrInfo::removeBranch(MachineBasicBlock &MBB,
+ int *BytesRemoved) const {
+ assert(!BytesRemoved && "not supported!");
+
+ unsigned Count = 0;
+ for (auto &MI : llvm::make_early_inc_range(MBB.terminators())) {
+ assert(MI.isBranch() && "not a branch?");
+ MI.eraseFromParent();
+ Count++;
+ }
+
+ return Count;
+}
+
+// See description in TargetInstrInfo.h
+unsigned PISAInstrInfo::insertBranch(
+ MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB,
+ ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const {
+
+ assert(!BytesAdded && "not supported!");
+ assert(TBB && "Should have at least one branch!");
+
+ if (!FBB) {
+ if (Cond.empty()) { // Unconditional branch
+ BuildMI(&MBB, DL, get(PISA::gotolabel)).addMBB(TBB);
+ } else { // Conditional branch
+ assert(Cond.size() == 2 && "wrong number of args?");
+ BuildMI(&MBB, DL, get(PISA::predgoto))
+ .add(Cond[0])
+ .add(Cond[1])
+ .addMBB(TBB);
+ }
+ return 1;
+ }
+
+ // Two-way Conditional Branch.
+ assert(Cond.size() == 2 && "wrong number of args?");
+ BuildMI(&MBB, DL, get(PISA::predgoto)).add(Cond[0]).add(Cond[1]).addMBB(TBB);
+ BuildMI(&MBB, DL, get(PISA::gotolabel)).addMBB(FBB);
+ return 2;
+}
+
+bool PISAInstrInfo::reverseBranchCondition(
+ SmallVectorImpl<MachineOperand> &Cond) const {
+ if (Cond.empty())
+ return true;
+
+ uint64_t DoNegate = Cond[0].getImm();
+ Cond[0].setImm(!DoNegate);
+
+ return false;
+}
+
+// We have to override this because LowerCopy() in ExpandPostRAPseudos
+// gets rid of identity copies without checking subregs. It doesn't check
+// subregs because register allocation has already happened (but not for PISA)
+// so there aren't any subregs.
+bool PISAInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
+ if (!MI.isCopy())
+ return false;
+
+ MachineOperand &DstMO = MI.getOperand(0);
+ MachineOperand &SrcMO = MI.getOperand(1);
+
+ // only generate instructions using defined registers
+ if (!SrcMO.isUndef())
+ copyPhysReg(*MI.getParent(), MI, MI.getDebugLoc(), DstMO.getReg(),
+ SrcMO.getReg(), SrcMO.isKill());
+
+ MI.eraseFromParent();
+
+ return true;
+}
+
+bool PISAInstrInfo::isSafeToMove(const MachineInstr &MI,
+ const MachineBasicBlock *MBB,
+ const MachineFunction &MF) const {
+ // Convergent instructions must not be moved across basic blocks because
+ // doing so can change the set of threads executing the instruction,
+ // potentially producing incorrect results.
+ if (MI.isConvergent())
+ return false;
+ return true;
+}
+
+void PISAInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
+ MachineBasicBlock::iterator I,
+ const DebugLoc &DL, Register DestReg,
+ Register SrcReg, bool KillSrc,
+ bool RenamableDest, bool RenamableSrc) const {
+
+ assert(I->isCopy() && "Copy instruction is expected");
+ auto &MRI = I->getMF()->getRegInfo();
+
+ auto GetSubregRc = [&](MachineOperand &MO) -> const TargetRegisterClass * {
+ unsigned Subreg = MO.getSubReg();
+ Register Reg = MO.getReg();
+ auto *SuperRC = Reg.isPhysical() ? RI.getMinimalPhysRegClass(Reg)
+ : MRI.getRegClass(Reg);
+ if (Subreg == 0)
+ return SuperRC;
+
+ return RI.getSubRegisterClass(SuperRC, Subreg);
+ };
+
+ auto &DstOp = I->getOperand(0);
+ auto &SrcOp = I->getOperand(1);
+ auto *DstSubRC = GetSubregRc(DstOp);
+ auto *SrcSubRC = GetSubregRc(SrcOp);
+
+ unsigned Op = 0;
+
+ const unsigned DstSubNumElts = RI.getNumEltsFromRegClass(DstSubRC);
+ const unsigned DstSubEltSize = RI.getBitSizeFromRegClass(DstSubRC);
+ const unsigned SrcSubNumElts = RI.getNumEltsFromRegClass(SrcSubRC);
+ const unsigned SrcSubEltSize = RI.getBitSizeFromRegClass(SrcSubRC);
+ const bool DstSubIsVector = DstSubNumElts > 1;
+ const bool SrcSubIsVector = SrcSubNumElts > 1;
+ const bool DstSubIsScalar = !DstSubIsVector;
+ const bool SrcSubIsScalar = !SrcSubIsVector;
+
+ if (DstSubIsVector && SrcSubIsVector) {
+ if (DstSubNumElts == 4 && DstSubEltSize == 8 && SrcSubNumElts == 2 &&
+ SrcSubEltSize == 16)
+ Op = PISA::mov_v4i8_v2i16_r;
+ else if (DstSubNumElts == 2 && DstSubEltSize == 16 && SrcSubNumElts == 4 &&
+ SrcSubEltSize == 8)
+ Op = PISA::mov_v2i16_v4i8_r;
+ else if (DstSubNumElts == 4 && DstSubEltSize == 16 && SrcSubNumElts == 2 &&
+ SrcSubEltSize == 32)
+ Op = PISA::mov_v4i16_v2i32_r;
+ else if (DstSubNumElts == 2 && DstSubEltSize == 32 && SrcSubNumElts == 4 &&
+ SrcSubEltSize == 16)
+ Op = PISA::mov_v2i32_v4i16_r;
+ else if (DstSubNumElts == 4 && DstSubEltSize == 32 && SrcSubNumElts == 2 &&
+ SrcSubEltSize == 64)
+ Op = PISA::mov_v4i32_v2i64_r;
+ else if (DstSubNumElts == 2 && DstSubEltSize == 64 && SrcSubNumElts == 4 &&
+ SrcSubEltSize == 32)
+ Op = PISA::mov_v2i64_v4i32_r;
+ else {
+ assert(DstSubNumElts == SrcSubNumElts && "num elts mismatch!");
+ if (DstSubNumElts == 2) {
+ switch (DstSubEltSize) {
+ case 8:
+ Op = PISA::mov_v2i8_r;
+ break;
+ case 16:
+ Op = PISA::mov_v2i16_r;
+ break;
+ case 32:
+ Op = PISA::mov_v2i32_r;
+ break;
+ case 64:
+ Op = PISA::mov_v2i64_r;
+ break;
+ default:
+ llvm_unreachable("unknown elt size!");
+ }
+ } else if (DstSubNumElts == 3) {
+ switch (DstSubEltSize) {
+ case 8:
+ Op = PISA::mov_v3i8_r;
+ break;
+ case 16:
+ Op = PISA::mov_v3i16_r;
+ break;
+ default:
+ llvm_unreachable("unknown elt size!");
+ }
+ } else if (DstSubNumElts == 4) {
+ switch (DstSubEltSize) {
+ case 8:
+ Op = PISA::mov_v4i8_r;
+ break;
+ case 16:
+ Op = PISA::mov_v4i16_r;
+ break;
+ case 32:
+ Op = PISA::mov_v4i32_r;
+ break;
+ default:
+ llvm_unreachable("unknown elt size!");
+ }
+ } else {
+ llvm_unreachable("unknown vec size!");
+ }
+ }
+ } else if (DstSubIsVector && SrcSubIsScalar) {
+ if (DstSubNumElts == 2 && DstSubEltSize == 8 && SrcSubEltSize == 16)
+ Op = PISA::mov_v2i8_i16_r;
+ else if (DstSubNumElts == 4 && DstSubEltSize == 8 && SrcSubEltSize == 32)
+ Op = PISA::mov_v4i8_i32_r;
+ else if (DstSubNumElts == 2 && DstSubEltSize == 16 && SrcSubEltSize == 32)
+ Op = PISA::mov_v2i16_i32_r;
+ else if (DstSubNumElts == 4 && DstSubEltSize == 16 && SrcSubEltSize == 64)
+ Op = PISA::mov_v4i16_i64_r;
+ else if (DstSubNumElts == 2 && DstSubEltSize == 32 && SrcSubEltSize == 64)
+ Op = PISA::mov_v2i32_i64_r;
+ else if (DstSubNumElts == 4 && DstSubEltSize == 32 && SrcSubEltSize == 128)
+ Op = PISA::mov_v4i32_i128_r;
+ else if (DstSubNumElts == 2 && DstSubEltSize == 64 && SrcSubEltSize == 128)
+ Op = PISA::mov_v2i64_i128_r;
+ else
+ llvm_unreachable("wrong copy instruction");
+ } else if (DstSubIsScalar && SrcSubIsVector) {
+ if (DstSubEltSize == 16 && SrcSubNumElts == 2 && SrcSubEltSize == 8)
+ Op = PISA::mov_i16_v2i8_r;
+ else if (DstSubEltSize == 32 && SrcSubNumElts == 2 && SrcSubEltSize == 16)
+ Op = PISA::mov_i32_v2i16_r;
+ else if (DstSubEltSize == 32 && SrcSubNumElts == 4 && SrcSubEltSize == 8)
+ Op = PISA::mov_i32_v4i8_r;
+ else if (DstSubEltSize == 64 && SrcSubNumElts == 2 && SrcSubEltSize == 32)
+ Op = PISA::mov_i64_v2i32_r;
+ else if (DstSubEltSize == 64 && SrcSubNumElts == 4 && SrcSubEltSize == 16)
+ Op = PISA::mov_i64_v4i16_r;
+ else if (DstSubEltSize == 128 && SrcSubNumElts == 2 && SrcSubEltSize == 64)
+ Op = PISA::mov_i128_v2i64_r;
+ else if (DstSubEltSize == 128 && SrcSubNumElts == 4 && SrcSubEltSize == 32)
+ Op = PISA::mov_i128_v4i32_r;
+ else
+ llvm_unreachable("wrong copy operation");
+ } else if (DstSubIsScalar && SrcSubIsScalar) {
+ const auto *TRI = static_cast<const PISARegisterInfo *>(
+ I->getMF()->getSubtarget().getRegisterInfo());
+ auto I16 = LLT::integer(16);
+
+ if ((DstSubEltSize == 1) && (SrcSubEltSize == 1)) {
+ // sel.16b %tmp, 1, 0, %p_in
+ // ucmp.ne.16b %p_out, %tmp, 0
+ auto TmpReg = MRI.createGenericVirtualRegister(I16);
+ MRI.setRegClass(TmpReg, TRI->getRegClassFromLLT(I16));
+ Op = PISA::sel_16_iip;
+ BuildMI(MBB, I, DL, get(Op))
+ .addDef(TmpReg)
+ .addReg(SrcOp.getReg())
+ .addImm(1)
+ .addImm(0);
+ Op = PISA::ucmp_ne_16b_pri;
+ BuildMI(MBB, I, DL, get(Op))
+ .addDef(DstOp.getReg())
+ .addReg(TmpReg)
+ .addImm(0);
+ return;
+ }
+ if (DstSubEltSize == 1) {
+ // ucmp.ne.??b %p, %src, 0
+ auto TmpReg = SrcOp.getReg();
+ if (SrcSubEltSize == 8) {
+ TmpReg = MRI.createGenericVirtualRegister(I16);
+ MRI.setRegClass(TmpReg, TRI->getRegClassFromLLT(I16));
+ Op = PISA::zext_16b_8b_r;
+ BuildMI(MBB, I, DL, get(Op)).addDef(TmpReg).addReg(SrcOp.getReg());
+ }
+ switch (SrcSubEltSize) {
+ default:
+ llvm_unreachable("unsupported source size!");
+ case 8:
+ case 16:
+ Op = PISA::ucmp_ne_16b_pri;
+ break;
+ case 32:
+ Op = PISA::ucmp_ne_32b_pri;
+ break;
+ case 64:
+ Op = PISA::ucmp_ne_64b_pri;
+ break;
+ }
+ BuildMI(MBB, I, DL, get(Op))
+ .addDef(DstOp.getReg())
+ .addReg(TmpReg)
+ .addImm(0);
+ return;
+ }
+ if (SrcSubEltSize == 1) {
+ // sel.??b %dst, 1, 0, %p
+ auto TmpReg = DstOp.getReg();
+ if (DstSubEltSize == 8) {
+ TmpReg = MRI.createGenericVirtualRegister(I16);
+ MRI.setRegClass(TmpReg, TRI->getRegClassFromLLT(I16));
+ }
+ switch (DstSubEltSize) {
+ default:
+ llvm_unreachable("unsupported destination size!");
+ case 8:
+ case 16:
+ Op = PISA::sel_16_iip;
+ break;
+ case 32:
+ Op = PISA::sel_32_iip;
+ break;
+ case 64:
+ Op = PISA::sel_64_iip;
+ break;
+ }
+ BuildMI(MBB, I, DL, get(Op))
+ .addDef(TmpReg)
+ .addReg(SrcOp.getReg())
+ .addImm(1)
+ .addImm(0);
+ if (DstSubEltSize == 8) {
+ Op = PISA::trunc_8b_16b_r;
+ BuildMI(MBB, I, DL, get(Op)).addDef(DstOp.getReg()).addReg(TmpReg);
+ }
+ return;
+ }
+ switch (DstSubEltSize) {
+ case 8:
+ Op = PISA::mov_i8_r;
+ break;
+ case 16:
+ Op = PISA::mov_i16_r;
+ break;
+ case 32:
+ Op = PISA::mov_i32_r;
+ break;
+ case 64:
+ Op = PISA::mov_i64_r;
+ break;
+ default:
+ llvm_unreachable("unknown elt size!");
+ }
+ }
+
+ unsigned DstSubreg = DstOp.getSubReg();
+ unsigned SrcSubreg = SrcOp.getSubReg();
+
+ RegState Flags = DstOp.isUndef() ? RegState::Undef : RegState::NoFlags;
+ BuildMI(MBB, I, DL, get(Op))
+ .addDef(DstOp.getReg(), Flags, DstSubreg)
+ .addReg(SrcOp.getReg(), getKillRegState(KillSrc), SrcSubreg);
+}
diff --git a/llvm/lib/Target/PISA/PISAInstrInfo.h b/llvm/lib/Target/PISA/PISAInstrInfo.h
index 0b411221edf54..4e73ee6e19977 100644
--- a/llvm/lib/Target/PISA/PISAInstrInfo.h
+++ b/llvm/lib/Target/PISA/PISAInstrInfo.h
@@ -8,13 +8,15 @@
#ifndef LLVM_LIB_TARGET_PISA_PISAINSTRINFO_H
#define LLVM_LIB_TARGET_PISA_PISAINSTRINFO_H
-
#include "PISARegisterInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#define GET_INSTRINFO_HEADER
#include "PISAGenInstrInfo.inc"
+#define GET_INSTRINFO_OPERAND_ENUM
+#include "PISAGenInstrInfo.inc"
+
namespace llvm {
class PISASubtarget;
@@ -25,6 +27,34 @@ class PISAInstrInfo : public PISAGenInstrInfo {
PISAInstrInfo(const PISASubtarget &STI);
const PISARegisterInfo &getRegisterInfo() const { return RI; }
+ bool isNoEmissionInstr(const MachineInstr &MI) const;
+ bool isFunctionParamInstr(const MachineInstr &MI) const;
+
+ bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB,
+ MachineBasicBlock *&FBB,
+ SmallVectorImpl<MachineOperand> &Cond,
+ bool AllowModify = false) const override;
+
+ unsigned removeBranch(MachineBasicBlock &MBB,
+ int *BytesRemoved = nullptr) const override;
+
+ unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB,
+ MachineBasicBlock *FBB, ArrayRef<MachineOperand> Cond,
+ const DebugLoc &DL,
+ int *BytesAdded = nullptr) const override;
+
+ bool
+ reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override;
+
+ bool expandPostRAPseudo(MachineInstr &MI) const override;
+
+ bool isSafeToMove(const MachineInstr &MI, const MachineBasicBlock *MBB,
+ const MachineFunction &MF) const override;
+
+ void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I,
+ const DebugLoc &DL, Register DestReg, Register SrcReg,
+ bool KillSrc, bool RenamableDest = false,
+ bool RenamableSrc = false) const override;
};
} // namespace llvm
diff --git a/llvm/lib/Target/PISA/PISAInstrInfo.td b/llvm/lib/Target/PISA/PISAInstrInfo.td
index de93f39b2dc01..154e1d8c9c6ea 100644
--- a/llvm/lib/Target/PISA/PISAInstrInfo.td
+++ b/llvm/lib/Target/PISA/PISAInstrInfo.td
@@ -1,4 +1,4 @@
-//===-- PISAInstrInfo.td - PISA Instruction defs ----------*- tablegen -*-===//
+//===-- PISAInstrInfo.td - PISA Instruction defs -----------*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,15 +6,2515 @@
//
//===----------------------------------------------------------------------===//
-class PISAInst<dag outs, dag ins, string asmstr, list<dag> pattern>
- : Instruction {
- let Namespace = "PISA";
- dag OutOperandList = outs;
- dag InOperandList = ins;
- let AsmString = asmstr;
- let Pattern = pattern;
- let hasSideEffects = 0;
+include "PISAInstrFormats.td"
+
+// Templates for imm type checks
+class uimmCheck<int num, ValueType VT>
+ : ImmLeaf<VT, "return isUInt<"#num#">(Imm);">;
+class simmCheck<int num, ValueType VT>
+ : ImmLeaf<VT, "return isInt<"#num#">(Imm);">;
+class immType<bit isSigned, ValueType VT, ValueType extVT = VT> {
+ ImmLeaf check = !if(isSigned, simmCheck<VT.Size, extVT>,
+ uimmCheck<VT.Size, extVT>);
+}
+
+// 'afn' specified for call
+class fastunaryop<SDPatternOperator nodeop> :
+ PatFrag<(ops node:$src), (nodeop node:$src), [{ /* empty */ }]> {
+ let GISelPredicateCode = [{ return useFastFP(MI); }];
+}
+// 'arcp' specified for call, used only for division
+class fastbinop<SDPatternOperator nodeop> :
+ PatFrag<(ops node:$lhs, node:$rhs), (nodeop node:$lhs, node:$rhs), [{ /* empty */ }]> {
+ let GISelPredicateCode = [{ return MI.getFlag(MachineInstr::FmArcp); }];
+}
+
+// operand handling
+class PrintDispatch<string PM, ValueType Ty, string OpType> : OperandWithDefaultOps<Ty, (ops (Ty 0))> {
+ let PrintMethod = PM;
+ let OperandNamespace = "PISA";
+ let OperandType = OpType;
+}
+def Negate : PrintDispatch<"negateRepr", i32, "OPERAND_NEGATE"> {
+ let ParserMatchClass = PISAAsmOperand<"NegateOpnd">;
+}
+
+// call target
+def FunctionCallTargetAsmOperand : AsmOperandClass {
+ let Name = "FunctionCallTargetOpnd";
+ let ParserMethod = "parseFunctionCallTargetOpnd";
+}
+// ... register or globalVar
+def FunctionCallTargetOpnd : Operand<i64> {
+ let PrintMethod="printFunctionCallTargetOpnd";
+ let ParserMatchClass = FunctionCallTargetAsmOperand;
+}
+// ... register
+def IndirectFunctionCallTargetOpnd : PISARegisterOperand<Reg64b> {
+ let PrintMethod="printFunctionCallTargetOpnd";
+}
+
+
+// mapping between LLVM and PISA names
+class AtomicMemOrder<string MO> {
+ string name = !if(!eq(MO,"monotonic"), "relaxed", MO);
+}
+
+// ftz modifier
+let RecomputePerFunction = 1 in {
+ def useFTZ : Predicate<"Subtarget->getTargetLowering()->useFTZ(*MF)">;
+ def noFTZ : Predicate<"!Subtarget->getTargetLowering()->useFTZ(*MF)">;
+}
+class FTZModifier<string ftzName, string ftzOp, Predicate ftzPred> {
+ string name = ftzName;
+ string op = ftzOp;
+ Predicate pred = ftzPred;
+}
+def noneFTZMod : FTZModifier<"", "", Predicate<"">>;
+def omitFTZMod : FTZModifier<"", "", noFTZ>;
+def usesFTZMod : FTZModifier<".ftz", "_ftz", useFTZ>;
+
+// saturation modifier
+def timm_false : TImmLeaf<i1, [{ return ((uint64_t)Imm) == 0; }]>;
+def timm_true : TImmLeaf<i1, [{ return ((uint64_t)Imm) == (uint64_t)-1; }]>;
+class SaturationModifier<string satName, string satOp, TImmLeaf satNode> {
+ string name = satName;
+ string op = satOp;
+ TImmLeaf node = satNode;
+}
+def NoSatMod : SaturationModifier<"", "", timm_false>;
+def DsatMod : SaturationModifier<".dsat", "_sat", timm_true>;
+
+// NaN-propagation modifier
+class NanPropagateModifier<string nanpName, TImmLeaf nanpNode> {
+ string name = nanpName;
+ TImmLeaf node = nanpNode;
+}
+def NoNanpMod : NanPropagateModifier<"", timm_false>;
+def NanpMod : NanPropagateModifier<".nanp", timm_true>;
+defvar AllNanpMods = [NoNanpMod, NanpMod];
+
+// wrapper of TImmLeaf with instruction qualifier name
+class NamedTImmLeaf<string _name, ValueType vt, code pred> : TImmLeaf<vt, pred> {
+ string name = _name;
+}
+
+// ired operation types (PISAIntrinsicUtils.h)
+def IRedOp_SUM_timm : NamedTImmLeaf<".sum", i8, [{ return Imm == pisa::IRedOp::SUM; }]>;
+def IRedOp_SMIN_timm : NamedTImmLeaf<".smin", i8, [{ return Imm == pisa::IRedOp::SMIN; }]>;
+def IRedOp_SMAX_timm : NamedTImmLeaf<".smax", i8, [{ return Imm == pisa::IRedOp::SMAX; }]>;
+def IRedOp_UMIN_timm : NamedTImmLeaf<".umin", i8, [{ return Imm == pisa::IRedOp::UMIN; }]>;
+def IRedOp_UMAX_timm : NamedTImmLeaf<".umax", i8, [{ return Imm == pisa::IRedOp::UMAX; }]>;
+def IRedOp_AND_timm : NamedTImmLeaf<".and", i8, [{ return Imm == pisa::IRedOp::AND; }]>;
+def IRedOp_OR_timm : NamedTImmLeaf<".or", i8, [{ return Imm == pisa::IRedOp::OR; }]>;
+def IRedOp_XOR_timm : NamedTImmLeaf<".xor", i8, [{ return Imm == pisa::IRedOp::XOR; }]>;
+def IRedOp_ABSMAX_timm : NamedTImmLeaf<".absmax", i8, [{ return Imm == pisa::IRedOp::ABSMAX; }]>;
+defvar IRedOpNodes = [IRedOp_SUM_timm, IRedOp_SMIN_timm, IRedOp_SMAX_timm,
+ IRedOp_UMIN_timm, IRedOp_UMAX_timm, IRedOp_AND_timm,
+ IRedOp_OR_timm, IRedOp_XOR_timm, IRedOp_ABSMAX_timm];
+
+// fred operation types (PISAIntrinsicUtils.h)
+def FRedOp_MIN_timm : NamedTImmLeaf<".min", i8, [{ return Imm == pisa::FRedOp::MIN; }]>;
+def FRedOp_MAX_timm : NamedTImmLeaf<".max", i8, [{ return Imm == pisa::FRedOp::MAX; }]>;
+def FRedOp_ABSMAX_timm : NamedTImmLeaf<".absmax", i8, [{ return Imm == pisa::FRedOp::ABSMAX; }]>;
+defvar FRedOpNodes = [FRedOp_MIN_timm, FRedOp_MAX_timm, FRedOp_ABSMAX_timm];
+
+
+// shfl mode types (PISAIntrinsicUtils.h)
+def SHFLMode_UP_timm : NamedTImmLeaf<".up", i8, [{ return Imm == pisa::SHFLMode::UP; }]>;
+def SHFLMode_DOWN_timm : NamedTImmLeaf<".dn", i8, [{ return Imm == pisa::SHFLMode::DOWN; }]>;
+def SHFLMode_XOR_timm : NamedTImmLeaf<".xor", i8, [{ return Imm == pisa::SHFLMode::XOR; }]>;
+def SHFLMode_IDX_timm : NamedTImmLeaf<".idx", i8, [{ return Imm == pisa::SHFLMode::IDX; }]>;
+defvar SHFLModeNodes = [SHFLMode_UP_timm, SHFLMode_DOWN_timm, SHFLMode_XOR_timm, SHFLMode_IDX_timm];
+
+
+// memory ordering types (AtomicOrdering.h)
+def MemoryOrder_relaxed_timm : NamedTImmLeaf<".relaxed", i8, [{ return Imm == (unsigned)llvm::AtomicOrdering::Monotonic; }]>;
+def MemoryOrder_acquire_timm : NamedTImmLeaf<".acquire", i8, [{ return Imm == (unsigned)llvm::AtomicOrdering::Acquire; }]>;
+def MemoryOrder_release_timm : NamedTImmLeaf<".release", i8, [{ return Imm == (unsigned)llvm::AtomicOrdering::Release; }]>;
+def MemoryOrder_acq_rel_timm : NamedTImmLeaf<".acq_rel", i8, [{ return Imm == (unsigned)llvm::AtomicOrdering::AcquireRelease; }]>;
+defvar MemoryOrderNodes = [MemoryOrder_relaxed_timm, MemoryOrder_acquire_timm, MemoryOrder_release_timm, MemoryOrder_acq_rel_timm];
+
+// rounding mode types (FloatingPointMode.h)
+def RoundingMode_RZ_timm : NamedTImmLeaf<".rz", i8, [{ return Imm == (int)llvm::RoundingMode::TowardZero; }]>;
+def RoundingMode_RE_timm : NamedTImmLeaf<".re", i8, [{ return Imm == (int)llvm::RoundingMode::NearestTiesToEven; }]>;
+def RoundingMode_RU_timm : NamedTImmLeaf<".ru", i8, [{ return Imm == (int)llvm::RoundingMode::TowardPositive; }]>;
+def RoundingMode_RD_timm : NamedTImmLeaf<".rd", i8, [{ return Imm == (int)llvm::RoundingMode::TowardNegative; }]>;
+def RoundingMode_RNA_timm : NamedTImmLeaf<".rna", i8, [{ return Imm == (int)llvm::RoundingMode::NearestTiesToAway; }]>;
+def RoundingMode_INV_timm : NamedTImmLeaf<"", i8, [{ return Imm == (int)llvm::RoundingMode::Invalid; }]>;
+defvar RoundingModeNodes = [RoundingMode_RZ_timm, RoundingMode_RE_timm, RoundingMode_RU_timm,
+ RoundingMode_RD_timm, RoundingMode_RNA_timm];
+defvar RoundingModeNone = [RoundingMode_INV_timm];
+defvar RoundingModeAllNodes = !listconcat(RoundingModeNodes, RoundingModeNone);
+
+
+//////
+// BEGIN: special register definitions
+multiclass SpecialRegPat<Intrinsic intr, Register reg> {
+ defvar RetTy = intr.RetTypes[0];
+ defvar VT = !if(!isa<LLVMQualPointerType>(RetTy),
+ !cast<pisaVT>("_p"#!cast<LLVMQualPointerType>(RetTy).Sig[1]),
+ !cast<pisaVT>("_"#RetTy.VT));
+ def : Pat<(intr),
+ (COPY_TO_REGCLASS (VT.VT reg), VT.RC)>;
+}
+
+defm : SpecialRegPat<int_pisa_local_id_x, SpecialReg_LocalIdX>;
+defm : SpecialRegPat<int_pisa_local_id_y, SpecialReg_LocalIdY>;
+defm : SpecialRegPat<int_pisa_local_id_z, SpecialReg_LocalIdZ>;
+defm : SpecialRegPat<int_pisa_local_size_x, SpecialReg_LocalSizeX>;
+defm : SpecialRegPat<int_pisa_local_size_y, SpecialReg_LocalSizeY>;
+defm : SpecialRegPat<int_pisa_local_size_z, SpecialReg_LocalSizeZ>;
+defm : SpecialRegPat<int_pisa_enqueued_local_size_x, SpecialReg_EnqueuedLocalSizeX>;
+defm : SpecialRegPat<int_pisa_enqueued_local_size_y, SpecialReg_EnqueuedLocalSizeY>;
+defm : SpecialRegPat<int_pisa_enqueued_local_size_z, SpecialReg_EnqueuedLocalSizeZ>;
+defm : SpecialRegPat<int_pisa_global_offset_x, SpecialReg_GlobalOffsetX>;
+defm : SpecialRegPat<int_pisa_global_offset_y, SpecialReg_GlobalOffsetY>;
+defm : SpecialRegPat<int_pisa_global_offset_z, SpecialReg_GlobalOffsetZ>;
+defm : SpecialRegPat<int_pisa_global_size_x, SpecialReg_GlobalSizeX>;
+defm : SpecialRegPat<int_pisa_global_size_y, SpecialReg_GlobalSizeY>;
+defm : SpecialRegPat<int_pisa_global_size_z, SpecialReg_GlobalSizeZ>;
+defm : SpecialRegPat<int_pisa_group_id_x, SpecialReg_GroupIdX>;
+defm : SpecialRegPat<int_pisa_group_id_y, SpecialReg_GroupIdY>;
+defm : SpecialRegPat<int_pisa_group_id_z, SpecialReg_GroupIdZ>;
+defm : SpecialRegPat<int_pisa_group_count_x, SpecialReg_GroupCountX>;
+defm : SpecialRegPat<int_pisa_group_count_y, SpecialReg_GroupCountY>;
+defm : SpecialRegPat<int_pisa_group_count_z, SpecialReg_GroupCountZ>;
+defm : SpecialRegPat<int_pisa_subgroup_size, SpecialReg_SubgroupSize>;
+defm : SpecialRegPat<int_pisa_lane_id, SpecialReg_LaneId>;
+defm : SpecialRegPat<int_pisa_activemask, SpecialReg_ActiveMask>;
+defm : SpecialRegPat<int_pisa_work_dim, SpecialReg_WorkDim>;
+// END: special register definitions
+//////
+
+//////
+// BEGIN: auxiliary class definitions
+// generate a table that maps name to instruction
+// - used in selection of insert/extract instructions
+class Name2InstrEntry<string name, Instruction inst> {
+ string Name = name;
+ Instruction Inst = inst;
+}
+def Name2InstrOpTable : GenericTable {
+ let FilterClass = "Name2InstrEntry";
+ let Fields = ["Name", "Inst"];
+}
+def lookupName2InstrOpEntry : SearchIndex {
+ let Table = Name2InstrOpTable;
+ let Key = ["Name"];
+}
+// Generate a table that maps a register-class name to lifetime.start opcode.
+class LifetimeStartEntry<string rcName, Instruction inst> {
+ string RegClassName = rcName;
+ Instruction Inst = inst;
+}
+def LifetimeStartTable : GenericTable {
+ let FilterClass = "LifetimeStartEntry";
+ let Fields = ["RegClassName", "Inst"];
+}
+def lookupLifetimeStartByRegClass : SearchIndex {
+ let Table = LifetimeStartTable;
+ let Key = ["RegClassName"];
+}
+// END: auxiliary class definitions
+//////
+
+//////
+// BEGIN: unary instruction patterns
+class UnaryInst_r<string name, SDPatternOperator node, pisaVT dstVT, pisaVT srcVT,
+ dag extraArgs = (ops)>
+ : PISAInst_r<name, dstVT, srcVT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (dstVT.VT dstRC:$dst), !con((node (srcVT.VT srcRC:$src)), extraArgs))],
+ [(set (dstVT.VT dstRC:$dst), (node (srcVT.VT srcRC:$src)))]);
+}
+class UnaryInst_i<string name, SDPatternOperator node, pisaVT dstVT, pisaVT srcVT,
+ dag extraArgs = (ops)>
+ : PISAInst_i<name, dstVT, srcVT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (dstVT.VT dstRC:$dst), !con((node (srcVT.VT immVal:$src)), extraArgs))],
+ [(set (dstVT.VT dstRC:$dst), (node (srcVT.VT immVal:$src)))]);
+}
+
+// unary integer instruction
+multiclass UnaryInst<string name, SDPatternOperator node, list<pisaVT> types = [_i16, _i32, _i64]> {
+ foreach VT = types in {
+ defvar directive = VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r": UnaryInst_r<name#directive, node, VT, VT>;
+ def opcode#"_i": UnaryInst_i<name#directive, node, VT, VT>;
+ }
+}
+
+// unary float instruction
+multiclass UnaryFloatInst<string name, SDPatternOperator node, list<pisaVT> types = [_bf16, _f16, _f32, _f64], bit isFast = 0> {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ defvar fastName = !if(isFast, ".fast", "");
+ foreach VT = types in {
+ defvar directive = !interleave([ftz.name, fastName, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r": UnaryInst_r<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_i": UnaryInst_i<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ }
+ }
+}
+
+// unary float instruction with rounding mode
+multiclass UnaryFloatInstRnd<string name, SDPatternOperator node, list<pisaVT> types = [_bf16, _f16, _f32, _f64]> {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach VT = types in {
+ foreach rndNode = RoundingModeNodes in {
+ defvar directive = !interleave([rndNode.name, ftz.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r": UnaryInst_r<name#directive, node, VT, VT, (node rndNode)>, Requires<[ftz.pred]>;
+ def opcode#"_i": UnaryInst_i<name#directive, node, VT, VT, (node rndNode)>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+}
+// END: unary instruction patterns
+//////
+
+//////
+// BEGIN: binary instruction patterns
+class BinaryInst_rr<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src1VT, pisaVT dstVT=src0VT,
+ dag extraArgs = (ops)>
+ : PISAInst_rr<name, dstVT, src0VT, src1VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (dstVT.VT dstRC:$dst), !con((node (src0VT.VT src0RC:$src0), (src1VT.VT src1RC:$src1)), extraArgs))],
+ [(set (dstVT.VT dstRC:$dst), (node (src0VT.VT src0RC:$src0), (src1VT.VT src1RC:$src1)))]);
+}
+class BinaryInst_ri<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src1VT, pisaVT dstVT=src0VT,
+ dag extraArgs = (ops)>
+ : PISAInst_ri<name, dstVT, src0VT, src1VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (dstVT.VT dstRC:$dst), !con((node (src0VT.VT src0RC:$src0), (src1VT.VT immVal1:$src1)), extraArgs))],
+ [(set (dstVT.VT dstRC:$dst), (node (src0VT.VT src0RC:$src0), (src1VT.VT immVal1:$src1)))]);
+}
+class BinaryInst_ir<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src1VT, pisaVT dstVT=src0VT,
+ bit isCommutative = true, dag extraArgs = (ops)>
+ : PISAInst_ir<name, dstVT, src0VT, src1VT> {
+ // Pattern is suppressed for commutative instructions: the canonicalizer always
+ // places immediates on the RHS, so the _ir form would never match. Pass
+ // isCommutative = false (e.g. for intrinsics) to emit a pattern.
+ let Pattern = !if(isCommutative,
+ [],
+ !if(!gt(!size(extraArgs), 0),
+ [(set (dstVT.VT dstRC:$dst), !con((node (src0VT.VT immVal0:$src0), (src1VT.VT src1RC:$src1)), extraArgs))],
+ [(set (dstVT.VT dstRC:$dst), (node (src0VT.VT immVal0:$src0), (src1VT.VT src1RC:$src1)))]));
+}
+class BinaryInst_ii<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src1VT, pisaVT dstVT=src0VT,
+ dag extraArgs = (ops)>
+ : PISAInst_ii<name, dstVT, src0VT, src1VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (dstVT.VT dstRC:$dst), !con((node (src0VT.VT immVal0:$src0), (src1VT.VT immVal1:$src1)), extraArgs))],
+ [(set (dstVT.VT dstRC:$dst), (node (src0VT.VT immVal0:$src0), (src1VT.VT immVal1:$src1)))]);
+}
+
+// binary integer instruction with optional saturation modifier
+multiclass BinaryInst<string name, SDPatternOperator node, list<pisaVT> types = [_i16, _i32, _i64], bit isCommutative = true, list<SaturationModifier> satMods = [NoSatMod]> {
+ foreach sat = satMods in {
+ foreach VT = types in {
+ defvar directive = !interleave([sat.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rr": BinaryInst_rr<name#directive, node, VT, VT>;
+ def opcode#"_ri": BinaryInst_ri<name#directive, node, VT, VT>;
+ def opcode#"_ir": BinaryInst_ir<name#directive, node, VT, VT, VT, isCommutative>;
+ def opcode#"_ii": BinaryInst_ii<name#directive, node, VT, VT>;
+ }
+ }
+}
+
+// binary float instruction with optional saturation modifier
+multiclass BinaryFloatInst<string name, SDPatternOperator node, list<pisaVT> types = [_bf16, _f16, _f32, _f64], bit isCommutative = true, list<SaturationModifier> satMods = [NoSatMod], bit isFast = false> {
+ foreach sat = satMods in {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ defvar fastName = !if(isFast, ".fast", "");
+ foreach VT = types in {
+ defvar directive = !interleave([ftz.name, fastName, sat.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rr" : BinaryInst_rr<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_ri" : BinaryInst_ri<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_ir" : BinaryInst_ir<name#directive, node, VT, VT, VT, isCommutative>, Requires<[ftz.pred]>;
+ def opcode#"_ii" : BinaryInst_ii<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+}
+
+// binary float instruction with a trailing i1 nanp immediate
+multiclass BinaryFloatInstNanp<string name, SDPatternOperator node, list<pisaVT> types = [_bf16, _f16, _f32, _f64], bit isCommutative = true, list<SaturationModifier> satMods = [NoSatMod]> {
+ foreach sat = satMods in {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach nanp = AllNanpMods in {
+ foreach VT = types in {
+ defvar directive = !interleave([ftz.name, sat.name, nanp.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rr" : BinaryInst_rr<name#directive, node, VT, VT, VT, (node nanp.node)>, Requires<[ftz.pred]>;
+ def opcode#"_ri" : BinaryInst_ri<name#directive, node, VT, VT, VT, (node nanp.node)>, Requires<[ftz.pred]>;
+ def opcode#"_ir" : BinaryInst_ir<name#directive, node, VT, VT, VT, isCommutative, (node nanp.node)>, Requires<[ftz.pred]>;
+ def opcode#"_ii" : BinaryInst_ii<name#directive, node, VT, VT, VT, (node nanp.node)>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+ }
+}
+
+// binary float instruction with .nanp modifier only
+multiclass BinaryFloatInstNanpOnly<string name, SDPatternOperator node, list<pisaVT> types = [_bf16, _f16, _f32, _f64], bit isCommutative = true> {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach VT = types in {
+ defvar directive = !interleave([ftz.name, ".nanp", VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rr" : BinaryInst_rr<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_ri" : BinaryInst_ri<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_ir" : BinaryInst_ir<name#directive, node, VT, VT, VT, isCommutative>, Requires<[ftz.pred]>;
+ def opcode#"_ii" : BinaryInst_ii<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ }
+ }
+}
+
+// binary float instruction with rounding mode
+multiclass BinaryFloatInstRnd<string name, SDPatternOperator node, list<pisaVT> types = [_bf16, _f16, _f32, _f64], list<NamedTImmLeaf> rndNodes = RoundingModeNodes> {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach VT = types in {
+ foreach rndNode = rndNodes in {
+ defvar directive = !interleave([rndNode.name, ftz.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrii": BinaryInst_rr<name#directive, node, VT, VT, VT, (node rndNode)>, Requires<[ftz.pred]>;
+ def opcode#"_riii": BinaryInst_ri<name#directive, node, VT, VT, VT, (node rndNode)>, Requires<[ftz.pred]>;
+ def opcode#"_irii": BinaryInst_ir<name#directive, node, VT, VT, VT, false, (node rndNode)>, Requires<[ftz.pred]>;
+ def opcode#"_iiii": BinaryInst_ii<name#directive, node, VT, VT, VT, (node rndNode)>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+}
+
+// binary float instruction with rounding mode and saturation modifier
+multiclass BinaryFloatInstRndSat<string name, SDPatternOperator node, list<pisaVT> types = [_bf16, _f16, _f32, _f64], list<NamedTImmLeaf> rndNodes = RoundingModeAllNodes, list<SaturationModifier> satMods = [NoSatMod, DsatMod], bit isCommutative = true, bit isFullPrecision = false> {
+ defvar fullName = !if(isFullPrecision, ".full", "");
+ foreach sat = satMods in {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach VT = types in {
+ foreach rndNode = rndNodes in {
+ defvar directive = !interleave([fullName, rndNode.name, ftz.name, sat.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ defvar resultVT = !if(isFullPrecision, _f32, VT);
+ def opcode#"_rrii": BinaryInst_rr<name#directive, node, VT, VT, resultVT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_riii": BinaryInst_ri<name#directive, node, VT, VT, resultVT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_irii": BinaryInst_ir<name#directive, node, VT, VT, resultVT, isCommutative, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_iiii": BinaryInst_ii<name#directive, node, VT, VT, resultVT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+ }
+}
+
+// END: binary instruction patterns
+//////
+
+//////
+// BEGIN: ternary instruction patterns
+class TernaryInst_rrr<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src2VT,
+ dag extraArgs = (ops)>
+ : PISAInst_rrr<name, src2VT, src0VT, src0VT, src2VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (src0VT.VT dstRC:$dst), !con((node (src0VT.VT src0RC:$src0), (src0VT.VT src1RC:$src1), (src0VT.VT src2RC:$src2)), extraArgs))],
+ [(set (src0VT.VT dstRC:$dst), (node (src0VT.VT src0RC:$src0), (src0VT.VT src1RC:$src1), (src0VT.VT src2RC:$src2)))]);
+}
+class TernaryInst_rri<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src2VT,
+ dag extraArgs = (ops)>
+ : PISAInst_rri<name, src2VT, src0VT, src0VT, src2VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (src0VT.VT dstRC:$dst), !con((node (src0VT.VT src0RC:$src0), (src0VT.VT src1RC:$src1), (src2VT.VT immVal2:$src2)), extraArgs))],
+ [(set (src0VT.VT dstRC:$dst), (node (src0VT.VT src0RC:$src0), (src0VT.VT src1RC:$src1), (src2VT.VT immVal2:$src2)))]);
+}
+class TernaryInst_rir<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src2VT,
+ dag extraArgs = (ops)>
+ : PISAInst_rir<name, src2VT, src0VT, src0VT, src2VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (src0VT.VT dstRC:$dst), !con((node (src0VT.VT src0RC:$src0), (src0VT.VT immVal1:$src1), (src2VT.VT src2RC:$src2)), extraArgs))],
+ [(set (src0VT.VT dstRC:$dst), (node (src0VT.VT src0RC:$src0), (src0VT.VT immVal1:$src1), (src2VT.VT src2RC:$src2)))]);
+}
+class TernaryInst_rii<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src2VT,
+ dag extraArgs = (ops)>
+ : PISAInst_rii<name, src2VT, src0VT, src0VT, src2VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (src0VT.VT dstRC:$dst), !con((node (src0VT.VT src0RC:$src0), (src0VT.VT immVal1:$src1), (src2VT.VT immVal2:$src2)), extraArgs))],
+ [(set (src0VT.VT dstRC:$dst), (node (src0VT.VT src0RC:$src0), (src0VT.VT immVal1:$src1), (src2VT.VT immVal2:$src2)))]);
+}
+class TernaryInst_irr<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src2VT,
+ dag extraArgs = (ops)>
+ : PISAInst_irr<name, src2VT, src0VT, src0VT, src2VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (src0VT.VT dstRC:$dst), !con((node (src0VT.VT immVal0:$src0), (src0VT.VT src1RC:$src1), (src2VT.VT src2RC:$src2)), extraArgs))],
+ [(set (src0VT.VT dstRC:$dst), (node (src0VT.VT immVal0:$src0), (src0VT.VT src1RC:$src1), (src2VT.VT src2RC:$src2)))]);
+}
+class TernaryInst_iri<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src2VT,
+ dag extraArgs = (ops)>
+ : PISAInst_iri<name, src2VT, src0VT, src0VT, src2VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (src0VT.VT dstRC:$dst), !con((node (src0VT.VT immVal0:$src0), (src0VT.VT src1RC:$src1), (src2VT.VT immVal2:$src2)), extraArgs))],
+ [(set (src0VT.VT dstRC:$dst), (node (src0VT.VT immVal0:$src0), (src0VT.VT src1RC:$src1), (src2VT.VT immVal2:$src2)))]);
+}
+class TernaryInst_iir<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src2VT,
+ dag extraArgs = (ops)>
+ : PISAInst_iir<name, src2VT, src0VT, src0VT, src2VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (src0VT.VT dstRC:$dst), !con((node (src0VT.VT immVal0:$src0), (src0VT.VT immVal1:$src1), (src2VT.VT src2RC:$src2)), extraArgs))],
+ [(set (src0VT.VT dstRC:$dst), (node (src0VT.VT immVal0:$src0), (src0VT.VT immVal1:$src1), (src2VT.VT src2RC:$src2)))]);
+}
+class TernaryInst_iii<string name, SDPatternOperator node, pisaVT src0VT, pisaVT src2VT,
+ dag extraArgs = (ops)>
+ : PISAInst_iii<name, src2VT, src0VT, src0VT, src2VT> {
+ let Pattern = !if(!gt(!size(extraArgs), 0),
+ [(set (src0VT.VT dstRC:$dst), !con((node (src0VT.VT immVal0:$src0), (src0VT.VT immVal1:$src1), (src2VT.VT immVal2:$src2)), extraArgs))],
+ [(set (src0VT.VT dstRC:$dst), (node (src0VT.VT immVal0:$src0), (src0VT.VT immVal1:$src1), (src2VT.VT immVal2:$src2)))]);
+}
+
+// ternary integer instruction with optional saturation modifier
+multiclass TernaryInst<string name, SDPatternOperator node, list<pisaVT> types = [_i16, _i32, _i64], list<SaturationModifier> satMods = [NoSatMod]> {
+ foreach sat = satMods in {
+ foreach VT = types in {
+ defvar directive = !interleave([sat.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrr" : TernaryInst_rrr<name#directive, node, VT, VT>;
+ def opcode#"_rri" : TernaryInst_rri<name#directive, node, VT, VT>;
+ def opcode#"_rir" : TernaryInst_rir<name#directive, node, VT, VT>;
+ def opcode#"_rii" : TernaryInst_rii<name#directive, node, VT, VT>;
+ def opcode#"_irr" : TernaryInst_irr<name#directive, node, VT, VT>;
+ def opcode#"_iri" : TernaryInst_iri<name#directive, node, VT, VT>;
+ def opcode#"_iir" : TernaryInst_iir<name#directive, node, VT, VT>;
+ def opcode#"_iii" : TernaryInst_iii<name#directive, node, VT, VT>;
+ }
+ }
+}
+
+// ternary float instruction with optional saturation modifier
+multiclass TernaryFloatInst<string name, SDPatternOperator node> {
+ foreach sat = [NoSatMod] in {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach VT = [_bf16, _f16, _f32, _f64] in {
+ defvar directive = !interleave([ftz.name, sat.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrr" : TernaryInst_rrr<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_rri" : TernaryInst_rri<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_rir" : TernaryInst_rir<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_rii" : TernaryInst_rii<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_irr" : TernaryInst_irr<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_iri" : TernaryInst_iri<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_iir" : TernaryInst_iir<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ def opcode#"_iii" : TernaryInst_iii<name#directive, node, VT, VT>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+}
+
+// ternary float instruction with rounding mode and optional saturation modifier
+multiclass TernaryFloatInstRndSat<string name, SDPatternOperator node, list<pisaVT> types> {
+ foreach sat = [NoSatMod, DsatMod] in {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach VT = types in {
+ foreach rndNode = RoundingModeAllNodes in {
+ defvar directive = !interleave([rndNode.name, ftz.name, sat.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrrii" : TernaryInst_rrr<name#directive, node, VT, VT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_rriii" : TernaryInst_rri<name#directive, node, VT, VT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_ririi" : TernaryInst_rir<name#directive, node, VT, VT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_riiii" : TernaryInst_rii<name#directive, node, VT, VT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_irrii" : TernaryInst_irr<name#directive, node, VT, VT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_iriii" : TernaryInst_iri<name#directive, node, VT, VT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_iirii" : TernaryInst_iir<name#directive, node, VT, VT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_iiiii" : TernaryInst_iii<name#directive, node, VT, VT, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+ }
+}
+
+// END: ternary instruction patterns
+//////
+
+//////
+// BEGIN: convert instruction patterns
+// convert instructions from multiple types to a single type
+multiclass ConvertMultiInst<string name, SDPatternOperator Node, pisaVT dstTy, list<pisaVT> srcTys> {
+ foreach srcTy = srcTys in {
+ defvar directive = !interleave([dstTy.directive, srcTy.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r" : UnaryInst_r<name#directive, Node, dstTy, srcTy>;
+ def opcode#"_i" : UnaryInst_i<name#directive, Node, dstTy, srcTy>;
+ }
+}
+
+// convert float instructions from multiple types to a single type
+multiclass ConvertMultiFloatInst<string name, SDPatternOperator node, pisaVT dstTy, list<pisaVT> srcTys> {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach srcTy = srcTys in {
+ defvar directive = !interleave([dstTy.directive, srcTy.directive, ftz.name], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r": UnaryInst_r<name#directive, node, dstTy, srcTy>, Requires<[ftz.pred]>;
+ def opcode#"_i": UnaryInst_i<name#directive, node, dstTy, srcTy>, Requires<[ftz.pred]>;
+ }
+ }
+}
+
+multiclass ConvertMultiFloatInstRnd<string name, SDPatternOperator node, pisaVT dstTy, list<pisaVT> srcTys> {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach srcTy = srcTys in {
+ foreach rndNode = RoundingModeNodes in {
+ defvar directive = !interleave([dstTy.directive, srcTy.directive, rndNode.name, ftz.name], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r": UnaryInst_r<name#directive, node, dstTy, srcTy, (node rndNode)>, Requires<[ftz.pred]>;
+ def opcode#"_i": UnaryInst_i<name#directive, node, dstTy, srcTy, (node rndNode)>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+}
+
+// convert float instructions with rounding mode and saturation modifier
+multiclass ConvertMultiFloatInstRndSat<string name, SDPatternOperator node, pisaVT dstTy, list<pisaVT> srcTys> {
+ foreach sat = [NoSatMod, DsatMod] in {
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach srcTy = srcTys in {
+ foreach rndNode = RoundingModeAllNodes in {
+ defvar directive = !interleave([dstTy.directive, srcTy.directive, rndNode.name, ftz.name, sat.name], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r": UnaryInst_r<name#directive, node, dstTy, srcTy, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ def opcode#"_i": UnaryInst_i<name#directive, node, dstTy, srcTy, (node rndNode, sat.node)>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+ }
+}
+
+// convert instructions from single type to multiple types
+multiclass ConvertSingleInst<string name, SDPatternOperator node, list<pisaVT> dstTys, pisaVT srcTy> {
+ foreach dstTy = dstTys in {
+ defvar directive = !interleave([dstTy.directive, srcTy.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r" : UnaryInst_r<name#directive, node, dstTy, srcTy>;
+ def opcode#"_i" : UnaryInst_i<name#directive, node, dstTy, srcTy>;
+ }
+}
+// END: convert instruction patterns
+//////
+
+// function parameters
+let isMeta = 1 in {
+ multiclass FunctionParameter {
+ foreach VT = VTs.CallReturn in {
+ def "_"#VT.name : PISAInst<(outs VT.RC:$var), (ins i32imm:$idx), " ; ">;
+ }
+ }
+ defm functionParameter : FunctionParameter<>;
+}
+
+//////
+// BEGIN: integer arithmetic instruction definitions
+// => dp4a
+multiclass DP4AInst {
+ foreach node = [int_pisa_dp4a_uu, int_pisa_dp4a_us, int_pisa_dp4a_su, int_pisa_dp4a_ss] in {
+ defvar type = !subst("int_pisa_dp4a_", "", !cast<string>(node));
+ foreach sat = [NoSatMod, DsatMod] in {
+ defvar invalid = !and(!eq(type, "uu"), !eq(sat, DsatMod));
+ if !not(invalid) then {
+ defvar directive = type#sat.name#".32b";
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrr": TernaryInst_rrr<NAME#"."#directive, node, _i32, _i32, (node sat.node)>;
+ def opcode#"_rri": TernaryInst_rri<NAME#"."#directive, node, _i32, _i32, (node sat.node)>;
+ def opcode#"_rir": TernaryInst_rir<NAME#"."#directive, node, _i32, _i32, (node sat.node)>;
+ def opcode#"_rii": TernaryInst_rii<NAME#"."#directive, node, _i32, _i32, (node sat.node)>;
+ def opcode#"_irr": TernaryInst_irr<NAME#"."#directive, node, _i32, _i32, (node sat.node)>;
+ def opcode#"_iri": TernaryInst_iri<NAME#"."#directive, node, _i32, _i32, (node sat.node)>;
+ def opcode#"_iir": TernaryInst_iir<NAME#"."#directive, node, _i32, _i32, (node sat.node)>;
+ def opcode#"_iii": TernaryInst_iii<NAME#"."#directive, node, _i32, _i32, (node sat.node)>;
+ }
+ }
+ }
+}
+defm dp4a : DP4AInst;
+
+// => iabs
+defm iabs : UnaryInst<"iabs", abs>;
+
+
+// => iadd
+defm iadd: BinaryInst<"iadd", add>;
+defm ptradd: BinaryInst<"iadd", ptradd>;
+
+// .. with .dsat
+defm iadd: BinaryInst<"iadd", saddsat, satMods=[DsatMod]>;
+
+
+// => ineg
+defm ineg: UnaryInst<"ineg", ineg>;
+
+// => isub
+defm isub: BinaryInst<"isub", sub, isCommutative=false>;
+// .. with .dsat
+defm isub: BinaryInst<"isub", ssubsat, isCommutative=false, satMods=[DsatMod]>;
+
+// => sdiv
+defm sdiv_quo: BinaryInst<"sdiv.quo", sdiv, isCommutative=false>;
+defm sdiv: BinaryInst<"sdiv", sdiv, isCommutative=false>;
+defm sdiv_rem: BinaryInst<"sdiv.rem", srem, isCommutative=false>;
+
+// umad/smad
+class MadInst_rrr<string name, pisaVT bitwidth> : PISAInst_rrr<name, bitwidth, bitwidth, bitwidth, bitwidth> {
+ let Pattern = [(set dstRC:$dst, (add (mul src0RC:$src0, src1RC:$src1), src2RC:$src2))];
+}
+class MadInst_rri<string name, pisaVT bitwidth> : PISAInst_rri<name, bitwidth, bitwidth, bitwidth, bitwidth> {
+ let Pattern = [(set dstRC:$dst, (add (mul src0RC:$src0, src1RC:$src1), (bitwidth.VT imm:$src2)))];
+}
+class MadInst_rir<string name, pisaVT bitwidth> : PISAInst_rir<name, bitwidth, bitwidth, bitwidth, bitwidth> {
+ let Pattern = [(set dstRC:$dst, (add (mul src0RC:$src0, (bitwidth.VT imm:$src1)), src2RC:$src2))];
+}
+class MadInst_rii<string name, pisaVT bitwidth> : PISAInst_rii<name, bitwidth, bitwidth, bitwidth, bitwidth> {
+ let Pattern = [(set dstRC:$dst, (add (mul src0RC:$src0, (bitwidth.VT imm:$src1)), (bitwidth.VT imm:$src2)))];
+}
+class MadInst_iii<string name, pisaVT bitwidth> : PISAInst_iii<name, bitwidth, bitwidth, bitwidth, bitwidth> {
+ let Pattern = [(set dstRC:$dst, (add (mul (bitwidth.VT imm:$src0), (bitwidth.VT imm:$src1)), (bitwidth.VT imm:$src2)))];
+}
+class MadFullInst_rrr<string name, pisaVT bitwidth, pisaVT extBitwidth, bit isSigned> :
+ PISAInst_rrr<name, extBitwidth, bitwidth, bitwidth, extBitwidth> {
+ SDNode extNode = !if(isSigned, sext, zext);
+ let Pattern = [(set dstRC:$dst, (add (mul (extNode src0RC:$src0), (extNode src1RC:$src1)), src2RC:$src2))];
+}
+class MadFullInst_rri<string name, pisaVT bitwidth, pisaVT extBitwidth, bit isSigned> :
+ PISAInst_rri<name, extBitwidth, bitwidth, bitwidth, extBitwidth> {
+ SDNode extNode = !if(isSigned, sext, zext);
+ let Pattern = [(set dstRC:$dst, (add (mul (extNode src0RC:$src0), (extNode src1RC:$src1)), (extBitwidth.VT imm:$src2)))];
+}
+class MadFullInst_rir<string name, pisaVT bitwidth, pisaVT extBitwidth, bit isSigned> :
+ PISAInst_rir<name, extBitwidth, bitwidth, extBitwidth, extBitwidth> {
+ ImmLeaf immCheck1 = immType<isSigned, bitwidth.VT, extBitwidth.VT>.check;
+ SDNode extNode = !if(isSigned, sext, zext);
+ let Pattern = [(set dstRC:$dst, (add (mul (extNode src0RC:$src0), (immCheck1:$src1)), src2RC:$src2))];
+}
+class MadFullInst_rii<string name, pisaVT bitwidth, pisaVT extBitwidth, bit isSigned> :
+ PISAInst_rii<name, extBitwidth, bitwidth, extBitwidth, extBitwidth> {
+ ImmLeaf immCheck1 = immType<isSigned, bitwidth.VT, extBitwidth.VT>.check;
+ SDNode extNode = !if(isSigned, sext, zext);
+ let Pattern = [(set dstRC:$dst, (add (mul (extNode src0RC:$src0), (immCheck1:$src1)), (extBitwidth.VT imm:$src2)))];
+}
+multiclass MadInst<string name, bit isSigned> {
+ foreach VT = [_i16, _i32, _i64] in {
+ defvar directive = VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrr" : MadInst_rrr<name#directive, VT>;
+ def opcode#"_rri" : MadInst_rri<name#directive, VT>;
+ def opcode#"_rir" : MadInst_rir<name#directive, VT>;
+ def opcode#"_rii" : MadInst_rii<name#directive, VT>;
+ def opcode#"_irr" : PISAInst_irr<name#directive, VT, VT, VT, VT>;
+ def opcode#"_iri" : PISAInst_iri<name#directive, VT, VT, VT, VT>;
+ def opcode#"_iir" : PISAInst_iir<name#directive, VT, VT, VT, VT>;
+ def opcode#"_iii" : MadInst_iii<name#directive, VT>;
+ }
+ foreach VT = [_i16, _i32] in {
+ defvar extBitwidth = !if(!eq(VT, _i16), _i32, _i64);
+ defvar directive = ".full"#VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrr" : MadFullInst_rrr<name#directive, VT, extBitwidth, isSigned>;
+ def opcode#"_rir" : MadFullInst_rir<name#directive, VT, extBitwidth, isSigned>;
+ def opcode#"_rri" : MadFullInst_rri<name#directive, VT, extBitwidth, isSigned>;
+ def opcode#"_rii" : MadFullInst_rii<name#directive, VT, extBitwidth, isSigned>;
+ def opcode#"_irr" : PISAInst_irr<name#directive, extBitwidth, extBitwidth, VT, extBitwidth>;
+ def opcode#"_iir" : PISAInst_iir<name#directive, extBitwidth, extBitwidth, extBitwidth, extBitwidth>;
+ def opcode#"_iri" : PISAInst_iri<name#directive, extBitwidth, extBitwidth, VT, extBitwidth>;
+ def opcode#"_iii" : PISAInst_iii<name#directive, extBitwidth, extBitwidth, extBitwidth, extBitwidth>;
+ }
+}
+
+// => smad
+defm smad: MadInst<"smad", 1>;
+defm int_smad: TernaryInst<"smad", int_pisa_smad>;
+
+// => umad
+defm umad: MadInst<"umad", 0>;
+
+// => smax
+defm smax: BinaryInst<"smax", smax>;
+
+// => smin
+defm smin: BinaryInst<"smin", smin>;
+
+// => smul/umul
+// umul/smul
+class MulFullInst_rr<string name, pisaVT bitwidth, pisaVT extBitwidth, bit isSigned>
+ : PISAInst2<name, extBitwidth, bitwidth, bitwidth> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1);
+ SDNode extNode = !if(isSigned, sext, zext);
+ let Pattern = [(set dstRC:$dst, (mul (extNode src0RC:$src0), (extNode src1RC:$src1)))];
+}
+class MulFullInst_ri<string name, pisaVT bitwidth, pisaVT extBitwidth, bit isSigned>
+ : PISAInst2<name, extBitwidth, bitwidth, bitwidth> {
+ ImmLeaf immCheck1 = immType<isSigned, bitwidth.VT, extBitwidth.VT>.check;
+ DAGOperand immOpd1 = extBitwidth.ImmOpnd;
+ let InOperandList = (ins src0RC:$src0, immOpd1:$src1);
+ SDNode extNode = !if(isSigned, sext, zext);
+ let Pattern = [(set dstRC:$dst, (mul (extNode src0RC:$src0), (immCheck1:$src1)))];
+}
+class MulFullInst_ir<string name, pisaVT bitwidth, pisaVT extBitwidth, bit isSigned>
+ : PISAInst2<name, extBitwidth, bitwidth, bitwidth> {
+ DAGOperand immOpd0 = extBitwidth.ImmOpnd;
+ let InOperandList = (ins immOpd0:$src0, src1RC:$src1);
+ SDNode extNode = !if(isSigned, sext, zext);
+ let Pattern = [];
+}
+class MulFullInst_ii<string name, pisaVT bitwidth, pisaVT extBitwidth, bit isSigned>
+ : PISAInst2<name, extBitwidth, bitwidth, bitwidth> {
+ DAGOperand immOpd0 = extBitwidth.ImmOpnd;
+ DAGOperand immOpd1 = extBitwidth.ImmOpnd;
+ let InOperandList = (ins immOpd0:$src0, immOpd1:$src1);
+ SDNode extNode = !if(isSigned, sext, zext);
+ let Pattern = [];
+}
+multiclass MulInst<string name, bit isSigned> {
+ defm NAME: BinaryInst<name, mul>;
+ foreach VT = [_i16, _i32] in {
+ defvar extBitwidth = !if(!eq(VT, _i16), _i32, _i64);
+ defvar directive = ".full"#VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rr" : MulFullInst_rr<name#directive, VT, extBitwidth, isSigned>;
+ def opcode#"_ri" : MulFullInst_ri<name#directive, VT, extBitwidth, isSigned>;
+ def opcode#"_ir" : MulFullInst_ir<name#directive, VT, extBitwidth, isSigned>;
+ def opcode#"_ii" : MulFullInst_ii<name#directive, VT, extBitwidth, isSigned>;
+ }
+}
+defm smul: MulInst<"smul", isSigned=true>;
+defm umul: MulInst<"umul", isSigned=false>;
+
+// uaddc/usubb
+class CarryOutInst_rr<string name, pisaVT VT> : PISAInst_rr<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $cout, $src0, $src1;";
+ let OutOperandList = (outs dstRC:$dst, PredRC:$cout);
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1);
+}
+class CarryOutInst_ri<string name, pisaVT VT> : PISAInst_ri<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $cout, $src0, $src1;";
+ let OutOperandList = (outs dstRC:$dst, PredRC:$cout);
+ let InOperandList = (ins src0RC:$src0, immOpd1:$src1);
+}
+class CarryOutInst_ir<string name, pisaVT VT> : PISAInst_ir<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $cout, $src0, $src1;";
+ let OutOperandList = (outs dstRC:$dst, PredRC:$cout);
+ let InOperandList = (ins immOpd0:$src0, src1RC:$src1);
+}
+class CarryOutInst_ii<string name, pisaVT VT> : PISAInst_ii<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $cout, $src0, $src1;";
+ let OutOperandList = (outs dstRC:$dst, PredRC:$cout);
+ let InOperandList = (ins immOpd0:$src0, immOpd1:$src1);
+}
+multiclass CarryOutInst<string name> {
+ foreach VT = [_i32] in {
+ defvar directive = VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rr" : CarryOutInst_rr<name#directive, VT>;
+ def opcode#"_ri" : CarryOutInst_ri<name#directive, VT>;
+ def opcode#"_ir" : CarryOutInst_ir<name#directive, VT>;
+ def opcode#"_ii" : CarryOutInst_ii<name#directive, VT>;
+ }
+}
+class CarryInInst_rr<string name, pisaVT VT> : PISAInst_rr<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $src0, $src1, $cin;";
+ let OutOperandList = (outs dstRC:$dst);
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, PredRC:$cin);
+}
+class CarryInInst_ri<string name, pisaVT VT> : PISAInst_ri<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $src0, $src1, $cin;";
+ let OutOperandList = (outs dstRC:$dst);
+ let InOperandList = (ins src0RC:$src0, immOpd1:$src1, PredRC:$cin);
+}
+class CarryInInst_ir<string name, pisaVT VT> : PISAInst_ir<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $src0, $src1, $cin;";
+ let OutOperandList = (outs dstRC:$dst);
+ let InOperandList = (ins immOpd0:$src0, src1RC:$src1, PredRC:$cin);
+}
+class CarryInInst_ii<string name, pisaVT VT> : PISAInst_ii<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $src0, $src1, $cin;";
+ let OutOperandList = (outs dstRC:$dst);
+ let InOperandList = (ins immOpd0:$src0, immOpd1:$src1, PredRC:$cin);
+}
+multiclass CarryInInst<string name> {
+ foreach VT = [_i32] in {
+ defvar directive = VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rr" : CarryInInst_rr<name#directive, VT>;
+ def opcode#"_ri" : CarryInInst_ri<name#directive, VT>;
+ def opcode#"_ir" : CarryInInst_ir<name#directive, VT>;
+ def opcode#"_ii" : CarryInInst_ii<name#directive, VT>;
+ }
+}
+class CarryInOutInst_rr<string name, pisaVT VT> : PISAInst_rr<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $cout, $src0, $src1, $cin;";
+ let OutOperandList = (outs dstRC:$dst, PredRC:$cout);
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, PredRC:$cin);
+}
+class CarryInOutInst_ri<string name, pisaVT VT> : PISAInst_ri<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $cout, $src0, $src1, $cin;";
+ let OutOperandList = (outs dstRC:$dst, PredRC:$cout);
+ let InOperandList = (ins src0RC:$src0, immOpd1:$src1, PredRC:$cin);
+}
+class CarryInOutInst_ir<string name, pisaVT VT> : PISAInst_ir<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $cout, $src0, $src1, $cin;";
+ let OutOperandList = (outs dstRC:$dst, PredRC:$cout);
+ let InOperandList = (ins immOpd0:$src0, src1RC:$src1, PredRC:$cin);
+}
+class CarryInOutInst_ii<string name, pisaVT VT> : PISAInst_ii<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $cout, $src0, $src1, $cin;";
+ let OutOperandList = (outs dstRC:$dst, PredRC:$cout);
+ let InOperandList = (ins immOpd0:$src0, immOpd1:$src1, PredRC:$cin);
+}
+multiclass CarryInOutInst<string name> {
+ foreach VT = [_i32] in {
+ defvar directive = VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rr" : CarryInOutInst_rr<name#directive, VT>;
+ def opcode#"_ri" : CarryInOutInst_ri<name#directive, VT>;
+ def opcode#"_ir" : CarryInOutInst_ir<name#directive, VT>;
+ def opcode#"_ii" : CarryInOutInst_ii<name#directive, VT>;
+ }
+}
+
+// => uaddc
+defm uaddc_co : CarryOutInst<"uaddc.co">;
+defm uaddc_ci : CarryInInst<"uaddc.ci">;
+defm uaddc_ci_co : CarryInOutInst<"uaddc.ci.co">;
+
+// => usubb
+defm usubb_co : CarryOutInst<"usubb.co">;
+defm usubb_ci : CarryInInst<"usubb.ci">;
+defm usubb_ci_co : CarryInOutInst<"usubb.ci.co">;
+
+// => udiv
+defm udiv_quo: BinaryInst<"udiv.quo", udiv, isCommutative=false>;
+defm udiv: BinaryInst<"udiv", udiv, isCommutative=false>;
+defm udiv_rem: BinaryInst<"udiv.rem", urem, isCommutative=false>;
+
+// => umax
+defm umax: BinaryInst<"umax", umax>;
+
+// => umin
+defm umin: BinaryInst<"umin", umin>;
+
+// END: integer arithmetic instruction definitions
+//////
+
+//////
+// BEGIN: floating point instruction definitions
+// => fabs
+defm fabs: UnaryFloatInst<"fabs", int_pisa_fabs>;
+
+
+// => fadd
+defm fadd: BinaryFloatInst<"fadd", fadd>;
+defm fadd: BinaryFloatInstRndSat<"fadd", int_pisa_fadd>;
+
+// => fcos
+defm fcos: UnaryFloatInst<"fcos", fcos, types=[_bf16, _f16, _f32]>;
+
+// => fdiv
+defm fdiv: BinaryFloatInst<"fdiv", fdiv, types=[_f32, _f64], isCommutative=false>;
+defm fdiv: BinaryFloatInst<"fdiv", fastbinop<fdiv>, isCommutative=false, isFast=1>;
+defm fdiv: BinaryFloatInstRnd<"fdiv", int_pisa_fdiv_rnd, [_f32, _f64]>;
+
+// => fexp2
+defm fexp2: UnaryFloatInst<"fexp2", fexp2, types=[_bf16, _f16, _f32]>;
+
+// => flog2
+defm flog2: UnaryFloatInst<"flog2", flog2, types=[_bf16, _f16, _f32]>;
+
+// => fmad
+defm fmad: TernaryFloatInst<"fmad", fma>;
+defm fmad: TernaryFloatInstRndSat<"fmad", int_pisa_fma, [_bf16, _f16, _f32, _f64]>;
+
+
+// => fmax
+defm fmax: BinaryFloatInst<"fmax", fmaxnum>;
+defm fmax: BinaryFloatInstNanp<"fmax", int_pisa_fmax_sat, satMods=[DsatMod]>;
+// fmax.nanp (NaN-propagating, no saturation) <= fmaximum, the
+// IEEE-754 maximum (any NaN input -> NaN). The plain fmax above (<= fmaxnum) is
+// maximumNumber, which suppresses NaN (returns the non-NaN operand).
+defm fmax: BinaryFloatInstNanpOnly<"fmax", fmaximum>;
+
+// => fmin
+defm fmin: BinaryFloatInst<"fmin", fminnum>;
+defm fmin: BinaryFloatInstNanp<"fmin", int_pisa_fmin_sat, satMods=[DsatMod]>;
+// fmin.nanp (NaN-propagating, no saturation) <= fminimum, the
+// IEEE-754 minimum (any NaN input -> NaN). The plain fmin above (<= fminnum) is
+// minimumNumber, which suppresses NaN (returns the non-NaN operand).
+defm fmin: BinaryFloatInstNanpOnly<"fmin", fminimum>;
+
+// => fmul
+defm fmul: BinaryFloatInst<"fmul", fmul>;
+defm fmul: BinaryFloatInstRndSat<"fmul", int_pisa_fmul>;
+
+// => fneg
+defm fneg: UnaryFloatInst<"fneg", fneg>;
+
+// => frc
+defm frc: UnaryFloatInst<"frc", int_pisa_frc, [_f32]>;
+
+// => frcp
+defm frcp: UnaryFloatInst<"frcp", int_pisa_frcp, types=[_bf16, _f16, _f32]>;
+
+// => frsqrt
+defm frsqrt: UnaryFloatInst<"frsqrt", int_pisa_frsqrt, isFast=1>;
+
+
+// => fsin
+defm fsin: UnaryFloatInst<"fsin", fsin, types=[_bf16, _f16, _f32]>;
+
+// => fsqrt
+defm fsqrt: UnaryFloatInst<"fsqrt", fsqrt, types=[_f32, _f64]>;
+defm fsqrt: UnaryFloatInst<"fsqrt", fastunaryop<fsqrt>, types=[_f32, _f64], isFast=1>;
+defm fsqrt: UnaryFloatInst<"fsqrt", fsqrt, types=[_bf16, _f16], isFast=1>;
+defm fsqrt: UnaryFloatInstRnd<"fsqrt", int_pisa_fsqrt_rnd, types=[_f32, _f64]>;
+
+// => fsub
+defm fsub: BinaryFloatInst<"fsub", fsub, isCommutative=false>;
+defm fsub: BinaryFloatInstRndSat<"fsub", int_pisa_fsub, isCommutative=false>;
+
+// => ftanh
+defm ftanh: UnaryFloatInst<"ftanh", ftanh, types=[_bf16, _f16, _f32]>;
+// END: floating point instruction definitions
+//////
+
+//////
+// BEGIN: bitwise instruction definitions
+// => and
+defm and : BinaryInst<"and", and>;
+
+// => bfi
+class Bfi_rrrr<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, src2RC:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node src0RC:$src0, src1RC:$src1, src2RC:$src2, src3RC:$src3))];
+}
+class Bfi_rrri<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, src2RC:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node src0RC:$src0, src1RC:$src1, src2RC:$src2, (VT.VT imm:$src3)))];
+}
+class Bfi_rrir<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, VT.ImmOpnd:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node src0RC:$src0, src1RC:$src1, (VT.VT imm:$src2), src3RC:$src3))];
+}
+class Bfi_rrii<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, VT.ImmOpnd:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node src0RC:$src0, src1RC:$src1, (VT.VT imm:$src2), (VT.VT imm:$src3)))];
+}
+class Bfi_rirr<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, VT.ImmOpnd:$src1, src2RC:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node src0RC:$src0, (VT.VT imm:$src1), src2RC:$src2, src3RC:$src3))];
+}
+class Bfi_riri<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, VT.ImmOpnd:$src1, src2RC:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node src0RC:$src0, (VT.VT imm:$src1), src2RC:$src2, (VT.VT imm:$src3)))];
+}
+class Bfi_riir<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, VT.ImmOpnd:$src1, VT.ImmOpnd:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node src0RC:$src0, (VT.VT imm:$src1), (VT.VT imm:$src2), src3RC:$src3))];
+}
+class Bfi_riii<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, VT.ImmOpnd:$src1, VT.ImmOpnd:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node src0RC:$src0, (VT.VT imm:$src1), (VT.VT imm:$src2), (VT.VT imm:$src3)))];
+}
+class Bfi_irrr<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins VT.ImmOpnd:$src0, src1RC:$src1, src2RC:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node (VT.VT imm:$src0), src1RC:$src1, src2RC:$src2, src3RC:$src3))];
+}
+class Bfi_irri<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins VT.ImmOpnd:$src0, src1RC:$src1, src2RC:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node (VT.VT imm:$src0), src1RC:$src1, src2RC:$src2, (VT.VT imm:$src3)))];
+}
+class Bfi_irir<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins VT.ImmOpnd:$src0, src1RC:$src1, VT.ImmOpnd:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node (VT.VT imm:$src0), src1RC:$src1, (VT.VT imm:$src2), src3RC:$src3))];
+}
+class Bfi_irii<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins VT.ImmOpnd:$src0, src1RC:$src1, VT.ImmOpnd:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node (VT.VT imm:$src0), src1RC:$src1, (VT.VT imm:$src2), (VT.VT imm:$src3)))];
+}
+class Bfi_iirr<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins VT.ImmOpnd:$src0, VT.ImmOpnd:$src1, src2RC:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node (VT.VT imm:$src0), (VT.VT imm:$src1), src2RC:$src2, src3RC:$src3))];
+}
+class Bfi_iiri<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins VT.ImmOpnd:$src0, VT.ImmOpnd:$src1, src2RC:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node (VT.VT imm:$src0), (VT.VT imm:$src1), src2RC:$src2, (VT.VT imm:$src3)))];
+}
+class Bfi_iiir<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins VT.ImmOpnd:$src0, VT.ImmOpnd:$src1, VT.ImmOpnd:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node (VT.VT imm:$src0), (VT.VT imm:$src1), (VT.VT imm:$src2), src3RC:$src3))];
+}
+class Bfi_iiii<string name, SDPatternOperator node, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins VT.ImmOpnd:$src0, VT.ImmOpnd:$src1, VT.ImmOpnd:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node (VT.VT imm:$src0), (VT.VT imm:$src1), (VT.VT imm:$src2), (VT.VT imm:$src3)))];
+}
+multiclass BfiInst {
+ defvar directive = "bfi.32b";
+ def _rrrr: Bfi_rrrr<directive, int_pisa_bfi, _i32>;
+ def _rrri: Bfi_rrri<directive, int_pisa_bfi, _i32>;
+ def _rrir: Bfi_rrir<directive, int_pisa_bfi, _i32>;
+ def _rrii: Bfi_rrii<directive, int_pisa_bfi, _i32>;
+ def _rirr: Bfi_rirr<directive, int_pisa_bfi, _i32>;
+ def _riri: Bfi_riri<directive, int_pisa_bfi, _i32>;
+ def _riir: Bfi_riir<directive, int_pisa_bfi, _i32>;
+ def _riii: Bfi_riii<directive, int_pisa_bfi, _i32>;
+ def _irrr: Bfi_irrr<directive, int_pisa_bfi, _i32>;
+ def _irri: Bfi_irri<directive, int_pisa_bfi, _i32>;
+ def _irir: Bfi_irir<directive, int_pisa_bfi, _i32>;
+ def _irii: Bfi_irii<directive, int_pisa_bfi, _i32>;
+ def _iirr: Bfi_iirr<directive, int_pisa_bfi, _i32>;
+ def _iiri: Bfi_iiri<directive, int_pisa_bfi, _i32>;
+ def _iiir: Bfi_iiir<directive, int_pisa_bfi, _i32>;
+ def _iiii: Bfi_iiii<directive, int_pisa_bfi, _i32>;
+}
+defm bfi : BfiInst;
+
+// => bfn
+class Bfn_irrr<string name, SDPatternOperator node, pisaVT opVT, pisaVT VT>
+ : PISAInst4<name, VT, opVT, VT, VT, VT> {
+ DAGOperand BfnOpcodeOpnd = PISABfnOpcodeOperand<opVT.VT, "Imm"#opVT.VT.Size#"Opnd">;
+ let InOperandList = (ins BfnOpcodeOpnd:$src0, src1RC:$src1, src2RC:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node timm:$src0, src1RC:$src1, src2RC:$src2, src3RC:$src3))];
+ let AsmString = name#".${src0}"#VT.directive#" \t$dst, $src1, $src2, $src3;";
+}
+class Bfn_irri<string name, SDPatternOperator node, pisaVT opVT, pisaVT VT>
+ : PISAInst4<name, VT, opVT, VT, VT, VT> {
+ DAGOperand BfnOpcodeOpnd = PISABfnOpcodeOperand<opVT.VT, "Imm"#opVT.VT.Size#"Opnd">;
+ let InOperandList = (ins BfnOpcodeOpnd:$src0, src1RC:$src1, src2RC:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node timm:$src0, src1RC:$src1, src2RC:$src2, (VT.VT imm:$src3)))];
+ let AsmString = name#".${src0}"#VT.directive#" \t$dst, $src1, $src2, $src3;";
+}
+class Bfn_irir<string name, SDPatternOperator node, pisaVT opVT, pisaVT VT>
+ : PISAInst4<name, VT, opVT, VT, VT, VT> {
+ DAGOperand BfnOpcodeOpnd = PISABfnOpcodeOperand<opVT.VT, "Imm"#opVT.VT.Size#"Opnd">;
+ let InOperandList = (ins BfnOpcodeOpnd:$src0, src1RC:$src1, VT.ImmOpnd:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node timm:$src0, src1RC:$src1, (VT.VT imm:$src2), src3RC:$src3))];
+ let AsmString = name#".${src0}"#VT.directive#" \t$dst, $src1, $src2, $src3;";
+}
+class Bfn_irii<string name, SDPatternOperator node, pisaVT opVT, pisaVT VT>
+ : PISAInst4<name, VT, opVT, VT, VT, VT> {
+ DAGOperand BfnOpcodeOpnd = PISABfnOpcodeOperand<opVT.VT, "Imm"#opVT.VT.Size#"Opnd">;
+ let InOperandList = (ins BfnOpcodeOpnd:$src0, src1RC:$src1, VT.ImmOpnd:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node timm:$src0, src1RC:$src1, (VT.VT imm:$src2), (VT.VT imm:$src3)))];
+ let AsmString = name#".${src0}"#VT.directive#" \t$dst, $src1, $src2, $src3;";
+}
+class Bfn_iirr<string name, SDPatternOperator node, pisaVT opVT, pisaVT VT>
+ : PISAInst4<name, VT, opVT, VT, VT, VT> {
+ DAGOperand BfnOpcodeOpnd = PISABfnOpcodeOperand<opVT.VT, "Imm"#opVT.VT.Size#"Opnd">;
+ let InOperandList = (ins BfnOpcodeOpnd:$src0, VT.ImmOpnd:$src1, src2RC:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node timm:$src0, (VT.VT imm:$src1), src2RC:$src2, src3RC:$src3))];
+ let AsmString = name#".${src0}"#VT.directive#" \t$dst, $src1, $src2, $src3;";
+}
+class Bfn_iiri<string name, SDPatternOperator node, pisaVT opVT, pisaVT VT>
+ : PISAInst4<name, VT, opVT, VT, VT, VT> {
+ DAGOperand BfnOpcodeOpnd = PISABfnOpcodeOperand<opVT.VT, "Imm"#opVT.VT.Size#"Opnd">;
+ let InOperandList = (ins BfnOpcodeOpnd:$src0, VT.ImmOpnd:$src1, src2RC:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node timm:$src0, (VT.VT imm:$src1), src2RC:$src2, (VT.VT imm:$src3)))];
+ let AsmString = name#".${src0}"#VT.directive#" \t$dst, $src1, $src2, $src3;";
+}
+class Bfn_iiir<string name, SDPatternOperator node, pisaVT opVT, pisaVT VT>
+ : PISAInst4<name, VT, opVT, VT, VT, VT> {
+ DAGOperand BfnOpcodeOpnd = PISABfnOpcodeOperand<opVT.VT, "Imm"#opVT.VT.Size#"Opnd">;
+ let InOperandList = (ins BfnOpcodeOpnd:$src0, VT.ImmOpnd:$src1, VT.ImmOpnd:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (node timm:$src0, (VT.VT imm:$src1), (VT.VT imm:$src2), src3RC:$src3))];
+ let AsmString = name#".${src0}"#VT.directive#" \t$dst, $src1, $src2, $src3;";
+}
+class Bfn_iiii<string name, SDPatternOperator node, pisaVT opVT, pisaVT VT>
+ : PISAInst4<name, VT, opVT, VT, VT, VT> {
+ DAGOperand BfnOpcodeOpnd = PISABfnOpcodeOperand<opVT.VT, "Imm"#opVT.VT.Size#"Opnd">;
+ let InOperandList = (ins BfnOpcodeOpnd:$src0, VT.ImmOpnd:$src1, VT.ImmOpnd:$src2, VT.ImmOpnd:$src3);
+ let Pattern = [(set dstRC:$dst, (node timm:$src0, (VT.VT imm:$src1), (VT.VT imm:$src2), (VT.VT imm:$src3)))];
+ let AsmString = name#".${src0}"#VT.directive#" \t$dst, $src1, $src2, $src3;";
+}
+multiclass BfnInst {
+ defvar directive = "bfn";
+ def _irrr: Bfn_irrr<directive, int_pisa_bfn, _i8, _i32>;
+ def _irri: Bfn_irri<directive, int_pisa_bfn, _i8, _i32>;
+ def _irir: Bfn_irir<directive, int_pisa_bfn, _i8, _i32>;
+ def _irii: Bfn_irii<directive, int_pisa_bfn, _i8, _i32>;
+ def _iirr: Bfn_iirr<directive, int_pisa_bfn, _i8, _i32>;
+ def _iiri: Bfn_iiri<directive, int_pisa_bfn, _i8, _i32>;
+ def _iiir: Bfn_iiir<directive, int_pisa_bfn, _i8, _i32>;
+ def _iiii: Bfn_iiii<directive, int_pisa_bfn, _i8, _i32>;
+}
+defm bfn : BfnInst;
+
+// => bfrev
+defm bfrev : UnaryInst<"bfrev", bitreverse, types=[_i32]>;
+
+// => cbit
+defm cbit : UnaryInst<"cbit", ctpop, types=[_i16, _i32]>;
+
+// fbh/fbl
+multiclass FirstBitInst<SDPatternOperator node, list<pisaVT> types> {
+ foreach VT = types in {
+ defvar directive = VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+ defvar uminOp = !cast<Instruction>(umin#opcode#"_ri");
+ defvar bitSize = VT.VT.Size;
+ def opcode#"_r" : PISAInst_r<NAME#directive, VT, VT>;
+ def : Pat<(node VT.VT:$src0),
+ (uminOp (!cast<Instruction>(NAME#opcode#"_r") VT.RC:$src0), bitSize)>;
+ def opcode#"_i" : PISAInst_i<NAME#directive, VT, VT>;
+ def : Pat<(node (VT.VT imm:$src0)),
+ (uminOp (!cast<Instruction>(NAME#opcode#"_i") (VT.VT imm:$src0)), bitSize)>;
+ }
+}
+
+// => fbh
+defm fbh0 : UnaryInst<"fbh", ctlz_zero_poison, types=[_i16, _i32]>;
+defm fbh : FirstBitInst<ctlz, [_i16, _i32]>;
+
+// => fbl
+defm fbl0 : UnaryInst<"fbl", cttz_zero_poison, types=[_i16, _i32]>;
+defm fbl : FirstBitInst<cttz, [_i16, _i32]>;
+
+// => not
+defm not : UnaryInst<"not", not>;
+
+// => or
+defm or : BinaryInst<"or", or>;
+
+// => sbfe
+defm sbfe : TernaryInst<"sbfe", int_pisa_sbfe, types=[_i32]>;
+
+
+// => ubfe
+defm ubfe : TernaryInst<"ubfe", int_pisa_ubfe, types=[_i32]>;
+
+// => xor
+defm xor : BinaryInst<"xor", xor>;
+
+// Temporarily support "bfe.32b" as an alias for "ubfe.32b" until all uses are replaced
+def : InstAlias<TernaryInst_rrr<"bfe.32b", int_pisa_ubfe, _i32, _i32>.AsmString, (ubfe_32b_rrr Reg32b:$dst, Reg32b:$src0, Reg32b:$src1, Reg32b:$src2)>;
+def : InstAlias<TernaryInst_rri<"bfe.32b", int_pisa_ubfe, _i32, _i32>.AsmString, (ubfe_32b_rri Reg32b:$dst, Reg32b:$src0, Reg32b:$src1, _i32.ImmOpnd:$src2)>;
+def : InstAlias<TernaryInst_rir<"bfe.32b", int_pisa_ubfe, _i32, _i32>.AsmString, (ubfe_32b_rir Reg32b:$dst, Reg32b:$src0, _i32.ImmOpnd:$src1, Reg32b:$src2)>;
+def : InstAlias<TernaryInst_rii<"bfe.32b", int_pisa_ubfe, _i32, _i32>.AsmString, (ubfe_32b_rii Reg32b:$dst, Reg32b:$src0, _i32.ImmOpnd:$src1, _i32.ImmOpnd:$src2)>;
+def : InstAlias<TernaryInst_irr<"bfe.32b", int_pisa_ubfe, _i32, _i32>.AsmString, (ubfe_32b_irr Reg32b:$dst, _i32.ImmOpnd:$src0, Reg32b:$src1, Reg32b:$src2)>;
+def : InstAlias<TernaryInst_iri<"bfe.32b", int_pisa_ubfe, _i32, _i32>.AsmString, (ubfe_32b_iri Reg32b:$dst, _i32.ImmOpnd:$src0, Reg32b:$src1, _i32.ImmOpnd:$src2)>;
+def : InstAlias<TernaryInst_iir<"bfe.32b", int_pisa_ubfe, _i32, _i32>.AsmString, (ubfe_32b_iir Reg32b:$dst, _i32.ImmOpnd:$src0, _i32.ImmOpnd:$src1, Reg32b:$src2)>;
+def : InstAlias<TernaryInst_iii<"bfe.32b", int_pisa_ubfe, _i32, _i32>.AsmString, (ubfe_32b_iii Reg32b:$dst, _i32.ImmOpnd:$src0, _i32.ImmOpnd:$src1, _i32.ImmOpnd:$src2)>;
+// END: bitwise instruction definitions
+//////
+
+//////
+// BEGIN: shift instruction definitions
+// asr/shr/shl
+multiclass ShiftInst<string name, SDNode node> {
+ foreach VT = [_i16, _i32, _i64] in {
+ defvar directive = VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rr": BinaryInst_rr<name#directive, node, VT, _i32>;
+ def opcode#"_ri": BinaryInst_ri<name#directive, node, VT, _i32>;
+ def opcode#"_ir": BinaryInst_ir<name#directive, node, VT, _i32, VT, false>;
+ def opcode#"_ii": BinaryInst_ii<name#directive, node, VT, _i32>;
+ }
+}
+
+// => asr
+defm asr : ShiftInst<"asr", sra>;
+
+// => shr
+defm shr : ShiftInst<"shr", srl>;
+
+// => shl
+defm shl : ShiftInst<"shl", shl>;
+
+// => shf
+defm shf_r : TernaryInst<"shf.r", fshr, types=[_i32]>;
+defm shf_l : TernaryInst<"shf.l", fshl, types=[_i32]>;
+// END: shift instruction definitions
+//////
+
+//////
+// BEGIN: data movement and type conversion instructions definitions
+// => addrcast
+def : GINodeEquiv<G_ADDRSPACE_CAST, addrspacecast>;
+class AddrcastToGeneric<string name, AddressSpace Out, AddressSpace In> : _PISAInst<name> {
+ let AsmString = name#" \t$dst, $src;";
+ let OutOperandList = (outs Out.RC:$dst);
+ let InOperandList = (ins In.RC:$src);
+ let Pattern = [(set (p0 Out.RC:$dst), (addrspacecast (In.PVT In.RC:$src)))];
+}
+class AddrcastToGenericFromFI<string name> : _PISAInst<name> {
+ let AsmString = name#" \t$dst, $frameindex;";
+ let OutOperandList = (outs generic.RC:$dst);
+ let InOperandList = (ins FrameIndex:$frameindex);
+}
+class AddrcastToGenericFromGVar<string name> : _PISAInst<name> {
+ let AsmString = name#" \t$dst, $gvar;";
+ let OutOperandList = (outs generic.RC:$dst);
+ let InOperandList = (ins PISAGlobalVariableOpnd:$gvar);
+}
+class AddrcastFromGeneric<string name, AddressSpace Out, AddressSpace In>
+ : AddrcastToGeneric<name, Out, In> {
+ let Pattern = [(set (Out.PVT Out.RC:$dst), (addrspacecast (p0 In.RC:$src)))];
+}
+class AddrcastToGenericCustomISel<string name, AddressSpace Out, AddressSpace In> : AddrcastToGeneric<name, Out, In> {
+ let Pattern = [];
+}
+multiclass AddrcastInst<string name> {
+ defvar Gen = generic;
+ // addrspacecast.generic.[private|shared] %dst, @V
+ foreach NonGen = [private, shared] in {
+ def _gen_#NonGen#_FI: AddrcastToGenericFromFI<name#"."#Gen#"."#NonGen>;
+ }
+ // addrspacecast.generic.[global|const] %dst, @G
+ foreach NonGen = [global, const] in {
+ def _gen_#NonGen#_var: AddrcastToGenericFromGVar<name#"."#Gen#"."#NonGen>;
+ }
+ foreach NonGen = [private, shared, global] in {
+ def _gen_#NonGen: AddrcastToGeneric<name#"."#Gen#"."#NonGen, Gen, NonGen>;
+ def _#NonGen#_gen: AddrcastFromGeneric<name#"."#NonGen#"."#Gen, NonGen, Gen>;
+ }
+ foreach NonGen = [const] in {
+ def _gen_#NonGen: AddrcastToGeneric<name#"."#Gen#"."#NonGen, Gen, NonGen>;
+ }
+}
+defm addrcast : AddrcastInst<"addrcast">;
+
+// => addrof
+multiclass AddrOf {
+ def _32b: PISAInst<(outs _i32.RegOpnd:$var), (ins FrameIndex:$frameindex), "addrof.32b $var, $frameindex;">;
+ def _64b: PISAInst<(outs _i64.RegOpnd:$var), (ins PISAGlobalVariableOpnd:$varname), "addrof.64b $var, $varname;">;
+ def _64b_func: PISAInst<(outs _i64.RegOpnd:$var), (ins FunctionOpnd:$funcname), "addrof.64b $var, $funcname;">;
+}
+defm addrof : AddrOf<>; // MIR for G_FRAME_INDEX
+
+// => extract
+class ExtractInst_r<string name, pisaVT dstVT, pisaVT srcVT>
+ : PISAInst_r<name, dstVT, srcVT> {
+}
+multiclass Extract {
+ defvar VTs = [_v2i32, _v3i32, _v4i32, _v5i32, _v6i32, _v7i32, _v8i32, _v16i32, _v32i32, _v64i32];
+ defvar lengths = [2, 3, 4, 5, 6, 7, 8, 16, 32, 64];
+ // For extract, src must be a vector type.
+ // For each src vector type...
+ foreach i = !range(VTs) in {
+ defvar srcVT = VTs[i];
+ defvar srcLength = lengths[i];
+ // The range of valid extract indices is [0, ..., length - 1] (i.e., for v4, [0, 1, 2, 3])...
+ foreach index = !range(srcLength) in {
+ // At each valid index, you can always extract a scalar.
+ def "_"#index#"_i32_"#srcVT.name#"_r" : ExtractInst_r<"extract."#index#".32b", _i32, srcVT>;
+ // For each valid destination vector type, check if it fits in remaining space
+ foreach j = !range(VTs) in {
+ defvar dstVT = VTs[j];
+ defvar dstLength = lengths[j];
+ // Destination vector is valid if dstLength <= (srcLength - index)
+ if !le(dstLength, !sub(srcLength, index)) then {
+ def "_"#index#"_"#dstVT.name#"_"#srcVT.name#"_r" : ExtractInst_r<"extract."#index#dstVT.directive, dstVT, srcVT>;
+ }
+ }
+ }
+ }
+}
+defset list<Instruction> ExtractInsts = {
+ defm extract : Extract;
+}
+foreach EI = ExtractInsts in {
+ def : Name2InstrEntry<!cast<string>(EI), EI>;
+}
+
+// => extract.dynamic
+multiclass ExtractDynamic {
+ defvar srcVTs = [_v2i32, _v3i32, _v4i32, _v5i32, _v6i32, _v7i32, _v8i32, _v16i32, _v32i32, _v64i32];
+ foreach srcVT = srcVTs in {
+ def "_"#srcVT.name : PISAInst_rr<"extract.dynamic"#srcVT.directive, _i32, srcVT, _i32>;
+ }
+}
+defset list<Instruction> ExtractDynamicInsts = {
+ defm extract_dynamic : ExtractDynamic;
+}
+foreach EDI = ExtractDynamicInsts in {
+ def : Name2InstrEntry<!cast<string>(EDI), EDI>;
+}
+
+// => f2i
+multiclass FP2IntInst<string name, SDPatternOperator node, bit isSigned> {
+ foreach srcVT = [_bf16, _f16, _f32, _f64] in {
+ foreach dstVT = [_i8, _i16, _i32, _i64] in {
+ defvar toName = !if(isSigned,".s",".u")#dstVT.VT.Size;
+ defvar directive = !interleave([toName, srcVT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r" : UnaryInst_r<name#directive, node, dstVT, srcVT>;
+ def opcode#"_i" : UnaryInst_i<name#directive, node, dstVT, srcVT>;
+ }
+ }
+}
+multiclass FP2IntSatInst<string name, SDPatternOperator node, bit isSigned> {
+ foreach srcVT = [_bf16, _f16, _f32, _f64] in {
+ foreach dstVT = [_i8, _i16, _i32, _i64] in {
+ defvar toName = !if(isSigned,".s",".u")#dstVT.VT.Size;
+ defvar directive = !interleave([toName, srcVT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r" : UnaryInst_r<name#directive, node, dstVT, srcVT, (node dstVT.VT)>;
+ def opcode#"_i" : UnaryInst_i<name#directive, node, dstVT, srcVT, (node dstVT.VT)>;
+ }
+ }
+}
+multiclass FP2IntRndInst<string name, SDPatternOperator node, bit isSigned> {
+ foreach srcVT = [_bf16, _f16, _f32, _f64] in {
+ foreach dstVT = [_i8, _i16, _i32, _i64] in {
+ defvar toName = !if(isSigned,".s",".u")#dstVT.VT.Size;
+ foreach rndNode = RoundingModeNodes in {
+ defvar directive = !interleave([toName, srcVT.directive, rndNode.name], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r": UnaryInst_r<name#directive, node, dstVT, srcVT, (node rndNode)>;
+ def opcode#"_i": UnaryInst_i<name#directive, node, dstVT, srcVT, (node rndNode)>;
+ }
+ }
+ }
+}
+defm f2i : FP2IntInst<"f2i", fp_to_sint, isSigned=true>;
+defm f2i_sat : FP2IntSatInst<"f2i", fp_to_sint_sat, isSigned=true>;
+defm f2i : FP2IntInst<"f2i", fp_to_uint, isSigned=false>;
+defm f2i_sat : FP2IntSatInst<"f2i", fp_to_uint_sat, isSigned=false>;
+defm f2i : FP2IntRndInst<"f2i", int_pisa_fptosi_rnd, isSigned=true>;
+defm f2i : FP2IntRndInst<"f2i", int_pisa_fptoui_rnd, isSigned=false>;
+
+// => fext
+defm fext : ConvertSingleInst<"fext", fpextend, [_f32, _f64], _bf16>;
+defm fext : ConvertSingleInst<"fext", fpextend, [_f32, _f64], _f16>;
+defm fext : ConvertSingleInst<"fext", fpextend, [_f64], _f32>;
+
+// => frnd
+def pisa_nearestint : PatFrags<(ops node:$src0), [(frint node:$src0),
+ (fnearbyint node:$src0),
+ (froundeven node:$src0)]>;
+defm frnd_re : UnaryFloatInst<"frnd.re", pisa_nearestint>;
+defm frnd_rd : UnaryFloatInst<"frnd.rd", ffloor>;
+defm frnd_ru : UnaryFloatInst<"frnd.ru", fceil>;
+defm frnd_rz : UnaryFloatInst<"frnd.rz", ftrunc>;
+defm frnd_rna: UnaryFloatInst<"frnd.rna", fround>;
+defm frnd0 : UnaryFloatInstRnd<"frnd", int_pisa_frnd_rnd>;
+
+// => ftrunc
+defm ftrunc : ConvertMultiFloatInst<"ftrunc", fpround, _bf16, [_f32, _f64]>;
+defm ftrunc : ConvertMultiFloatInst<"ftrunc", fpround, _f16, [_f32, _f64]>;
+defm ftrunc : ConvertMultiFloatInst<"ftrunc", fpround, _f32, [_f64]>;
+// .. llvm.fptrunc.round
+defm ftrunc : ConvertMultiFloatInstRnd<"ftrunc", fptrunc_round, _bf16, [_f32, _f64]>;
+defm ftrunc : ConvertMultiFloatInstRnd<"ftrunc", fptrunc_round, _f16, [_f32, _f64]>;
+defm ftrunc : ConvertMultiFloatInstRnd<"ftrunc", fptrunc_round, _f32, [_f64]>;
+defm ftrunc0 : ConvertMultiFloatInstRndSat<"ftrunc", int_pisa_ftrunc, _bf16, [_f32, _f64]>;
+defm ftrunc0 : ConvertMultiFloatInstRndSat<"ftrunc", int_pisa_ftrunc, _f16, [_f32, _f64]>;
+defm ftrunc0 : ConvertMultiFloatInstRndSat<"ftrunc", int_pisa_ftrunc, _f32, [_f64]>;
+
+
+// => i2f
+multiclass Int2FPInst<string name, SDPatternOperator node, bit isSigned> {
+ foreach srcVT = [_i8, _i16, _i32, _i64] in {
+ defvar fromName = !if(isSigned,".s",".u")#srcVT.VT.Size;
+ foreach dstVT = [_bf16, _f16, _f32, _f64] in {
+ defvar directive = !interleave([dstVT.directive, fromName], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_r" : UnaryInst_r<name#directive, node, dstVT, srcVT>;
+ def opcode#"_i" : UnaryInst_i<name#directive, node, dstVT, srcVT>;
+ }
+ }
+}
+multiclass Int2FPInstRndSat<string name, SDPatternOperator node, bit isSigned> {
+ foreach sat = [NoSatMod, DsatMod] in {
+ foreach srcVT = [_i8, _i16, _i32, _i64] in {
+ defvar fromName = !if(isSigned,".s",".u")#srcVT.VT.Size;
+ foreach dstVT = [_bf16, _f16, _f32, _f64] in {
+ foreach rndNode = RoundingModeAllNodes in {
+ defvar directive = !interleave([dstVT.directive, fromName, rndNode.name, sat.name], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rii": UnaryInst_r<name#directive, node, dstVT, srcVT, (node rndNode, sat.node)>;
+ def opcode#"_iii": UnaryInst_i<name#directive, node, dstVT, srcVT, (node rndNode, sat.node)>;
+ }
+ }
+ }
+ }
+}
+defm i2f : Int2FPInst<"i2f", sint_to_fp, true>;
+defm i2f : Int2FPInst<"i2f", uint_to_fp, false>;
+defm i2f: Int2FPInstRndSat<"i2f", int_pisa_sitofp, true>;
+defm i2f: Int2FPInstRndSat<"i2f", int_pisa_uitofp, false>;
+
+// => insert
+class InsertInst_rr<string name, pisaVT dstVT, pisaVT srcVT>
+ : PISAInst_rr<name, dstVT, dstVT, srcVT> {
+ let AsmString = name#" \t$dst, $src1;";
+}
+multiclass Insert {
+ defvar VTs = [_v2i32, _v3i32, _v4i32, _v5i32, _v6i32, _v7i32, _v8i32, _v16i32, _v32i32, _v64i32];
+ defvar lengths = [2, 3, 4, 5, 6, 7, 8, 16, 32, 64];
+ // For insert, dst must be a vector type.
+ // For each dst vector type...
+ foreach i = !range(VTs) in {
+ defvar dstVT = VTs[i];
+ defvar dstLength = lengths[i];
+ // The range of valid insert indices is [0, ..., length - 1] (i.e., for v4, [0, 1, 2, 3])...
+ foreach index = !range(dstLength) in {
+ // At each valid index, you can always insert a scalar.
+ let Constraints = "$src0 = $dst" in {
+ def "_"#index#"_"#dstVT.name#"_i32_r" : InsertInst_rr<"insert."#index#".32b", dstVT, _i32>;
+ // For each valid source vector type, check if it fits in remaining space
+ foreach j = !range(VTs) in {
+ defvar srcVT = VTs[j];
+ defvar srcLength = lengths[j];
+ // Source vector is valid if srcLength <= (dstLength - index)
+ if !le(srcLength, !sub(dstLength, index)) then {
+ def "_"#index#"_"#dstVT.name#"_"#srcVT.name#"_r" : InsertInst_rr<"insert."#index#srcVT.directive, dstVT, srcVT>;
+ }
+ }
+ }
+ }
+ }
+}
+defset list<Instruction> InsertInsts = {
+ defm insert : Insert;
+}
+foreach II = InsertInsts in {
+ def : Name2InstrEntry<!cast<string>(II), II>;
+}
+
+// => insert.dynamic
+multiclass InsertDynamic {
+ let Constraints = "$src0 = $dst" in {
+ defvar srcVTs = [_v2i32, _v3i32, _v4i32, _v5i32, _v6i32, _v7i32, _v8i32, _v16i32, _v32i32, _v64i32];
+ foreach srcVT = srcVTs in {
+ let AsmString = "insert.dynamic"#srcVT.directive#" \t$dst, $src1, $src2;" in {
+ def "_"#srcVT.name : PISAInst_rrr<"insert.dynamic"#srcVT.directive, srcVT, srcVT, _i32, _i32>;
+ }
+ }
+ }
+}
+defset list<Instruction> InsertDynamicInsts = {
+ defm insert_dynamic : InsertDynamic;
+}
+foreach IDI = InsertDynamicInsts in {
+ def : Name2InstrEntry<!cast<string>(IDI), IDI>;
+}
+
+// => isaddr
+class IsaddrInst_r<string name, Intrinsic intrinsic, pisaVT dstVT, pisaVT srcVT>
+ : UnaryInst_r<name, intrinsic, dstVT, srcVT>;
+// FIXME: Need special selection as the input pointer may be from any address space.
+multiclass IsaddrInst<string name> {
+ def _private: IsaddrInst_r<name#".private", int_pisa_isaddr_private, _i32, _i64>;
+ def _shared : IsaddrInst_r<name#".shared", int_pisa_isaddr_shared, _i32, _i64>;
+ def _global : IsaddrInst_r<name#".global", int_pisa_isaddr_global, _i32, _i64>;
+}
+defm isaddr : IsaddrInst<"isaddr">;
+
+
+// => mov
+class MovInst_i<string name, pisaVT dstVT, pisaVT srcVT> : PISAInst_i<name, dstVT, srcVT> {
+ let Pattern = [(set dstRC:$dst, (srcVT.VT immVal:$src))];
+ let isMoveImm = 1;
+}
+class MovInst_r<string name, pisaVT dstVT, pisaVT srcVT> : PISAInst_r<name, dstVT, srcVT> {
+ let isMoveReg = 1;
+}
+multiclass VectorScalar<string name, pisaIntVT VT1, pisaVectorVT VT2> {
+ defvar directive = VT1.directive;
+ defvar opcode = !subst(".", "_", directive);
+ def VT1.name#_#VT2.name#_r : MovInst_r<name#directive, VT1, VT2>;
+ def VT2.name#_#VT1.name#_r : MovInst_r<name#directive, VT2, VT1>;
+}
+multiclass VectorVector<string name, string bitwidth, pisaVectorVT VT1, pisaVectorVT VT2> {
+ def VT1.name#_#VT2.name#_r : MovInst_r<name#"."#bitwidth, VT1, VT2>;
+ def VT2.name#_#VT1.name#_r : MovInst_r<name#"."#bitwidth, VT2, VT1>;
+}
+multiclass Mov {
+ foreach VT = VTs.MovImm in {
+ defvar directive = "mov."#VT.VT.Size#"b";
+ def _#VT.name#_i: MovInst_i<directive, VT, VT>;
+ }
+ foreach VT = VTs.MovReg in {
+ defvar directive = "mov."#VT.VT.Size#"b";
+ def _#VT.name#_r : MovInst_r<directive, VT, VT>;
+ }
+
+ // 16bit
+ defm _: VectorScalar<"mov", _i16, _v2i8>;
+
+ // 32bit
+ defm _: VectorScalar<"mov", _i32, _v4i8>;
+ defm _: VectorScalar<"mov", _i32, _v2i16>;
+ defm _: VectorVector<"mov", "32b", _v2i16, _v4i8>;
+
+ // 64bit
+ defm _: VectorScalar<"mov", _i64, _v4i16>;
+ defm _: VectorScalar<"mov", _i64, _v2i32>;
+ defm _: VectorVector<"mov", "64b", _v2i32, _v4i16>;
+
+ // 128bit
+ defm _: VectorScalar<"mov", _i128, _v4i32>;
+ defm _: VectorScalar<"mov", _i128, _v2i64>;
+ defm _: VectorVector<"mov", "128b", _v2i64, _v4i32>;
+}
+defm mov : Mov;
+
+// .. ptr G_CONSTANT imm
+def : Pat<(_p0.VT imm:$imm), (mov_i64_i imm:$imm)>;
+def : Pat<(_p1.VT imm:$imm), (mov_i64_i imm:$imm)>;
+def : Pat<(_p2.VT imm:$imm), (mov_i64_i imm:$imm)>;
+def : Pat<(_p3.VT imm:$imm), (mov_i32_i imm:$imm)>;
+def : Pat<(_p4.VT imm:$imm), (mov_i32_i imm:$imm)>;
+
+
+// => sext
+defm sext : ConvertMultiInst<"sext", sext, _i64, [_i8, _i16, _i32]>;
+defm sext : ConvertMultiInst<"sext", sext, _i32, [_i8, _i16]>;
+defm sext : ConvertMultiInst<"sext", sext, _i16, [_i8]>;
+
+// => trunc
+defm trunc : ConvertMultiInst<"trunc", trunc, _i8, [_i16, _i32, _i64]>;
+defm trunc : ConvertMultiInst<"trunc", trunc, _i16, [_i32, _i64]>;
+defm trunc : ConvertMultiInst<"trunc", trunc, _i32, [_i64]>;
+
+// => zext
+defm zext : ConvertMultiInst<"zext", zanyext, _i64, [_i8, _i16, _i32]>;
+defm zext : ConvertMultiInst<"zext", zanyext, _i32, [_i8, _i16]>;
+defm zext : ConvertMultiInst<"zext", zanyext, _i16, [_i8]>;
+// END: data movement and type conversion instructions definitions
+//////
+
+//////
+// BEGIN: comparison and selection instruction definitions
+// ucmp/scmp
+class CmpBinaryInstPred_rr<string name, CondCode setcnd, pisaVT srcVT>: PISAInst_rr<name, _i1, srcVT, srcVT> {
+ let Pattern = [(set i1:$dst, (setcc srcVT.VT:$src0, srcVT.VT:$src1, (OtherVT setcnd)))];
+}
+class CmpBinaryInstPred_ri<string name, CondCode setcnd, pisaVT srcVT>: PISAInst_ri<name, _i1, srcVT, srcVT> {
+ let Pattern = [(set i1:$dst, (setcc srcVT.VT:$src0, immVal1:$src1, setcnd))];
+}
+class CmpBinaryInstPred_ir<string name, CondCode setcnd, pisaVT srcVT>: PISAInst_ir<name, _i1, srcVT, srcVT> {
+ let Pattern = [(set i1:$dst, (setcc (srcVT.VT immVal0:$src0), srcVT.VT:$src1, setcnd))];
+}
+class CmpBinaryInstPred_ii<string name, CondCode setcnd, pisaVT srcVT>: PISAInst_ii<name, _i1, srcVT, srcVT> {
+ let Pattern = [(set i1:$dst, (setcc (srcVT.VT immVal0:$src0), immVal1:$src1, setcnd))];
+}
+
+def selextend : PatFrag<(ops node:$in), (select node:$in, -1, 0)>;
+class CmpBinaryInstReg_rr<string name, CondCode setcnd, pisaVT srcVT>: PISAInst_rr<name, _i32, srcVT, srcVT> {
+ let Pattern = [(set i32:$dst, (selextend (i1 (setcc srcVT.VT:$src0, srcVT.VT:$src1, setcnd))))];
+}
+class CmpBinaryInstReg_ri<string name, CondCode setcnd, pisaVT srcVT>: PISAInst_ri<name, _i32, srcVT, srcVT> {
+ let Pattern = [(set i32:$dst, (selextend (i1 (setcc srcVT.VT:$src0, immVal1:$src1, setcnd))))];
+}
+class CmpBinaryInstReg_ir<string name, CondCode setcnd, pisaVT srcVT>: PISAInst_ir<name, _i32, srcVT, srcVT> {
+ let Pattern = [(set i32:$dst, (selextend (i1 (setcc (srcVT.VT immVal0:$src0), srcVT.VT:$src1, setcnd))))];
+}
+class CmpBinaryInstReg_ii<string name, CondCode setcnd, pisaVT srcVT>: PISAInst_ii<name, _i32, srcVT, srcVT> {
+ let Pattern = [(set i32:$dst, (selextend (i1 (setcc (srcVT.VT immVal0:$src0), immVal1:$src1, setcnd))))];
+}
+multiclass CmpInst<string name, list<pisaVT> types, bit isSigned> {
+ defvar prefix = !if(isSigned, "", "u");
+ foreach cc = ["ne", "eq", "gt", "ge", "lt", "le"] in {
+ defvar ccName = !toupper(!if(!or(!eq(cc, "ne"), !eq(cc, "eq")), cc, prefix#cc));
+ defvar cond = !cast<CondCode>("SET"#ccName);
+ foreach VT = types in {
+ defvar directive = "."#cc#VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+
+ // Defs for cmp opcodes where dst is a predicate
+ // cmp is directly matched, e.g.
+ // %2 = icmp slt i32 %0, %1
+ def opcode#"_prr" : CmpBinaryInstPred_rr<name#directive, cond, VT>;
+ def opcode#"_pri" : CmpBinaryInstPred_ri<name#directive, cond, VT>;
+ def opcode#"_pir" : CmpBinaryInstPred_ir<name#directive, cond, VT>;
+ def opcode#"_pii" : CmpBinaryInstPred_ii<name#directive, cond, VT>;
+
+ // Defs for cmp opcodes where dst is a 32b register
+ // cmp+sext is matched, e.g.
+ // %2 = icmp slt i32 %0, %1
+ // %3 = sext i1 %2 to i32
+ def opcode#"_rrr" : CmpBinaryInstReg_rr<name#directive, cond, VT>;
+ def opcode#"_rri" : CmpBinaryInstReg_ri<name#directive, cond, VT>;
+ def opcode#"_rir" : CmpBinaryInstReg_ir<name#directive, cond, VT>;
+ def opcode#"_rii" : CmpBinaryInstReg_ii<name#directive, cond, VT>;
+ }
+ }
+}
+
+// => ucmp
+defm ucmp : CmpInst<"ucmp", [_i16, _i32, _i64], 0>;
+
+// => scmp
+defm scmp : CmpInst<"scmp", [_i16, _i32, _i64], 1>;
+
+// => fcmp
+multiclass FCmpInst<string name, list<pisaVT> types> {
+ foreach cc = ["oge", "ogt", "ole", "olt", "oeq", "une"] in {
+ defvar cond = !substr(cc, 1);
+ defvar node = !cast<CondCode>("SET"#!toupper(cc));
+ foreach ftz = [omitFTZMod, usesFTZMod] in {
+ foreach VT = types in {
+ defvar directive = !interleave(["."#cond, ftz.name, VT.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+
+ // Defs for fcmp opcodes where dst is a predicate
+ // fcmp is directly matched, e.g.
+ // %2 = fcmp olt f32 %0, %1
+ def opcode#"_prr" : CmpBinaryInstPred_rr<name#directive, node, VT>, Requires<[ftz.pred]>;
+ def opcode#"_pri" : CmpBinaryInstPred_ri<name#directive, node, VT>, Requires<[ftz.pred]>;
+ def opcode#"_pir" : CmpBinaryInstPred_ir<name#directive, node, VT>, Requires<[ftz.pred]>;
+ def opcode#"_pii" : CmpBinaryInstPred_ii<name#directive, node, VT>, Requires<[ftz.pred]>;
+
+ // Defs for fcmp opcodes where dst is a 32b register
+ // fcmp+sext is matched, e.g.
+ // %2 = fcmp olt i32 %0, %1
+ // %3 = sext i1 %2 to i32
+ def opcode#"_rrr" : CmpBinaryInstReg_rr<name#directive, node, VT>, Requires<[ftz.pred]>;
+ def opcode#"_rri" : CmpBinaryInstReg_ri<name#directive, node, VT>, Requires<[ftz.pred]>;
+ def opcode#"_rir" : CmpBinaryInstReg_ir<name#directive, node, VT>, Requires<[ftz.pred]>;
+ def opcode#"_rii" : CmpBinaryInstReg_ii<name#directive, node, VT>, Requires<[ftz.pred]>;
+ }
+ }
+ }
+}
+defm fcmp : FCmpInst<"fcmp", [_bf16, _f16, _f32, _f64]>;
+
+
+// => sel
+class SelInst_rrp<string name, pisaVT VT> : PISAInst_rr<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $src0, $src1, $p;";
+ let InOperandList = (ins PredRC:$p, src0RC:$src0, src1RC:$src1);
+ let Pattern = [(set VT.VT:$dst, (select i1:$p, VT.VT:$src0, src1RC:$src1))];
+}
+class SelInst_rip<string name, pisaVT VT> : PISAInst_ri<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $src0, $src1, $p;";
+ let InOperandList = (ins PredRC:$p, src0RC:$src0, immOpd1:$src1);
+ let Pattern = [(set VT.VT:$dst, (select i1:$p, VT.VT:$src0, immVal1:$src1))];
+}
+class SelInst_irp<string name, pisaVT VT> : PISAInst_ir<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $src0, $src1, $p;";
+ let InOperandList = (ins PredRC:$p, immOpd0:$src0, src1RC:$src1);
+ let Pattern = [(set VT.VT:$dst, (select i1:$p, immVal0:$src0, VT.VT:$src1))];
+}
+class SelInst_iip<string name, pisaVT VT> : PISAInst_ii<name, VT, VT, VT> {
+ DAGOperand PredRC = _i1.RegOpnd;
+ let AsmString = name#" \t$dst, $src0, $src1, $p;";
+ let InOperandList = (ins PredRC:$p, immOpd0:$src0, immOpd1:$src1);
+ let Pattern = [(set VT.VT:$dst, (select i1:$p, immVal0:$src0, immVal1:$src1))];
+}
+class SelInst_rrr<string name, pisaVT VT, pisaVT predVT> : PISAInst_rrr<name, VT, VT, VT, predVT> {
+ let Pattern = [(set VT.VT:$dst, (pisa_select predVT.VT:$src2, VT.VT:$src0, VT.VT:$src1))];
+}
+class SelInst_rir<string name, pisaVT VT, pisaVT predVT> : PISAInst_rir<name, VT, VT, VT, predVT> {
+ let Pattern = [(set VT.VT:$dst, (pisa_select predVT.VT:$src2, VT.VT:$src0, immVal1:$src1))];
+}
+class SelInst_irr<string name, pisaVT VT, pisaVT predVT> : PISAInst_irr<name, VT, VT, VT, predVT> {
+ let Pattern = [(set VT.VT:$dst, (pisa_select predVT.VT:$src2, immVal0:$src0, VT.VT:$src1))];
+}
+class SelInst_iir<string name, pisaVT VT, pisaVT predVT> : PISAInst_iir<name, VT, VT, VT, predVT> {
+ let Pattern = [(set VT.VT:$dst, (pisa_select predVT.VT:$src2, immVal0:$src0, immVal1:$src1))];
+}
+multiclass SelInst<string name> {
+ foreach VT = [_i16, _i32, _i64, _bf16, _f16, _f32, _f64] in {
+ defvar directive = "."#VT.bitwidth;
+ defvar opcode = VT.opcode;
+
+ // select on predicate
+ def opcode#"_rrp" : SelInst_rrp<name#directive, VT>;
+ def opcode#"_irp" : SelInst_irp<name#directive, VT>;
+ def opcode#"_rip" : SelInst_rip<name#directive, VT>;
+ def opcode#"_iip" : SelInst_iip<name#directive, VT>;
+ // select on register
+ defvar predVT = !if(!eq(VT.bitwidth,"16b"), _i16, !if(!eq(VT.bitwidth,"32b"), _i32, _i64));
+ def opcode#"_rrr"#predVT.bitwidth : SelInst_rrr<name#directive, VT, predVT>;
+ def opcode#"_irr"#predVT.bitwidth : SelInst_irr<name#directive, VT, predVT>;
+ def opcode#"_rir"#predVT.bitwidth : SelInst_rir<name#directive, VT, predVT>;
+ def opcode#"_iir"#predVT.bitwidth : SelInst_iir<name#directive, VT, predVT>;
+ }
+}
+class SelInst_rrnp<string name, pisaVT VT> : PISAInst_rr<name, VT, VT, VT> {
+ let AsmString = name#" \t$dst, $src0, $src1, $negate$p;";
+ let InOperandList = (ins Negate:$negate, _i1.RegOpnd:$p, src0RC:$src0, src1RC:$src1);
+}
+class SelInst_rinp<string name, pisaVT VT> : PISAInst_ri<name, VT, VT, VT> {
+ let AsmString = name#" \t$dst, $src0, $src1, $negate$p;";
+ let InOperandList = (ins Negate:$negate, _i1.RegOpnd:$p, src0RC:$src0, immOpd1:$src1);
+}
+class SelInst_irnp<string name, pisaVT VT> : PISAInst_ir<name, VT, VT, VT> {
+ let AsmString = name#" \t$dst, $src0, $src1, $negate$p;";
+ let InOperandList = (ins Negate:$negate, _i1.RegOpnd:$p, immOpd0:$src0, src1RC:$src1);
+}
+class SelInst_iinp<string name, pisaVT VT> : PISAInst_ii<name, VT, VT, VT> {
+ let AsmString = name#" \t$dst, $src0, $src1, $negate$p;";
+ let InOperandList = (ins Negate:$negate, _i1.RegOpnd:$p, immOpd0:$src0, immOpd1:$src1);
+}
+multiclass InvSelInst<string name> {
+ foreach VT = [_i16, _i32, _i64, _bf16, _f16, _f32, _f64] in {
+ defvar directive = VT.directive;
+ defvar opcode = !subst(".", "_", directive);
+
+ // select on negative predicate
+ def opcode#"_rrnp" : SelInst_rrnp<name#directive, VT>;
+ def opcode#"_irnp" : SelInst_irnp<name#directive, VT>;
+ def opcode#"_rinp" : SelInst_rinp<name#directive, VT>;
+ def opcode#"_iinp" : SelInst_iinp<name#directive, VT>;
+ }
+}
+defm sel : SelInst<"sel">;
+defm sel : InvSelInst<"sel">;
+
+// emit patterns to match the pointer variants for sel and cmp
+// e.g.,
+// %2:registers(s1) = G_ICMP intpred(eq), %0(p2), %1
+// %3:reg64b(p2) = G_SELECT %2(s1), %0, %1
+
+foreach VT = VTs.Ptr in {
+ defvar selop = !cast<Instruction>("sel_"#VT.VT.Size#"_rrp");
+ def : Pat<(select i1:$p, VT.VT:$src0, VT.VT:$src1),
+ (selop Pred:$p, VT.RC:$src0, VT.RC:$src1)>;
+ foreach isSigned = [0, 1] in {
+ defvar opcode = !if(isSigned, "scmp", "ucmp");
+ defvar prefix = !if(isSigned, "", "u");
+ foreach cc = ["ne", "eq", "gt", "ge", "lt", "le"] in {
+ defvar ccName = !toupper(!if(!or(!eq(cc, "ne"), !eq(cc, "eq")), cc, prefix#cc));
+ defvar cond = !cast<CondCode>("SET"#ccName);
+ defvar cmpop = !cast<Instruction>(opcode#"_"#cc#VT.opcode#"b_prr");
+ def : Pat<(setcc VT.VT:$src0, VT.VT:$src1, cond),
+ (cmpop VT.RC:$src0, VT.RC:$src1)>;
+ }
+ }
+}
+// END: comparison and selection instruction definitions
+//////
+
+//////
+// BEGIN: control flow instruction definitions
+// => call
+let isCall = 1 in {
+ // Define function call instructions with a FunctionCallTargetOpnd:
+ def functionCall_void: PISAInst<(outs),
+ (ins FunctionCallTargetOpnd:$func, variable_ops),
+ "call void, $func">;
+ foreach VT = VTs.CallReturn in {
+ def "functionCall_"#VT.name#"_r": PISAInst<(outs VT.RegOpnd:$ret),
+ (ins FunctionCallTargetOpnd:$func, variable_ops),
+ "call $ret, $func">;
+ }
+ // Define function call instructions with a register call target:
+ def "indirectFunctionCall_void_r": PISAInst<(outs),
+ (ins IndirectFunctionCallTargetOpnd:$func, variable_ops),
+ "call void, $func">;
+ foreach VT = VTs.CallReturn in {
+ def "indirectFunctionCall_"#VT.name#"_r_i64_r": PISAInst<(outs VT.RegOpnd:$ret),
+ (ins IndirectFunctionCallTargetOpnd:$func, variable_ops),
+ "call $ret, $func">;
+ }
+}
+
+// => call.cond
+let isCall=1, isTerminator=1 in {
+ // Define function call instructions with a FunctionCallTargetOpnd:
+ def predCall_void: PISAInst<(outs),
+ (ins Negate:$negate, _i1.RegOpnd:$p, FunctionCallTargetOpnd:$func, variable_ops),
+ "call.cond $negate$p, void, $func">;
+ foreach VT = VTs.CallReturn in {
+ def "predCall_"#VT.name#"_r": PISAInst<(outs VT.RegOpnd:$ret),
+ (ins Negate:$negate, _i1.RegOpnd:$p, FunctionCallTargetOpnd:$func, variable_ops),
+ "call.cond $negate$p, $ret, $func">;
+ }
+ // Define function call instructions with a register call target:
+ def "indirectPredCall_void_r": PISAInst<(outs),
+ (ins Negate:$negate, _i1.RegOpnd:$p, IndirectFunctionCallTargetOpnd:$func, variable_ops),
+ "call.cond $negate$p, void, $func">;
+ foreach VT = VTs.CallReturn in {
+ def "indirectPredCall_"#VT.name#"_r_i64_r": PISAInst<(outs VT.RegOpnd:$ret),
+ (ins Negate:$negate, _i1.RegOpnd:$p, IndirectFunctionCallTargetOpnd:$func, variable_ops),
+ "call.cond $negate$p, $ret, $func">;
+ }
+}
+
+
+// => goto
+def BrTargetOpnd : PISASymbolOperand<OtherVT, "BrTargetOpnd">;
+def : GINodeEquiv<G_BRCOND, brcond>;
+
+let isTerminator=1, isBranch=1 in {
+ def gotolabel : PISAInst<(outs), (ins BrTargetOpnd:$label),
+ "goto $label;",
+ [(br bb:$label)]> {
+ let isBarrier = 1;
+ let Defs = [SpecialReg_ActiveMask];
+ }
+ // FIXME: Need refactoring and adding a complex predicate operand, which is
+ // composed of a negate operand and a predicate register.
+ def predgoto : PISAInst<(outs), (ins Negate:$negate, _i1.RegOpnd:$cond, BrTargetOpnd:$label),
+ "goto.cond $negate$cond, $label;",
+ [(brcond i1:$cond, bb:$label)]> {
+ let Defs = [SpecialReg_ActiveMask];
+ }
+}
+
+// => return
+class SimpleOp<string name, list<dag> pattern = []> : PISAInst<(outs), (ins), name, pattern>;
+multiclass RetVal<string name> {
+ foreach VT = VTs.CallReturn in {
+ def "_"#VT.name#"_r": PISAInst<(outs), (ins VT.RegOpnd:$value), name#" $value;">;
+ }
+ // support return of immediates
+ foreach VT = VTs.Integer in {
+ def "_"#VT.name#"_i": PISAInst<(outs), (ins VT.ImmOpnd:$value), name#" $value;">;
+ }
+ foreach VT = VTs.Float in {
+ def "_"#VT.name#"_i": PISAInst<(outs), (ins VT.ImmOpnd:$value), name#" $value;">;
+ }
+}
+let isReturn = 1, isBarrier = 1, isTerminator = 1, isNotDuplicable = 1 in {
+ def ret: SimpleOp<"return;">;
+ defm retValue: RetVal<"return">;
+}
+
+// => return.cond
+multiclass PredRet<string name> {
+ foreach VT = VTs.CallReturn in {
+ def "_"#VT.name#"_r": PISAInst<(outs), (ins Negate:$negate, _i1.RegOpnd:$cond, VT.RegOpnd:$value), name#" $negate$cond, $value;">;
+ }
+}
+let isReturn = 1, isBarrier = 1, isTerminator=1, isBranch=1 in {
+ defm predRet: PredRet<"return.cond">;
+}
+// END: control flow instruction definitions
+//////
+
+//////
+// BEGIN: subgroup communication instruction definitions
+// ired/fred
+class Red_rrr<string name, SDPatternOperator node, TImmLeaf opNode, pisaVT src0VT, pisaVT src1VT>
+ : PISAInst_rrr<name, src0VT, src0VT, src1VT, src0VT> {
+ let Pattern = [(set (src0VT.VT dstRC:$dst), (node opNode, (src0VT.VT src0RC:$src0), (src1VT.VT src1RC:$src1), (src0VT.VT src2RC:$src2)))];
+ let AsmString = name#" \t$dst, $src0, $src1;";
+ let Constraints = "$src2 = $dst";
+}
+class Red_rir<string name, SDPatternOperator node, TImmLeaf opNode, pisaVT src0VT, pisaVT src1VT>
+ : PISAInst_rir<name, src0VT, src0VT, src1VT, src0VT> {
+ let Pattern = [(set (src0VT.VT dstRC:$dst), (node opNode, (src0VT.VT src0RC:$src0), (src1VT.VT imm:$src1), (src0VT.VT src2RC:$src2)))];
+ let AsmString = name#" \t$dst, $src0, $src1;";
+ let Constraints = "$src2 = $dst";
+}
+class Red_rir_bf<string name, SDPatternOperator node, TImmLeaf opNode, pisaVT src0VT, pisaVT src1VT>
+ : PISAInst_rir<name, src0VT, src0VT, src1VT, src0VT> {
+ let Pattern = [(set (src0VT.VT dstRC:$dst), (node opNode, (src0VT.VT src0RC:$src0), (src1VT.VT imm:$src1), (src0VT.VT src2RC:$src2)))];
+ let AsmString = name#" \t$dst, $src0, $src1;";
+ let Constraints = "$src2 = $dst";
+}
+class Red_rir_bf_nanp<string name, SDPatternOperator node, TImmLeaf opNode, TImmLeaf nanpNode, pisaVT src0VT, pisaVT src1VT>
+ : PISAInst_rir<name, src0VT, src0VT, src1VT, src0VT> {
+ let Pattern = [(set (src0VT.VT dstRC:$dst), (node opNode, (src0VT.VT src0RC:$src0), (src1VT.VT imm:$src1), (src0VT.VT src2RC:$src2), nanpNode))];
+ let AsmString = name#" \t$dst, $src0, $src1;";
+ let Constraints = "$src2 = $dst";
+}
+// fred 5-operand variants - match int_pisa_fred with nanp ImmArg.
+class Red_rrr_nanp<string name, SDPatternOperator node, TImmLeaf opNode, TImmLeaf nanpNode, pisaVT src0VT, pisaVT src1VT>
+ : PISAInst_rrr<name, src0VT, src0VT, src1VT, src0VT> {
+ let Pattern = [(set (src0VT.VT dstRC:$dst), (node opNode, (src0VT.VT src0RC:$src0), (src1VT.VT src1RC:$src1), (src0VT.VT src2RC:$src2), nanpNode))];
+ let AsmString = name#" \t$dst, $src0, $src1;";
+ let Constraints = "$src2 = $dst";
+}
+class Red_rir_nanp<string name, SDPatternOperator node, TImmLeaf opNode, TImmLeaf nanpNode, pisaVT src0VT, pisaVT src1VT>
+ : PISAInst_rir<name, src0VT, src0VT, src1VT, src0VT> {
+ let Pattern = [(set (src0VT.VT dstRC:$dst), (node opNode, (src0VT.VT src0RC:$src0), (src1VT.VT imm:$src1), (src0VT.VT src2RC:$src2), nanpNode))];
+ let AsmString = name#" \t$dst, $src0, $src1;";
+ let Constraints = "$src2 = $dst";
+}
+
+// => fred
+multiclass FRedInst<string name> {
+ foreach opNode = FRedOpNodes in {
+ // scalar bf16/f16/f32: loop over AllNanpMods to emit nanp=0 and nanp=1 variants.
+ // Directive: fred.<op>[.nanp].<type>
+ foreach nanp = AllNanpMods in {
+ foreach type = [_bf16, _f16, _f32] in {
+ defvar directive = !interleave([opNode.name, nanp.name, type.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrr": Red_rrr_nanp<name#directive, int_pisa_fred, opNode, nanp.node, type, _i32>;
+ def opcode#"_rir": Red_rir_nanp<name#directive, int_pisa_fred, opNode, nanp.node, type, _i32>;
+ }
+ }
+ }
+}
+let isConvergent = 1 in {
+ defm fred: FRedInst<"fred">;
+}
+
+// => ired
+multiclass IRedInst<string name> {
+ foreach opNode = IRedOpNodes in {
+ // defvar red = IRedOpMode<opNode>.name;
+ foreach type = [_i16, _i32] in {
+ defvar directive = !interleave([opNode.name, type.directive], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrr": Red_rrr<name#directive, int_pisa_ired, opNode, type, _i32>;
+ def opcode#"_rir": Red_rir<name#directive, int_pisa_ired, opNode, type, _i32>;
+ }
+ }
+}
+let isConvergent = 1 in {
+ defm ired: IRedInst<"ired">;
+}
+
+// => redfirstidx
+let isConvergent = 1 in {
+ defm redfirstidx: UnaryInst<"redfirstidx", int_pisa_redfirstidx, types=[_i32]>;
+}
+
+// => shfl
+class Shuffle_rrrr<string name, TImmLeaf modeNode, TImmLeaf sgNode, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, src2RC:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (int_pisa_shfl modeNode, src0RC:$src0, src1RC:$src1, src2RC:$src2, src3RC:$src3, sgNode))];
+ let AsmString = name#" \t$dst, $src0, $src1, $src2;";
+ let Constraints = "$src3 = $dst";
+}
+class Shuffle_rrir<string name, TImmLeaf modeNode, TImmLeaf sgNode, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, src1RC:$src1, VT.ImmOpnd:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (int_pisa_shfl modeNode, src0RC:$src0, src1RC:$src1, (VT.VT imm:$src2), src2RC:$src3, sgNode))];
+ let AsmString = name#" \t$dst, $src0, $src1, $src2;";
+ let Constraints = "$src3 = $dst";
+}
+class Shuffle_rirr<string name, TImmLeaf modeNode, TImmLeaf sgNode, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, VT.ImmOpnd:$src1, src2RC:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (int_pisa_shfl modeNode, src0RC:$src0, (VT.VT imm:$src1), src2RC:$src2, src3RC:$src3, sgNode))];
+ let AsmString = name#" \t$dst, $src0, $src1, $src2;";
+ let Constraints = "$src3 = $dst";
+}
+class Shuffle_riir<string name, TImmLeaf modeNode, TImmLeaf sgNode, pisaVT VT>
+ : PISAInst4<name, VT, VT, VT, VT, VT> {
+ let InOperandList = (ins src0RC:$src0, VT.ImmOpnd:$src1, VT.ImmOpnd:$src2, src3RC:$src3);
+ let Pattern = [(set dstRC:$dst, (int_pisa_shfl modeNode, src0RC:$src0, (VT.VT imm:$src1), (VT.VT imm:$src2), src3RC:$src3, sgNode))];
+ let AsmString = name#" \t$dst, $src0, $src1, $src2;";
+ let Constraints = "$src3 = $dst";
+}
+multiclass ShflInst<string name> {
+ foreach modeNode = SHFLModeNodes in {
+ foreach sgNode = [timm_false, timm_true] in {
+ defvar sg = !if(!eq(!cast<string>(sgNode), !cast<string>(timm_true)), ".sg", "");
+ defvar directive = !interleave([modeNode.name, sg, ".32b"], "");
+ defvar opcode = !subst(".", "_", directive);
+ def opcode#"_rrrr" : Shuffle_rrrr<name#directive, modeNode, sgNode, _i32>;
+ def opcode#"_rrir" : Shuffle_rrir<name#directive, modeNode, sgNode, _i32>;
+ def opcode#"_rirr" : Shuffle_rirr<name#directive, modeNode, sgNode, _i32>;
+ def opcode#"_riir" : Shuffle_riir<name#directive, modeNode, sgNode, _i32>;
+ }
+ }
+}
+let isConvergent = 1 in {
+ defm shfl: ShflInst<"shfl">;
+}
+// END: subgroup communication instruction definitions
+//////
+
+//////
+// BEGIN: memory instruction definitions
+
+// => fatom
+defm fatomic_load_add : binary_atomic_op_fp<atomic_load_fadd>;
+defm fatomic_load_sub : binary_atomic_op_fp<atomic_load_fsub>;
+defm fatomic_load_min : binary_atomic_op_fp<atomic_load_fmin>;
+defm fatomic_load_max : binary_atomic_op_fp<atomic_load_fmax>;
+
+// 2 source operands
+multiclass BinaryFloatAtomicInst<string Op> {
+ foreach AS = [global, shared, generic] in {
+ foreach VT = [_bf16, _f16, _f32, _f64] in {
+ // fatom.[shared|generic]* are not allowed for f64
+ defvar invalid0 = !and(!or(!eq(AS, shared), !eq(AS, generic)), !eq(VT, _f64));
+ // fatom.*.[min|max] are not allowed for f64
+ defvar invalid1 = !and(!or(!eq(Op, "min"), !eq(Op, "max")), !eq(VT, _f64));
+ defvar invalidOperations = !or(invalid0, invalid1);
+ // GlobalISel only checks memory size, not type, when matching MemoryVT.
+ // bf16 and f16 are both 16-bit, so we need a GISelPredicateCode to
+ // disambiguate them by checking the register type of the result operand.
+ defvar needsMemVTCheck = !or(!eq(VT, _bf16), !eq(VT, _f16));
+ defvar memVTCheck = !if(!eq(VT, _bf16),
+ "MRI.getType(MI.getOperand(0).getReg()).isBFloat16()",
+ "MRI.getType(MI.getOperand(0).getReg()).isFloat16()");
+ if !not(invalidOperations) then {
+ foreach MO = ["monotonic", "release", "acquire", "acq_rel"] in {
+ defvar fullName = !strconcat("fatomic_load_", Op, "_"#VT.VT, "_", MO);
+ defvar node = !cast<PatFrag>(fullName);
+ defvar MOStr = AtomicMemOrder<MO>.name;
+
+ defvar opcode = "_"#VT.name#"_"#AS.name#"_"#MO#"_"#Op;
+ defvar directive = "fatom."#AS.name#"."#MOStr;
+ if needsMemVTCheck then {
+ let Pattern = [(set VT.VT:$dst,
+ (BinaryAtomAddrspaceMemVTPat<AS, node, memVTCheck>
+ PISAMem_ri<AS>.Pattern:$addr, VT.VT:$src0))] in {
+ def opcode#"_ri" : PISABinaryAtom_ri<directive, Op, AS, VT, node>;
+ }
+ let Pattern = [(set VT.VT:$dst,
+ (BinaryAtomAddrspaceMemVTPat<AS, node, memVTCheck>
+ PISAMem_rr<AS>.Pattern:$addr, VT.VT:$src0))] in {
+ def opcode#"_rr" : PISABinaryAtom_rr<directive, Op, AS, VT, node>;
+ }
+ } else {
+ def opcode#"_ri" : PISABinaryAtom_ri<directive, Op, AS, VT, node>;
+ def opcode#"_rr" : PISABinaryAtom_rr<directive, Op, AS, VT, node>;
+ }
+ if !eq(MO, "monotonic") then {
+ // .memorder is optional
+ defvar opcodeA = "_"#VT.name#"_"#AS.name#"_"#Op;
+ defvar directiveA = "fatom."#AS.name;
+ if needsMemVTCheck then {
+ let Pattern = [(set VT.VT:$dst,
+ (BinaryAtomAddrspaceMemVTPat<AS, node, memVTCheck>
+ PISAMem_ri<AS>.Pattern:$addr, VT.VT:$src0))] in {
+ def opcodeA#"_ri" : PISABinaryAtom_ri<directiveA, Op, AS, VT, node>;
+ }
+ let Pattern = [(set VT.VT:$dst,
+ (BinaryAtomAddrspaceMemVTPat<AS, node, memVTCheck>
+ PISAMem_rr<AS>.Pattern:$addr, VT.VT:$src0))] in {
+ def opcodeA#"_rr" : PISABinaryAtom_rr<directiveA, Op, AS, VT, node>;
+ }
+ } else {
+ def opcodeA#"_ri" : PISABinaryAtom_ri<directiveA, Op, AS, VT, node>;
+ def opcodeA#"_rr" : PISABinaryAtom_rr<directiveA, Op, AS, VT, node>;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+defm atomic_load_fadd_impl : BinaryFloatAtomicInst<"add">;
+defm atomic_load_fsub_impl : BinaryFloatAtomicInst<"sub">;
+defm atomic_load_fmin_impl : BinaryFloatAtomicInst<"min">;
+defm atomic_load_fmax_impl : BinaryFloatAtomicInst<"max">;
+
+// fatom.*.cas.* intrinsic
+class FAtomCasAddrspacePat<AddressSpace Addrspace, SDPatternOperator pat> :
+ PatFrag<(ops node:$ptr, node:$opnd1, node:$opnd2, node:$opnd3), (pat node:$ptr, node:$opnd1, node:$opnd2, node:$opnd3)> {
+ let AddressSpaces = [ Addrspace.num ];
+ let IsLoad = true;
+ let IsStore = true;
+}
+class FAtomCasInst_rri<string directive, string op, AddressSpace AS, pisaVT VT, SDPatternOperator node, SDPatternOperator moNode>
+ : PISATernaryAtom<directive, op, AS, VT, PISAMem_rr<AS>, node> {
+ let Pattern = [(set VT.VT:$dst, (FAtomCasAddrspacePat<AS, node> ADDR_rr:$addr, VT.VT:$src0, VT.VT:$src1, moNode))];
+}
+class FAtomCasInst_rii<string directive, string op, AddressSpace AS, pisaVT VT, SDPatternOperator node, SDPatternOperator moNode>
+ : PISATernaryAtom<directive, op, AS, VT, PISAMem_ri<AS>, node> {
+ let Pattern = [(set VT.VT:$dst, (FAtomCasAddrspacePat<AS, node> ADDR_ri:$addr, VT.VT:$src0, VT.VT:$src1, moNode))];
+}
+multiclass FAtomCasInst<string name> {
+ defvar Op = "cas";
+ foreach AS = [global, shared, generic] in {
+ foreach VT = [_bf16, _f16, _f32] in {
+ foreach moNode = MemoryOrderNodes in {
+ defvar directive = !interleave([AS.directive, moNode.name], "");
+ defvar opcode = !subst(".", "_", !interleave([VT.opcode, AS.directive, moNode.name, "_", Op], ""));
+ def opcode#"_rri" : FAtomCasInst_rri<name#directive, Op, AS, VT, int_pisa_cas_fatom, moNode>;
+ def opcode#"_rii" : FAtomCasInst_rii<name#directive, Op, AS, VT, int_pisa_cas_fatom, moNode>;
+ if !eq(moNode.name, ".relaxed") then {
+ // .memorder is optional
+ defvar directiveA = AS.directive;
+ defvar opcodeA = !subst(".", "_", !interleave([VT.opcode, AS.directive, "_", Op], ""));
+ def opcodeA#"_rri" : FAtomCasInst_rri<name#directiveA, Op, AS, VT, int_pisa_cas_fatom, moNode>;
+ def opcodeA#"_rii" : FAtomCasInst_rii<name#directiveA, Op, AS, VT, int_pisa_cas_fatom, moNode>;
+ }
+ }
+ }
+ }
+}
+defm fatom : FAtomCasInst<"fatom">;
+
+// => iatom
+multiclass load_atomic_op_ord {
+ def NAME#_monotonic : PatFrag<(ops node:$ptr), (!cast<SDPatternOperator>(NAME) node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingMonotonic = true;
+ }
+ def NAME#_acquire : PatFrag<(ops node:$ptr), (!cast<SDPatternOperator>(NAME) node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingAcquire = true;
+ }
+ def NAME#_release : PatFrag<(ops node:$ptr), (!cast<SDPatternOperator>(NAME) node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingRelease = true;
+ }
+ def NAME#_acq_rel : PatFrag<(ops node:$ptr), (!cast<SDPatternOperator>(NAME) node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingAcquireRelease = true;
+ }
+ def NAME#_seq_cst : PatFrag<(ops node:$ptr), (!cast<SDPatternOperator>(NAME) node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingSequentiallyConsistent = true;
+ }
+}
+class IAtomOpName<string Op> {
+ string name = !cond(!eq(Op, "min"): "smin",
+ !eq(Op, "max"): "smax",
+ !eq(Op, "swap"): "xchg",
+ true : Op);
+}
+
+def atomic_swap_i128 : PatFrag<(ops node:$ptr, node:$val), (atomic_swap node:$ptr, node:$val)> {
+ let IsAtomic = true;
+ let MemoryVT = i128;
+ }
+defm atomic_swap_i128 : binary_atomic_op_ord;
+def atomic_cmp_swap_i128 : PatFrag<(ops node:$ptr, node:$cmp, node:$val),
+ (atomic_cmp_swap node:$ptr, node:$cmp, node:$val)> {
+ let IsAtomic = true;
+ let MemoryVT = i128;
+ }
+defm atomic_cmp_swap_i128 : ternary_atomic_op_ord;
+
+// Define atomic inc/dec as atomic add/sub with imm 1
+multiclass atomic_load_incdec<SDNode atomic_op> {
+ foreach vt = [i16, i32, i64] in {
+ def _#vt : PatFrag<(ops node:$ptr), (atomic_op node:$ptr, 1)> {
+ let IsAtomic = true;
+ let MemoryVT = vt;
+ }
+ defm NAME#_#vt : load_atomic_op_ord;
+ }
+}
+defm atomic_load_inc : atomic_load_incdec<atomic_load_add>;
+defm atomic_load_dec : atomic_load_incdec<atomic_load_sub>;
+
+// 1 source operand
+multiclass UnaryAtomicInst<string Op> {
+ foreach AS = [global, shared, generic] in {
+ defvar VTs = !if(!eq(AS, global), [_i16, _i32, _i64], [_i16, _i32]);
+ foreach VT = VTs in {
+ foreach MO = ["monotonic", "release", "acquire", "acq_rel"] in {
+ defvar fullName = NAME#"_"#VT.VT#"_"#MO;
+ defvar node = !cast<PatFrag>(fullName);
+ defvar MOStr = AtomicMemOrder<MO>.name;
+ defvar OpStr = IAtomOpName<Op>.name;
+
+ defvar opcode = "_"#VT.name#"_"#AS.name#"_"#MO#"_"#Op;
+ defvar directive = "iatom."#AS.name#"."#MOStr;
+ def opcode#"_ri" : PISAUnaryAtom_ri<directive, OpStr, AS, VT, node>;
+ def opcode#"_rr" : PISAUnaryAtom_rr<directive, OpStr, AS, VT, node>;
+ if !eq(MO, "monotonic") then {
+ // .memorder is optional
+ defvar opcodeA = "_"#VT.name#"_"#AS.name#"_"#Op;
+ defvar directiveA = "iatom."#AS.name;
+ def opcodeA#"_ri" : PISAUnaryAtom_ri<directiveA, OpStr, AS, VT, node>;
+ def opcodeA#"_rr" : PISAUnaryAtom_rr<directiveA, OpStr, AS, VT, node>;
+ }
+ }
+ }
+ }
+}
+
+// 2 source operands
+defm atomic_load_incwrap : binary_atomic_op<atomic_load_uinc_wrap>;
+defm atomic_load_decwrap : binary_atomic_op<atomic_load_udec_wrap>;
+
+multiclass BinaryAtomicInst<string Op> {
+ foreach AS = [global, shared, generic] in {
+ foreach VT = [_i16, _i32, _i64, _i128] in {
+ // i128 is only allowed for "swap" op.
+ defvar invalid0 = !and(!eq(VT, _i128), !ne(Op, "swap"));
+ // iatom.[shared|generic].<non-cas-op> is only allowed for i16, i32
+ defvar invalid1 = !and(!or(!eq(AS, shared), !eq(AS, generic)), !eq(VT, _i64));
+ // iatom*.[inc|dec]wrap is only allowed for i32, i64
+ defvar invalid2 = !and(!or(!eq(Op, "incwrap"), !eq(Op, "decwrap")), !eq(VT, _i16));
+ defvar invalidOperations = !or(invalid0, invalid1, invalid2);
+ if !not(invalidOperations) then {
+ foreach MO = ["monotonic", "release", "acquire", "acq_rel"] in {
+ defvar opPrefix = !if(!eq(Op,"swap"), "atomic_", "atomic_load_");
+ defvar fullName = !strconcat(opPrefix , Op, "_"#VT.VT , "_", MO);
+ defvar node = !cast<PatFrag>(fullName);
+ defvar MOStr = AtomicMemOrder<MO>.name;
+ defvar OpStr = IAtomOpName<Op>.name;
+
+ defvar opcode = "_"#VT.name#"_"#AS.name#"_"#MO#"_"#Op;
+ defvar directive = "iatom."#AS.name#"."#MOStr;
+ def opcode#"_ri" : PISABinaryAtom_ri<directive, OpStr, AS, VT, node>;
+ def opcode#"_rr" : PISABinaryAtom_rr<directive, OpStr, AS, VT, node>;
+ if !eq(MO, "monotonic") then {
+ // .memorder is optional
+ defvar opcodeA = "_"#VT.name#"_"#AS.name#"_"#Op;
+ defvar directiveA = "iatom."#AS.name;
+ def opcodeA#"_ri" : PISABinaryAtom_ri<directiveA, OpStr, AS, VT, node>;
+ def opcodeA#"_rr" : PISABinaryAtom_rr<directiveA, OpStr, AS, VT, node>;
+ }
+ }
+ }
+ }
+ }
+}
+
+// 3 source operands
+multiclass TernaryAtomicInst<string Op> {
+ foreach AS = [global, shared, generic] in {
+ foreach VT = [_i16, _i32, _i64, _i128] in {
+ foreach MO = ["monotonic", "release", "acquire", "acq_rel"] in {
+ defvar fullName = !strconcat("atomic_", Op, "_"#VT.VT , "_", MO);
+ defvar node = !cast<SDPatternOperator>(fullName);
+ defvar MOStr = AtomicMemOrder<MO>.name;
+
+ defvar opcode = "_"#VT.name#"_"#AS.name#"_"#MO#"_"#Op;
+ defvar directive = "iatom."#AS.name#"."#MOStr;
+ def opcode#"_ri" : PISATernaryAtom_ri<directive, "cas", AS, VT, node>;
+ def opcode#"_rr" : PISATernaryAtom_rr<directive, "cas", AS, VT, node>;
+ if !eq(MO, "monotonic") then {
+ // .memorder is optional
+ defvar opcodeA = "_"#VT.name#"_"#AS.name#"_"#Op;
+ defvar directiveA = "iatom."#AS.name;
+ def opcodeA#"_ri" : PISATernaryAtom_ri<directiveA, "cas", AS, VT, node>;
+ def opcodeA#"_rr" : PISATernaryAtom_rr<directiveA, "cas", AS, VT, node>;
+ }
+ }
+ }
+ }
+}
+
+defm atomic_load_inc : UnaryAtomicInst<"inc">;
+defm atomic_load_dec : UnaryAtomicInst<"dec">;
+
+defm atomic_load_incwrap_impl : BinaryAtomicInst<"incwrap">;
+defm atomic_load_decwrap_impl : BinaryAtomicInst<"decwrap">;
+defm atomic_load_add_impl : BinaryAtomicInst<"add">;
+defm atomic_load_sub_impl : BinaryAtomicInst<"sub">;
+defm atomic_load_and_impl : BinaryAtomicInst<"and">;
+defm atomic_load_or_impl : BinaryAtomicInst<"or">;
+defm atomic_load_xor_impl : BinaryAtomicInst<"xor">;
+defm atomic_load_min_impl : BinaryAtomicInst<"min">;
+defm atomic_load_max_impl : BinaryAtomicInst<"max">;
+defm atomic_load_umin_impl : BinaryAtomicInst<"umin">;
+defm atomic_load_umax_impl : BinaryAtomicInst<"umax">;
+defm atomic_swap_impl : BinaryAtomicInst<"swap">;
+
+// TODO: llvm's cmpxchg instruction takes 2 memory orderings. Why?
+defm atomic_cmp_swap : TernaryAtomicInst<"cmp_swap">;
+
+// => ld
+multiclass load_atomic_op<SDNode atomic_op> {
+ foreach vt = VTs.AtomicLoadStore in {
+ def _#vt : PatFrag<(ops node:$ptr), (atomic_op node:$ptr)> {
+ let IsAtomic = true;
+ let MemoryVT = vt.VT;
+ }
+ defm NAME#_#vt : load_atomic_op_ord;
+ }
+}
+defm load_atomic : load_atomic_op<atomic_load>;
+// atomic loads
+multiclass AtomicLoadInst<string name> {
+ foreach pVT = VTs.AtomicLoadStore in {
+ foreach MO = ["monotonic", "acquire", "seq_cst"] in {
+ defvar MOStr = AtomicMemOrder<MO>.name;
+ defvar fullName = "load_atomic_"#pVT#"_"#MO;
+ defvar node = !cast<PatFrag>(fullName);
+ foreach AS = [global, generic, shared] in {
+ defvar directive = name#"."#AS.name#"."#MOStr;
+ def "_"#AS#"_"#MO#"_"#pVT.name#"_rr" : PISAAtomicLoad_rr<directive, AS, pVT, node>;
+ def "_"#AS#"_"#MO#"_"#pVT.name#"_ri" : PISAAtomicLoad_ri<directive, AS, pVT, node>;
+ }
+ // .addrspace defaults to .generic when not specified
+ defvar directive = name#"."#MOStr;
+ def "_generic_"#MO#"_"#pVT.name#"_alias_rr" : PISAAtomicLoad_rr<directive, generic, pVT, node>;
+ def "_generic_"#MO#"_"#pVT.name#"_alias_ri" : PISAAtomicLoad_ri<directive, generic, pVT, node>;
+ }
+ }
+}
+// non-atomic loads
+multiclass LoadInst<string name> {
+ foreach pVT = VTs.LoadStore in {
+ foreach AS = [global, const, generic, private, shared] in {
+ defvar directive = name#"."#AS;
+ def "_"#AS#"_"#pVT.name#"_ri" : PISALoad_ri<directive, AS, pVT, load>;
+ def "_"#AS#"_"#pVT.name#"_rr" : PISALoad_rr<directive, AS, pVT, load>;
+ // 'ld.weak' is an alias for 'ld'
+ defvar directiveA = name#"."#AS#".weak";
+ def "_"#AS#"_weak_"#pVT.name#"_ri" : PISALoad_ri<directiveA, AS, pVT, load>;
+ def "_"#AS#"_weak_"#pVT.name#"_rr" : PISALoad_rr<directiveA, AS, pVT, load>;
+ }
+ // .addrspace defaults to .generic when not specified
+ def "_generic_"#pVT.name#"_alias_ri" : PISALoad_ri<name, generic, pVT, load>;
+ def "_generic_"#pVT.name#"_alias_rr" : PISALoad_rr<name, generic, pVT, load>;
+ // 'ld.weak' is an alias for 'ld'
+ def "_generic_weak_"#pVT.name#"_alias_ri" : PISALoad_ri<name#".weak", generic, pVT, load>;
+ def "_generic_weak_"#pVT.name#"_alias_rr" : PISALoad_rr<name#".weak", generic, pVT, load>;
+ }
+}
+// kernel param loads (ld.param)
+multiclass LoadParamInst {
+ foreach VT = VTs.LoadParam in {
+ def _#VT.name: PISAInst<(outs VT.RegOpnd:$res),
+ (ins MEM_ii:$addr, variable_ops),
+ "ld.param"#VT.directive#" $res, $addr;">;
+ def : Pat<(VT.VT (load MEM_ii:$addr)), (!cast<Instruction>("loadParam_"#VT.name) MEM_ii:$addr)>;
+ def _#VT.name#"_ir": PISAInst<(outs VT.RegOpnd:$res),
+ (ins MEM_ir:$addr, variable_ops),
+ "ld.param"#VT.directive#" $res, $addr;">;
+ def : Pat<(VT.VT (load MEM_ir:$addr)), (!cast<Instruction>("loadParam_"#VT.name#"_ir") MEM_ir:$addr)>;
+ }
+}
+defm loadParam: LoadParamInst;
+defm ld : AtomicLoadInst<"ld">;
+defm ld : LoadInst<"ld">;
+
+
+// => st
+multiclass store_atomic_op_ord {
+ def NAME#_monotonic : PatFrag<(ops node:$val, node:$ptr), (!cast<SDPatternOperator>(NAME) node:$val, node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingMonotonic = true;
+ }
+ def NAME#_acquire : PatFrag<(ops node:$val, node:$ptr), (!cast<SDPatternOperator>(NAME) node:$val, node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingAcquire = true;
+ }
+ def NAME#_release : PatFrag<(ops node:$val, node:$ptr), (!cast<SDPatternOperator>(NAME) node:$val, node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingRelease = true;
+ }
+ def NAME#_acq_rel : PatFrag<(ops node:$val, node:$ptr), (!cast<SDPatternOperator>(NAME) node:$val, node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingAcquireRelease = true;
+ }
+ def NAME#_seq_cst : PatFrag<(ops node:$val, node:$ptr), (!cast<SDPatternOperator>(NAME) node:$val, node:$ptr)> {
+ let IsAtomic = true;
+ let IsAtomicOrderingSequentiallyConsistent = true;
+ }
+}
+multiclass store_atomic_op<SDNode atomic_op> {
+ foreach vt = VTs.AtomicLoadStore in {
+ def _#vt : PatFrag<(ops node:$val, node:$ptr), (atomic_op node:$val, node:$ptr)> {
+ let IsAtomic = true;
+ let MemoryVT = vt.VT;
+ }
+ defm NAME#_#vt : store_atomic_op_ord;
+ }
+}
+defm store_atomic : store_atomic_op<atomic_store>;
+// atomic stores
+multiclass AtomicStoreInst<string name> {
+ foreach pVT = VTs.AtomicLoadStore in {
+ foreach MO = ["monotonic", "release", "seq_cst"] in {
+ defvar MOStr = AtomicMemOrder<MO>.name;
+ defvar fullName = "store_atomic_"#pVT#"_"#MO;
+ defvar node = !cast<PatFrag>(fullName);
+ foreach AS = [global, generic, shared] in {
+ defvar directive = name#"."#AS.name#"."#MOStr;
+ def "_"#AS#"_"#MO#"_"#pVT.name#"_rr" : PISAAtomicStore_rr<directive, AS, pVT, node>;
+ def "_"#AS#"_"#MO#"_"#pVT.name#"_ri" : PISAAtomicStore_ri<directive, AS, pVT, node>;
+ }
+ // .addrspace defaults to .generic when not specified
+ defvar directive = name#"."#MOStr;
+ def "_generic_"#MO#"_"#pVT.name#"_alias_rr" : PISAAtomicStore_rr<directive, generic, pVT, node>;
+ def "_generic_"#MO#"_"#pVT.name#"_alias_ri" : PISAAtomicStore_ri<directive, generic, pVT, node>;
+ }
+ }
+}
+// non-atomic stores
+multiclass StoreInst<string name> {
+ foreach pVT = VTs.LoadStore in {
+ foreach AS = [global, generic, private, shared] in {
+ defvar directive = name#"."#AS;
+ def "_"#AS#"_"#pVT.name#"_ri" : PISAStore_ri<directive, AS, pVT, store>;
+ def "_"#AS#"_"#pVT.name#"_rr" : PISAStore_rr<directive, AS, pVT, store>;
+ // 'st.weak' is an alias for 'st'
+ defvar directiveA = name#"."#AS#".weak";
+ def "_"#AS#"_weak_"#pVT.name#"_ri" : PISAStore_ri<directiveA, AS, pVT, store>;
+ def "_"#AS#"_weak_"#pVT.name#"_rr" : PISAStore_rr<directiveA, AS, pVT, store>;
+ }
+ // .addrspace defaults to .generic when not specified
+ def "_generic_"#pVT.name#"_alias_ri" : PISAStore_ri<name, generic, pVT, store>;
+ def "_generic_"#pVT.name#"_alias_rr" : PISAStore_rr<name, generic, pVT, store>;
+ // 'st.weak' is an alias for 'st'
+ def "_generic_weak_"#pVT.name#"_alias_ri" : PISAStore_ri<name#".weak", generic, pVT, store>;
+ def "_generic_weak_"#pVT.name#"_alias_rr" : PISAStore_rr<name#".weak", generic, pVT, store>;
+ }
+}
+defm st : AtomicStoreInst<"st">;
+defm st : StoreInst<"st">;
+
+// END: memory instruction definitions
+//////
+
+//////
+// BEGIN: synchronization instruction definitions
+
+// => barrier.workgroup
+let isConvergent = 1, hasSideEffects = 1 in {
+ def barrier_wg : PISAInst<(outs), (ins), "barrier.workgroup;", [(int_pisa_workgroup_barrier)]>;
+}
+
+// => fence
+multiclass FenceInst<string name> {
+ foreach as = [generic, global, shared, default] in {
+ foreach scope = [
+ workgroup,
+ gpu,
+ system] in {
+ // system scope is only supported for global fence
+ if !or(!ne(scope, system), !eq(as, global)) then {
+ defvar asName = !if(!empty(as.name), "", "."#as.name);
+ defvar opcode = !if(!empty(as.name), "_"#scope, "_"#as.name#"_"#scope);
+ defvar asmstr = name#asName#"${order}."#scope#";";
+ def opcode: PISAInst<(outs), (ins FenceMemOrderOpnd:$order), asmstr>;
+ }
+ }
+ }
+ defvar directive = ".subgroup";
+ defvar opcode = !subst(".", "_", directive);
+ def opcode: PISAInst<(outs), (ins), name#directive#";">;
+}
+defm fence : FenceInst<"fence">;
+
+// END: synchronization instruction definitions
+//////
+
+
+
+// => lifetime.start
+let hasSideEffects = 1, mayLoad = 0, mayStore = 0, isReMaterializable = 0 in {
+ multiclass LifetimeStart {
+ foreach VT = VTs.LifetimeTypes in {
+ def "_"#VT.name#"_r" : PISAInst<(outs VT.RegOpnd:$dst), (ins),
+ "lifetime.start \t$dst;", []>;
+ }
+ }
+ defm lifetime_start : LifetimeStart;
+}
+// Register-class to lifetime.start opcode.
+foreach VT = VTs.LifetimeTypes in {
+ def : LifetimeStartEntry<!cast<string>(VT.RC),
+ !cast<Instruction>("lifetime_start_"#VT.name#"_r")>;
}
-// Placeholder instruction. Real PISA instructions are added in later changes.
-def NOP : PISAInst<(outs), (ins), "nop", []>;
diff --git a/llvm/lib/Target/PISA/PISAMCInstLower.cpp b/llvm/lib/Target/PISA/PISAMCInstLower.cpp
new file mode 100644
index 0000000000000..9517dd490472f
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAMCInstLower.cpp
@@ -0,0 +1,134 @@
+//===-- PISAMCInstLower.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PISAMCInstLower.h"
+#include "MCTargetDesc/PISAMCExpr.h"
+#include "PISA.h"
+#include "PISARegManager.h"
+#include "PISASubtarget.h"
+#include "PISAUtils.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineOperand.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/Target/TargetMachine.h"
+
+using namespace llvm;
+
+MCOperand PISAMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
+ MCSymbol &Sym) const {
+ const MCExpr *Expr = PISAGlobalAddressMCExpr::create(Sym, OutContext);
+ return MCOperand::createExpr(Expr);
+}
+
+MCSymbol &
+PISAMCInstLower::getGlobalAddressSymbol(const MachineOperand &MO) const {
+ assert(MO.isGlobal());
+ return *AP.getSymbol(MO.getGlobal());
+}
+
+void PISAMCInstLower::lower(const MachineInstr *MI, MCInst &OutMI) const {
+ OutMI.setOpcode(MI->getOpcode());
+ for (unsigned OpNo = 0, E = MI->getNumOperands(); OpNo != E; ++OpNo) {
+ const MachineOperand &MO = MI->getOperand(OpNo);
+ MCOperand MCOp;
+ switch (MO.getType()) {
+ default:
+ llvm_unreachable("unknown operand type");
+ case MachineOperand::MO_Metadata: {
+ // Skip call to addOperand() outside the switch as MCInst doesn't
+ // support metadata operand types.
+ continue;
+ }
+ case MachineOperand::MO_ExternalSymbol: {
+ // Convert external symbol operand (kernel arg name on loadParam
+ // instructions) to an MCExpr for PISAInstPrinter::printParamMemOperand.
+ MCSymbol *Sym = OutContext.getOrCreateSymbol(MO.getSymbolName());
+ MCOp = MCOperand::createExpr(MCSymbolRefExpr::create(Sym, OutContext));
+ break;
+ }
+ case MachineOperand::MO_FrameIndex: {
+ // MachineOperand of type MO_FrameIndex is lowered to immediate operand
+ // with value equal to frame index. Whether an immediate operand is
+ // frame index is indicated by flags that are stored in MCInst instance.
+ // During operand printing, we inspect flag value first and decide if
+ // the operand has to be printed as private variable or a generic
+ // immediate value.
+ unsigned int FrameIndex = MO.getIndex();
+ MCOp = MCOperand::createImm(FrameIndex);
+ setVariableRef(OutMI, OpNo);
+ break;
+ }
+ case MachineOperand::MO_GlobalAddress: {
+ MCOp = lowerSymbolOperand(MO, getGlobalAddressSymbol(MO));
+ break;
+ }
+ case MachineOperand::MO_MachineBasicBlock:
+ MCOp = MCOperand::createExpr(
+ MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), OutContext));
+ break;
+ case MachineOperand::MO_Register: {
+ Register CurReg = MO.getReg();
+ unsigned EncodedVal = CurReg;
+ if (CurReg.isVirtual()) {
+ auto &MRI = MI->getParent()->getParent()->getRegInfo();
+ auto *RC = MRI.getRegClass(CurReg);
+ auto NumElts = TRI.getNumEltsFromRegClass(RC);
+ auto EltSize = TRI.getBitSizeFromRegClass(RC);
+ auto Bank = RegMgr.getRegBank(NumElts, EltSize);
+ EncodedVal = RegMgr.encodeVirtualRegister(Bank, CurReg);
+ }
+ MCOp = MCOperand::createReg(EncodedVal);
+ auto SubReg = MO.getSubReg();
+ auto IsMov = MI->isMoveImmediate() || MI->isMoveReg();
+ auto Swizzle = TRI.getSwizzle(SubReg);
+ if (IsMov && !SubReg && CurReg.isVirtual()) {
+ // vector args in mov instructions always print swizzle
+ auto &MRI = MI->getParent()->getParent()->getRegInfo();
+ auto *RC = MRI.getRegClass(CurReg);
+ auto NumElts = TRI.getNumEltsFromRegClass(RC);
+ if (NumElts == 2)
+ Swizzle = PISA::Swizzle::XY;
+ else if (NumElts == 4)
+ Swizzle = PISA::Swizzle::XYZW;
+ else
+ assert((NumElts == 1) && "unknown swizzle value");
+ }
+ setSwizzle(OutMI, OpNo, Swizzle);
+ break;
+ }
+ case MachineOperand::MO_Immediate:
+ MCOp = MCOperand::createImm(MO.getImm());
+ break;
+ case MachineOperand::MO_FPImmediate:
+ // All floating point values are bitcasted
+ // into integer ones. Double- and single-precison ones could be specially
+ // taged in MC as SFP or DFP immediate. But, in general, as each type of
+ // immediate operand has its own methods, general immediate operands
+ // won't loss any semantics.
+ uint64_t ImmVal =
+ MO.getFPImm()->getValueAPF().bitcastToAPInt().getZExtValue();
+ // TODO: bfloat16 has a different exponent/mantissa layout than
+ // IEEE half; a separate kBFPImmediate kind may be needed to
+ // distinguish them (see also getFPImmInReg TODO).
+ if (MO.getFPImm()->getType()->isHalfTy() ||
+ MO.getFPImm()->getType()->isBFloatTy())
+ MCOp = MCOperand::createHFPImm(ImmVal);
+ else if (MO.getFPImm()->getType()->isFloatTy())
+ MCOp = MCOperand::createSFPImm(ImmVal);
+ else if (MO.getFPImm()->getType()->isDoubleTy())
+ MCOp = MCOperand::createDFPImm(ImmVal);
+ else
+ MCOp = MCOperand::createImm(ImmVal);
+ break;
+ }
+
+ OutMI.addOperand(MCOp);
+ }
+}
diff --git a/llvm/lib/Target/PISA/PISAMCInstLower.h b/llvm/lib/Target/PISA/PISAMCInstLower.h
new file mode 100644
index 0000000000000..9575cbb5293b4
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAMCInstLower.h
@@ -0,0 +1,103 @@
+//===-- PISAMCInstLower.h -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_PISAMCINSTLOWER_H
+#define LLVM_LIB_TARGET_PISA_PISAMCINSTLOWER_H
+
+#include "PISADefines.h"
+#include "PISARegisterInfo.h"
+#include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Support/Compiler.h"
+#include <cassert>
+
+namespace llvm {
+class MCContext;
+class MCOperand;
+class MCSymbol;
+class MachineInstr;
+class MachineOperand;
+
+namespace PISA {
+class RegManager;
+} // namespace PISA
+
+struct PISAMCOpnd {
+ // given immediate value represents the index of a variable (.e.g, @R0).
+ bool IsVariable;
+ // swizzle of given operand
+ unsigned Swizzle;
+};
+
+class PISAMCInst : public MCInst {
+public:
+ SmallDenseMap<unsigned, PISAMCOpnd> Opnds;
+
+ // Source line number in the original assembly file, used for debug info and
+ // error reporting
+ unsigned SourceLine = 0;
+ unsigned getSourceLine() const { return SourceLine; }
+ void setSourceLine(unsigned L) { SourceLine = L; }
+};
+
+// This class is used to lower a MachineInstr into an MCInst.
+class LLVM_LIBRARY_VISIBILITY PISAMCInstLower {
+public:
+ static void setVariableRef(MCInst &MI, unsigned OpNo) {
+ auto *MC = static_cast<PISAMCInst *>(&MI);
+ auto It = MC->Opnds.find(OpNo);
+ if (It == MC->Opnds.end())
+ MC->Opnds[OpNo] = {true, 0};
+ else
+ It->second.IsVariable = true;
+ }
+
+ static bool isVariableRef(const MCInst &MI, unsigned int OpNo) {
+ const auto *MC = static_cast<const PISAMCInst *>(&MI);
+ auto It = MC->Opnds.find(OpNo);
+ if (It == MC->Opnds.end())
+ return false;
+ return It->second.IsVariable;
+ }
+
+ static void setSwizzle(MCInst &MI, unsigned OpNo, PISA::Swizzle Swizzle) {
+ static_assert(static_cast<unsigned>(PISA::Swizzle::NONE) <= 7);
+ auto *MC = static_cast<PISAMCInst *>(&MI);
+ auto It = MC->Opnds.find(OpNo);
+ if (It == MC->Opnds.end())
+ MC->Opnds[OpNo] = {false, static_cast<unsigned>(Swizzle)};
+ else
+ It->second.Swizzle = static_cast<unsigned>(Swizzle);
+ }
+
+ static PISA::Swizzle getSwizzle(const MCInst &MI, unsigned OpNo) {
+ const auto *MC = static_cast<const PISAMCInst *>(&MI);
+ auto It = MC->Opnds.find(OpNo);
+ if (It == MC->Opnds.end())
+ return static_cast<PISA::Swizzle>(PISA::Swizzle::NONE);
+ return static_cast<PISA::Swizzle>(It->second.Swizzle);
+ }
+
+ PISAMCInstLower(MCContext &Ctx, const PISARegisterInfo &TRI,
+ const PISA::RegManager &RegMgr, const AsmPrinter &AP)
+ : OutContext(Ctx), TRI(TRI), RegMgr(RegMgr), AP(AP) {}
+ void lower(const MachineInstr *MI, MCInst &OutMI) const;
+
+private:
+ MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol &Sym) const;
+ MCSymbol &getGlobalAddressSymbol(const MachineOperand &MO) const;
+
+private:
+ MCContext &OutContext;
+ const PISARegisterInfo &TRI;
+ const PISA::RegManager &RegMgr;
+ const AsmPrinter &AP;
+};
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_PISAMCINSTLOWER_H
diff --git a/llvm/lib/Target/PISA/PISARegManager.cpp b/llvm/lib/Target/PISA/PISARegManager.cpp
new file mode 100644
index 0000000000000..11171220b1272
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISARegManager.cpp
@@ -0,0 +1,62 @@
+//===-- PISARegManager.cpp - Manage PISA virtual registers ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PISARegManager.h"
+#include "PISAInstrInfo.h"
+#include "PISASubtarget.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+
+using namespace llvm;
+using namespace PISA;
+
+void RegManager::computeMapping() {
+ std::array<unsigned, RegType::NUM_TYPE> Count{};
+ auto *TII = MF.getSubtarget<PISASubtarget>().getInstrInfo();
+ for (auto &MBB : MF) {
+ for (auto &MI : MBB) {
+ for (auto &MO : MI.operands()) {
+ if (!MO.isReg() || (!MO.isDef() && !MO.isUndef()))
+ continue;
+
+ Register CurReg = MO.getReg();
+
+ if (CurReg.isPhysical())
+ continue;
+
+ if (Mapping.count(CurReg) != 0)
+ continue;
+
+ unsigned Flags = Usage::None;
+ if (TII->isNoEmissionInstr(MI))
+ Flags |= Usage::NoEmissionDef;
+
+ auto Type = getRegType(MRI.getRegClass(MO.getReg()));
+ RegInfo Info{Type, Count[Type]++, static_cast<Usage>(Flags)};
+ Mapping[CurReg] = Info;
+ }
+ }
+ }
+}
+
+unsigned RegManager::getRegIdx(Register Reg) const {
+ const auto *I = Mapping.find(Reg);
+ assert(I != Mapping.end() && "missing?");
+ return I->second.Idx;
+}
+
+unsigned RegManager::encodeVirtualRegister(RegBank Bank, Register Reg) const {
+ auto &MRI = MF.getRegInfo();
+ unsigned Idx = getRegIdx(Reg);
+ auto Type = getRegType(MRI.getRegClass(Reg));
+ return RegEncoder::encodeVirtualRegister(Idx, Bank, Type);
+}
+
+RegManager::RegManager(const MachineFunction &MF)
+ : MF(MF), MRI(MF.getRegInfo()) {
+ computeMapping();
+}
diff --git a/llvm/lib/Target/PISA/PISARegManager.h b/llvm/lib/Target/PISA/PISARegManager.h
new file mode 100644
index 0000000000000..3feeab6a7af8e
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISARegManager.h
@@ -0,0 +1,47 @@
+//===-- PISARegManager.h - Manage PISA virtual registers ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_PISAREGMANAGER_H
+#define LLVM_LIB_TARGET_PISA_PISAREGMANAGER_H
+
+#include "MCTargetDesc/PISARegEncoder.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
+
+namespace llvm {
+namespace PISA {
+
+class RegManager : public RegEncoder {
+public:
+ enum Usage { None = 0, NoEmissionDef = (1 << 0) };
+ struct RegInfo {
+ RegType Type;
+ unsigned Idx;
+ Usage Flags;
+ };
+
+public:
+ RegManager(const MachineFunction &MF);
+ unsigned getRegIdx(Register Reg) const;
+ void setRegIdx(Register Reg, unsigned Idx) { Mapping[Reg].Idx = Idx; }
+ unsigned encodeVirtualRegister(RegBank Bank, Register Reg) const;
+ auto mapping() const { return make_range(Mapping.begin(), Mapping.end()); }
+ bool exists(Register Reg) const { return Mapping.count(Reg) > 0; }
+
+private:
+ MapVector<Register, RegInfo> Mapping;
+ const MachineFunction &MF;
+ const MachineRegisterInfo &MRI;
+ void computeMapping();
+};
+
+} // namespace PISA
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_PISAREGMANAGER_H
diff --git a/llvm/lib/Target/PISA/PISARegisterBanks.td b/llvm/lib/Target/PISA/PISARegisterBanks.td
new file mode 100644
index 0000000000000..ee28b20fb07e1
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISARegisterBanks.td
@@ -0,0 +1,15 @@
+//===-- PISARegisterBanks.td - Describe PISA RegBanks ------*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+def RegistersRegBank : RegisterBank<"Registers",
+ [Pred, Reg8b, Reg16b, Reg32b, Reg64b, Reg128b,
+ RegV2_8b, RegV3_8b, RegV4_8b,
+ RegV2_16b, RegV3_16b, RegV4_16b,
+ RegV2_32b, RegV3_32b, RegV4_32b, RegV5_32b, RegV6_32b, RegV7_32b, RegV8_32b, RegV16_32b, RegV32_32b, RegV64_32b,
+ RegV2_64b, RegV3_64b, RegV4_64b]
+>;
diff --git a/llvm/lib/Target/PISA/PISARegisterInfo.cpp b/llvm/lib/Target/PISA/PISARegisterInfo.cpp
index f66db9c9e0283..fdf4c7c68e7af 100644
--- a/llvm/lib/Target/PISA/PISARegisterInfo.cpp
+++ b/llvm/lib/Target/PISA/PISARegisterInfo.cpp
@@ -9,6 +9,9 @@
#include "PISARegisterInfo.h"
#include "PISA.h"
#include "PISASubtarget.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringTable.h"
#include "llvm/CodeGen/MachineFunction.h"
#define GET_REGINFO_TARGET_DESC
@@ -16,14 +19,273 @@
using namespace llvm;
-PISARegisterInfo::PISARegisterInfo() : PISAGenRegisterInfo(PISA::DummyReg) {}
+namespace {
+struct SpecialRegEntry {
+ StringTable::Offset Name;
+};
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+#define GET_SpecialRegNames_IMPL
+#include "PISAGenSearchableTables.inc"
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+} // anonymous namespace
+
+static_assert(PISA::NUM_TARGET_SUBREGS == 63, "updated needed!");
+DenseMap<unsigned, PISARegisterInfo::SwizzleDesc> PISARegisterInfo::SwizzleMap =
+ {
+ {PISA::NoSubRegister, {PISA::Swizzle::NONE, nullptr}},
+ {PISA::sub8_0, {PISA::Swizzle::X, ".x"}},
+ {PISA::sub8_1, {PISA::Swizzle::Y, ".y"}},
+ {PISA::sub8_2, {PISA::Swizzle::Z, ".z"}},
+ {PISA::sub8_3, {PISA::Swizzle::W, ".w"}},
+ {PISA::sub16_0, {PISA::Swizzle::X, ".x"}},
+ {PISA::sub16_1, {PISA::Swizzle::Y, ".y"}},
+ {PISA::sub16_2, {PISA::Swizzle::Z, ".z"}},
+ {PISA::sub16_3, {PISA::Swizzle::W, ".w"}},
+ {PISA::sub32_0, {PISA::Swizzle::X, ".x"}},
+ {PISA::sub32_1, {PISA::Swizzle::Y, ".y"}},
+ {PISA::sub32_2, {PISA::Swizzle::Z, ".z"}},
+ {PISA::sub32_3, {PISA::Swizzle::W, ".w"}},
+ {PISA::sub64_0, {PISA::Swizzle::X, ".x"}},
+ {PISA::sub64_1, {PISA::Swizzle::Y, ".y"}},
+ {PISA::sub64_2, {PISA::Swizzle::Z, ".z"}},
+ {PISA::sub64_3, {PISA::Swizzle::W, ".w"}},
+ {PISA::sub8_xy, {PISA::Swizzle::XY, ".xy"}},
+ {PISA::sub8_zw, {PISA::Swizzle::ZW, ".zw"}},
+ {PISA::sub16_xy, {PISA::Swizzle::XY, ".xy"}},
+ {PISA::sub16_zw, {PISA::Swizzle::ZW, ".zw"}},
+ {PISA::sub32_xy, {PISA::Swizzle::XY, ".xy"}},
+ {PISA::sub32_zw, {PISA::Swizzle::ZW, ".zw"}},
+ {PISA::sub64_xy, {PISA::Swizzle::XY, ".xy"}},
+ {PISA::sub64_zw, {PISA::Swizzle::ZW, ".zw"}},
+};
+
+bool PISARegisterInfo::shouldCoalesce(
+ MachineInstr *MI, const TargetRegisterClass *SrcRC, unsigned SubReg,
+ const TargetRegisterClass *DstRC, unsigned DstSubReg,
+ const TargetRegisterClass *NewRC, LiveIntervals &LIS) const {
+
+ if (!MI->isCopy())
+ return false;
+
+ auto IsLegalSwizzle = [&](unsigned Subreg) {
+ auto Swizzle = SwizzleMap.find(Subreg);
+ return Swizzle != SwizzleMap.end();
+ };
+
+ return IsLegalSwizzle(SubReg) && IsLegalSwizzle(DstSubReg);
+}
+
+PISARegisterInfo::PISARegisterInfo() : PISAGenRegisterInfo(PISA::DummyReg) {
+ // Tablegen can sometimes synthesize register classes if you don't set
+ // subregs explicitly on some regs. For now at least, we probably want to
+ // be explicit about what regclasses exist. If you added a register class
+ // explicitly, go ahead and update this number. If not, you might want to
+ // figure out what happened.
+ // New reg classes must also be reflected in PISATargetLowering constructor
+ static_assert(std::size(PISAMCRegisterClassStorage.Classes) == 30);
+ unsigned NumRCs = getNumRegClasses();
+ for (unsigned I = 0; I < NumRCs; I++) {
+ auto *RC = getRegClass(I);
+ auto RCD = std::make_unique<RegClassDescription>();
+ if (RC == &PISA::RegV64_32bRegClass) {
+ // RegV64_32b has no per-element sub-register structure (LLVM's
+ // LaneBitmask cannot represent 64 lanes), so derive the metadata
+ // from the known type rather than from lane masks.
+ RCD->NumElements = 64;
+ RCD->ScalarBitSize = 32;
+ } else {
+ RCD->NumElements = RC->LaneMask.getNumLanes();
+ RCD->ScalarBitSize = getScalarBitSize(RC, RCD->NumElements);
+ }
+ auto Key = std::make_pair(RCD->NumElements, RCD->ScalarBitSize);
+ if (!VecRegClassMap[Key])
+ VecRegClassMap[std::make_pair(RCD->NumElements, RCD->ScalarBitSize)] = RC;
+ RegClassMap[RC] = std::move(RCD);
+ }
+
+ // Precompute special register BitVector for O(1) lookup.
+ SpecialRegs.resize(getNumRegs());
+ for (unsigned I = 1, E = getNumRegs(); I < E; ++I) {
+ MCRegister Reg(I);
+ if (lookupSpecialRegByName(getName(Reg)))
+ SpecialRegs.set(I);
+ }
+}
+
+BitVector PISARegisterInfo::getReservedRegs(const MachineFunction &MF) const {
+ // Reserve DummyReg (used as a sentinel in machine instructions) and all
+ // special registers (hardware-defined values like %localid, %groupid, etc.
+ // that appear in machine instructions without explicit definitions).
+ //
+ // General-purpose physical registers are left unreserved so that
+ // RegisterClassInfo::getNumAllocatableRegs returns non-zero values for
+ // every register class. This is required because the register coalescer
+ // refuses to coalesce into register classes with zero allocatable
+ // registers. PISA works entirely with virtual registers and disables
+ // physical register allocation (createTargetRegisterAllocator returns
+ // nullptr), so unreserved physical registers are never actually allocated.
+ BitVector Reserved(getNumRegs());
+ Reserved.set(PISA::DummyReg);
+ Reserved |= SpecialRegs;
+ return Reserved;
+}
const MCPhysReg *
PISARegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
- static const MCPhysReg CalleeSavedRegs[] = {0};
- return CalleeSavedRegs;
+ static const MCPhysReg CalleeSavedReg = {0};
+ return &CalleeSavedReg;
}
-BitVector PISARegisterInfo::getReservedRegs(const MachineFunction &MF) const {
- return BitVector(getNumRegs());
+unsigned PISARegisterInfo::getScalarBitSize(const TargetRegisterClass *RC,
+ unsigned NumElts) const {
+ unsigned RegSizeInBits = getRegSizeInBits(*RC);
+ assert((RegSizeInBits % NumElts == 0) && "not divisible?");
+ return RegSizeInBits / NumElts;
+}
+
+unsigned PISARegisterInfo::getSubRegIdx(unsigned Size, unsigned Idx) const {
+ assert(Idx < 4 && "Only 4 sub-registers supported!");
+
+ static unsigned Subregs[4][4] = {
+ {PISA::sub8_0, PISA::sub8_1, PISA::sub8_2, PISA::sub8_3},
+ {PISA::sub16_0, PISA::sub16_1, PISA::sub16_2, PISA::sub16_3},
+ {PISA::sub32_0, PISA::sub32_1, PISA::sub32_2, PISA::sub32_3},
+ {PISA::sub64_0, PISA::sub64_1, PISA::sub64_2, PISA::sub64_3}};
+
+ switch (Size) {
+ case 8:
+ return Subregs[0][Idx];
+ case 16:
+ return Subregs[1][Idx];
+ case 32:
+ return Subregs[2][Idx];
+ case 64:
+ return Subregs[3][Idx];
+ default:
+ assert(0 && "unknown type size!");
+ break;
+ }
+ return 0;
+}
+
+unsigned PISARegisterInfo::getCompositeSubRegIdx(unsigned Size, unsigned Base,
+ unsigned Count) const {
+ // Only nameable 2-element pairs: .xy (base 0) and .zw (base 2).
+ if (Count != 2 || (Base != 0 && Base != 2))
+ return 0;
+ bool Low = (Base == 0);
+ switch (Size) {
+ case 8:
+ return Low ? PISA::sub8_xy : PISA::sub8_zw;
+ case 16:
+ return Low ? PISA::sub16_xy : PISA::sub16_zw;
+ case 32:
+ return Low ? PISA::sub32_xy : PISA::sub32_zw;
+ case 64:
+ return Low ? PISA::sub64_xy : PISA::sub64_zw;
+ default:
+ return 0;
+ }
+}
+
+PISA::Swizzle PISARegisterInfo::getSwizzle(unsigned SubReg) const {
+ auto Swizzle = SwizzleMap.find(SubReg);
+ assert((Swizzle != SwizzleMap.end()) && "invalid swizzle!");
+ return Swizzle->second.Swizzle;
+}
+
+const char *PISARegisterInfo::getSwizzleName(unsigned SubReg) const {
+ auto Swizzle = SwizzleMap.find(SubReg);
+ assert((Swizzle != SwizzleMap.end()) && "invalid swizzle!");
+ return Swizzle->second.SwizzleName;
+}
+
+bool PISARegisterInfo::isSelectorSwizzle(PISA::Swizzle Swizzle) {
+ switch (Swizzle) {
+ case PISA::Swizzle::X:
+ case PISA::Swizzle::Y:
+ case PISA::Swizzle::Z:
+ case PISA::Swizzle::W:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const TargetRegisterClass *PISARegisterInfo::getRegClassFromLLT(LLT Ty) const {
+ if (Ty.isScalar() || Ty.isPointer()) {
+ switch (Ty.getSizeInBits()) {
+ case 1:
+ return &PISA::PredRegClass;
+ case 8:
+ return &PISA::Reg8bRegClass;
+ case 16:
+ return &PISA::Reg16bRegClass;
+ case 32:
+ return &PISA::Reg32bRegClass;
+ case 64:
+ return &PISA::Reg64bRegClass;
+ case 128:
+ return &PISA::Reg128bRegClass;
+ default:
+ break;
+ }
+ } else if (Ty.isVector()) {
+ unsigned NumElts = Ty.getNumElements();
+ unsigned BitSize = Ty.getScalarSizeInBits();
+ return getVectorRegClass(NumElts, BitSize);
+ }
+
+ llvm_unreachable("unhandled LLT!");
+}
+
+unsigned
+PISARegisterInfo::getNumEltsFromRegClass(const TargetRegisterClass *RC) const {
+ auto I = RegClassMap.find(RC);
+ assert(I != RegClassMap.end());
+ auto &RCD = I->second;
+ return RCD->NumElements;
+}
+
+unsigned
+PISARegisterInfo::getBitSizeFromRegClass(const TargetRegisterClass *RC) const {
+ auto I = RegClassMap.find(RC);
+ assert(I != RegClassMap.end());
+ auto &RCD = I->second;
+ return RCD->ScalarBitSize;
+}
+
+const TargetRegisterClass *
+PISARegisterInfo::getVectorRegClass(unsigned NumElts, unsigned BitSize) const {
+ auto P = std::make_pair(NumElts, BitSize);
+ auto I = VecRegClassMap.find(P);
+ assert(I != VecRegClassMap.end());
+ return I->second;
+}
+
+bool PISARegisterInfo::isSpecialReg(Register Reg) const {
+ return Reg.isPhysical() && SpecialRegs.test(Reg.asMCReg());
+}
+
+const TargetRegisterClass *
+PISARegisterInfo::getMatchingSuperRegClass(const TargetRegisterClass *A,
+ const TargetRegisterClass *B,
+ unsigned SubIdx) const {
+ switch (SubIdx) {
+ default:
+ return PISAGenRegisterInfo::getMatchingSuperRegClass(A, B, SubIdx);
+ // generic code (above) is unable to find 'hi' subreg
+ // index within 2-element vector ('.zw' within v2b?).
+ case PISA::sub8_zw:
+ case PISA::sub16_zw:
+ case PISA::sub32_zw:
+ case PISA::sub64_zw:
+ return PISAGenRegisterInfo::getSubClassWithSubReg(A, SubIdx);
+ }
}
diff --git a/llvm/lib/Target/PISA/PISARegisterInfo.h b/llvm/lib/Target/PISA/PISARegisterInfo.h
index f10780d825eda..b49ba8a98de02 100644
--- a/llvm/lib/Target/PISA/PISARegisterInfo.h
+++ b/llvm/lib/Target/PISA/PISARegisterInfo.h
@@ -9,6 +9,7 @@
#ifndef LLVM_LIB_TARGET_PISA_PISAREGISTERINFO_H
#define LLVM_LIB_TARGET_PISA_PISAREGISTERINFO_H
+#include "PISADefines.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
@@ -30,6 +31,60 @@ class PISARegisterInfo : public PISAGenRegisterInfo {
Register getFrameRegister(const MachineFunction &MF) const override {
return 0;
}
+ const TargetRegisterClass *getRegClassFromLLT(LLT Ty) const;
+ unsigned getNumEltsFromRegClass(const TargetRegisterClass *RC) const;
+ unsigned getBitSizeFromRegClass(const TargetRegisterClass *RC) const;
+ const TargetRegisterClass *getVectorRegClass(unsigned NumElts,
+ unsigned BitSize) const;
+ unsigned getSubRegIdx(unsigned Size, unsigned Elt) const;
+
+ // Return the composite sub-register index (.xy / .zw) covering `Count`
+ // consecutive elements of `Size` bits starting at element `Base`, or 0 if
+ // there is no nameable composite for that slice. Only 2-element pairs at
+ // base 0 (.xy) or base 2 (.zw) are supported.
+ unsigned getCompositeSubRegIdx(unsigned Size, unsigned Base,
+ unsigned Count) const;
+
+ PISA::Swizzle getSwizzle(unsigned SubReg) const;
+ const char *getSwizzleName(unsigned SubReg) const;
+ static bool isSelectorSwizzle(PISA::Swizzle Swizzle);
+
+ bool isSpecialReg(Register Reg) const;
+
+ bool shouldCoalesce(MachineInstr *MI, const TargetRegisterClass *SrcRC,
+ unsigned SubReg, const TargetRegisterClass *DstRC,
+ unsigned DstSubReg, const TargetRegisterClass *NewRC,
+ LiveIntervals &LIS) const override;
+
+ const TargetRegisterClass *
+ getMatchingSuperRegClass(const TargetRegisterClass *A,
+ const TargetRegisterClass *B,
+ unsigned SubIdx) const override;
+
+private:
+ unsigned getScalarBitSize(const TargetRegisterClass *RC,
+ unsigned NumElts) const;
+
+ struct RegClassDescription {
+ unsigned NumElements;
+ unsigned ScalarBitSize;
+ };
+
+ // Maps TargetRegisterClass -> RegClassDescription
+ DenseMap<const TargetRegisterClass *, std::unique_ptr<RegClassDescription>>
+ RegClassMap;
+ // Maps <NumElts, BitSize> -> TargetRegisterClass (vector reg class)
+ DenseMap<std::pair<unsigned, unsigned>, const TargetRegisterClass *>
+ VecRegClassMap;
+
+ // map SubReg info to Swizzle
+ struct SwizzleDesc {
+ PISA::Swizzle Swizzle;
+ const char *SwizzleName;
+ };
+ static DenseMap<unsigned, SwizzleDesc> SwizzleMap;
+
+ BitVector SpecialRegs;
};
} // namespace llvm
diff --git a/llvm/lib/Target/PISA/PISARegisterInfo.td b/llvm/lib/Target/PISA/PISARegisterInfo.td
index b2c06a57c27a2..ae407b269a2b3 100644
--- a/llvm/lib/Target/PISA/PISARegisterInfo.td
+++ b/llvm/lib/Target/PISA/PISARegisterInfo.td
@@ -1,4 +1,4 @@
-//===-- PISARegisterInfo.td - PISA Register defs ----------*- tablegen -*-===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -8,32 +8,262 @@
let Namespace = "PISA" in {
+ class SpecialRegister <string name> : Register<name> {
+ string Name = NAME;
+ }
+
class PISARegisterClass<list<ValueType> regTypes, int alignment, dag regList>
- : RegisterClass<"PISA", regTypes, alignment, regList> {}
+ : RegisterClass<"PISA", regTypes, alignment, regList> {}
- // .reg style registers.
- class GeneralRegisterClass<list<ValueType> regTypes, int alignment,
- dag regList>
- : PISARegisterClass<regTypes, alignment, regList> {}
+ // .reg style registers
+ class GeneralRegisterClass<list<ValueType> regTypes, int alignment, dag regList> :
+ PISARegisterClass<regTypes, alignment, regList> {
+ // Keep flags in sync with RegType in RegEncoder
+ let TSFlags{1-0} = 1;
+ }
- // .pred style registers.
- class PredRegisterClass<list<ValueType> regTypes, int alignment, dag regList>
- : PISARegisterClass<regTypes, alignment, regList> {}
+ // .pred style registers
+ class PredRegisterClass<list<ValueType> regTypes, int alignment, dag regList> :
+ PISARegisterClass<regTypes, alignment, regList> {
+ // Keep flags in sync with RegType in RegEncoder
+ let TSFlags{1-0} = 2;
+ }
def DummyReg : Register<"%null">;
- foreach Index = 0-63 in {
- def Reg32b_#Index : Register<"">;
+ multiclass VectorSpecialRegSubRegs<int n, string name> {
+ def X : SpecialRegister<name#".x">;
+ if !ge(n, 2) then {
+ def Y : SpecialRegister<name#".y">;
+ }
+ if !ge(n, 3) then {
+ def Z : SpecialRegister<name#".z">;
+ }
+ if !ge(n, 4) then {
+ def W : SpecialRegister<name#".w">;
+ }
+ }
+
+ // Registers to describe Vector-subregister relationships
+ class VecReg<list<Register> subregs> : RegisterWithSubRegs<"", subregs>;
+
+ foreach Index = 0-3 in { // .x, .y, .z, .w
+ def sub8_#Index : SubRegIndex<8, !shl(Index, 3)>;
+ def sub16_#Index : SubRegIndex<16, !shl(Index, 4)>;
+ def sub32_#Index : SubRegIndex<32, !shl(Index, 5)>;
+ def sub64_#Index : SubRegIndex<64, !shl(Index, 6)>;
+ }
+ foreach Index = 0-1 in { // .xy, .zw
+ defvar Part = !if(!eq(Index, 0), "xy", "zw");
+ def sub8_#Part : SubRegIndex<!shl(8, 1), !shl(Index, 4)>;
+ def sub16_#Part : SubRegIndex<!shl(16, 1), !shl(Index, 5)>;
+ def sub32_#Part : SubRegIndex<!shl(32, 1), !shl(Index, 6)>;
+ def sub64_#Part : SubRegIndex<!shl(64, 1), !shl(Index, 7)>;
+ }
+ foreach Index = 4-31 in { // large vector support
+ def sub32_#Index : SubRegIndex<32, !shl(Index, 5)>;
+ }
+
+ // Special registers
+ defset list<SpecialRegister> LocalIdSubRegs = {
+ defm SpecialReg_LocalId : VectorSpecialRegSubRegs<3, "%localid">;
+ }
+ defset list<SpecialRegister> LocalSizeSubRegs = {
+ defm SpecialReg_LocalSize : VectorSpecialRegSubRegs<3, "%localsize">;
+ }
+ defset list<SpecialRegister> EnqueuedLocalSizeSubRegs = {
+ defm SpecialReg_EnqueuedLocalSize : VectorSpecialRegSubRegs<3, "%enqueuedlocalsize">;
+ }
+ defset list<SpecialRegister> GlobalOffsetSubRegs = {
+ defm SpecialReg_GlobalOffset : VectorSpecialRegSubRegs<3, "%globaloffset">;
}
+ defset list<SpecialRegister> GlobalSizeSubRegs = {
+ defm SpecialReg_GlobalSize : VectorSpecialRegSubRegs<3, "%globalsize">;
+ }
+ defset list<SpecialRegister> GroupIdSubRegs = {
+ defm SpecialReg_GroupId : VectorSpecialRegSubRegs<3, "%groupid">;
+ }
+ defset list<SpecialRegister> GroupCountSubRegs = {
+ defm SpecialReg_GroupCount : VectorSpecialRegSubRegs<3, "%groupcount">;
+ }
+
+ def SpecialReg_ActiveMask : SpecialRegister<"%activemask">;
+ def SpecialReg_LaneId : SpecialRegister<"%laneid">;
+ def SpecialReg_SubgroupSize : SpecialRegister<"%subgroupsize">;
+ def SpecialReg_WorkDim : SpecialRegister<"%workdim">;
+
+
+
+ //
+ // Scalar registers
+ //
foreach Index = 0-3 in {
+ def Reg8b_#Index : Register<"">;
+ def Reg16b_#Index : Register<"">;
+ def Reg32b_#Index : Register<"">;
def Reg64b_#Index : Register<"">;
+ def Reg128b_#Index : Register<"">;
+ }
+ foreach Index = 4-63 in {
+ def Reg32b_#Index : Register<"">;
+ }
+
+ // Predicate physical registers (used only so the Pred register class has
+ // non-zero allocatable registers, PISA never allocates physical registers).
+ foreach Index = 0-3 in {
def Pred_#Index : Register<"">;
}
- def Pred : PredRegisterClass<[i1], 8,
- (add DummyReg, (sequence "Pred_%u", 0, 3))>;
+ def Pred : PredRegisterClass<[i1], 8, (add DummyReg, (sequence "Pred_%u", 0, 3))>;
+ def Reg8b : GeneralRegisterClass<[i8], 8, (add (sequence "Reg8b_%u", 0, 3))>;
+ def Reg16b : GeneralRegisterClass<[i16, f16, bf16], 16, (add (sequence "Reg16b_%u", 0, 3))>;
def Reg32b : GeneralRegisterClass<[i32, f32], 32,
- (add DummyReg, (sequence "Reg32b_%u", 0, 63))>;
+ (add GroupIdSubRegs, GroupCountSubRegs, SpecialReg_SubgroupSize,
+ LocalIdSubRegs, LocalSizeSubRegs, SpecialReg_ActiveMask,
+ EnqueuedLocalSizeSubRegs, SpecialReg_LaneId, SpecialReg_WorkDim,
+ DummyReg,
+ (sequence "Reg32b_%u", 0, 63))>;
def Reg64b : GeneralRegisterClass<[i64, f64], 64,
- (add (sequence "Reg64b_%u", 0, 3))>;
+ (add
+ GlobalOffsetSubRegs, GlobalSizeSubRegs,
+ (sequence "Reg64b_%u", 0, 3))>;
+ def Reg128b : GeneralRegisterClass<[i128], 128, (add (sequence "Reg128b_%u", 0, 3))>;
+
+ //
+ // Vector registers
+ //
+ // 8b subregisters
+ def Reg8bx2 : VecReg<[Reg8b_0, Reg8b_1]> {
+ let SubRegIndices = [sub8_0, sub8_1];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg8bx3 : VecReg<[Reg8b_0, Reg8b_1, Reg8b_2]> {
+ let SubRegIndices = [sub8_0, sub8_1, sub8_2];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg8bx2H : VecReg<[Reg8b_2, Reg8b_3]> {
+ let SubRegIndices = [sub8_2, sub8_3];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg8bx4 : VecReg<[Reg8bx2, Reg8bx2H]> {
+ let SubRegIndices = [sub8_xy, sub8_zw];
+ let CoveredBySubRegs = 1;
+ }
+
+ // 8b vectors
+ def RegV2_8b : GeneralRegisterClass<[v2i8], 8, (add Reg8bx2)>;
+ def RegV3_8b : GeneralRegisterClass<[v3i8], 8, (add Reg8bx3)>;
+ def RegV4_8b : GeneralRegisterClass<[v4i8], 8, (add Reg8bx4)>;
+ let isAllocatable = 0 in {
+ def RegV2_8bH : GeneralRegisterClass<[v2i8], 8, (add Reg8bx2H)>;
+ }
+
+ // 16b subregisters
+ def Reg16bx2 : VecReg<[Reg16b_0, Reg16b_1]> {
+ let SubRegIndices = [sub16_0, sub16_1];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg16bx3 : VecReg<[Reg16b_0, Reg16b_1, Reg16b_2]> {
+ let SubRegIndices = [sub16_0, sub16_1, sub16_2];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg16bx2H : VecReg<[Reg16b_2, Reg16b_3]> {
+ let SubRegIndices = [sub16_2, sub16_3];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg16bx4 : VecReg<[Reg16bx2, Reg16bx2H]> {
+ let SubRegIndices = [sub16_xy, sub16_zw];
+ let CoveredBySubRegs = 1;
+ }
+
+ // 16b vectors
+ def RegV2_16b : GeneralRegisterClass<[v2i16, v2f16, v2bf16], 16, (add Reg16bx2)>;
+ def RegV3_16b : GeneralRegisterClass<[v3i16, v3f16], 16, (add Reg16bx3)>;
+ def RegV4_16b : GeneralRegisterClass<[v4i16, v4f16], 16, (add Reg16bx4)>;
+ let isAllocatable = 0 in {
+ def RegV2_16bH : GeneralRegisterClass<[v2i16, v2f16, v2bf16], 16, (add Reg16bx2H)>;
+ }
+
+ // 32b subregisters
+ def Reg32bx2 : VecReg<[Reg32b_0, Reg32b_1]> {
+ let SubRegIndices = [sub32_0, sub32_1];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg32bx3 : VecReg<[Reg32b_0, Reg32b_1, Reg32b_2]> {
+ let SubRegIndices = [sub32_0, sub32_1, sub32_2];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg32bx2H : VecReg<[Reg32b_2, Reg32b_3]> {
+ let SubRegIndices = [sub32_2, sub32_3];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg32bx4 : VecReg<[Reg32bx2, Reg32bx2H]> {
+ let SubRegIndices = [sub32_xy, sub32_zw];
+ let CoveredBySubRegs = 1;
+ }
+
+ // 32b vectors
+ def RegV2_32b : GeneralRegisterClass<[v2i32, v2f32], 32, (add Reg32bx2)>;
+ def RegV3_32b : GeneralRegisterClass<[v3i32, v3f32], 32, (add Reg32bx3)>;
+ def RegV4_32b : GeneralRegisterClass<[v4i32, v4f32], 32, (add Reg32bx4)>;
+ let isAllocatable = 0 in {
+ def RegV2_32bH : GeneralRegisterClass<[v2i32, v2f32], 32, (add Reg32bx2H)>;
+ }
+
+ // large vector support
+ foreach length = {5-8,16,32} in {
+ // 32b vector registers
+ // Define one VecReg for each 32b vector length
+ def "Reg32bx"#length : VecReg<!foreach(i, !range(length), !cast<Register>("Reg32b_"#i))> {
+ let SubRegIndices = !foreach(i, !range(length), !cast<SubRegIndex>("sub32_"#i));
+ let CoveredBySubRegs = 1;
+ }
+ // 32b vector register classes
+ // Define one GeneralRegisterClass for each 32b vector length
+ def "RegV"#length#"_32b" : GeneralRegisterClass<[!cast<ValueType>("v"#length#"i32"), !cast<ValueType>("v"#length#"f32")], 32, (add !cast<VecReg>("Reg32bx"#length))>;
+ }
+
+ // v64 register: defined without per-element sub-register indices because
+ // LLVM's LaneBitmask (64 bits) cannot represent 64 individual sub-register
+ // lanes plus the existing sub-register indices. All element access uses
+ // extract/insert instructions rather than sub-register operations.
+ def Reg32bx64 : Register<"">;
+ def RegV64_32b : GeneralRegisterClass<[v64i32, v64f32], 32, (add Reg32bx64)>;
+
+ // 64b subregisters
+ def Reg64bx2 : VecReg<[Reg64b_0, Reg64b_1]> {
+ let SubRegIndices = [sub64_0, sub64_1];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg64bx3 : VecReg<[Reg64b_0, Reg64b_1, Reg64b_2]> {
+ let SubRegIndices = [sub64_0, sub64_1, sub64_2];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg64bx2H : VecReg<[Reg64b_2, Reg64b_3]> {
+ let SubRegIndices = [sub64_2, sub64_3];
+ let CoveredBySubRegs = 1;
+ }
+ def Reg64bx4 : VecReg<[Reg64bx2, Reg64bx2H]> {
+ let SubRegIndices = [sub64_xy, sub64_zw];
+ let CoveredBySubRegs = 1;
+ }
+
+ // 64b vectors
+ def RegV2_64b : GeneralRegisterClass<[v2i64, v2f64], 64, (add Reg64bx2)>;
+ def RegV3_64b : GeneralRegisterClass<[v3i64, v3f64], 64, (add Reg64bx3)>;
+ def RegV4_64b : GeneralRegisterClass<[v4i64, v4f64], 64, (add Reg64bx4)>;
+ let isAllocatable = 0 in {
+ def RegV2_64bH : GeneralRegisterClass<[v2i64, v2f64], 64, (add Reg64bx2H)>;
+ }
+}
+
+def SpecialRegNames : GenericTable {
+ let FilterClass = "SpecialRegister";
+ let CppTypeName = "SpecialRegEntry";
+ let Fields = ["Name"];
+}
+
+def lookupSpecialRegByName : SearchIndex {
+ let Table = SpecialRegNames;
+ let Key = ["Name"];
}
diff --git a/llvm/lib/Target/PISA/PISAUtils.cpp b/llvm/lib/Target/PISA/PISAUtils.cpp
new file mode 100644
index 0000000000000..8ecd2c0065ecb
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAUtils.cpp
@@ -0,0 +1,52 @@
+//===-- PISAUtils.cpp ---- PISA Utility Functions -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PISAUtils.h"
+#include "MCTargetDesc/PISABaseInfo.h"
+#include "PISA.h"
+#include "PISAInstrInfo.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/Demangle/Demangle.h"
+#include "llvm/IR/IntrinsicsPISA.h"
+
+namespace llvm::PISA {
+std::tuple<MachineInstr *, unsigned>
+getDefIgnoringBitcasts(Register Reg, const MachineRegisterInfo &MRI,
+ bool NoVectors) {
+ auto *DefMI = MRI.getVRegDef(Reg);
+ unsigned Opc = DefMI->getOpcode();
+ Register SrcReg = 0;
+ while (Opc == TargetOpcode::G_BITCAST ||
+ isPreISelGenericOptimizationHint(Opc)) {
+ SrcReg = DefMI->getOperand(1).getReg();
+ auto SrcTy = MRI.getType(SrcReg);
+ if (!SrcTy.isValid())
+ break;
+ auto DstTy = MRI.getType(DefMI->getOperand(0).getReg());
+ if (NoVectors && (DstTy.isVector() || SrcTy.isVector()))
+ break;
+ DefMI = MRI.getVRegDef(SrcReg);
+ Opc = DefMI->getOpcode();
+ }
+ unsigned RegIdx = 0;
+ if (SrcReg != 0) {
+ for (unsigned I = 0; I < DefMI->getNumOperands(); I++) {
+ if (DefMI->getOperand(I).isReg() &&
+ DefMI->getOperand(I).getReg() == SrcReg) {
+ RegIdx = I;
+ break;
+ }
+ }
+ }
+ return std::make_tuple(DefMI, RegIdx);
+}
+
+} // namespace llvm::PISA
diff --git a/llvm/lib/Target/PISA/PISAUtils.h b/llvm/lib/Target/PISA/PISAUtils.h
new file mode 100644
index 0000000000000..7975037a75337
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAUtils.h
@@ -0,0 +1,36 @@
+//===-- PISAUtils.h ---- PISA Utility Functions ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_PISAUTILS_H
+#define LLVM_LIB_TARGET_PISA_PISAUTILS_H
+
+#include "MCTargetDesc/PISABaseInfo.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/Register.h"
+#include "llvm/IR/IRBuilder.h"
+#include <string>
+
+namespace llvm {
+class MachineInstr;
+class MCInst;
+class MachineInstrBuilder;
+class StringRef;
+
+namespace PISA {
+// Similar to getDefIgnoringCopies, but skips any bitcast instructions.
+// Return defining instruction (or nil) and index of the (bitcasted) Reg
+// within defining instruction.
+//
+// - when NoVectors is true, stop search if G_BITCAST src/dst is a vector type
+std::tuple<MachineInstr *, unsigned>
+getDefIgnoringBitcasts(Register Reg, const MachineRegisterInfo &MRI,
+ bool NoVectors = false);
+
+} // namespace PISA
+} // namespace llvm
+#endif // LLVM_LIB_TARGET_PISA_PISAUTILS_H
>From 7fe085861f61e0267efadc6b365f5f03614c4c5c Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 4 Aug 2026 17:47:13 -0700
Subject: [PATCH 2/2] Remove unnecessary edits
---
llvm/lib/Target/PISA/CMakeLists.txt | 1 +
llvm/lib/Target/PISA/MCTargetDesc/CMakeLists.txt | 1 -
llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.h | 2 +-
llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.cpp | 2 +-
llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.h | 2 +-
llvm/lib/Target/PISA/PISA.td | 8 ++++----
llvm/lib/Target/PISA/PISAInstrInfo.h | 1 +
llvm/lib/Target/PISA/PISAInstrInfo.td | 2 +-
llvm/lib/Target/PISA/PISARegisterInfo.td | 6 +++---
9 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/Target/PISA/CMakeLists.txt b/llvm/lib/Target/PISA/CMakeLists.txt
index 5c3bfe6ec02bf..321a75d7f88b6 100644
--- a/llvm/lib/Target/PISA/CMakeLists.txt
+++ b/llvm/lib/Target/PISA/CMakeLists.txt
@@ -1,6 +1,7 @@
add_llvm_component_group(PISA)
set(LLVM_TARGET_DEFINITIONS PISA.td)
+
tablegen(LLVM PISAGenAsmWriter.inc -gen-asm-writer)
tablegen(LLVM PISAGenInstrInfo.inc -gen-instr-info)
tablegen(LLVM PISAGenMCCodeEmitter.inc -gen-emitter)
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/PISA/MCTargetDesc/CMakeLists.txt
index 80a21ad6c6aa2..45ea6b7a1d9d9 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/CMakeLists.txt
+++ b/llvm/lib/Target/PISA/MCTargetDesc/CMakeLists.txt
@@ -18,4 +18,3 @@ add_llvm_component_library(LLVMPISADesc
ADD_TO_COMPONENT
PISA
)
-
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.h b/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.h
index 9094807ab82ca..cd96106880fa1 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.h
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAInstPrinter.h
@@ -15,8 +15,8 @@
#include "llvm/MC/MCInstPrinter.h"
namespace llvm {
-class PISAInstPrinter : public MCInstPrinter {
+class PISAInstPrinter : public MCInstPrinter {
public:
using MCInstPrinter::MCInstPrinter;
typedef std::function<void(const MCInst *, unsigned, raw_ostream &)>
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.cpp b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.cpp
index 3ffd668f3d05a..06cfc4c3748a9 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.cpp
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.cpp
@@ -46,7 +46,7 @@ static MCRegisterInfo *createPISAMCRegisterInfo(const Triple &TT) {
static MCSubtargetInfo *createPISAMCSubtargetInfo(const Triple &TT,
StringRef CPU, StringRef FS) {
- return createPISAMCSubtargetInfoImpl(TT, CPU, /*TuneCPU*/ CPU, FS);
+ return createPISAMCSubtargetInfoImpl(TT, CPU, /*TuneCPU=*/CPU, FS);
}
static MCInstPrinter *createPISAMCInstPrinter(const Triple &T,
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.h b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.h
index 625d0f16077cf..1d1c4a6e0bde1 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.h
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISAMCTargetDesc.h
@@ -44,7 +44,7 @@ MCTargetStreamer *createPISANullTargetStreamer(MCStreamer &S);
} // namespace llvm
-// Defines symbolic names for PISA registers. This defines a mapping from
+// Defines symbolic names for PISA registers. This defines a mapping from
// register name to register number.
#define GET_REGINFO_ENUM
#include "PISAGenRegisterInfo.inc"
diff --git a/llvm/lib/Target/PISA/PISA.td b/llvm/lib/Target/PISA/PISA.td
index 0a88485ca6420..011b1cc101faa 100644
--- a/llvm/lib/Target/PISA/PISA.td
+++ b/llvm/lib/Target/PISA/PISA.td
@@ -23,20 +23,20 @@ include "PISAInstrInfo.td"
include "PISACombine.td"
defm : RemapAllTargetPseudoPointerOperands<Reg64b>;
+
def PISAInstrInfo : InstrInfo;
class Proc<string Name, list<SubtargetFeature> Features>
: Processor<Name, NoItineraries, Features>;
-// valid -mcpu values
-def : Proc<"100", [Feature100]>;
+// Valid -mcpu values.
+def : Proc<"100", [Feature100]>;
def PISAInstPrinter : AsmWriter {
- string AsmWriterClassName = "InstPrinter";
+ string AsmWriterClassName = "InstPrinter";
bit isMCAsmWriter = 1;
}
-
def PISA : Target {
let InstructionSet = PISAInstrInfo;
let AssemblyWriters = [PISAInstPrinter];
diff --git a/llvm/lib/Target/PISA/PISAInstrInfo.h b/llvm/lib/Target/PISA/PISAInstrInfo.h
index 4e73ee6e19977..34ec289f7a4c5 100644
--- a/llvm/lib/Target/PISA/PISAInstrInfo.h
+++ b/llvm/lib/Target/PISA/PISAInstrInfo.h
@@ -8,6 +8,7 @@
#ifndef LLVM_LIB_TARGET_PISA_PISAINSTRINFO_H
#define LLVM_LIB_TARGET_PISA_PISAINSTRINFO_H
+
#include "PISARegisterInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
diff --git a/llvm/lib/Target/PISA/PISAInstrInfo.td b/llvm/lib/Target/PISA/PISAInstrInfo.td
index 154e1d8c9c6ea..05a8bfe1263ff 100644
--- a/llvm/lib/Target/PISA/PISAInstrInfo.td
+++ b/llvm/lib/Target/PISA/PISAInstrInfo.td
@@ -1,4 +1,4 @@
-//===-- PISAInstrInfo.td - PISA Instruction defs -----------*- tablegen -*-===//
+//===-- PISAInstrInfo.td - PISA Instruction defs ----------*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/llvm/lib/Target/PISA/PISARegisterInfo.td b/llvm/lib/Target/PISA/PISARegisterInfo.td
index ae407b269a2b3..2d757baba2e00 100644
--- a/llvm/lib/Target/PISA/PISARegisterInfo.td
+++ b/llvm/lib/Target/PISA/PISARegisterInfo.td
@@ -1,4 +1,4 @@
-//===----------------------------------------------------------------------===//
+//===-- PISARegisterInfo.td - PISA Register defs ----------*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -13,9 +13,9 @@ let Namespace = "PISA" in {
}
class PISARegisterClass<list<ValueType> regTypes, int alignment, dag regList>
- : RegisterClass<"PISA", regTypes, alignment, regList> {}
+ : RegisterClass<"PISA", regTypes, alignment, regList> {}
- // .reg style registers
+ // .reg style registers.
class GeneralRegisterClass<list<ValueType> regTypes, int alignment, dag regList> :
PISARegisterClass<regTypes, alignment, regList> {
// Keep flags in sync with RegType in RegEncoder
More information about the llvm-branch-commits
mailing list