1 /*========================== begin_copyright_notice ============================
2
3 Copyright (C) 2019-2021 Intel Corporation
4
5 SPDX-License-Identifier: MIT
6
7 ============================= end_copyright_notice ===========================*/
8
9 #include "iga_main.hpp"
10 #include "io.hpp"
11
12 // internal headers
13 #include "IR/Messages.hpp"
14 #include "Frontend/IRToString.hpp"
15 #include "ColoredIO.hpp"
16
17 #include <algorithm>
18 #include <fstream>
19 #include <iostream>
20 #include <iomanip>
21 #include <sstream>
22 #include <string>
23
24
emitSpan(std::ostream & os,char chr,int len)25 static void emitSpan(std::ostream &os, char chr, int len) {
26 for (int i = 0; i < len; i++) {
27 if (chr != '-')
28 emitYellowText(os, chr);
29 else
30 os << chr;
31 }
32 }
emitDataElem(std::ostream & os,int chIx,const iga::MessageInfo & mi)33 static void emitDataElem(
34 std::ostream &os,
35 int chIx,
36 const iga::MessageInfo &mi)
37 {
38 static const char *CHANNELS = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
39 int zeroPadding =
40 (mi.elemSizeBitsRegFile - mi.elemSizeBitsMemory) / 8;
41 if (mi.hasAttr(iga::MessageInfo::Attr::EXPAND_HIGH)) {
42 emitSpan(os, CHANNELS[chIx], mi.elemSizeBitsMemory / 8);
43 emitSpan(os, '-', zeroPadding);
44 } else {
45 emitSpan(os, '-', zeroPadding);
46 emitSpan(os, CHANNELS[chIx], mi.elemSizeBitsMemory / 8);
47 }
48 }
emitRegName(std::ostream & os,int grfNum)49 static void emitRegName(std::ostream &os, int grfNum) {
50 os << " reg[" << std::setw(2) << std::right << grfNum << "]";
51 }
52
formatSIMT(const Opts &,std::ostream & os,const iga::MessageInfo & mi,int grfSizeB)53 static void formatSIMT(
54 const Opts &,
55 std::ostream &os,
56 const iga::MessageInfo &mi,
57 int grfSizeB)
58 {
59 if (mi.elemSizeBitsRegFile == 0)
60 return;
61 int currGrf = 0;
62 for (int vecIx = 0; vecIx < mi.elemsPerAddr; vecIx++) {
63 int dataSize = mi.elemSizeBitsRegFile/8;
64 int bytesPerVecElem = mi.execWidth*dataSize;
65 int regsPerVecElem = std::max<int>(1, bytesPerVecElem/grfSizeB);
66 // 32B, float (4B), SIMD16 ==> 8
67 // 32B/4B == 8
68 int chsPerGrf = std::min<int>(grfSizeB/dataSize, mi.execWidth);
69 int chIxBase = 0;
70 for (int regIx = 0; regIx < regsPerVecElem; regIx++) {
71 // walk the channels in reverse order
72 emitSpan(os, '-', grfSizeB - mi.execWidth*dataSize);
73 for (int chIxR = chsPerGrf - 1; chIxR >= 0; chIxR--) {
74 emitDataElem(os, chIxBase + chIxR, mi);
75 }
76 emitRegName(os, currGrf++);
77 if (regIx == 0 && mi.elemsPerAddr > 1)
78 os << " start of SIMT vector element [" << vecIx << "]";
79 os << "\n";
80
81 chIxBase += chsPerGrf;
82 }
83 }
84 }
85
formatSIMD(const Opts &,std::ostream & os,const iga::MessageInfo & mi,int grfSizeB)86 static void formatSIMD(
87 const Opts &,
88 std::ostream &os,
89 const iga::MessageInfo &mi,
90 int grfSizeB)
91 {
92 int currGrf = 0;
93 int dataSize = mi.elemSizeBitsRegFile/8;
94 int vecElemsPerReg = grfSizeB/dataSize;
95 // outer loop: these should all be SIMD1, but the logical extension
96 // exists and we can make this algorithm aware of that possible
97 // extension
98 for (int chIx = 0; chIx < mi.execWidth; chIx++) {
99 int totalRegs = std::max<int>(1, mi.elemsPerAddr/vecElemsPerReg);
100 int elemsLeft = mi.elemsPerAddr;
101 int vecIx = 0;
102 for (int regIx = 0; regIx < totalRegs; regIx++) {
103 // at vecIx, walk in reverse order
104 if (elemsLeft < vecElemsPerReg) {
105 // pad out the last register
106 // this can happen if the message payload doesn't fill
107 // a register
108 emitSpan(os, '-', (vecElemsPerReg - elemsLeft)*dataSize);
109 }
110 int elemsToPrint = std::min<int>(elemsLeft, vecElemsPerReg);
111 for (int vi = elemsToPrint - 1; vi >= 0; --vi) {
112 emitDataElem(os, chIx, mi);
113 }
114 emitRegName(os, currGrf++);
115 if (vecElemsPerReg == 1) {
116 os << " vector elem " << vecIx;
117 } else {
118 os << " vector elems " << vecIx << ".." <<
119 vecIx + elemsToPrint - 1;
120 }
121 os << "\n";
122 vecIx += vecElemsPerReg;
123 elemsLeft -= vecElemsPerReg;
124 }
125 }
126 }
127
toLower(char c)128 static char toLower(char c) {return (char)std::tolower(c);}
129
decodeSendDescriptor(const Opts & opts)130 bool decodeSendDescriptor(const Opts &opts)
131 {
132 std::ofstream ofs(opts.outputFile);
133 std::ostream &os = opts.outputFile.empty() ? std::cout : ofs;
134
135 ensurePlatformIsSet(opts);
136
137 size_t minArgs = opts.platform < IGA_XE ? 2 : 3;
138
139 if (opts.inputFiles.size() != minArgs &&
140 opts.inputFiles.size() != minArgs + 1)
141 {
142 if (minArgs == 2)
143 fatalExitWithMessage("-Xdsd: for this platform expects: "
144 "ExecSize? ExDesc Desc");
145 else
146 fatalExitWithMessage("-Xdsd: for this platform expects: "
147 "SFID ExecSize? ExDesc Desc");
148 }
149
150 auto parseInt = [&] (const char *which, std::string inp) {
151 uint32_t val = 0;
152 try {
153 int base = 10;
154 if (inp.find("0x") == 0 || inp.find("0X") == 0) {
155 inp = inp.substr(2);
156 base = 16;
157 }
158 val = std::stoul(inp, nullptr, base);
159 } catch (std::invalid_argument i) {
160 fatalExitWithMessage(
161 "-Xdsd: ", which, ": ", inp, ": parse error");
162 } catch (std::out_of_range i) {
163 fatalExitWithMessage(
164 "-Xdsd: ", which, ": ", inp, ": value out of range");
165 }
166 return val;
167 };
168
169 auto parseSendDescArg =
170 [&] (const char *which, std::string inp) {
171 if (inp.find("a0.") == 0) {
172 inp = inp.substr(3);
173 iga::RegRef a0rr;
174 try {
175 a0rr.subRegNum = (uint16_t)std::stoul(inp, nullptr, 10);
176 } catch (...) {
177 fatalExitWithMessage(
178 "-Xdsd: ", which, ": ", inp,
179 ": invalid a0 subregister");
180 }
181 return iga::SendDesc(a0rr);
182 }
183 uint32_t val = parseInt(which, inp);
184 return iga::SendDesc(val);
185 };
186 auto tryParseSFID = [&](std::string sfidSym) {
187 std::transform(
188 sfidSym.begin(), sfidSym.end(), sfidSym.begin(), toLower);
189 return iga::FromSyntax<iga::SFID>(sfidSym);
190 };
191 int argOff = 0;
192 iga::SFID sfid = tryParseSFID(opts.inputFiles[argOff]);
193 if (sfid != iga::SFID::INVALID) {
194 argOff++;
195 if (opts.platform < IGA_XE)
196 fatalExitWithMessage(
197 "-Xdsd: ", opts.inputFiles[argOff],
198 ": SFID is encoded in ExDesc[3:0] for this platform");
199 } else if (sfid == iga::SFID::INVALID && opts.platform >= IGA_XE) {
200 fatalExitWithMessage(
201 "-Xdsd: ", opts.inputFiles[argOff],
202 ": invalid SFID for this platform");
203 }
204 // Formats are:
205 // <=GEN11: ExecSize? ExDesc Desc
206 // >=XE: SFID ExecSize? ExDesc Desc
207 //
208 // If ExecSize is not given, then we deduce it from the platform.
209
210 // ExecSize is '(' INT ('|' ('M0' | 'M4' | ...))? ')'
211 // e.g. all the following produce ExecSize::SIMD8:
212 // "(8)", "( 8 )", "(8|M24)"
213 //
214 iga::ExecSize execSize = iga::ExecSize::INVALID;
215 auto tryExecSize = [&](std::string str) {
216 if (str.empty() ||
217 str.substr(0, 1) != "(" ||
218 str.substr(str.size() - 1) != ")")
219 {
220 return iga::ExecSize::INVALID;
221 }
222 size_t off = 1;
223 while (off < str.size() && std::isspace(str[off]))
224 off++;
225 size_t len = 0;
226 int execSizeInt = 0;
227 while (off + len < str.size() && std::isdigit(str[off + len])) {
228 execSizeInt = 10 * execSizeInt + str[off + len] - '0';
229 len++;
230 }
231 while (off + len < str.size() && std::isspace(str[off + len])) {
232 len++;
233 }
234 if (len == 0 || off + len == str.size() || (str[off + len] != ')' && str[off + len] != '|'))
235 return iga::ExecSize::INVALID;
236 switch (execSizeInt) {
237 case 1: return iga::ExecSize::SIMD1;
238 case 2: return iga::ExecSize::SIMD2;
239 case 4: return iga::ExecSize::SIMD4;
240 case 8: return iga::ExecSize::SIMD8;
241 case 16: return iga::ExecSize::SIMD16;
242 case 32: return iga::ExecSize::SIMD32;
243 default: return iga::ExecSize::INVALID;
244 }
245 };
246
247 // ExecSize? (optional SIMD argument)
248 if (opts.inputFiles.size() > minArgs) {
249 execSize = tryExecSize(opts.inputFiles[argOff]);
250 if (execSize == iga::ExecSize::INVALID)
251 fatalExitWithMessage(
252 "-Xdsd: ", opts.inputFiles[argOff], ": invalid ExecSize");
253 argOff++;
254 }
255
256 std::string exDescStr = opts.inputFiles[argOff];
257
258 // ExDesc
259 const iga::SendDesc exDesc =
260 parseSendDescArg("ExDesc", exDescStr);
261 if (opts.platform < IGA_XE && sfid == iga::SFID::INVALID) {
262 // decode it from ex_desc[3:0]
263 if (exDesc.isImm())
264 sfid = iga::sfidFromEncoding(
265 static_cast<iga::Platform>(opts.platform),
266 exDesc.imm & 0xF);
267 if (sfid == iga::SFID::INVALID) {
268 std::stringstream ss;
269 ss << "0x" << std::hex << std::uppercase << (exDesc.imm & 0xF);
270 fatalExitWithMessage(
271 "-Xdsd: ", ss.str(),
272 ": invalid or unsupported SFID for this platform");
273 }
274 }
275 auto desc = parseSendDescArg("Desc", opts.inputFiles[argOff + 1]);
276
277 auto emitDiagnostics =
278 [&](const iga::DiagnosticList &ds, bool errors) {
279 for (const auto &d : ds) {
280 std::stringstream ss;
281 if (d.first.len != 0) {
282 int off = d.first.off;
283 const char *which = "Desc";
284 if (off >= 32) {
285 off -= 32;
286 which = "ExDesc";
287 }
288 ss << which << "[";
289 if (d.first.len > 1) {
290 ss << off + d.first.len - 1 << ":";
291 }
292 ss << off << "]: ";
293 }
294 ss << d.second << "\n";
295 if (errors)
296 emitRedText(std::cerr, ss.str());
297 else
298 emitYellowText(std::cerr, ss.str());
299 }
300 };
301
302 iga::Platform p = iga::Platform(opts.platform);
303
304 if (execSize == iga::ExecSize::INVALID) {
305 // Normally tryDecode() gets the ExecSize from IR or decoded from the
306 // descriptor; since we don't have that here, cheat and guess with a
307 // sort of pre-decode scam. It's not ideal, but I can't think of
308 // anything better right now.
309 execSize =
310 p >= iga::Platform::XE_HPC ?
311 iga::ExecSize::SIMD32 : iga::ExecSize::SIMD16;
312 if (sfid == iga::SFID::TGM) {
313 // typed LSC messages default to half the SIMD size
314 execSize =
315 p >= iga::Platform::XE_HPC ?
316 iga::ExecSize::SIMD16 : iga::ExecSize::SIMD8;
317 }
318 }
319 //
320 iga::DecodedDescFields decodedFields;
321 const auto dr = iga::tryDecode(
322 p, sfid, execSize,
323 exDesc, desc,
324 &decodedFields);
325 emitDiagnostics(dr.warnings, false);
326 emitDiagnostics(dr.errors, true);
327 if (!decodedFields.empty()) {
328 os << "DESCRIPTOR FIELDS:\n";
329 for (const auto &df : decodedFields) {
330 std::stringstream ss;
331 const auto &f = std::get<0>(df);
332 const auto &val = std::get<1>(df);
333 const auto &meaning = std::get<2>(df);
334 int off = f.offset;
335 ss << " ";
336 if (off >= 32) {
337 off -= 32;
338 ss << "ExDesc";
339 } else {
340 ss << "Desc";
341 }
342 ss << "[";
343 if (f.length > 1) {
344 ss << std::dec << off + f.length - 1 << ":";
345 }
346 ss << off;
347 ss << "]";
348 while (ss.tellp() < 16) {
349 ss << ' ';
350 }
351 ss << " " << std::setw(32) << std::left << f.name;
352 ss << " ";
353 std::stringstream ssh;
354 int fw = (f.length + 4 - 1)/4;
355 ssh << "0x" << std::hex << std::uppercase << std::setw(fw) <<
356 std::setfill('0') << val;
357 ss << std::setw(12) << std::right << ssh.str();
358 if (!meaning.empty())
359 ss << " " << meaning;
360 ss << "\n";
361
362 os << ss.str();
363 }
364 os << "\n";
365 }
366
367 emitGreenText(os, dr.info.symbol);
368 if (!dr.info.description.empty()) {
369 os << " (";
370 emitYellowText(os, dr.info.description);
371 os << ")";
372 }
373 os << "\n";
374 if (dr.syntax.isValid()) {
375 os << dr.syntax.str("..","D","A","X") << "\n";
376 }
377 auto emitDesc = [&](iga::SendDesc sda, bool treatSigned = false) {
378 std::stringstream ss;
379 if (sda.isImm()) {
380 if (treatSigned && (int32_t)sda.imm < 0) {
381 ss << "-0x" << std::hex << std::uppercase << -(int32_t)sda.imm;
382 } else {
383 ss << "0x" << std::hex << std::uppercase << sda.imm;
384 }
385 } else {
386 ss << "a0." << (int)sda.reg.subRegNum;
387 }
388 return ss.str();
389 };
390 if (dr) {
391 os << " Op: ";
392 emitYellowText(os, ToSyntax(dr.info.op));
393 os << "\n";
394 if (dr.info.addrType != iga::AddrType::INVALID) {
395 os << " Address Type: ";
396 emitYellowText(os, ToSymbol(dr.info.addrType));
397 os << "\n";
398 }
399 if (dr.info.addrType == iga::AddrType::FLAT) {
400 if (dr.info.hasAttr(iga::MessageInfo::Attr::SLM)) {
401 os << " Surface: ";
402 emitYellowText(os, "SLM");
403 os << "\n";
404 }
405 } else if (dr.info.addrType == iga::AddrType::BTI) {
406 os << " Surface: ";
407 std::stringstream ss;
408 ss << "surface binding table index " <<
409 emitDesc(dr.info.surfaceId);
410 emitYellowText(os,ss.str());
411 os << "\n";
412 }
413 else if (
414 dr.info.addrType == iga::AddrType::SS ||
415 dr.info.addrType == iga::AddrType::BSS)
416 {
417 os << " Surface: ";
418 std::stringstream ss;
419 ss << "surface object " << emitDesc(dr.info.surfaceId);
420 emitYellowText(os, ss.str());
421 os << "\n";
422 }
423 os << " Address Size: ";
424 emitYellowText(os, dr.info.addrSizeBits);
425 os << "b (per channel)\n";
426 os << " Data Size";
427 if (dr.info.elemSizeBitsRegFile != dr.info.elemSizeBitsMemory) {
428 os << " (RegFile): ";
429 } else {
430 os << ": ";
431 }
432 emitYellowText(os, dr.info.elemSizeBitsRegFile);
433 os << "b (per channel)\n";
434 if (dr.info.elemSizeBitsRegFile != dr.info.elemSizeBitsMemory) {
435 os << " Data Size (Memory): ";
436 emitYellowText(os, dr.info.elemSizeBitsMemory);
437 os << "b (per channel)\n";
438 }
439 if (dr.info.elemsPerAddr > 0) {
440 os << " Data Elements Per Address: ";
441 emitYellowText(os, dr.info.elemsPerAddr);
442 os << " element" << (dr.info.elemsPerAddr != 1 ? "s" : "");
443 }
444 const iga::SendOpDefinition &opInfo = iga::lookupSendOp(dr.info.op);
445 if (opInfo.hasChMask()) {
446 os << " (";
447 emitYellowText(os, ".");
448 if (dr.info.channelsEnabled & 0x1)
449 emitYellowText(os, "X");
450 if (dr.info.channelsEnabled & 0x2)
451 emitYellowText(os, "Y");
452 if (dr.info.channelsEnabled & 0x4)
453 emitYellowText(os, "Z");
454 if (dr.info.channelsEnabled & 0x8)
455 emitYellowText(os, "W");
456 os << " enabled)";
457 }
458 os << "\n";
459 //
460 os << " Execution Width: ";
461 emitYellowText(os, dr.info.execWidth);
462 os << " channel" << (dr.info.execWidth != 1 ? "s" : "") << "\n";
463 bool emitCacheSettings =
464 dr.info.isLoad() || dr.info.isStore() || dr.info.isAtomic();
465 emitCacheSettings |= dr.info.op == iga::SendOp::READ_STATE;
466 if (emitCacheSettings) {
467 auto emitCaching =
468 [&] (const char *name, iga::CacheOpt co) {
469 os << " " << name << " Caching: ";
470 emitYellowText(os, ToSymbol(co));
471 if (opts.verbosity > 0) {
472 if (co == iga::CacheOpt::DEFAULT)
473 os << " (uses state \"MOCS\" settings)";
474 else
475 os << " (overrides state \"MOCS\" settings)";
476 }
477 os << "\n";
478 };
479 emitCaching("L1", dr.info.cachingL1);
480 emitCaching("L3", dr.info.cachingL3);
481 }
482 os << "\n";
483 os << " Immediate Offset: ";
484 emitYellowText(os, emitDesc(dr.info.immediateOffset, true));
485 os << "\n";
486 os << "\n";
487 os << " Attributes:\n";
488 auto emitAttr =
489 [&] (const char *attrDesc) {
490 os << " - ";
491 emitYellowText(os,attrDesc);
492 os << "\n";
493 };
494 auto checkAttr =
495 [&] (iga::MessageInfo::Attr attr, const char *attrDesc) {
496 if (dr.info.hasAttr(attr)) {
497 emitAttr(attrDesc);
498 }
499 };
500 checkAttr(iga::MessageInfo::Attr::ATOMIC_RETURNS,
501 "atomic returns result");
502 checkAttr(iga::MessageInfo::Attr::HAS_CHMASK,
503 "uses channel mask");
504 checkAttr(iga::MessageInfo::Attr::SCRATCH,
505 "scratch");
506 checkAttr(iga::MessageInfo::Attr::SLM,
507 "slm");
508 checkAttr(iga::MessageInfo::Attr::TRANSPOSED,
509 "transposed");
510 checkAttr(iga::MessageInfo::Attr::TYPED,
511 "typed");
512
513 bool isZeroRlen = desc.isImm() && ((desc.imm >> 20) & 0x1F) == 0;
514 if (dr.info.isLoad() && isZeroRlen) {
515 emitAttr("prefetch (no data read into GRF; "
516 "instruction only prefetches to cache)");
517 }
518 os << "\n";
519
520 // don't pretend to understand the sampler
521 const auto &mi = dr.info;
522 bool showDataPayload =
523 mi.op != iga::SendOp::READ_STATE &&
524 mi.op != iga::SendOp::LOAD_STATUS &&
525 mi.op != iga::SendOp::SAMPLER_LOAD &&
526 mi.op != iga::SendOp::RENDER_READ &&
527 mi.op != iga::SendOp::RENDER_WRITE &&
528 mi.op != iga::SendOp::FENCE &&
529 mi.op != iga::SendOp::CCS_PC &&
530 mi.op != iga::SendOp::CCS_PU &&
531 mi.op != iga::SendOp::CCS_SC &&
532 mi.op != iga::SendOp::CCS_SU;
533 if (showDataPayload) {
534 os << "DATA PAYLOAD\n";
535 int grfSize = opts.platform >= IGA_XE_HPC ? 64 : 32;
536 if (mi.hasAttr(iga::MessageInfo::Attr::TRANSPOSED)) {
537 // formatSIMD(opts, os, msgInfo, grfSize);
538 formatSIMD(opts, os, mi, grfSize);
539 } else {
540 formatSIMT(opts, os, mi, grfSize);
541 }
542 if (opts.verbosity > 0) {
543 os <<
544 "\n"
545 " legend:\n"
546 " * 0, 1, ... A, ... V indicates the SIMD channel (base 32)\n"
547 " each character represents one byte in the register file\n"
548 " * '-' means undefined or zero\n";
549 }
550 }
551 }
552
553 return !dr;
554 }
555