1//===-- VOPDInstructions.td - Vector Instruction Definitions --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9//===----------------------------------------------------------------------===//
10// Encodings
11//===----------------------------------------------------------------------===//
12
13class VOPDe<bits<4> opX, bits<5> opY> : Enc64 {
14  bits<9> src0X;
15  bits<8> vsrc1X;
16  bits<8> vdstX;
17  bits<9> src0Y;
18  bits<8> vsrc1Y;
19  bits<8> vdstY;
20
21  let Inst{8-0} = src0X;
22  let Inst{16-9} = vsrc1X;
23  let Inst{21-17} = opY;
24  let Inst{25-22} = opX;
25  let Inst{31-26} = 0x32; // encoding
26  let Inst{40-32} = src0Y;
27  let Inst{48-41} = vsrc1Y;
28  let Inst{55-49} = vdstY{7-1};
29  let Inst{63-56} = vdstX;
30}
31
32class VOPD_MADKe<bits<4> opX, bits<5> opY> : Enc96 {
33  bits<9> src0X;
34  bits<8> vsrc1X;
35  bits<8> vdstX;
36  bits<9> src0Y;
37  bits<8> vsrc1Y;
38  bits<8> vdstY;
39  bits<32> imm;
40
41  let Inst{8-0} = src0X;
42  let Inst{16-9} = vsrc1X;
43  let Inst{21-17} = opY;
44  let Inst{25-22} = opX;
45  let Inst{31-26} = 0x32; // encoding
46  let Inst{40-32} = src0Y;
47  let Inst{48-41} = vsrc1Y;
48  let Inst{55-49} = vdstY{7-1};
49  let Inst{63-56} = vdstX;
50  let Inst{95-64} = imm;
51}
52
53//===----------------------------------------------------------------------===//
54// VOPD classes
55//===----------------------------------------------------------------------===//
56
57
58class GFXGenD<GFXGen Gen, list<string> DXPseudos, list<string> DYPseudos,
59              Predicate subtargetPred = Gen.AssemblerPredicate> :
60    GFXGen<Gen.AssemblerPredicate, Gen.DecoderNamespace, Gen.Suffix,
61           Gen.Subtarget> {
62  list<string> VOPDXPseudos = DXPseudos;
63  list<string> VOPDYPseudos = DYPseudos;
64  Predicate SubtargetPredicate = subtargetPred;
65}
66
67class VOPD_Base<dag outs, dag ins, string asm, VOP_Pseudo VDX, VOP_Pseudo VDY,
68                VOPD_Component XasVC, VOPD_Component YasVC, GFXGenD Gen>
69    : VOPAnyCommon<outs, ins, asm, []>,
70      VOP<NAME>,
71      SIMCInstr<NAME, Gen.Subtarget> {
72  // Fields for table indexing
73  Instruction Opcode = !cast<Instruction>(NAME);
74  bits<5> OpX = XasVC.VOPDOp;
75  bits<5> OpY = YasVC.VOPDOp;
76  bits<4> SubTgt = Gen.Subtarget;
77
78  let VALU = 1;
79
80  let DecoderNamespace = Gen.DecoderNamespace;
81  let AssemblerPredicate = Gen.AssemblerPredicate;
82  let WaveSizePredicate = isWave32;
83  let isCodeGenOnly = 0;
84  let SubtargetPredicate = Gen.SubtargetPredicate;
85  let AsmMatchConverter  = "cvtVOPD";
86  let Size = 8;
87  let ReadsModeReg = !or(VDX.ReadsModeReg, VDY.ReadsModeReg);
88  let mayRaiseFPException = ReadsModeReg;
89
90  // V_DUAL_FMAC and V_DUAL_DOT2ACC_F32_F16 need a dummy src2 tied to dst for
91  // passes to track its uses. Its presence does not affect VOPD formation rules
92  // because the rules for src2 and dst are the same. src2X and src2Y should not
93  // be encoded.
94  bit hasSrc2AccX = !or(!eq(VDX.Mnemonic, "v_fmac_f32"), !eq(VDX.Mnemonic, "v_dot2c_f32_f16"));
95  bit hasSrc2AccY = !or(!eq(VDY.Mnemonic, "v_fmac_f32"), !eq(VDY.Mnemonic, "v_dot2c_f32_f16"));
96  string ConstraintsX = !if(hasSrc2AccX, "$src2X = $vdstX", "");
97  string ConstraintsY = !if(hasSrc2AccY, "$src2Y = $vdstY", "");
98  let Constraints =
99      ConstraintsX # !if(!and(hasSrc2AccX, hasSrc2AccY), ", ", "") # ConstraintsY;
100  string DisableEncodingX = !if(hasSrc2AccX, "$src2X", "");
101  string DisableEncodingY = !if(hasSrc2AccY, "$src2Y", "");
102  let DisableEncoding =
103      DisableEncodingX # !if(!and(hasSrc2AccX, hasSrc2AccY), ", ", "") # DisableEncodingY;
104
105  let Uses = RegListUnion<VDX.Uses, VDY.Uses>.ret;
106  let Defs = RegListUnion<VDX.Defs, VDY.Defs>.ret;
107  let SchedRW = !listconcat(VDX.SchedRW, VDY.SchedRW);
108}
109
110class VOPD<dag outs, dag ins, string asm, VOP_Pseudo VDX, VOP_Pseudo VDY,
111           VOPD_Component XasVC, VOPD_Component YasVC, GFXGenD Gen>
112    : VOPD_Base<outs, ins, asm, VDX, VDY, XasVC, YasVC, Gen>,
113      VOPDe<XasVC.VOPDOp{3-0}, YasVC.VOPDOp> {
114  let Inst{16-9} = !if (!eq(VDX.Mnemonic, "v_mov_b32"), 0x0, vsrc1X);
115  let Inst{48-41} = !if (!eq(VDY.Mnemonic, "v_mov_b32"), 0x0, vsrc1Y);
116}
117
118class VOPD_MADK<dag outs, dag ins, string asm, VOP_Pseudo VDX, VOP_Pseudo VDY,
119                VOPD_Component XasVC, VOPD_Component YasVC, GFXGenD Gen>
120    : VOPD_Base<outs, ins, asm, VDX, VDY, XasVC, YasVC, Gen>,
121      VOPD_MADKe<XasVC.VOPDOp{3-0}, YasVC.VOPDOp> {
122  let Inst{16-9} = !if (!eq(VDX.Mnemonic, "v_mov_b32"), 0x0, vsrc1X);
123  let Inst{48-41} = !if (!eq(VDY.Mnemonic, "v_mov_b32"), 0x0, vsrc1Y);
124  let Size = 12;
125  let FixedSize = 1;
126}
127
128// V_DUAL_DOT2ACC_F32_BF16 is a legal instruction, but V_DOT2ACC_F32_BF16 is
129// not. V_DUAL_DOT2C_F32_BF16 is a legal instruction on GFX12, but
130// V_DOT2C_F32_F16_e32 is not. Since we generate the DUAL form by converting
131// from the normal form we will never generate them.
132defvar VOPDPseudosCommon = [
133  "V_FMAC_F32_e32", "V_FMAAK_F32", "V_FMAMK_F32", "V_MUL_F32_e32",
134  "V_ADD_F32_e32", "V_SUB_F32_e32", "V_SUBREV_F32_e32", "V_MUL_LEGACY_F32_e32",
135  "V_MOV_B32_e32", "V_CNDMASK_B32_e32", "V_MAX_F32_e32", "V_MIN_F32_e32"
136];
137defvar VOPDPseudosGFX11 = ["V_DOT2C_F32_F16_e32"];
138defvar VOPDYOnlyPseudosCommon = ["V_ADD_U32_e32", "V_LSHLREV_B32_e32",
139                                 "V_AND_B32_e32"];
140
141defvar VOPDXPseudosGFX11 = !listconcat(VOPDPseudosCommon, VOPDPseudosGFX11);
142defvar VOPDXPseudosGFX12 = VOPDPseudosCommon;
143defvar VOPDYPseudosGFX11 = !listconcat(VOPDXPseudosGFX11, VOPDYOnlyPseudosCommon);
144defvar VOPDYPseudosGFX12 = !listconcat(VOPDXPseudosGFX12, VOPDYOnlyPseudosCommon);
145
146def GFX11GenD : GFXGenD<GFX11Gen, VOPDXPseudosGFX11, VOPDYPseudosGFX11>;
147def GFX12GenD : GFXGenD<GFX12Gen, VOPDXPseudosGFX12, VOPDYPseudosGFX12>;
148
149
150def VOPDDstYOperand : RegisterOperand<VGPR_32, "printRegularOperand"> {
151  let DecoderMethod = "decodeOperandVOPDDstY";
152}
153
154class getRenamed<string VOPDName, GFXGen Gen> {
155  string ret = !if(!eq(Gen.Subtarget, GFX12Gen.Subtarget),
156                   !if(!eq(VOPDName, "v_dual_max_f32"),
157                       "v_dual_max_num_f32",
158                       !if(!eq(VOPDName, "v_dual_min_f32"),
159                           "v_dual_min_num_f32",
160                           VOPDName)),
161                   VOPDName);
162}
163
164foreach Gen = [GFX11GenD, GFX12GenD] in {
165  foreach x = Gen.VOPDXPseudos in {
166    foreach y = Gen.VOPDYPseudos in {
167      defvar xInst = !cast<VOP_Pseudo>(x);
168      defvar yInst = !cast<VOP_Pseudo>(y);
169      defvar XasVC = !cast<VOPD_Component>(x);
170      defvar YasVC = !cast<VOPD_Component>(y);
171      defvar xAsmName = getRenamed<XasVC.VOPDName, Gen>.ret;
172      defvar yAsmName = getRenamed<YasVC.VOPDName, Gen>.ret;
173      defvar isMADK = !or(!eq(x, "V_FMAAK_F32"), !eq(x, "V_FMAMK_F32"),
174                          !eq(y, "V_FMAAK_F32"), !eq(y, "V_FMAMK_F32"));
175      // If X or Y is MADK (have a mandatory immediate), all src operands which
176      // may contain an optional literal must use the VSrc_*_Deferred operand
177      // type. Optional literal operands in MADK VOPD components always use this
178      // operand form. If Both X and Y are MADK, the mandatory literal of X
179      // additionally must use an alternate operand format which defers to the
180      // 'real' Y literal
181      defvar isOpXMADK = !or(!eq(x, "V_FMAAK_F32"), !eq(x, "V_FMAMK_F32"));
182      defvar isOpYMADK = !or(!eq(y, "V_FMAAK_F32"), !eq(y, "V_FMAMK_F32"));
183      defvar OpName = "V_DUAL_" # !substr(x,2) # "_X_" # !substr(y,2) # Gen.Suffix;
184      defvar outs = (outs VGPRSrc_32:$vdstX, VOPDDstYOperand:$vdstY);
185      if !or(isOpXMADK, isOpYMADK) then {
186        if !and(isOpXMADK, isOpYMADK) then {
187          defvar X_MADK_Pfl = !cast<VOP_MADK_Base>(xInst.Pfl);
188          defvar ins = !con(xInst.Pfl.InsVOPDXDeferred, yInst.Pfl.InsVOPDY);
189          defvar asm = xAsmName #" "# X_MADK_Pfl.AsmVOPDXDeferred #" :: "# yAsmName #" "# yInst.Pfl.AsmVOPDY;
190          def OpName : VOPD_MADK<outs, ins, asm, xInst, yInst, XasVC, YasVC, Gen>;
191        } else {
192          defvar asm = xAsmName #" "# xInst.Pfl.AsmVOPDX #" :: "# yAsmName #" "# yInst.Pfl.AsmVOPDY;
193          if isOpXMADK then {
194            assert !not(isOpYMADK), "Expected only OpX as MADK";
195            defvar ins = !con(xInst.Pfl.InsVOPDX, yInst.Pfl.InsVOPDYDeferred);
196            def OpName : VOPD_MADK<outs, ins, asm, xInst, yInst, XasVC, YasVC, Gen>;
197          } else {
198            assert !not(isOpXMADK), "Expected only OpY as MADK";
199            defvar ins = !con(xInst.Pfl.InsVOPDXDeferred, yInst.Pfl.InsVOPDY);
200            def OpName : VOPD_MADK<outs, ins, asm, xInst, yInst, XasVC, YasVC, Gen>;
201          }
202        }
203      } else {
204        defvar ins = !con(xInst.Pfl.InsVOPDX, yInst.Pfl.InsVOPDY);
205        defvar asm = xAsmName #" "# xInst.Pfl.AsmVOPDX #" :: "# yAsmName #" "# yInst.Pfl.AsmVOPDY;
206        def OpName : VOPD<outs, ins, asm, xInst, yInst, XasVC, YasVC, Gen>;
207      }
208    }
209  }
210}
211
212