1//===- AMDGPUMIRFormatter.cpp ---------------------------------------------===//
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/// \file
10/// Implementation of AMDGPU overrides of MIRFormatter.
11//
12//===----------------------------------------------------------------------===//
13
14#include "AMDGPUMIRFormatter.h"
15#include "SIMachineFunctionInfo.h"
16#include "llvm/TargetParser/TargetParser.h"
17
18using namespace llvm;
19
20const char SWaitAluImmPrefix = '.';
21StringLiteral SWaitAluDelim = "_";
22
23StringLiteral VaVdstName = "VaVdst";
24StringLiteral VaSdstName = "VaSdst";
25StringLiteral VaSsrcName = "VaSsrc";
26StringLiteral HoldCntName = "HoldCnt";
27StringLiteral VmVsrcName = "VmVsrc";
28StringLiteral VaVccName = "VaVcc";
29StringLiteral SaSdstName = "SaSdst";
30
31StringLiteral AllOff = "AllOff";
32
33void AMDGPUMIRFormatter::printSWaitAluImm(uint64_t Imm, raw_ostream &OS) const {
34 bool NonePrinted = true;
35 ListSeparator Delim(SWaitAluDelim);
36 auto PrintFieldIfNotMax = [&](StringRef Descr, uint64_t Num, unsigned Max) {
37 if (Num != Max) {
38 OS << Delim << Descr << SWaitAluDelim << Num;
39 NonePrinted = false;
40 }
41 };
42 OS << SWaitAluImmPrefix;
43 PrintFieldIfNotMax(VaVdstName, AMDGPU::DepCtr::decodeFieldVaVdst(Encoded: Imm),
44 AMDGPU::DepCtr::getVaVdstBitMask());
45 PrintFieldIfNotMax(VaSdstName, AMDGPU::DepCtr::decodeFieldVaSdst(Encoded: Imm),
46 AMDGPU::DepCtr::getVaSdstBitMask());
47 PrintFieldIfNotMax(VaSsrcName, AMDGPU::DepCtr::decodeFieldVaSsrc(Encoded: Imm),
48 AMDGPU::DepCtr::getVaSsrcBitMask());
49 PrintFieldIfNotMax(
50 HoldCntName,
51 AMDGPU::DepCtr::decodeFieldHoldCnt(Encoded: Imm,
52 Version: AMDGPU::getIsaVersion(GPU: STI.getCPU())),
53 AMDGPU::DepCtr::getHoldCntBitMask(Version: AMDGPU::getIsaVersion(GPU: STI.getCPU())));
54 PrintFieldIfNotMax(VmVsrcName, AMDGPU::DepCtr::decodeFieldVmVsrc(Encoded: Imm),
55 AMDGPU::DepCtr::getVmVsrcBitMask());
56 PrintFieldIfNotMax(VaVccName, AMDGPU::DepCtr::decodeFieldVaVcc(Encoded: Imm),
57 AMDGPU::DepCtr::getVaVccBitMask());
58 PrintFieldIfNotMax(SaSdstName, AMDGPU::DepCtr::decodeFieldSaSdst(Encoded: Imm),
59 AMDGPU::DepCtr::getSaSdstBitMask());
60 if (NonePrinted)
61 OS << AllOff;
62}
63
64void AMDGPUMIRFormatter::printImm(raw_ostream &OS, const MachineInstr &MI,
65 std::optional<unsigned int> OpIdx, int64_t Imm) const {
66
67 switch (MI.getOpcode()) {
68 case AMDGPU::S_WAITCNT_DEPCTR:
69 printSWaitAluImm(Imm, OS);
70 break;
71 case AMDGPU::S_DELAY_ALU:
72 assert(OpIdx == 0);
73 printSDelayAluImm(Imm, OS);
74 break;
75 default:
76 MIRFormatter::printImm(OS, MI, OpIdx, Imm);
77 break;
78 }
79}
80
81/// Implement target specific parsing of immediate mnemonics. The mnemonic is
82/// a string with a leading dot.
83bool AMDGPUMIRFormatter::parseImmMnemonic(const unsigned OpCode,
84 const unsigned OpIdx,
85 StringRef Src, int64_t &Imm,
86 ErrorCallbackType ErrorCallback) const
87{
88
89 switch (OpCode) {
90 case AMDGPU::S_WAITCNT_DEPCTR:
91 return parseSWaitAluImmMnemonic(OpIdx, Imm, Src, ErrorCallback);
92 case AMDGPU::S_DELAY_ALU:
93 return parseSDelayAluImmMnemonic(OpIdx, Imm, Src, ErrorCallback);
94 default:
95 break;
96 }
97 return true; // Don't know what this is
98}
99
100void AMDGPUMIRFormatter::printSDelayAluImm(int64_t Imm,
101 llvm::raw_ostream &OS) const {
102 // Construct an immediate string to represent the information encoded in the
103 // s_delay_alu immediate.
104 // .id0_<dep>[_skip_<count>_id1<dep>]
105 constexpr int64_t None = 0;
106 constexpr int64_t Same = 0;
107
108 uint64_t Id0 = (Imm & 0xF);
109 uint64_t Skip = ((Imm >> 4) & 0x7);
110 uint64_t Id1 = ((Imm >> 7) & 0xF);
111 auto Outdep = [&](uint64_t Id) {
112 if (Id == None)
113 OS << "NONE";
114 else if (Id < 5)
115 OS << "VALU_DEP_" << Id;
116 else if (Id < 8)
117 OS << "TRANS32_DEP_" << Id - 4;
118 else
119 OS << "SALU_CYCLE_" << Id - 8;
120 };
121
122 OS << ".id0_";
123 Outdep(Id0);
124
125 // If the second inst is "same" and "none", no need to print the rest of the
126 // string.
127 if (Skip == Same && Id1 == None)
128 return;
129
130 // Encode the second delay specification.
131 OS << "_skip_";
132 if (Skip == 0)
133 OS << "SAME";
134 else if (Skip == 1)
135 OS << "NEXT";
136 else
137 OS << "SKIP_" << Skip - 1;
138
139 OS << "_id1_";
140 Outdep(Id1);
141}
142
143bool AMDGPUMIRFormatter::parseSWaitAluImmMnemonic(
144 const unsigned int OpIdx, int64_t &Imm, StringRef &Src,
145 MIRFormatter::ErrorCallbackType &ErrorCallback) const {
146 // TODO: For now accept integer masks for compatibility with old MIR.
147 if (!Src.consumeInteger(Radix: 10, Result&: Imm))
148 return false;
149
150 // Initialize with all checks off.
151 Imm = AMDGPU::DepCtr::getDefaultDepCtrEncoding(STI);
152 // The input is in the form: .Name1_Num1_Name2_Num2
153 // Drop the '.' prefix.
154 bool ConsumePrefix = Src.consume_front(Prefix: SWaitAluImmPrefix);
155 if (!ConsumePrefix)
156 return ErrorCallback(Src.begin(), "expected prefix");
157 if (Src.empty())
158 return ErrorCallback(Src.begin(), "expected <CounterName>_<CounterNum>");
159
160 // Special case for all off.
161 if (Src == AllOff)
162 return false;
163
164 // Parse a counter name, number pair in each iteration.
165 while (!Src.empty()) {
166 // Src: Name1_Num1_Name2_Num2
167 // ^
168 size_t DelimIdx = Src.find(Str: SWaitAluDelim);
169 if (DelimIdx == StringRef::npos)
170 return ErrorCallback(Src.begin(), "expected <CounterName>_<CounterNum>");
171 // Src: Name1_Num1_Name2_Num2
172 // ^^^^^
173 StringRef Name = Src.substr(Start: 0, N: DelimIdx);
174 // Save the position of the name for accurate error reporting.
175 StringRef::iterator NamePos = Src.begin();
176 [[maybe_unused]] bool ConsumeName = Src.consume_front(Prefix: Name);
177 assert(ConsumeName && "Expected name");
178 [[maybe_unused]] bool ConsumeDelim = Src.consume_front(Prefix: SWaitAluDelim);
179 assert(ConsumeDelim && "Expected delimiter");
180 // Src: Num1_Name2_Num2
181 // ^
182 DelimIdx = Src.find(Str: SWaitAluDelim);
183 // Src: Num1_Name2_Num2
184 // ^^^^
185 int64_t Num;
186 // Save the position of the number for accurate error reporting.
187 StringRef::iterator NumPos = Src.begin();
188 if (Src.consumeInteger(Radix: 10, Result&: Num) || Num < 0)
189 return ErrorCallback(NumPos,
190 "expected non-negative integer counter number");
191 unsigned Max;
192 if (Name == VaVdstName) {
193 Max = AMDGPU::DepCtr::getVaVdstBitMask();
194 Imm = AMDGPU::DepCtr::encodeFieldVaVdst(Encoded: Imm, VaVdst: Num);
195 } else if (Name == VmVsrcName) {
196 Max = AMDGPU::DepCtr::getVmVsrcBitMask();
197 Imm = AMDGPU::DepCtr::encodeFieldVmVsrc(Encoded: Imm, VmVsrc: Num);
198 } else if (Name == VaSdstName) {
199 Max = AMDGPU::DepCtr::getVaSdstBitMask();
200 Imm = AMDGPU::DepCtr::encodeFieldVaSdst(Encoded: Imm, VaSdst: Num);
201 } else if (Name == VaSsrcName) {
202 Max = AMDGPU::DepCtr::getVaSsrcBitMask();
203 Imm = AMDGPU::DepCtr::encodeFieldVaSsrc(Encoded: Imm, VaSsrc: Num);
204 } else if (Name == HoldCntName) {
205 const AMDGPU::IsaVersion &Version = AMDGPU::getIsaVersion(GPU: STI.getCPU());
206 Max = AMDGPU::DepCtr::getHoldCntBitMask(Version);
207 Imm = AMDGPU::DepCtr::encodeFieldHoldCnt(Encoded: Imm, HoldCnt: Num, Version);
208 } else if (Name == VaVccName) {
209 Max = AMDGPU::DepCtr::getVaVccBitMask();
210 Imm = AMDGPU::DepCtr::encodeFieldVaVcc(Encoded: Imm, VaVcc: Num);
211 } else if (Name == SaSdstName) {
212 Max = AMDGPU::DepCtr::getSaSdstBitMask();
213 Imm = AMDGPU::DepCtr::encodeFieldSaSdst(Encoded: Imm, SaSdst: Num);
214 } else {
215 return ErrorCallback(NamePos, "invalid counter name");
216 }
217 // Don't allow the values to reach their maximum value.
218 if (Num >= Max)
219 return ErrorCallback(NumPos, "counter value too large");
220 // Src: Name2_Num2
221 Src.consume_front(Prefix: SWaitAluDelim);
222 }
223 return false;
224}
225
226bool AMDGPUMIRFormatter::parseSDelayAluImmMnemonic(
227 const unsigned int OpIdx, int64_t &Imm, llvm::StringRef &Src,
228 llvm::MIRFormatter::ErrorCallbackType &ErrorCallback) const
229{
230 assert(OpIdx == 0);
231
232 Imm = 0;
233 bool Expected = Src.consume_front(Prefix: ".id0_");
234 if (!Expected)
235 return ErrorCallback(Src.begin(), "Expected .id0_");
236
237 auto ExpectInt = [&](StringRef &Src, int64_t Offset) -> int64_t {
238 int64_t Dep;
239 if (!Src.consumeInteger(Radix: 10, Result&: Dep))
240 return Dep + Offset;
241
242 return -1;
243 };
244
245 auto DecodeDelay = [&](StringRef &Src) -> int64_t {
246 if (Src.consume_front(Prefix: "NONE"))
247 return 0;
248 if (Src.consume_front(Prefix: "VALU_DEP_"))
249 return ExpectInt(Src, 0);
250 if (Src.consume_front(Prefix: "TRANS32_DEP_"))
251 return ExpectInt(Src, 4);
252 if (Src.consume_front(Prefix: "SALU_CYCLE_"))
253 return ExpectInt(Src, 8);
254
255 return -1;
256 };
257
258 int64_t Delay0 = DecodeDelay(Src);
259 int64_t Skip = 0;
260 int64_t Delay1 = 0;
261 if (Delay0 == -1)
262 return ErrorCallback(Src.begin(), "Could not decode delay0");
263
264
265 // Set the Imm so far, to that early return has the correct value.
266 Imm = Delay0;
267
268 // If that was the end of the string, the second instruction is "same" and
269 // "none"
270 if (Src.begin() == Src.end())
271 return false;
272
273 Expected = Src.consume_front(Prefix: "_skip_");
274 if (!Expected)
275 return ErrorCallback(Src.begin(), "Expected _skip_");
276
277
278 if (Src.consume_front(Prefix: "SAME")) {
279 Skip = 0;
280 } else if (Src.consume_front(Prefix: "NEXT")) {
281 Skip = 1;
282 } else if (Src.consume_front(Prefix: "SKIP_")) {
283 if (Src.consumeInteger(Radix: 10, Result&: Skip)) {
284 return ErrorCallback(Src.begin(), "Expected integer Skip value");
285 }
286 Skip += 1;
287 } else {
288 ErrorCallback(Src.begin(), "Unexpected Skip Value");
289 }
290
291 Expected = Src.consume_front(Prefix: "_id1_");
292 if (!Expected)
293 return ErrorCallback(Src.begin(), "Expected _id1_");
294
295 Delay1 = DecodeDelay(Src);
296 if (Delay1 == -1)
297 return ErrorCallback(Src.begin(), "Could not decode delay1");
298
299 Imm = Imm | (Skip << 4) | (Delay1 << 7);
300 return false;
301}
302
303bool AMDGPUMIRFormatter::parseCustomPseudoSourceValue(
304 StringRef Src, MachineFunction &MF, PerFunctionMIParsingState &PFS,
305 const PseudoSourceValue *&PSV, ErrorCallbackType ErrorCallback) const {
306 SIMachineFunctionInfo *MFI = MF.getInfo<SIMachineFunctionInfo>();
307 const AMDGPUTargetMachine &TM =
308 static_cast<const AMDGPUTargetMachine &>(MF.getTarget());
309 if (Src == "GWSResource") {
310 PSV = MFI->getGWSPSV(TM);
311 return false;
312 }
313 llvm_unreachable("unknown MIR custom pseudo source value");
314}
315