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