1 /*========================== begin_copyright_notice ============================
2 
3 Copyright (C) 2017-2021 Intel Corporation
4 
5 SPDX-License-Identifier: MIT
6 
7 ============================= end_copyright_notice ===========================*/
8 
9 #include "BufferedLexer.hpp"
10 #include "Floats.hpp"
11 #include "KernelParser.hpp"
12 #include "Lexemes.hpp"
13 #include "Parser.hpp"
14 #include "../IR/InstBuilder.hpp"
15 #include "../IR/Types.hpp"
16 #include "../strings.hpp"
17 
18 #include <algorithm>
19 #include <limits>
20 #include <unordered_map>
21 #include <string>
22 #include <vector>
23 
24 
25 using namespace iga;
26 
27 static const IdentMap<FlagModifier> FLAGMODS {
28     {"lt", FlagModifier::LT},
29     {"le", FlagModifier::LE},
30     {"gt", FlagModifier::GT},
31     {"ge", FlagModifier::GE},
32     {"eq", FlagModifier::EQ},
33     {"ne", FlagModifier::NE},
34     {"ov", FlagModifier::OV},
35     {"un", FlagModifier::UN},
36     {"eo", FlagModifier::EO},
37     // aliased by different operations
38     {"ze", FlagModifier::EQ},
39     {"nz", FlagModifier::NE},
40 };
41 static const IdentMap<FlagModifier> FLAGMODS_LEGACY {
42     {"l", FlagModifier::LT},
43     {"g", FlagModifier::GT},
44     {"e", FlagModifier::EQ},
45     {"o", FlagModifier::OV},
46     {"u", FlagModifier::UN},
47     {"z", FlagModifier::EQ},
48 };
49 static const IdentMap<Type> SRC_TYPES {
50     // dpas types
51     {"u1", Type::U1},
52     {"u2", Type::U2},
53     {"u4", Type::U4},
54     {"s2", Type::S2},
55     {"s4", Type::S4},
56     {"b",  Type::B},
57     {"ub", Type::UB},
58     {"w",  Type::W},
59     {"uw", Type::UW},
60     {"d",  Type::D},
61     {"ud", Type::UD},
62     {"q",  Type::Q},
63     {"uq", Type::UQ},
64     {"hf", Type::HF},
65     {"f",  Type::F},
66     {"df", Type::DF},
67     {"v",  Type::V},
68     {"uv", Type::UV},
69     {"vf", Type::VF},
70     {"nf", Type::NF},
71     {"bf", Type::BF},
72     {"bf16", Type::BF},
73     {"bf8", Type::BF8},
74     {"qf", Type::QF},
75     {"tf32", Type::TF32},
76     ///////////////////////////////////////////
77     // non-standard names
78     {"u8",  Type::UB},
79     {"u16", Type::UW},
80     {"u32", Type::UD},
81     {"u64", Type::UQ},
82     {"s8",  Type::B},
83     {"s16", Type::W},
84     {"s32", Type::D},
85     {"s64", Type::Q},
86     {"f16", Type::HF},
87     {"f32", Type::F},
88     {"f64", Type::DF},
89 };
90 static const IdentMap<Type> DST_TYPES {
91     {"b",  Type::B},
92     {"ub", Type::UB},
93     {"w",  Type::W},
94     {"uw", Type::UW},
95     {"d",  Type::D},
96     {"ud", Type::UD},
97     {"q",  Type::Q},
98     {"uq", Type::UQ},
99     //
100     {"hf", Type::HF},
101     {"bf", Type::BF},
102     {"bf16", Type::BF},
103     {"bf8", Type::BF8},
104     {"qf", Type::QF},
105     {"tf32", Type::TF32},
106     {"f",  Type::F},
107     {"df", Type::DF},
108     {"nf", Type::NF},
109     ///////////////////////////////////////////
110     // non-standard names
111     {"u8", Type::UB},
112     {"u16", Type::UW},
113     {"u32", Type::UD},
114     {"u64", Type::UQ},
115     {"s8", Type::B},
116     {"s16", Type::W},
117     {"s32", Type::D},
118     {"s64", Type::Q},
119     {"f16", Type::HF},
120     {"f32", Type::F},
121     {"f64", Type::DF},
122 };
123 static const IdentMap<MathMacroExt> MATHMACROREGS = {
124     {"mme0", MathMacroExt::MME0},
125     {"mme1", MathMacroExt::MME1},
126     {"mme2", MathMacroExt::MME2},
127     {"mme3", MathMacroExt::MME3},
128     {"mme4", MathMacroExt::MME4},
129     {"mme5", MathMacroExt::MME5},
130     {"mme6", MathMacroExt::MME6},
131     {"mme7", MathMacroExt::MME7},
132     {"nomme", MathMacroExt::NOMME},
133 };
134 static const IdentMap<MathMacroExt> MATHMACROREGS_OLDSTYLE = {
135     {"acc2", MathMacroExt::MME0},
136     {"acc3", MathMacroExt::MME1},
137     {"acc4", MathMacroExt::MME2},
138     {"acc5", MathMacroExt::MME3},
139     {"acc6", MathMacroExt::MME4},
140     {"acc7", MathMacroExt::MME5},
141     {"acc8", MathMacroExt::MME6},
142     {"acc9", MathMacroExt::MME7},
143     {"noacc", MathMacroExt::NOMME},
144 };
145 
146 
GenParser(const Model & model,InstBuilder & handler,const std::string & inp,ErrorHandler & eh,const ParseOpts & pots)147 GenParser::GenParser(
148     const Model &model,
149     InstBuilder &handler,
150     const std::string &inp,
151     ErrorHandler &eh,
152     const ParseOpts &pots)
153     : Parser(inp,eh)
154     , m_model(model)
155     , m_builder(handler)
156     , m_opts(pots)
157 {
158     initSymbolMaps();
159 }
160 
161 
LookupReg(const std::string & str,const RegInfo * & ri,int & reg)162 bool GenParser::LookupReg(
163     const std::string &str,
164     const RegInfo*& ri,
165     int& reg)
166 {
167     ri = nullptr;
168     reg = 0;
169 
170     // given something like "r13", parse the "r"
171     // "cr0" -> "cr"
172     size_t len = 0;
173     while (len < str.length() && !isdigit(str[len]))
174         len++;
175     if (len == 0)
176         return false;
177     auto itr = m_regmap.find(str.substr(0,len));
178     if (itr == m_regmap.end()) {
179         return false;
180     }
181     ri = itr->second;
182     reg = 0;
183     if (ri->numRegs > 0) {
184         // if it's a numbered register like "r13" or "cr1", then
185         // parse the number part
186         size_t off = len;
187         while (off < str.size() && isdigit(str[off])) {
188             char c = str[off++];
189             reg = 10*reg + c - '0';
190         }
191         if (off < str.size()) {
192             // we have something like "r13xyz"; we don't treat this
193             // as a register, but fallback so it can be treated as an
194             // identifier (an immediate reference)
195             reg = 0;
196             ri = nullptr;
197             return false;
198         }
199     } // else it was something like "null" or "ip"
200       // either way, we are done
201     return true;
202 }
203 
PeekReg(const RegInfo * & regInfo,int & regNum)204 bool GenParser::PeekReg(const RegInfo*& regInfo, int& regNum) {
205     const Token &tk = Next();
206     if (tk.lexeme != IDENT) {
207         return false;
208     } else if (LookupReg(GetTokenAsString(tk), regInfo, regNum)) {
209         // helpful translation that permits acc2-acc9 and translates them
210         // to mme0-7 with a warning (or whatever they map to on the given
211         // platform
212         if (regInfo->regName == RegName::ARF_ACC &&
213             regNum >= regInfo->numRegs)
214         {
215             const RegInfo *mme =
216                 m_model.lookupRegInfoByRegName(RegName::ARF_MME);
217             IGA_ASSERT(mme != nullptr, "unable to find MMR on this platform");
218             if (mme) {
219                 // switch acc to mme
220                 regInfo = mme;
221                 // adjust the reg num (e.g. acc2 -> mme0 on GEN8)
222                 regNum -= mme->regNumBase;
223                 WarningAtT(tk.loc,
224                     "old-style access to mme via acc "
225                     "(use mme", regNum,
226                     " for acc", regNum + mme->regNumBase, ")");
227             }
228         }
229         if (!regInfo->isRegNumberValid(regNum)) {
230             WarningS(tk.loc, "register number out of bounds");
231         }
232         return true;
233     } else {
234         return false;
235     }
236 }
237 
ConsumeReg(const RegInfo * & regInfo,int & regNum)238 bool GenParser::ConsumeReg(const RegInfo*& regInfo, int& regNum) {
239     const Token &tk = Next();
240     if (tk.lexeme != IDENT) {
241         return false;
242     }
243     if (PeekReg(regInfo, regNum)) {
244         Skip();
245         return true;
246     } else {
247         return false;
248     }
249 }
250 
isFloating(const ImmVal & v)251 static bool isFloating(const ImmVal &v) {
252     switch (v.kind) {
253     case ImmVal::Kind::F16:
254     case ImmVal::Kind::F32:
255     case ImmVal::Kind::F64:
256         return true;
257     default:
258         return false;
259     }
260 }
isSignedInt(const ImmVal & v)261 static bool isSignedInt(const ImmVal &v) {
262     switch (v.kind) {
263     case ImmVal::Kind::S8:
264     case ImmVal::Kind::S16:
265     case ImmVal::Kind::S32:
266     case ImmVal::Kind::S64:
267         return true;
268     default:
269         return false;
270     }
271 }
isUnsignedInt(const ImmVal & v)272 static bool isUnsignedInt(const ImmVal &v) {
273     switch (v.kind) {
274     case ImmVal::Kind::U8:
275     case ImmVal::Kind::U16:
276     case ImmVal::Kind::U32:
277     case ImmVal::Kind::U64:
278         return true;
279     default:
280         return false;
281     }
282 }
isIntegral(const ImmVal & v)283 static bool isIntegral(const ImmVal &v) {
284     return isSignedInt(v) || isUnsignedInt(v);
285 }
286 
287 
288 // expression parsing
289 // &,|
290 // <<,>>
291 // +,-
292 // *,/,%
293 // -(unary neg)
TryParseConstExpr(ImmVal & v)294 bool GenParser::TryParseConstExpr(ImmVal &v)
295 {
296     return TryParseConstExpr(ExprParseOpts(), v);
297 }
TryParseConstExpr(const ExprParseOpts & pos,ImmVal & v)298 bool GenParser::TryParseConstExpr(
299     const ExprParseOpts &pos, ImmVal &v)
300 {
301     return parseBitwiseExpr(pos, false, v);
302 }
303 //
TryParseIntConstExpr(ImmVal & v,const char * forWhat)304 bool GenParser::TryParseIntConstExpr(ImmVal &v, const char *forWhat)
305 {
306     return TryParseIntConstExpr(ExprParseOpts(), v, forWhat);
307 }
TryParseIntConstExpr(const ExprParseOpts & pos,ImmVal & v,const char * forWhat)308 bool GenParser::TryParseIntConstExpr(
309     const ExprParseOpts &pos, ImmVal &v, const char *forWhat) {
310     // allow floats in the parse, but check the result (better error)
311     Loc loc = NextLoc();
312     ExprParseOpts epos = pos; // allow floats, throw tantrum at end
313     epos.allowFloat = true;
314     bool z = TryParseConstExpr(epos, v);
315     if (!z) {
316         return false;
317     } else if (!isIntegral(v)) {
318         std::stringstream ss;
319         if (forWhat) {
320             ss << forWhat << " must be a constant integer expression";
321         } else {
322             ss << "expected constant integer expression";
323         }
324         FailS(loc, ss.str());
325     }
326     return true;
327 }
TryParseIntConstExprAdd(ImmVal & v,const char * forWhat)328 bool GenParser::TryParseIntConstExprAdd(ImmVal &v, const char *forWhat)
329 {
330     return TryParseIntConstExprPrimary(ExprParseOpts(), v, forWhat);
331 }
TryParseIntConstExprAdd(const ExprParseOpts & pos,ImmVal & v,const char * forWhat)332 bool GenParser::TryParseIntConstExprAdd(
333     const ExprParseOpts &pos, ImmVal &v, const char *forWhat)
334 {
335     Loc loc = NextLoc();
336     ExprParseOpts epos = pos; // allow floats, throw tantrum at end
337     epos.allowFloat = true;
338     bool z = parseAddExpr(epos, false, v);
339     if (!z) {
340         return false;
341     } else if (!isIntegral(v)) {
342         std::stringstream ss;
343         if (forWhat) {
344             ss << forWhat << " must be a constant integer expression";
345         } else {
346             ss << "expected constant integer expression";
347         }
348         FailS(loc, ss.str());
349     }
350     return true;
351 }
TryParseIntConstExprPrimary(ImmVal & v,const char * forWhat)352 bool GenParser::TryParseIntConstExprPrimary(ImmVal &v, const char *forWhat)
353 {
354     return TryParseIntConstExprPrimary(ExprParseOpts(), v, forWhat);
355 }
TryParseIntConstExprPrimary(const ExprParseOpts & pos,ImmVal & v,const char * forWhat)356 bool GenParser::TryParseIntConstExprPrimary(
357     const ExprParseOpts &pos, ImmVal &v, const char *forWhat)
358 {
359     Loc loc = NextLoc();
360     ExprParseOpts epos = pos; // allow floats, throw tantrum at end
361     epos.allowFloat = true;
362     bool z = parsePrimaryExpr(epos, false, v);
363     if (!z) {
364         return false;
365     } else if (!isIntegral(v)) {
366         std::stringstream ss;
367         if (forWhat) {
368             ss << forWhat << " must be a constant integer expression";
369         } else {
370             ss << "expected constant integer expression";
371         }
372         FailS(loc, ss.str());
373     }
374     return true;
375 }
TryParseIntConstExprAddChain(const ExprParseOpts & pos,ImmVal & v,const char * forWhat)376 bool GenParser::TryParseIntConstExprAddChain(
377     const ExprParseOpts &pos, ImmVal &v, const char *forWhat)
378 {
379     Loc loc = NextLoc();
380     ExprParseOpts epos = pos; // allow floats, throw tantrum at end
381     epos.allowFloat = true;
382 
383     // treat this as
384     // 0 + .... (or 0 - ...)
385     v = (int64_t)0;
386     while (LookingAtAnyOf(ADD, SUB)) {
387         Token t = Next(); Skip();
388         ImmVal r;
389         parseMulExpr(pos, true, r);
390         v = evalBinExpr(v, t, r);
391     }
392     if (!isIntegral(v)) {
393         std::stringstream ss;
394         if (forWhat) {
395             ss << forWhat << " must be a constant integer expression";
396         } else {
397             ss << "expected constant integer expression";
398         }
399         FailS(loc, ss.str());
400     }
401     return true;
402 }
TryParseIntConstExprAddChain(ImmVal & v,const char * forWhat)403 bool GenParser::TryParseIntConstExprAddChain(ImmVal &v, const char *forWhat)
404 {
405     return TryParseIntConstExprAddChain(ExprParseOpts(), v, forWhat);
406 }
407 
ensureIntegral(const Token & t,const ImmVal & v)408 void GenParser::ensureIntegral(const Token &t, const ImmVal &v) {
409     if (!isIntegral(v)) {
410         FailS(t.loc, "argument to operator must be integral");
411     }
412 }
checkNumTypes(const ImmVal & v1,const Token & op,const ImmVal & v2)413 void GenParser::checkNumTypes(
414     const ImmVal &v1, const Token &op, const ImmVal &v2)
415 {
416     if (isFloating(v1) && !isFloating(v2)) {
417         FailAtT(op.loc, "right operand to ", GetTokenAsString(op),
418             " must be floating point");
419     } else if (isFloating(v2) && !isFloating(v1)) {
420         FailAtT(op.loc, "left operand to ", GetTokenAsString(op),
421             " must be floating point");
422     }
423 }
424 // target must be float
checkIntTypes(const ImmVal & v1,const Token & op,const ImmVal & v2)425 void GenParser::checkIntTypes(
426     const ImmVal &v1, const Token &op, const ImmVal &v2)
427 {
428     if (isFloating(v1)) {
429         FailAtT(op.loc, "left operand to ", GetTokenAsString(op),
430             " must be integral");
431     } else if (isFloating(v2)) {
432         FailAtT(op.loc, "right operand to ", GetTokenAsString(op),
433             " must be integral");
434     }
435 }
436 
evalBinExpr(const ImmVal & v1,const Token & op,const ImmVal & v2)437 ImmVal GenParser::evalBinExpr(
438     const ImmVal &v1, const Token &op, const ImmVal &v2)
439 {
440     bool isF = isFloating(v1) || isFloating(v2);
441     bool isU = isUnsignedInt(v1) || isUnsignedInt(v2);
442     ImmVal result = v1;
443     switch (op.lexeme) {
444     case AMP:
445     case CIRC:
446     case PIPE:
447     case LSH:
448     case RSH:
449     case MOD:
450         // integral only operations
451         checkIntTypes(v1, op, v2);
452         switch (op.lexeme) {
453         case AMP:
454             if (isU) {
455                 result.u64 &= v2.u64;
456             } else {
457                 result.s64 &= v2.s64;
458             }
459             break;
460         case CIRC:
461             if (isU) {
462                 result.u64 ^= v2.u64;
463             } else {
464                 result.s64 ^= v2.s64;
465             }
466             break;
467         case PIPE:
468             if (isU) {
469                 result.u64 |= v2.u64;
470             } else {
471                 result.s64 |= v2.s64;
472             }
473             break;
474         case LSH:
475             if (isU) {
476                 result.u64 <<= v2.u64;
477             } else {
478                 result.s64 <<= v2.s64;
479             }
480             break;
481         case RSH:
482             if (isU) {
483                 result.u64 >>= v2.u64;
484             } else {
485                 result.s64 >>= v2.s64;
486             }
487             break;
488         case MOD:
489             if (isU) {
490                 result.u64 %= v2.u64;
491             } else {
492                 result.s64 %= v2.s64;
493             }
494             break;
495         default:
496             break;
497         }
498         break;
499     case ADD:
500     case SUB:
501     case MUL:
502     case DIV:
503         checkNumTypes(v1, op, v2);
504         switch (op.lexeme) {
505         case ADD:
506             if (isF) {
507                 result.f64 += v2.f64;
508             } else if (isU) {
509                 result.u64 += v2.u64;
510             } else {
511                 result.s64 += v2.s64;
512             }
513             break;
514         case SUB:
515             if (isF) {
516                 result.f64 -= v2.f64;
517             } else if (isU) {
518                 result.u64 -= v2.u64;
519             } else {
520                 result.s64 -= v2.s64;
521             }
522             break;
523         case MUL:
524             if (isF) {
525                 result.f64 *= v2.f64;
526             } else if (isU) {
527                 result.u64 *= v2.u64;
528             } else {
529                 result.s64 *= v2.s64;
530             }
531             break;
532         case DIV:
533             if (isF) {
534                 result.f64 /= v2.f64;
535             } else {
536                 if (v2.u64 == 0) {
537                     FailS(op.loc, "(integral) division by zero");
538                 }
539                 if (isU) {
540                     result.u64 /= v2.u64;
541                 } else {
542                     result.s64 /= v2.s64;
543                 }
544             }
545             break;
546         default:
547             break;
548         }
549         break;
550     default:
551         break;
552     }
553     return result;
554 }
555 
parseBitwiseExpr(const ExprParseOpts & pos,bool consumed,ImmVal & v)556 bool GenParser::parseBitwiseExpr(
557     const ExprParseOpts &pos, bool consumed, ImmVal &v)
558 {
559     return parseBitwiseORExpr(pos, consumed, v);
560 }
561 // E -> E ('|' E)*
parseBitwiseORExpr(const ExprParseOpts & pos,bool consumed,ImmVal & v)562 bool GenParser::parseBitwiseORExpr(
563     const ExprParseOpts &pos, bool consumed, ImmVal &v)
564 {
565     if (!parseBitwiseXORExpr(pos, consumed, v)) {
566         return false;
567     }
568     while (LookingAt(PIPE)) {
569         Token t = Next(); Skip();
570         ImmVal r;
571         parseBitwiseXORExpr(pos, true, r);
572         v = evalBinExpr(v, t, r);
573     }
574     return true;
575 }
576 // E -> E ('^' E)*
parseBitwiseXORExpr(const ExprParseOpts & pos,bool consumed,ImmVal & v)577 bool GenParser::parseBitwiseXORExpr(
578     const ExprParseOpts &pos, bool consumed, ImmVal &v)
579 {
580     if (!parseBitwiseANDExpr(pos, consumed, v)) {
581         return false;
582     }
583     while (LookingAt(CIRC)) {
584         Token t = Next(); Skip();
585         ImmVal r;
586         parseBitwiseANDExpr(pos, true, r);
587         v = evalBinExpr(v, t, r);
588     }
589     return true;
590 }
591 // E -> E ('&' E)*
parseBitwiseANDExpr(const ExprParseOpts & pos,bool consumed,ImmVal & v)592 bool GenParser::parseBitwiseANDExpr(
593     const ExprParseOpts &pos, bool consumed, ImmVal &v)
594 {
595     if (!parseShiftExpr(pos, consumed, v)) {
596         return false;
597     }
598     while (LookingAt(AMP)) {
599         Token t = Next(); Skip();
600         ImmVal r;
601         parseShiftExpr(pos, true, r);
602         v = evalBinExpr(v, t, r);
603     }
604     return true;
605 }
606 
607 // E -> E (('<<'|'>>') E)*
parseShiftExpr(const ExprParseOpts & pos,bool consumed,ImmVal & v)608 bool GenParser::parseShiftExpr(
609     const ExprParseOpts &pos, bool consumed, ImmVal &v)
610 {
611     if (!parseAddExpr(pos, consumed, v)) {
612         return false;
613     }
614     while (LookingAtAnyOf(LSH, RSH)) {
615         Token t = Next(); Skip();
616         ImmVal r;
617         parseAddExpr(pos, true, r);
618         v = evalBinExpr(v, t, r);
619     }
620     return true;
621 }
622 // E -> E (('+'|'-') E)*
parseAddExpr(const ExprParseOpts & pos,bool consumed,ImmVal & v)623 bool GenParser::parseAddExpr(
624     const ExprParseOpts &pos, bool consumed, ImmVal &v)
625 {
626     if (!parseMulExpr(pos, consumed, v)) {
627         return false;
628     }
629     while (LookingAtAnyOf(ADD, SUB)) {
630         Token t = Next(); Skip();
631         ImmVal r;
632         parseMulExpr(pos, true, r);
633         v = evalBinExpr(v, t, r);
634     }
635     return true;
636 }
637 
638 // E -> E (('*'|'/'|'%') E)*
parseMulExpr(const ExprParseOpts & pos,bool consumed,ImmVal & v)639 bool GenParser::parseMulExpr(
640     const ExprParseOpts &pos, bool consumed, ImmVal &v)
641 {
642     if (!parseUnExpr(pos, consumed, v)) {
643         return false;
644     }
645     while (LookingAtAnyOf({MUL, DIV, MOD})) {
646         Token t = Next(); Skip();
647         ImmVal r;
648         parseUnExpr(pos, true, r);
649         v = evalBinExpr(v, t, r);
650     }
651     return true;
652 }
653 
654 // E -> ('-'|'~') E
parseUnExpr(const ExprParseOpts & pos,bool consumed,ImmVal & v)655 bool GenParser::parseUnExpr(
656     const ExprParseOpts &pos, bool consumed, ImmVal &v)
657 {
658     if (!LookingAtAnyOf(SUB, TILDE)) {
659         if (!parsePrimaryExpr(pos, consumed, v)) {
660             return false;
661         }
662     } else {
663         Token t = Next(); Skip();
664         parsePrimaryExpr(pos, true, v);
665         switch (t.lexeme) {
666         case SUB:
667             v.Negate();
668             break;
669         case TILDE:
670             ensureIntegral(t, v);
671             v.u64 = ~v.u64;
672             break;
673         default:
674             break;
675         }
676     }
677     return true;
678 }
679 
680 // special symbol (e.g. nan, inf, ...)
681 // grouped expression (E)
682 // literal
parsePrimaryExpr(const ExprParseOpts & pos,bool consumed,ImmVal & v)683 bool GenParser::parsePrimaryExpr(
684     const ExprParseOpts &pos, bool consumed, ImmVal &v)
685 {
686     Token t = Next();
687     bool isQuietNaN = false;
688     if (pos.allowFloat && LookingAtIdentEq(t, "nan")) {
689         WarningT("nan is deprecated, us snan(...) or qnan(...)");
690         v.kind = ImmVal::Kind::F64;
691         v.f64 = std::numeric_limits<double>::signaling_NaN();
692         Skip();
693     } else if (pos.allowFloat &&
694         ((isQuietNaN = LookingAtIdentEq(t, "qnan")) ||
695             LookingAtIdentEq(t, "snan")))
696     {
697         auto nanSymLoc = NextLoc();
698         Skip();
699         if (Consume(LPAREN)) {
700             auto payloadLoc = NextLoc();
701             ImmVal payload;
702             payload.u64 = 0;
703             parseBitwiseExpr(pos, true, payload);
704             if (payload.u64 >= F64_QNAN_BIT) {
705                 FailS(payloadLoc, "NaN payload overflows");
706             } else if (payload.u64 == 0 && !isQuietNaN) {
707                 // signaling NaN has a 0 in the high mantissa, so we must
708                 // ensure that at least one bit in the mantissa is set
709                 // otherwise the entire mantissa is 0's and the pattern
710                 // would be an "infinity" pattern, not a NaN
711                 FailS(payloadLoc, "NaN payload must be nonzero for snan");
712             }
713             ConsumeOrFail(RPAREN, "expected )");
714             v.u64 = payload.u64 | F64_EXP_MASK;
715             if (isQuietNaN) {
716                 v.u64 |= F64_QNAN_BIT;
717             }
718         } else {
719             WarningS(nanSymLoc,
720                 "bare qnan and snan tokens deprecated"
721                 " (pass in a valid payload)");
722             v.u64 = F64_EXP_MASK;
723             if (isQuietNaN) {
724                 v.u64 |= F64_QNAN_BIT; // the quiet bit suffices
725             } else {
726                 v.u64 |= 1; // set something other than the quiet bit
727                             // non-zero in the payload
728             }
729         }
730         v.kind = ImmVal::Kind::F64;
731     } else if (pos.allowFloat && LookingAtIdentEq(t, "inf")) {
732         v.f64 = std::numeric_limits<double>::infinity();
733         v.kind = ImmVal::Kind::F64;
734         Skip();
735     } else if (pos.allowFloat && LookingAt(FLTLIT)) {
736         ParseFltFrom(t.loc, v.f64);
737         v.kind = ImmVal::Kind::F64;
738         Skip();
739     } else if (LookingAtAnyOf({INTLIT02, INTLIT10, INTLIT16})) {
740         // we parse as unsigned, but tag as signed for negation etc...
741         ParseIntFrom<uint64_t>(t.loc, v.u64);
742         v.kind = ImmVal::Kind::S64;
743         Skip();
744     } else if (Consume(LPAREN)) {
745         // (E)
746         parseBitwiseExpr(pos, true, v);
747         Consume(RPAREN);
748     } else if (LookingAt(IDENT)) {
749         // TEST CASES
750         // LABEL:
751         // // jmpi  LABEL                    // passes
752         // // goto (16) (2 + LABEL) LABEL    // fails
753         // // goto (16) LABEL LABEL          // passes
754         // // join (16) LABEL                // passes
755         // // mov (1) r65:ud LABEL:ud        // fails
756         // // mov (1) r65:ud (2 + LABEL):ud  // fails (poor diagnostic)
757         if (!pos.symbols.empty()) {
758             std::string str = GetTokenAsString();
759             auto itr = pos.symbols.find(str);
760             if (itr != pos.symbols.end()) {
761                 v = itr->second;
762                 return true;
763             }
764         }
765         if (consumed)
766             FailT("unbound identifier");
767         return false;
768     } else {
769         // something else: error unless we haven't consumed anything
770         if (consumed) {
771             FailT("syntax error in constant expression");
772         }
773         return false;
774     }
775     return true;
776 } // parsePrimary
777 
778 
initSymbolMaps()779 void GenParser::initSymbolMaps()
780 {
781     // map the register names
782     // this maps just the non-number part.
783     // e.g. with cr0, this maps "cr"; see LookupReg()
784     int tableLen;
785     const RegInfo *table = GetRegisterSpecificationTable(tableLen);
786     for (int i = 0; i < tableLen; i++) {
787         const RegInfo *ri = table + i;
788         if (ri->supportedOn(platform())) {
789             m_regmap[ri->syntax] = ri;
790         }
791     }
792 }
793 
794 
795 class KernelParser : GenParser
796 {
797     // maps mnemonics and registers for faster lookup
798     std::unordered_map<std::string,const OpSpec*>   opmap;
799 
800     ExecSize              m_defaultExecutionSize;
801     Type                  m_defaultRegisterType;
802 
803     // instruction state
804     const OpSpec         *m_opSpec;
805     bool                  m_hasWrEn;
806     Type                  m_unifType;
807     const Token          *m_unifTypeTk;
808     RegRef                m_flagReg;
809     ExecSize              m_execSize;
810     ChannelOffset         m_chOff;
811     Loc                   m_execSizeLoc;
812     Loc                   m_mnemonicLoc;
813     Operand::Kind         m_srcKinds[3];
814     Loc                   m_srcLocs[3];
815     int                   m_sendSrcLens[2]; // send message lengths
816     Loc                   m_sendSrcLenLocs[2]; // locations so we can referee
817     bool                  m_implicitExBSO; // send src1 suffixed with length implies exBSO, but ensure the inst opt is given
818 
819 private:
820     // A helper function to add ARF_NULL src to given src operand
addNullSrc(int srcOpIx,bool isImmOrLbl)821     void addNullSrc(int srcOpIx, bool isImmOrLbl) {
822         // For instructions those have implicit types, match the type to
823         // the expected one. Otherwise, ARF_NULL operand should have Type::INVALID
824         Type type = Type::INVALID;
825         m_opSpec->implicitSrcTypeVal(srcOpIx, isImmOrLbl, type);
826         m_builder.InstSrcOpRegDirect(
827             srcOpIx,
828             m_srcLocs[srcOpIx],
829             SrcModifier::NONE,
830             RegName::ARF_NULL,
831             REGREF_ZERO_ZERO,
832             Region::SRC010,
833             type);
834     }
835 
836 public:
KernelParser(const Model & model,InstBuilder & handler,const std::string & inp,ErrorHandler & eh,const ParseOpts & pots)837     KernelParser(
838         const Model &model,
839         InstBuilder &handler,
840         const std::string &inp,
841         ErrorHandler &eh,
842         const ParseOpts &pots)
843         : GenParser(model, handler, inp, eh, pots)
844         , m_defaultExecutionSize(ExecSize::SIMD1)
845         , m_defaultRegisterType(Type::INVALID)
846     {
847         initSymbolMaps();
848     }
849 
ParseListing()850     void ParseListing() {
851         ParseProgram();
852     }
853 
initSymbolMaps()854     void initSymbolMaps() {
855         // map mnemonics names to their ops
856         // subops only get mapped by their fully qualified names in this pass
857         for (const OpSpec *os : m_model.ops()) {
858             if (os->isValid()) {
859                 opmap[os->mnemonic] = os;
860             }
861         }
862     }
863 
864 
865     // Program = (Label? Insts* (Label Insts))?
ParseProgram()866     void ParseProgram() {
867         m_builder.ProgramStart();
868 
869         // parse default type directives
870         if (m_opts.supportLegacyDirectives) {
871             // e.g. .default_execution_size...
872             ParseLegacyDirectives();
873         }
874 
875         // first block doesn't need a label
876         if (!LookingAtLabelDef() && !EndOfFile()) {
877             ParseBlock(NextLoc(),""); // unnamed
878         }
879         // successive blocks need a label
880         std::string label;
881         Loc lblLoc = NextLoc();
882         while (ConsumeLabelDef(label)) {
883             ParseBlock(lblLoc, label);
884             lblLoc = NextLoc();
885         }
886         if (!EndOfFile()) {
887             FailT("expected instruction, block, or EOF");
888         }
889 
890         m_builder.ProgramEnd();
891     }
892 
893     // fix to support .default_execution_size
ParseLegacyDirectives()894     bool ParseLegacyDirectives() {
895         int parsed = 0;
896         try {
897             while (LookingAtSeq(Lexeme::DOT, Lexeme::IDENT)) {
898                 Skip();
899                 parsed++;
900                 if (ConsumeIdentEq("default_execution_size")) {
901                     Consume(LPAREN);
902                     auto loc = NextLoc();
903                     int dftExecSize;
904                     if (!ConsumeIntLit<int>(dftExecSize)) {
905                         FailT("expected SIMD width (integral value)");
906                     }
907                     if (dftExecSize !=  1 &&
908                         dftExecSize !=  2 &&
909                         dftExecSize !=  4 &&
910                         dftExecSize !=  8 &&
911                         dftExecSize != 16 &&
912                         dftExecSize != 32)
913                     {
914                         FailS(loc, "invalid default execution size; "
915                             "must be 1, 2, 4, 8, 16, 32");
916                     }
917                     m_defaultExecutionSize = (ExecSize)dftExecSize;
918                     Consume(RPAREN);
919                     Consume(NEWLINE);
920                 } else if (ConsumeIdentEq("default_register_type")) {
921                     m_defaultRegisterType = TryParseOpType(DST_TYPES);
922                     if (m_defaultRegisterType == Type::INVALID) {
923                         FailT("expected default register type");
924                     }
925                     Consume(NEWLINE);
926                 } else {
927                     FailT("unexpected directive name");
928                 }
929             } // while
930         } catch (const SyntaxError &s) {
931             RecoverFromSyntaxError(s);
932             // returning true will make ParseBlock restart the loop
933             // (so more directives get another shot)
934             return true;
935         }
936         return parsed > 0;
937     }
938 
RecoverFromSyntaxError(const SyntaxError & s)939     void RecoverFromSyntaxError(const SyntaxError &s) {
940         // record the error in this instruction
941         m_errorHandler.reportError(s.loc, s.message);
942         // bail if we've reached the max number of errors
943         if (m_errorHandler.getErrors().size() >=
944             m_opts.maxSyntaxErrors)
945         {
946             throw s;
947         }
948         // attempt resync by skipping until we pass a newline
949         // DumpLookaheads();
950         while (!Consume(Lexeme::NEWLINE) &&
951             // !Consume(Lexeme::SEMI) &&
952             //   don't sync on ; since this is typically part of a
953             //   region rather than an operand separator
954             //  ...     r12.0<8;8,1>:d
955             //  ^ error        ^ syncs here otherwise
956             !LookingAt(Lexeme::END_OF_FILE))
957         {
958             // DumpLookaheads();
959             Skip();
960         }
961     }
962 
963 
ParseBlock(const Loc & lblLoc,const std::string & label)964     void ParseBlock(const Loc &lblLoc, const std::string &label) {
965         m_builder.BlockStart(lblLoc, label);
966         auto lastInst = NextLoc();
967         while (true) {
968             if (Consume(Lexeme::NEWLINE) || Consume(Lexeme::SEMI)) {
969                 continue;
970             } else if (m_opts.supportLegacyDirectives &&
971                 ParseLegacyDirectives())
972             {
973                 // .default_execution_size...
974                 continue;
975             }
976             // if we make it here, then we are not looking at a legacy
977             // directive and we are not looking at a newline
978             if (LookingAtLabelDef() || EndOfFile()) {
979                 break;
980             }
981             // not at EOF and not looking at the beginning of new block
982             // so we better be looking at an instruction
983             ParseInst();
984             lastInst = NextLoc(-1); // peek at the end of the last instruction
985         }
986 
987         m_builder.BlockEnd(ExtentTo(lblLoc,lastInst));
988     }
989 
990 
ParseInst()991     void ParseInst() {
992         try {
993             // parse one instruction
994             // DumpLookaheads();
995             ParseInstCanThrowException();
996         } catch (SyntaxError &s) {
997             // we resync here and return from ParseInst
998             RecoverFromSyntaxError(s);
999         }
1000     }
1001 
1002 
1003     // Instruction = RegularInst | LdStInst
1004     // RegularInst = Predication? Mnemonic UniformType? EMask
1005     //                 ConditionModifier? Operands InstOptions?
1006     // LdStInst = ... comes from Sends/Interface.hpp ...
ParseInstCanThrowException()1007     void ParseInstCanThrowException() {
1008         // ShowCurrentLexicalContext();
1009         const Loc startLoc = NextLoc();
1010         m_builder.InstStart(startLoc);
1011 
1012         m_flagReg = REGREF_INVALID;
1013         m_execSizeLoc = Loc::INVALID;
1014         m_opSpec = nullptr;
1015         m_unifType = Type::INVALID;
1016         m_unifTypeTk = nullptr;
1017         m_mnemonicLoc = Loc::INVALID;
1018         for (size_t i = 0; i < sizeof(m_srcKinds)/sizeof(m_srcKinds[0]); i++) {
1019             m_srcLocs[i] = Loc::INVALID;
1020             m_srcKinds[i] = Operand::Kind::INVALID;
1021         }
1022         for (size_t i = 0;
1023             i < sizeof(m_sendSrcLens)/sizeof(m_sendSrcLens[0]);
1024             i++)
1025         {
1026             m_sendSrcLens[i] = -1;
1027             m_sendSrcLenLocs[i] = Loc::INVALID;
1028         }
1029         m_implicitExBSO = false;
1030 
1031         // (W&~f0) mov (8|M0) r1 r2
1032         // ^
1033         ParseWrEnPred();
1034 
1035         // (W&~f0) mov (8|M0) r1 r2
1036         //         ^
1037         //
1038         //         math.sqrt (8|M0) ...
1039         //         ^
1040         m_mnemonicLoc = NextLoc();
1041         m_opSpec = ParseMnemonic();
1042         if (m_opSpec) {
1043             // looking at a regular instruction (non special-ld-st inst)
1044             m_builder.InstOp(m_opSpec);
1045             FinishNonLdStInstBody();
1046         } else if (!ParseLdStInst()) {
1047             FailS(m_mnemonicLoc, "invalid mnemonic");
1048         }
1049 
1050         // .... {...}
1051         ParseInstOpts();
1052 
1053         if (!LookingAt(Lexeme::NEWLINE) &&
1054             !LookingAt(Lexeme::SEMI) &&
1055             !LookingAt(Lexeme::END_OF_FILE))
1056         {
1057             FailAfterPrev("expected newline, ';', or EOF");
1058         }
1059 
1060         m_builder.InstEnd(ExtentToPrevEnd(startLoc));
1061     }
1062 
1063 
1064     // ExecInfo = '(' ExecSize EmOffNm? ')'
1065     //   where EmOffNm = '|' EmOff  (',' 'NM')?
1066     //                 | '|' 'NM'
1067     //         EmOff = 'M0' | 'M4' | ...
1068     //         ExecSize = '1' | '2' | ... | '32'
ParseExecInfo(ExecSize dftExecSize,ExecSize & execSize,ChannelOffset & chOff)1069     void ParseExecInfo(
1070         ExecSize dftExecSize,
1071         ExecSize &execSize,
1072         ChannelOffset &chOff)
1073     {
1074         Loc execSizeLoc = m_execSizeLoc = NextLoc(0);
1075         Loc execOffsetLoc = NextLoc(0);
1076         // we are careful here since we might have things like:
1077         //    jmpi        (1*16)
1078         //    jmpi (1|M0) ...
1079         //    jmpi (1)    ...
1080         // We resolve that by looking ahead two symbols
1081         int execSizeVal = 1;
1082         if (LookingAt(LPAREN) && (LookingAtFrom(2,RPAREN) || LookingAtFrom(2,PIPE))) {
1083             Skip();
1084             execSizeLoc = NextLoc();
1085             ConsumeIntLitOrFail(execSizeVal, "expected SIMD width");
1086 
1087             if (Consume(PIPE)) {
1088                 static const IdentMap<ChannelOffset> EM_OFFS {
1089                     {"M0", ChannelOffset::M0}
1090                     , {"M4", ChannelOffset::M4}
1091                     , {"M8", ChannelOffset::M8}
1092                     , {"M12", ChannelOffset::M12}
1093                     , {"M16", ChannelOffset::M16}
1094                     , {"M20", ChannelOffset::M20}
1095                     , {"M24", ChannelOffset::M24}
1096                     , {"M28", ChannelOffset::M28}
1097                 };
1098                 execOffsetLoc = NextLoc();
1099                 ConsumeIdentOneOfOrFail(
1100                     EM_OFFS,
1101                     chOff,
1102                     "expected ChOff",
1103                     "invalid ChOff");
1104                 //if (m_chOff % m_execSize != 0) {
1105                 //    Fail(execOffsetLoc,
1106                 //        "invalid execution mask offset for execution size");
1107                 //} else if (m_chOff + m_execSize > 32) {
1108                 //    Fail(execOffsetLoc,
1109                 //        "invalid execution mask offset for execution size");
1110                 //}
1111             } else {
1112                 chOff = ChannelOffset::M0;
1113             }
1114             ConsumeOrFail(RPAREN,"expected )");
1115         } else {
1116             if (m_opSpec && m_opSpec->hasImpicitEm()) {
1117                 chOff = ChannelOffset::M0;
1118                 execSizeVal = 1;
1119             } else if (m_opts.supportLegacyDirectives) {
1120                 chOff = ChannelOffset::M0;
1121                 execSizeVal = (int)dftExecSize;
1122             } else {
1123                 FailT("expected '(' (start of execution size info)");
1124             }
1125         }
1126 
1127         switch (execSizeVal) {
1128         case 1: execSize = ExecSize::SIMD1; break;
1129         case 2: execSize = ExecSize::SIMD2; break;
1130         case 4: execSize = ExecSize::SIMD4; break;
1131         case 8: execSize = ExecSize::SIMD8; break;
1132         case 16: execSize = ExecSize::SIMD16; break;
1133         case 32: execSize = ExecSize::SIMD32; break;
1134         default: FailT("invalid SIMD width");
1135         }
1136 
1137         m_builder.InstExecInfo(
1138             execSizeLoc, execSize, execOffsetLoc, chOff);
1139     }
1140 
1141 
SendOperandDefaultType(int srcIx) const1142     Type SendOperandDefaultType(int srcIx) const {
1143         auto t = srcIx == 1 ? Type::INVALID : Type::UD;
1144         if (srcIx < 0) {
1145             if (m_opSpec->hasImplicitDstType())
1146                 t = m_opSpec->implicitDstType();
1147         } else {
1148             if (m_opSpec->hasImplicitSrcType(srcIx, false))
1149                 t = m_opSpec->implicitSrcType(srcIx, false);
1150         }
1151         return t;
1152     }
1153 
1154 
ParseSendOperandTypeWithDefault(int srcIx)1155     Type ParseSendOperandTypeWithDefault(int srcIx) {
1156         // sends's second parameter doesn't have a valid type
1157         Type t = Type::INVALID;
1158         if (Consume(COLON)) {
1159             if (!LookingAt(IDENT)) {
1160                 FailT("expected a send operand type");
1161             }
1162             if (!IdentLookupFrom(0, DST_TYPES, t)) {
1163                 FailT("unexpected operand type for send");
1164             }
1165             Skip();
1166         } else {
1167             t = SendOperandDefaultType(srcIx);
1168         }
1169         return t;
1170     }
1171 
1172 
1173     bool ParseLdStInst();
1174     void ParseLdStOpControls(
1175         Loc mneLoc,
1176         const SendOpDefinition &opInfo,
1177         VectorMessageArgs &vma);
1178 
1179     // e.g.  add (8|M8) ...
1180     //           ^
1181     // e.g.  nop ...
1182     //           ^
FinishNonLdStInstBody()1183     void FinishNonLdStInstBody() {
1184         // if (Consume(COLON)) {
1185         //    m_unifTypeTk = &Next(0);
1186         //    ConsumeIdentOneOfOrFail(
1187         //        dstTypes,
1188         //        m_unifType,
1189         //        "expected uniform type",
1190         //        "invalid uniform type");
1191         // }
1192 
1193         // (W&~f0) mov (8|M0) (le)f0.0  r1:f  r2:f
1194         //             ^
1195         ChannelOffset chOff;
1196         ParseExecInfo(m_defaultExecutionSize, m_execSize, chOff); // sets m_execSize
1197 
1198         ParseFlagModOpt();
1199         switch (m_opSpec->format) {
1200         case OpSpec::NULLARY:
1201             // nop ...
1202             // illegal ...
1203             break; // fallthrough to instruction options
1204         case OpSpec::BASIC_UNARY_REG:
1205         case OpSpec::BASIC_UNARY_REGIMM:
1206             ParseDstOp();
1207             ParseSrcOp(0);
1208             if (m_opSpec->format == OpSpec::BASIC_UNARY_REG &&
1209                 m_srcKinds[0] != Operand::Kind::DIRECT &&
1210                 m_srcKinds[0] != Operand::Kind::INDIRECT)
1211             {
1212                 FailS(m_srcLocs[0], "src0 must be a register");
1213             }
1214             break;
1215         case OpSpec::SYNC_UNARY:
1216             // implicit destination
1217             ParseSyncSrc0Op();
1218             if (m_model.supportsWait() &&
1219                 m_srcKinds[0] != Operand::Kind::DIRECT)
1220             {
1221                 FailS(m_srcLocs[0], "src0 must be a notification register");
1222             }
1223             break;
1224         case OpSpec::SEND_UNARY:
1225             // <=Gen11
1226             ParseSendDstOp();
1227             ParseSendSrcOp(0, false);
1228             ParseSendDescsLegacy();
1229             break;
1230         case OpSpec::SEND_BINARY:
1231             if (platform() <= Platform::XE) {
1232                 ParseSendInstructionLegacy();
1233             } else if (platform() >= Platform::XE_HP) {
1234                 ParseSendInstructionXeHP();
1235             } else {
1236                 IGA_ASSERT_FALSE("invalid format for platform");
1237             }
1238             break;
1239         case OpSpec::BASIC_BINARY_REG_IMM:
1240         case OpSpec::BASIC_BINARY_REG_REG:
1241         case OpSpec::BASIC_BINARY_REG_REGIMM:
1242             ParseDstOp();
1243             ParseSrcOp(0);
1244             ParseSrcOp(1);
1245             break;
1246         case OpSpec::MATH_BINARY_REG_REGIMM:
1247             ParseDstOp();
1248             ParseSrcOp(0);
1249             if (m_opSpec->getSourceCount(m_builder.getSubfunction()) > 1) {
1250                 // math sometimes has only one operand
1251                 ParseSrcOp(1);
1252             }
1253             break;
1254         case OpSpec::TERNARY_REGIMM_REG_REGIMM:
1255             if (m_opSpec->isDpasFamily()) {
1256                 ParseDpasOp(-1, true);    // dst
1257                 ParseDpasOp(0, false);    // src0
1258                 ParseDpasOp(1, false);    // src1
1259                 ParseDpasOp(2, false);    // src2
1260                 break;
1261             }
1262             ParseDstOp();
1263             ParseSrcOp(0);
1264             ParseSrcOp(1);
1265             ParseSrcOp(2);
1266             break;
1267         case OpSpec::JUMP_UNARY_IMM:
1268             ParseSrcOpLabel(0);
1269             break;
1270         case OpSpec::JUMP_UNARY_REGIMM:
1271             // e.g. jmpi or brd  (registers or labels)
1272             ParseSrcOp(0);
1273             break;
1274         case OpSpec::JUMP_UNARY_REG:
1275             // e.g. ret (..) r12
1276             ParseSrcOp(0);
1277             if (m_srcKinds[0] != Operand::Kind::DIRECT &&
1278                 m_srcKinds[0] != Operand::Kind::INDIRECT)
1279             {
1280                 FailS(m_srcLocs[0], "src0 must be a register");
1281             }
1282             break;
1283         case OpSpec::JUMP_BINARY_BRC: {
1284             //   brc (...) lbl lbl
1285             //   brc (...) reg null
1286             //
1287             // for legacy reasons we support a unary form
1288             //   brc (...) reg
1289             // we do add an explicit null parameter for src1
1290             // it's very possible there are some simulator tests floating
1291             // aroudn that require this old form.
1292             auto brcStart = NextLoc();
1293             ParseSrcOp(0);
1294             if (m_srcKinds[0] == Operand::Kind::IMMEDIATE ||
1295                 m_srcKinds[0] == Operand::Kind::LABEL ||
1296                 LookingAtIdentEq("null"))
1297             {
1298                 ParseSrcOp(1);
1299             } else {
1300                 // legacy format
1301                 // add an explicit null operand
1302                 //
1303                 // trb: this is no longer a warning, but we still set the
1304                 // parameter in the IR for the time being
1305                 //
1306                 // if (m_parseOpts.deprecatedSyntaxWarnings) {
1307                 //    Warning(brcStart,
1308                 //        "deprecated syntax: register version "
1309                 //        "of brc takes an explicit null parameter");
1310                 // }
1311                 // m_handler.InstSrcOpRegDirect(
1312                 //     1,
1313                 //     brcStart,
1314                 //     SrcModifier::NONE,
1315                 //     RegName::ARF_NULL,
1316                 //     REGREF_ZERO_ZERO,
1317                 //     Region::SRC010,
1318                 //     Type::UD);
1319             }
1320             break;
1321         }
1322         case OpSpec::JUMP_UNARY_CALL_REGIMM:
1323             // call (..) dst src
1324             // calla (..) dst src
1325             ParseDstOp();
1326             ParseSrcOp(0);
1327             break;
1328         case OpSpec::JUMP_BINARY_IMM_IMM:
1329             // e.g. else, cont, break, goto, halt
1330             ParseSrcOpLabel(0);
1331             ParseSrcOpLabel(1);
1332             break;
1333         default:
1334             IGA_ASSERT_FALSE("unhandled syntax class in parser");
1335         }
1336     }
1337 
1338 
1339     // Original binary send <=XE (sends, sendsc)
ParseSendInstructionLegacy()1340     void ParseSendInstructionLegacy() {
1341         ParseSendDstOp();
1342         ParseSendSrcOp(0, false);
1343         ParseSendSrcOp(1,
1344             m_model.supportsUnarySend() &&
1345             m_opts.supportLegacyDirectives);
1346         ParseSendDescsLegacy();
1347     }
1348 
1349     // XE_HP offers new : syntax for separate src1Len for certain messages
1350     // XE_HPG, XE_HPC require : syntax
1351     //   We are flexible here and permit src1Len to come out of the immediate
1352     //   descriptor for certain cases (with a warning).
ParseSendInstructionXeHP()1353     void ParseSendInstructionXeHP() {
1354         ParseSendDstOp();
1355         ParseSendSrcOp(0, false);
1356         int src1Len = -1;
1357         ParseSendSrc1OpWithOptLen(src1Len);
1358         ParseSendDescsWithOptSrc1Len(src1Len);
1359     }
1360 
1361     // Predication = ('(' WrEnPred ')')?
1362     //   where WrEnPred = WrEn
1363     //                  | Pred
1364     //                  | WrEn '&' Pred
1365     //         WrEn = 'W'
1366     //         Pred = '~'? FlagReg ('.' PreCtrl?)
ParseWrEnPred()1367     void ParseWrEnPred() {
1368         m_hasWrEn = false;
1369         if (Consume(LPAREN)) {
1370             const Loc nmLoc = NextLoc(0);
1371             if (ConsumeIdentEq("W")) {
1372                 m_hasWrEn = true;
1373                 m_builder.InstNoMask(nmLoc);
1374                 if (Consume(AMP)) {
1375                     ParsePred();
1376                 }
1377             } else {
1378                 ParsePred();
1379             }
1380             ConsumeOrFail(RPAREN, "expected )");
1381         }
1382     }
1383 
1384 
1385     // Pred = '~'? FlagReg ('.' PreCtrl?)
ParsePred()1386     void ParsePred() {
1387         static const IdentMap<PredCtrl> PREDCTRLS {
1388             {"xyzw", PredCtrl::SEQ}, // lack of a specific pred control defaults to SEQ
1389             {"anyv", PredCtrl::ANYV},
1390             {"allv", PredCtrl::ALLV},
1391             {"any2h", PredCtrl::ANY2H},
1392             {"all2h", PredCtrl::ALL2H},
1393             {"any4h", PredCtrl::ANY4H},
1394             {"all4h", PredCtrl::ALL4H},
1395             {"any8h", PredCtrl::ANY8H},
1396             {"all8h", PredCtrl::ALL8H},
1397             {"any16h", PredCtrl::ANY16H},
1398             {"all16h", PredCtrl::ALL16H},
1399             {"any32h", PredCtrl::ANY32H},
1400             {"all32h", PredCtrl::ALL32H},
1401             {"any", PredCtrl::ANY},
1402             {"all", PredCtrl::ALL},
1403         };
1404 
1405         const Loc prLoc = NextLoc(0);
1406         bool predInv = Consume(TILDE);
1407         ParseFlagRegRef(m_flagReg);
1408         PredCtrl predCtrl = PredCtrl::NONE;
1409         if (Consume(DOT)) {
1410             ConsumeIdentOneOfOrFail(
1411                 PREDCTRLS,
1412                 predCtrl,
1413                 "expected predication control",
1414                 "invalid predication control");
1415         } else {
1416             predCtrl = PredCtrl::SEQ;
1417         }
1418         m_builder.InstPredication(prLoc, predInv, m_flagReg, predCtrl);
1419     }
1420 
1421 
TryConsumeMmenonic()1422     const OpSpec *TryConsumeMmenonic() {
1423         const Token &tk = Next();
1424         if (tk.lexeme != IDENT) {
1425             return nullptr;
1426         }
1427         const char *p = &m_lexer.GetSource()[tk.loc.offset];
1428         std::string s;
1429         s.reserve((size_t)tk.loc.extent + 1);
1430         for (size_t i = 0; i < tk.loc.extent; i++) {
1431             s += *p++;
1432         }
1433         auto itr = opmap.find(s);
1434         if (itr == opmap.end()) {
1435             return nullptr;
1436         } else {
1437             Skip();
1438             return itr->second;
1439         }
1440     }
1441 
1442 #if 0
1443     // returns the number of characters off
1444     size_t similarityR(
1445         size_t mismatches, size_t shortestStrLen,
1446         const std::string &left, size_t li,
1447         const std::string &right, size_t ri)
1448     {
1449         if (mismatches >= shortestStrLen) { // everything mismatches
1450             return shortestStrLen;
1451         } else if (li == left.size() - 1) { // the rest of right mismatches
1452             return mismatches + right.size() - ri;
1453         } else if (ri == right.size() - 1) { // the rest of left mismatches
1454             return mismatches + left.size() - li;
1455         } else {
1456             if (left[li] != right[ri]) {
1457                 mismatches++;
1458             }
1459             // 1. delete both
1460             auto db = similarityR(
1461                 mismatches, shortestStrLen,
1462                 left, li + 1,
1463                 right, ri + 1);
1464             // 2. delete left
1465             auto dr = similarityR(
1466                 mismatches, shortestStrLen,
1467                 left, li,
1468                 right, ri + 1);
1469             // 3. delete right
1470             auto dl = similarityR(
1471                 mismatches, shortestStrLen,
1472                 left, li + 1,
1473                 right, ri);
1474             return std::min<size_t>(db, std::min<size_t>(dr, dl));
1475         }
1476     }
1477     // roughly the number of characters
1478     float similarity(const std::string &left, const std::string &right) {
1479         if (left.size() > 8 || right.size() > 8)
1480             return 0;
1481         auto shortestLen = std::min<size_t>(left.size(), right.size());
1482         size_t minEdits = similarityR(0, shortestLen, left, 0, right, 0);
1483         return 1.0f - (float)minEdits/(float)shortestLen;
1484     }
1485 #endif
failWithUnexpectedSubfunction(const Loc & loc,const std::string &)1486     void failWithUnexpectedSubfunction(
1487         const Loc &loc,
1488         const std::string &)
1489     {
1490         FailAtT(loc, "unexpected subfunction for op");
1491 #if 0
1492         std::vector<std::pair<float,const char *>> matches;
1493         for (int i = (int)m_opSpec->op + 1;
1494             i < (int)m_opSpec->op + m_opSpec->subopsLength;
1495             i++)
1496         {
1497             const OpSpec &sf = m_model.lookupOpSpec((iga::Op)i);
1498             if (sf.isValid()) {
1499                 auto sim = similarity(sfIdent, sf.mnemonic.str());
1500                 std::cout << sf.mnemonic << ": " << sim << "\n";
1501                 if (sim >= 0.66f) {
1502                     matches.emplace_back(sim, sf.mnemonic.str());
1503                 }
1504             }
1505         }
1506         std::sort(matches.begin(), matches.end(),
1507             std::greater<std::pair<float,const char *>>());
1508         if (!matches.empty()) {
1509             ss << "; did you mean ";
1510             if (matches.size() > 2) {
1511                 ss << "one of: ";
1512             }
1513             // only show the top five
1514             size_t len = std::min<size_t>(5, matches.size());
1515             for (size_t i = 0; i < len; i++) {
1516                 if (i > 0 && i == len - 1) {
1517                     if (len > 2) {
1518                         ss << ", or ";
1519                     } else {
1520                         ss << " or ";
1521                     }
1522                 } else {
1523                     ss << ", ";
1524                 }
1525                 ss << matches[i].second;
1526             }
1527         }
1528 #endif
1529     }
1530 
1531     // Parse BFN expressions.
1532     //
1533     //   BFNExpr = BFNBinExpr
1534     //     where BFNBinExpr = (BFNUnExpr ('&'|'^'|'|') BFNUnExpr)*
1535     //                          regular boolean precedence: & > ^ > |
1536     //           BFNUnExpr = '~'? BFNPrimExpr
1537     //           BFNPrimExpr = '(' BFNExpr ')' | 's0' | 's1' | 's2'
1538     //
1539     // E.g.
1540     // bfn.(s0^(s1|~s2)) ...
1541     //     ^
ParseBfnMnemonicSubfuncExpr()1542     uint8_t ParseBfnMnemonicSubfuncExpr() {
1543         Skip(1); // skip LPAREN
1544         uint32_t val = ParseBfnMnemonicSubfuncExprOR();
1545         ConsumeOrFail(RPAREN, "expected )");
1546         return (uint8_t)val;
1547     }
ParseBfnMnemonicSubfuncExprOR()1548     uint32_t ParseBfnMnemonicSubfuncExprOR() {
1549         uint32_t val = ParseBfnMnemonicSubfuncExprXOR();
1550         while (Consume(PIPE)) {
1551             val |= ParseBfnMnemonicSubfuncExprXOR();
1552         }
1553         return val;
1554     }
ParseBfnMnemonicSubfuncExprXOR()1555     uint32_t ParseBfnMnemonicSubfuncExprXOR() {
1556         uint32_t val = ParseBfnMnemonicSubfuncExprAND();
1557         while (Consume(CIRC)) {
1558             val ^= ParseBfnMnemonicSubfuncExprAND();
1559         }
1560         return val;
1561     }
ParseBfnMnemonicSubfuncExprAND()1562     uint32_t ParseBfnMnemonicSubfuncExprAND() {
1563         uint32_t val = ParseBfnMnemonicSubfuncExprNEG();
1564         while (Consume(AMP)) {
1565             val &= ParseBfnMnemonicSubfuncExprNEG();
1566         }
1567         return val;
1568     }
ParseBfnMnemonicSubfuncExprNEG()1569     uint32_t ParseBfnMnemonicSubfuncExprNEG() {
1570         bool c = Consume(TILDE);
1571         uint32_t val = ParseBfnMnemonicSubfuncExprATOM();
1572         if (c) {
1573             val = (~val & 0xFF);
1574         }
1575         return val;
1576     }
ParseBfnMnemonicSubfuncExprATOM()1577     uint32_t ParseBfnMnemonicSubfuncExprATOM() {
1578         if (Consume(LPAREN)) {
1579             uint32_t val = ParseBfnMnemonicSubfuncExprOR();
1580             ConsumeOrFail(RPAREN, "expected )");
1581             return val;
1582         } else if (LookingAt(IDENT) || LookingAt(INTLIT10)) {
1583             auto symTkn = Next();
1584             auto sfIdent = GetTokenAsString(symTkn);
1585             Skip();
1586             if (sfIdent == "s0") {
1587                 return 0xAA;
1588             } else if (sfIdent == "s1") {
1589                 return 0xCC;
1590             } else if (sfIdent == "s2") {
1591                 return 0xF0;
1592             } else if (sfIdent == "zeros" || sfIdent == "0") {
1593                 return 0x00;
1594             } else if (sfIdent == "ones" || sfIdent == "1") {
1595                 return 0xFF;
1596             } else {
1597                 FailAtT(symTkn.loc, "syntax error in symbolic expression");
1598             }
1599         } else {
1600             FailT("syntax error in symbolic expression");
1601         }
1602         return 0;
1603     }
1604 
1605     //
1606     // Mnemoninc
1607     //     = Ident SubMnemonic?
1608     //     | Ident BrCtl
1609     //     | Ident '(' BFNExpr ')'
1610     //   SubMnemoninc
1611     //     = '.' SfIdent
1612     //     | '.' HEX_INT | '.' DEC_INT
1613     //
1614     //   SfIdent =
1615     //     | BFNExpr = see above
1616     //     | BrnchCtl
1617     //     | SFID
1618     //     | SyncFc
1619     //     | ... other subfunction identifier
ParseMnemonic()1620     const OpSpec *ParseMnemonic() {
1621         const Loc mnemonicLoc = NextLoc();
1622         const OpSpec *pOs = TryConsumeMmenonic();
1623         if (!pOs) {
1624             return nullptr;
1625         }
1626         m_builder.InstOp(pOs);
1627         // GED will reject this otherwise
1628         if (!m_hasWrEn && pOs->op == Op::JMPI) {
1629             WarningAtT(mnemonicLoc,
1630                 "jmpi must have (W) specified (automatically adding)");
1631             m_builder.InstNoMask(mnemonicLoc);
1632         }
1633 
1634         // TODO: abstract this all into a something that's contained in
1635         // the IGA bxml enums files.
1636         //   e.g. use OpSpec::supportsSubfunction()
1637         //        bool Subfunction::FromString(Op, Subfunction&)
1638         if (pOs->supportsBranchCtrl()) {
1639             BranchCntrl brctl = BranchCntrl::OFF;
1640             if (Consume(DOT)) {
1641                 if (!ConsumeIdentEq("b")) {
1642                     FailT("expected 'b' (branch control)");
1643                 }
1644                 brctl = BranchCntrl::ON;
1645             }
1646             m_builder.InstSubfunction(brctl);
1647         } else if (pOs->is(Op::MATH)) {
1648             // e.g. math.*, send.*, etc...
1649             m_builder.InstSubfunction(ParseSubfunctionFromBxmlEnum<MathFC>());
1650         } else if (pOs->is(Op::SYNC)) {
1651             m_builder.InstSubfunction(ParseSubfunctionFromBxmlEnum<SyncFC>());
1652         } else if (pOs->isOneOf(Op::SEND, Op::SENDC)) {
1653             if (platform() >= Platform::XE)
1654                 m_builder.InstSubfunction(
1655                     ParseSubfunctionFromBxmlEnum<SFID>());
1656             // else: it's part of ExDesc
1657         } else if (pOs->is(Op::BFN)) {
1658             m_builder.InstSubfunction(ParseSubfunctionFromBxmlEnumBfnFC());
1659         } else if (pOs->isOneOf(Op::DPAS, Op::DPASW)) {
1660             m_builder.InstSubfunction(ParseSubfunctionFromBxmlEnum<DpasFC>());
1661         } else {
1662             // isOldSend: pre XE will have a subfunction,
1663             // but we pull it from ExDesc
1664             bool isOldSend =
1665                 platform() < Platform::XE || pOs->isSendOrSendsFamily();
1666             IGA_ASSERT(!pOs->supportsSubfunction() || isOldSend,
1667                 "INTERNAL ERROR: subfunction expected");
1668         }
1669 
1670         if (LookingAt(DOT)) {
1671             // maybe an old condition modifier or saturation
1672             FlagModifier fm;
1673             if (LookingAtIdentEq(1, "sat")) {
1674                 FailT("saturation flag prefixes destination operand: "
1675                     "e.g. op (..) (sat)dst ...");
1676             } else if (
1677                 IdentLookupFrom(1, FLAGMODS, fm) ||
1678                 IdentLookupFrom(1, FLAGMODS_LEGACY, fm))
1679             {
1680                 FailT("conditional modifier follows execution mask "
1681                     "info: e.g. op (16|M0)  (le)f0.0 ...");
1682             } else {
1683                 // didn't match flag modifier
1684                 FailT("unexpected . (expected execution size)");
1685             }
1686         }
1687 
1688         return pOs;
1689     }
1690 
1691     template <typename T>
ParseSubfunctionFromBxmlEnum()1692     T ParseSubfunctionFromBxmlEnum() {
1693         if (!Consume(DOT)) {
1694             FailAfterPrev("expected operation subfunction");
1695         }
1696 
1697         auto sfLoc = NextLoc();
1698         if (LookingAt(IDENT)) {
1699             auto loc = NextLoc();
1700             const std::string sfIdent = GetTokenAsString();
1701             Skip();
1702             auto x = FromSyntax<T>(sfIdent);
1703             if (x == T::INVALID) {
1704                 FailAtT(loc, "invalid subfunction");
1705             }
1706             return x;
1707         } else if (LookingAtAnyOf(INTLIT10, INTLIT16)) {
1708             uint32_t val;
1709             (void)ConsumeIntLit(val);
1710             return (T)val;
1711         }
1712         FailAtT(sfLoc, "invalid subfunction");
1713         return (T)-1;
1714     }
1715 
1716     // we treat BFN a little different since we permit a BFN expression
1717     // e.g.  bfn.(s0&~s1^s2)
1718     //
1719     // template <> BfnFC ParseSubfunctionFromBxmlEnum()
1720     // gcc: doesn't permit this specialization
ParseSubfunctionFromBxmlEnumBfnFC()1721     BfnFC ParseSubfunctionFromBxmlEnumBfnFC() {
1722         if (!Consume(DOT)) {
1723             FailAfterPrev("expected subfunction");
1724         }
1725         uint8_t fc = 0;
1726         if (LookingAt(LPAREN)) {
1727             fc = ParseBfnMnemonicSubfuncExpr();
1728         } else if (LookingAtAnyOf(INTLIT10, INTLIT16)) {
1729             auto loc = NextLoc();
1730             uint32_t val = 0;
1731             (void)ConsumeIntLit(val);
1732             if (val > 0xFF) {
1733                 FailAtT(loc, "subfunction value is out of bounds");
1734             }
1735             fc = (uint8_t)val;
1736         } else {
1737             FailT("invalid subexpression");
1738         }
1739         return BfnFC(fc);
1740     }
1741 
1742     // FlagModifierOpt = '(' FlagModif ')' FlagReg
ParseFlagModOpt()1743     void ParseFlagModOpt() {
1744         Loc loc = NextLoc();
1745         if (Consume(LBRACK)) {
1746             // try the full form [(le)f0.0]
1747             FlagModifier flagMod;
1748             if (!TryParseFlagModFlag(flagMod)) {
1749                 FailT("expected flag modifier function");
1750             }
1751             ParseFlagModFlagReg();
1752             ConsumeOrFail(RBRACK, "expected ]");
1753             if (m_opts.deprecatedSyntaxWarnings) {
1754                 WarningAtT(
1755                     loc,
1756                     "deprecated flag modifier syntax (omit the brackets)");
1757             }
1758             m_builder.InstFlagModifier(m_flagReg, flagMod);
1759         } else {
1760             // try the short form
1761             // (le)f0.0
1762             FlagModifier flagMod;
1763             if (!TryParseFlagModFlag(flagMod)) {
1764                 // soft fail (we might be looking at "(sat)r0.0")
1765                 return;
1766             }
1767             ParseFlagModFlagReg();
1768             m_builder.InstFlagModifier(m_flagReg, flagMod);
1769         }
1770     }
1771     // e.g. "(le)"
TryParseFlagModFlag(FlagModifier & flagMod)1772     bool TryParseFlagModFlag(FlagModifier &flagMod) {
1773         if (!LookingAt(LPAREN)) {
1774             return false;
1775         }
1776         if (!IdentLookupFrom(1, FLAGMODS, flagMod)) {
1777             Loc loc = NextLoc(1);
1778             if (!IdentLookupFrom(1, FLAGMODS_LEGACY, flagMod)) {
1779                 return false;
1780             } else if (m_opts.deprecatedSyntaxWarnings) {
1781                 // deprecated syntax
1782                 WarningAtT(loc,
1783                     "deprecated flag modifier syntax: "
1784                     "use ", ToSyntax(flagMod), " for this function");
1785             }
1786         }
1787         Skip(2);
1788         ConsumeOrFail(RPAREN, "expected )");
1789         return true;
1790     }
1791     // e.g. "f0.1"
ParseFlagModFlagReg()1792     void ParseFlagModFlagReg() {
1793         const Loc flregLoc = NextLoc();
1794         RegRef fr;
1795         ParseFlagRegRef(fr);
1796         if (m_flagReg.regNum != REGREF_INVALID.regNum &&
1797             (m_flagReg.regNum != fr.regNum ||
1798                 m_flagReg.subRegNum != fr.subRegNum))
1799         {
1800             FailAtT(flregLoc,
1801                 "flag register must be same for predication "
1802                 "and flag modifier");
1803         }
1804         m_flagReg = fr;
1805     }
1806 
1807 
1808     // Examples:
1809     //   r13.4<2>:hf
1810     //   (sat)r13.0<1>:f
1811     //   r[a0.3,16]<1>:ud
ParseDstOp()1812     void ParseDstOp() {
1813         // We want to track the beginning of the "operand", not just the register.
1814         // That includes the saturate flag prefix(sat) on the dst.
1815         // That is we need:
1816         // (sat)r13.3:ud
1817         // ^    ^
1818         // | | not here
1819         // |
1820         // | here
1821         const Loc opStart = NextLoc(0);
1822         // ParseSatOpt increments m_offset.
1823         if (ParseSatOpt()) {
1824             m_builder.InstDstOpSaturate();
1825         }
1826         // Note that, if there is SAT token, opStart != regStart.
1827         // This is because m_offset changed.
1828         const Loc regStart = NextLoc(0);
1829         if (ConsumeIdentEq("r")) {
1830             ParseDstOpRegInd(opStart, 0);
1831         } else {
1832             const RegInfo *regInfo;
1833             int regNum;
1834             if (!ConsumeReg(regInfo, regNum)) {
1835                 FailT("invalid destination register");
1836             }
1837             if (regInfo && !regInfo->isRegNumberValid(regNum)) {
1838                 FailT("invalid destination register number "
1839                     "(", regInfo->syntax, " only has ", regInfo->numRegs,
1840                     " registers on this platform)");
1841             }
1842             if (LookingAt(LBRACK)) {
1843                 ParseDstOpRegInd(opStart, regNum * 32);
1844             } else {
1845                 assert(regInfo != nullptr);
1846                 FinishDstOpRegDirSubRegRgnTy(
1847                     opStart, regStart, *regInfo, regNum);
1848             }
1849         }
1850     }
1851 
1852 
1853     // r13
1854     // null
1855     // r13:w (non-standard)
ParseSendDstOp()1856     void ParseSendDstOp() {
1857         const Loc regStart = NextLoc(0);
1858         if (ConsumeIdentEq("r")) {
1859             ParseDstOpRegInd(regStart, 0);
1860         } else {
1861             const RegInfo *ri = NULL;
1862             int regNum = 0;
1863             if (!ConsumeReg(ri, regNum)) {
1864                 FailT("invalid send destination register");
1865             }
1866             if (!ri->isRegNumberValid(regNum)) {
1867                 FailT("invalid destination register number "
1868                     "(", ri->syntax, " only has ", ri->numRegs,
1869                     " registers on this platform)");
1870             }
1871             if (LookingAt(LBRACK)) {
1872                 FailT("this form of indirect (r3[a0.0,16]) is invalid for "
1873                     "send dst operand; use regular form: r[a0.0,16]");
1874             } else {
1875                 FinishDstOpRegDirSubRegRgnTy(regStart, regStart, *ri, regNum);
1876             }
1877         }
1878     }
1879 
1880 
1881     // (sat)
ParseSatOpt()1882     bool ParseSatOpt() {
1883         // TODO: expand to tokens so (sat) can be imm expr
1884         return Consume(SAT);
1885     }
1886 
1887 
1888     // .mme2 or .nomme
ParseMathMacroReg()1889     MathMacroExt ParseMathMacroReg() {
1890         auto loc = NextLoc();
1891         MathMacroExt mme = MathMacroExt::INVALID;
1892         const char *EXPECTED =
1893             "expected math macro register "
1894             "(e.g. .mme0, ..., .mme7, or .nomme)";
1895         ConsumeOrFail(DOT, EXPECTED);
1896         if (ConsumeIdentOneOf<MathMacroExt>(MATHMACROREGS, mme)) {
1897             return mme;
1898         }
1899         // determine if we support the old-style
1900         bool supportOldStyleAcc2ToAcc7 = true;
1901         supportOldStyleAcc2ToAcc7 = platform() < Platform::XE;
1902 
1903         if (supportOldStyleAcc2ToAcc7) {
1904             if (ConsumeIdentOneOf<MathMacroExt>(MATHMACROREGS_OLDSTYLE, mme)) {
1905                 // warn?
1906                 WarningS(loc, "old-style math macro register (use mme)");
1907                 return mme;
1908             }
1909         }
1910 
1911         // favor the new error message
1912         FailS(loc, EXPECTED);
1913         return mme;
1914     }
1915 
1916 
1917     // REG ('.' INT)? DstRgn (':' DstTy)?
1918     //   where DstRgn = '<' INT '>'
1919     //
1920     // E.g. r13.4<2>:t
FinishDstOpRegDirSubRegRgnTy(const Loc & opStart,const Loc & regnameLoc,const RegInfo & ri,int regNum)1921     void FinishDstOpRegDirSubRegRgnTy(
1922         const Loc &opStart,
1923         const Loc &regnameLoc,
1924         const RegInfo &ri,
1925         int regNum)
1926     {
1927         // subregister or implicit accumulator operand
1928         // e.g. .4 or .acc3
1929         Loc subregLoc = NextLoc(0);
1930         int subregNum = 0;
1931 
1932         // <1>
1933         Region::Horz rgnHz = Region::Horz::HZ_1;
1934 
1935         MathMacroExt mmeReg = MathMacroExt::INVALID;
1936         if (m_opSpec->isSendOrSendsFamily() && Consume(DOT)) {
1937             ConsumeIntLitOrFail(subregNum, "expected subregister");
1938             // whine about them using a subregister on a send operand
1939             if (m_opts.deprecatedSyntaxWarnings) {
1940                 WarningAtT(subregLoc,
1941                     "send operand subregisters have no effect"
1942                     " and are deprecated syntax");
1943             }
1944             if (m_opSpec->isSendOrSendsFamily() && LookingAt(LANGLE)) {
1945                 if (m_opts.deprecatedSyntaxWarnings) {
1946                     // whine about them using a region
1947                     WarningT("send operand region has no effect and is"
1948                         " deprecated syntax");
1949                 }
1950             }
1951             rgnHz = ParseDstOpRegion();
1952         } else if (m_builder.isMacroOp()) {
1953             // implicit accumulator operand
1954             mmeReg = ParseMathMacroReg();
1955         } else {
1956             // non-send with subregister.
1957             // regular subregister
1958             if (Consume(DOT)) {
1959                 ConsumeIntLitOrFail(subregNum, "expected subregister");
1960             } else {
1961                 // implicit subregister
1962                 //  E.g. r12:d  ...
1963                 subregNum = 0;
1964             }
1965         }
1966 
1967         // <1>
1968         rgnHz = ParseDstOpRegion();
1969 
1970         // :t
1971         Type dty = Type::INVALID;
1972         if (m_opSpec->isSendOrSendsFamily()) {
1973             // special handling for send types
1974             dty = ParseSendOperandTypeWithDefault(-1);
1975         } else {
1976             dty = ParseDstOpTypeWithDefault();
1977         }
1978 
1979         // ensure the subregister is not out of bounds
1980         if (dty != Type::INVALID) {
1981             int typeSize = TypeSizeInBits(dty)/8;
1982             if (!ri.isSubRegByteOffsetValid(
1983                 regNum, subregNum * typeSize, m_model.getGRFByteSize())) {
1984                 if (ri.regName == RegName::GRF_R) {
1985                     ErrorAtT(subregLoc,
1986                         "subregister out of bounds for data type ",
1987                         ToSyntax(dty));
1988                 } else {
1989                     // Print warning for non-GRF register, in case for those
1990                     // valid but strange case e.g. null0.20:f
1991                     WarningAtT(subregLoc,
1992                         "subregister out of bounds for data type");
1993                 }
1994             } else if (typeSize < ri.accGran) {
1995                 WarningAtT(regnameLoc,
1996                     "access granularity too small for data type");
1997             }
1998         }
1999 
2000         if (m_builder.isMacroOp()) {
2001             m_builder.InstDstOpRegMathMacroExtReg(
2002                 opStart, ri.regName, regNum, mmeReg, rgnHz, dty);
2003         } else {
2004             RegRef reg(regNum, subregNum);
2005             m_builder.InstDstOpRegDirect(
2006                 opStart, ri.regName, reg, rgnHz, dty);
2007         }
2008     }
2009 
2010 
2011     // e.g. [a0.4,  16]
2012     //  or  [a0.4 + 16]
2013     //  or  [a0.4 - 16]
ParseIndOpArgs(RegRef & addrRegRef,int & addrOff,RegName & regName)2014     void ParseIndOpArgs(RegRef &addrRegRef, int &addrOff, RegName& regName) {
2015         ConsumeOrFail(LBRACK, "expected [");
2016         if (!ParseAddrRegRefOpt(addrRegRef, regName)) {
2017             FailT("expected address subregister");
2018         }
2019         Loc addrOffLoc = NextLoc();
2020         if (Consume(COMMA)) {
2021             bool neg = Consume(SUB);
2022             ConsumeIntLitOrFail(addrOff, "expected indirect address offset");
2023             if (neg)
2024                 addrOff *= -1;
2025         } else if (Consume(ADD)) {
2026             ConsumeIntLitOrFail(addrOff, "expected indirect address offset");
2027         } else if (Consume(SUB)) {
2028             ConsumeIntLitOrFail(addrOff, "expected indirect address offset");
2029             addrOff *= -1;
2030         } else {
2031             addrOff = 0;
2032         }
2033 
2034         // check if the imm offset out of bound
2035         int addroffMax = 511;
2036         int addroffMin = -512;
2037         if (platform() >= Platform::XE_HPC) {
2038             addroffMax = 1023;
2039             addroffMin = -1024;
2040         }
2041         if (addrOff < addroffMin || addrOff > addroffMax)
2042         {
2043             FailAtT(addrOffLoc,
2044                 "immediate offset is out of range; must be in "
2045                 "[", addroffMin, ",", std::to_string(addroffMax), "]");
2046         }
2047 
2048         ConsumeOrFail(RBRACK, "expected ]");
2049     }
2050 
2051 
2052     // '[' 'a0' '.' INT (',' INT)? ']' ('<' INT '>')?)? TY?
2053     // e.g [a0.3,16]<1>:t
ParseDstOpRegInd(const Loc & opStart,int baseAddr)2054     void ParseDstOpRegInd(const Loc &opStart, int baseAddr) {
2055         // [a0.4,16]
2056         int addrOff;
2057         RegRef addrRegRef;
2058         RegName regName = RegName::INVALID;
2059         ParseIndOpArgs(addrRegRef, addrOff, regName);
2060         addrOff += baseAddr;
2061         //
2062         // <1>
2063         Region::Horz rgnHz = ParseDstOpRegion();
2064         //
2065         // :t
2066         Type dty = ParseDstOpTypeWithDefault();
2067         m_builder.InstDstOpRegIndirect(
2068             opStart, addrRegRef, addrOff, rgnHz, dty);
2069     }
2070 
2071 
2072     // '<' INT '>'
ParseDstOpRegion()2073     Region::Horz ParseDstOpRegion() {
2074         if (!LookingAt(LANGLE)) {
2075             if (m_opSpec->hasImplicitDstRegion(m_builder.isMacroOp())) {
2076                 return m_opSpec->implicitDstRegion(m_builder.isMacroOp()).getHz();
2077             } else {
2078                 return Region::Horz::HZ_1;
2079             }
2080         }
2081 
2082         Region::Horz rgnHz = Region::Horz::HZ_1;
2083         if (Consume(LANGLE)) {
2084             const Loc loc = NextLoc();
2085             int rgnHzInt;
2086             ConsumeIntLitOrFail(rgnHzInt, "destination region argument");
2087             switch (rgnHzInt) {
2088             case 1: rgnHz = Region::Horz::HZ_1; break;
2089             case 2: rgnHz = Region::Horz::HZ_2; break;
2090             case 4: rgnHz = Region::Horz::HZ_4; break;
2091             default:
2092                 FailS(loc, "invalid destination region");
2093             }
2094             ConsumeOrFail(RANGLE,"expected >");
2095         }
2096 
2097         return rgnHz;
2098     }
2099 
2100 
2101     // E.g. 3 in "a0.3"
ParseAddrRegRefOpt(RegRef & addrReg,RegName & regName)2102     bool ParseAddrRegRefOpt(RegRef& addrReg, RegName& regName) {
2103         const RegInfo *ri;
2104         int regNum;
2105         regName = RegName::INVALID;
2106         if (!ConsumeReg(ri, regNum)) {
2107             return false;
2108         }
2109         regName = ri->regName;
2110         if (regNum != 0 ||
2111             (ri->regName != RegName::ARF_A
2112             )) {
2113             FailT("expected address register for indirect access (a0)");
2114         }
2115         if (!Consume(DOT)) {
2116             FailT("expected .");
2117         }
2118         addrReg.regNum = addrReg.subRegNum = 0;
2119         ConsumeIntLitOrFail(
2120             addrReg.subRegNum, "expected address register subregister");
2121         // TODO: range-check against RegInfo for "a"
2122         return true;
2123     }
2124 
2125 
2126     // same as ParseSrcOp, but semantically chekcs that it's a label
ParseSrcOpLabel(int srcOpIx)2127     void ParseSrcOpLabel(int srcOpIx) {
2128         ParseSrcOp(srcOpIx);
2129         if (m_srcKinds[srcOpIx] != Operand::Kind::LABEL &&
2130             m_srcKinds[srcOpIx] != Operand::Kind::IMMEDIATE)
2131         {
2132             FailAtT(m_srcLocs[srcOpIx],
2133                 "src", srcOpIx, " must be an immediate label");
2134         }
2135     }
2136 
2137 
ParseSrcOp(int srcOpIx)2138     void ParseSrcOp(int srcOpIx) {
2139         m_srcLocs[srcOpIx] = NextLoc();
2140 
2141         // TODO: sink this down to the immediate literal case specially
2142         // For now, we leave it here since TryParseConstExpr() will otherwise
2143         // fail on "-r12:f" would fail: - as unary negation then r12 fails to
2144         // resolve
2145         const Token regnameTk = Next();
2146         const RegInfo *regInfo;
2147         int regNum;
2148 
2149         // first try and parse as register
2150         m_lexer.Mark();
2151         bool pipeAbs = false;
2152         SrcModifier srcMods = ParseSrcModifierOpt(pipeAbs);
2153         if (ConsumeIdentEq("r")) {
2154             // canonical register indirect
2155             // r[a0.4,-32]
2156             m_srcKinds[srcOpIx] = Operand::Kind::INDIRECT;
2157             ParseSrcOpInd(srcOpIx, m_srcLocs[srcOpIx], srcMods, 0);
2158             // register, symbolic immediate, label, ...
2159             if (pipeAbs) {
2160                 ConsumeOrFailAfterPrev(PIPE, "expected |");
2161             }
2162             if (!m_opSpec->supportsSourceModifiers() &&
2163                 srcMods != SrcModifier::NONE)
2164             {
2165                 FailS(m_srcLocs[srcOpIx], "source modifier not supported");
2166             }
2167         } else if (ConsumeReg(regInfo, regNum)) {
2168             // normal register access
2169             //   r13.3<0;1,0>
2170             //   acc3
2171             m_srcKinds[srcOpIx] = Operand::Kind::DIRECT;
2172             if (!m_opSpec->supportsSourceModifiers() &&
2173                 srcMods != SrcModifier::NONE)
2174             {
2175                 FailS(m_srcLocs[srcOpIx], "source modifier not supported");
2176             }
2177             FinishSrcOpRegDirSubRegRgnTy(
2178                 srcOpIx,
2179                 m_srcLocs[srcOpIx],
2180                 regnameTk.loc,
2181                 srcMods,
2182                 *regInfo,
2183                 regNum);
2184             if (pipeAbs) {
2185                 ConsumeOrFailAfterPrev(PIPE, "expected |");
2186             }
2187         } else {
2188             // backtrack to before any "source modifier"
2189             m_lexer.Reset();
2190             if (pipeAbs) {
2191                 Skip(1);
2192             }
2193 
2194             // try as raw label first
2195             if ((m_opSpec->isBranching() || m_opSpec->op == Op::MOV) &&
2196                 LookingAt(IDENT) &&
2197                 !LookingAtIdentEq("snan") &&
2198                 !LookingAtIdentEq("qnan") &&
2199                 !LookingAtIdentEq("nan") &&
2200                 !LookingAtIdentEq("inf"))
2201             {
2202                 if (pipeAbs) {
2203                     FailS(regnameTk.loc, "unexpected |");
2204                 }
2205                 // e.g. LABEL64 (mustn't be an expression)
2206                 m_srcKinds[srcOpIx] = Operand::Kind::LABEL;
2207                 std::string str = GetTokenAsString();
2208                 Skip(1);
2209                 FinishSrcOpImmLabel(
2210                     srcOpIx,
2211                     m_srcLocs[srcOpIx],
2212                     regnameTk.loc,
2213                     str);
2214             } else {
2215                 ImmVal immVal;
2216                 if (TryParseConstExpr(immVal))
2217                 {
2218                     // (does not match labels)
2219                     m_srcKinds[srcOpIx] = Operand::Kind::IMMEDIATE;
2220                     if (pipeAbs) {
2221                         immVal.Abs();
2222                     }
2223                     FinishSrcOpImmValue(
2224                         srcOpIx,
2225                         m_srcLocs[srcOpIx],
2226                         regnameTk,
2227                         immVal);
2228                     if (pipeAbs) {
2229                         ConsumeOrFailAfterPrev(PIPE, "expected |");
2230                     }
2231                 }
2232             }
2233         }
2234     } // ParseSrcOp
2235 
2236     // .allrd/.allwr SBID set first
2237     //   "($11,$2,$7)" means ((1 << 11) | (1 << 2) | (1 << 7))
2238     // we do also permit
2239     //   "()" as the empty set
ParseSyncSrc0Op()2240     void ParseSyncSrc0Op() {
2241         auto sf = m_builder.getSubfunction().sync;
2242         bool isSyncSetOp = sf == SyncFC::ALLRD || sf == SyncFC::ALLWR;
2243         bool lookingAtPfx =
2244             LookingAtSeq(LPAREN, DOLLAR) || LookingAtSeq(LPAREN, RPAREN);
2245         if (!isSyncSetOp || !lookingAtPfx) {
2246             // e.g. other sync op, or immediate literal, or null
2247             ParseSrcOp(0);
2248             return;
2249         }
2250         m_srcLocs[0] = NextLoc();
2251         Skip(); // LPAREN
2252         uint32_t sbidSet = 0x0;
2253         auto parseSbid = [&] () {
2254             uint32_t sbid = 0;
2255             ConsumeOrFail(DOLLAR, "expected SBID");
2256             auto nxt = NextLoc();
2257             ConsumeIntLitOrFail(sbid, "expected SBID");
2258             if (sbid >= 32)
2259                 FailAtT(nxt, "SBID out of bounds");
2260             sbidSet |= 1 << sbid;
2261             return true;
2262         };
2263         while (LookingAt(DOLLAR)) {
2264             parseSbid();
2265             if (!Consume(COMMA)) {
2266                 break;
2267             }
2268         }
2269         ConsumeOrFail(RPAREN, "expected )");
2270         ImmVal val;
2271         val = sbidSet;
2272         auto srcTy = ParseSrcOpTypeWithoutDefault(0, true);
2273         m_builder.InstSrcOpImmValue(0, m_srcLocs[0], val, srcTy);
2274     }
2275 
ParseSrcOpInd(int srcOpIx,const Loc & opStart,const SrcModifier & srcMods,int baseOff)2276     void ParseSrcOpInd(
2277         int srcOpIx,
2278         const Loc &opStart,
2279         const SrcModifier &srcMods,
2280         int baseOff)
2281     {
2282         m_srcKinds[srcOpIx] = Operand::Kind::INDIRECT;
2283 
2284         // [a0.4 + 16]
2285         int addrOff;
2286         RegRef addrRegRef;
2287         RegName regName = RegName::INVALID;
2288         ParseIndOpArgs(addrRegRef, addrOff, regName);
2289         addrOff += baseOff;
2290 
2291         // regioning... <V;W,H> or <V,H>
2292         Region rgn = ParseSrcOpRegionInd(srcOpIx);
2293 
2294         // :t
2295         Type sty = ParseSrcOpTypeWithDefault(srcOpIx, true);
2296 
2297         IGA_ASSERT(regName != RegName::INVALID, "SrcOpInd must not have INVALID register name");
2298         m_builder.InstSrcOpRegIndirect(
2299             srcOpIx, opStart, srcMods, regName, addrRegRef, addrOff, rgn, sty);
2300     }
2301 
2302 
2303     // REG ('.' INT)? SrcRgn? (':' SrcTy)?
2304     //    ^ HERE
2305     //
2306     // E.g. r13.4<2>:f
2307     // E.g. r13.4<0;1,0>:f
2308     // E.g. r13.4<8;8,1>:f
2309     // E.g. r13.acc2:f
2310     // E.g. ce:ud
2311     // E.g. ip:ud
2312     // E.g. a0.1
FinishSrcOpRegDirSubRegRgnTy(int srcOpIx,const Loc & opStart,const Loc & regnameLoc,const SrcModifier & srcMod,const RegInfo & ri,int regNum)2313     void FinishSrcOpRegDirSubRegRgnTy(
2314         int srcOpIx,
2315         const Loc &opStart,
2316         const Loc &regnameLoc,
2317         const SrcModifier &srcMod,
2318         const RegInfo &ri,
2319         int regNum)
2320     {
2321         // subregister or implicit accumulator operand
2322         // e.g. .4 or .acc3
2323         int subregNum;
2324         Region rgn;
2325         Loc subregLoc = NextLoc(1);
2326         MathMacroExt mme = MathMacroExt::INVALID;
2327         bool hasExplicitSubreg = false;
2328         if (m_builder.isMacroOp()) {
2329             // implicit accumulator operand
2330             // r13.acc2:f
2331             subregNum = 0;
2332             mme = ParseMathMacroReg();
2333             // region is implicitly <1;1,0>
2334             rgn = Region::SRC110;
2335             // below we can override it if we really really want to
2336         } else {
2337             // regular src with subregister
2338             // r13.4....
2339             //    ^
2340             if (Consume(DOT)) {
2341                 ConsumeIntLitOrFail(subregNum, "expected subregister");
2342                 hasExplicitSubreg = true;
2343             } else { // default to 0
2344                 subregLoc = NextLoc(0);
2345                 subregNum = 0;
2346             }
2347         }
2348 
2349         // for ternary ops <V;H> or <H>
2350         // for other regions <V;W,H>
2351         if (m_opSpec->isTernary()) {
2352             if (srcOpIx < 2) {
2353                 rgn = ParseSrcOpRegionVH(srcOpIx, hasExplicitSubreg);
2354             } else {
2355                 rgn = ParseSrcOpRegionH(srcOpIx, hasExplicitSubreg);
2356             }
2357         } else {
2358             rgn = ParseSrcOpRegionVWH(ri, srcOpIx, hasExplicitSubreg);
2359         }
2360 
2361         // :t
2362         Type sty = Type::INVALID;
2363         if (m_opSpec->isSendOrSendsFamily()) {
2364             sty = ParseSendOperandTypeWithDefault(srcOpIx);
2365         } else {
2366             sty = ParseSrcOpTypeWithDefault(srcOpIx, false);
2367         }
2368 
2369         if (sty != Type::INVALID) {
2370             // ensure the subregister is not out of bounds
2371             int typeSize = TypeSizeInBits(sty)/8;
2372             if (ri.isRegNumberValid(regNum) &&
2373                 !ri.isSubRegByteOffsetValid(regNum, subregNum * typeSize, m_model.getGRFByteSize()))
2374             {
2375                 // don't add an extra error if the parent register is
2376                 // already out of bounds
2377                 WarningS(subregLoc, "subregister out of bounds");
2378             } else if (typeSize < ri.accGran) {
2379                 WarningS(regnameLoc, "register access granularity too small type");
2380             }
2381         }
2382 
2383         if (m_builder.isMacroOp()) {
2384             m_builder.InstSrcOpRegMathMacroExtReg(
2385                 srcOpIx,
2386                 opStart,
2387                 srcMod,
2388                 ri.regName,
2389                 regNum,
2390                 mme,
2391                 rgn,
2392                 sty);
2393         } else {
2394             RegRef reg((uint8_t)regNum, (uint8_t)subregNum);
2395             m_builder.InstSrcOpRegDirect(
2396                 srcOpIx,
2397                 opStart,
2398                 srcMod,
2399                 ri.regName,
2400                 reg,
2401                 rgn,
2402                 sty);
2403         }
2404     }
2405 
2406 
2407     // '<' INT ';' INT ',' INT '>'     (VxWxH)
ParseSrcOpRegionVWH(const RegInfo & ri,int srcOpIx,bool hasExplicitSubreg)2408     Region ParseSrcOpRegionVWH(
2409         const RegInfo &ri, int srcOpIx, bool hasExplicitSubreg)
2410     {
2411         if (m_opSpec->hasImplicitSrcRegion(
2412             srcOpIx, m_execSize, m_builder.isMacroOp()))
2413         {
2414             if (!LookingAt(LANGLE)) {
2415                 return m_opSpec->implicitSrcRegion(
2416                     srcOpIx, m_execSize, m_builder.isMacroOp());
2417             } else {
2418                 WarningT(
2419                     m_opSpec->mnemonic.str(),
2420                     ".Src", srcOpIx, " region should be implicit");
2421             }
2422         }
2423 
2424         Region rgn = Region::SRC010;
2425         if (Consume(LANGLE)) {
2426             rgn.set(ParseRegionVert());
2427             ConsumeOrFailAfterPrev(SEMI, "expected ;");
2428             rgn.set(ParseRegionWidth());
2429             ConsumeOrFailAfterPrev(COMMA, "expected ,");
2430             rgn.set(ParseRegionHorz());
2431             ConsumeOrFailAfterPrev(RANGLE, "expected >");
2432         } else if (m_opSpec->isSendOrSendsFamily()) {
2433             rgn = defaultSendOperandRegion(ri.regName, srcOpIx);
2434         } else if (ri.supportsRegioning()) {
2435             // N.B. <1;1,0> won't coissue on PreGEN11
2436             rgn = hasExplicitSubreg || m_execSize == ExecSize::SIMD1 ?
2437                 Region::SRC010 :
2438                 Region::SRC110;
2439         } else {
2440             rgn = Region::SRC010;
2441         }
2442         return rgn;
2443     }
2444 
2445     // '<' INT ';' INT '>'   (CNL Align1 ternary)
ParseSrcOpRegionVH(int srcOpIx,bool hasExplicitSubreg)2446     Region ParseSrcOpRegionVH(int srcOpIx, bool hasExplicitSubreg)
2447     {
2448         if (m_opSpec->hasImplicitSrcRegion(
2449             srcOpIx, m_execSize, m_builder.isMacroOp()))
2450         {
2451             if (!LookingAt(LANGLE)) {
2452                 return m_opSpec->implicitSrcRegion(
2453                     srcOpIx, m_execSize, m_builder.isMacroOp());
2454             } else if (m_opts.deprecatedSyntaxWarnings) {
2455                 WarningT(
2456                     m_opSpec->mnemonic.str(), ".Src", srcOpIx,
2457                     " region should be implicit");
2458             }
2459         }
2460         Region rgn = Region::SRC010;
2461         if (Consume(LANGLE)) {
2462             rgn.set(ParseRegionVert());
2463             ConsumeOrFailAfterPrev(SEMI, "expected ;");
2464             rgn.set(Region::Width::WI_INVALID);
2465             rgn.set(ParseRegionHorz());
2466             ConsumeOrFailAfterPrev(RANGLE, "expected >");
2467         } else {
2468             bool scalarAccess = hasExplicitSubreg || m_execSize == ExecSize::SIMD1;
2469             if (scalarAccess) {
2470                 rgn = Region::SRC0X0;
2471             } else {
2472                 // packed access
2473                 rgn = platform() >= Platform::XE ?
2474                     Region::SRC1X0 : Region::SRC2X1; // most conservative mux
2475             }
2476         }
2477         return rgn;
2478     }
2479 
2480 
2481     // '<' INT '>'   (CNL Align1 ternary src2)
ParseSrcOpRegionH(int srcOpIx,bool hasExplicitSubreg)2482     Region ParseSrcOpRegionH(int srcOpIx, bool hasExplicitSubreg)
2483     {
2484         if (m_opSpec->hasImplicitSrcRegion(
2485             srcOpIx, m_execSize, m_builder.isMacroOp()))
2486         {
2487             if (!LookingAt(LANGLE)) {
2488                 return m_opSpec->implicitSrcRegion(
2489                     srcOpIx, m_execSize, m_builder.isMacroOp());
2490             } else if (m_opts.deprecatedSyntaxWarnings) {
2491                 WarningT(
2492                     m_opSpec->mnemonic.str(), ".Src", srcOpIx,
2493                     " region should be implicit");
2494             }
2495         }
2496 
2497         Region rgn;
2498         rgn.bits = 0; // needed to clear _padding
2499         if (Consume(LANGLE)) {
2500             rgn.set(
2501                 Region::Vert::VT_INVALID,
2502                 Region::Width::WI_INVALID,
2503                 ParseRegionHorz());
2504             ConsumeOrFailAfterPrev(RANGLE, "expected >");
2505         } else {
2506             rgn = hasExplicitSubreg || m_execSize == ExecSize::SIMD1 ?
2507                 Region::SRCXX0 :
2508                 Region::SRCXX1;
2509         }
2510         return rgn;
2511     }
2512 
defaultSendOperandRegion(RegName rn,int opIx) const2513     Region defaultSendOperandRegion(RegName rn, int opIx) const {
2514         Region r = Region::INVALID;
2515         if (opIx < 0) {
2516             if (m_opSpec->hasImplicitDstRegion(m_builder.isMacroOp())) {
2517                 r = m_opSpec->implicitDstRegion(m_builder.isMacroOp());
2518             }
2519         } else {
2520             if (m_opSpec->hasImplicitSrcRegion(
2521                 opIx, m_execSize, m_builder.isMacroOp())) {
2522                 r =  m_opSpec->implicitSrcRegion(
2523                     0, m_execSize, m_builder.isMacroOp());
2524             } else if (rn == RegName::ARF_NULL) {
2525                 r = Region::SRC010;
2526             } else {
2527                 r = Region::SRC110;
2528             }
2529         }
2530         return r;
2531     }
2532 
2533     // '<' INT ',' INT '>'             (VxH mode)
2534     // '<' INT ';' INT ',' INT '>'
ParseSrcOpRegionInd(int srcOpIx)2535     Region ParseSrcOpRegionInd(int srcOpIx) {
2536         if (m_opSpec->hasImplicitSrcRegion(
2537             srcOpIx, m_execSize, m_builder.isMacroOp()))
2538         {
2539             if (!LookingAt(LANGLE)) {
2540                 return m_opSpec->implicitSrcRegion(
2541                     srcOpIx, m_execSize, m_builder.isMacroOp());
2542             } else if (m_opts.deprecatedSyntaxWarnings) {
2543                 WarningT(m_opSpec->mnemonic.str(),
2544                     ".Src", srcOpIx, " region should be implicit");
2545             }
2546         }
2547 
2548         Region rgn;
2549         rgn.bits = 0;
2550         if (Consume(LANGLE)) {
2551             Loc arg1Loc = NextLoc();
2552             int arg1;
2553             ConsumeIntLitOrFail(arg1, "syntax error in source region");
2554             if (Consume(COMMA)) {
2555                 // <W,H>
2556                 rgn.set(Region::Vert::VT_VxH);
2557                 switch(arg1) {
2558                 case  1:
2559                 case  2:
2560                 case  4:
2561                 case  8:
2562                 case 16:
2563                     rgn.w = arg1;
2564                     break;
2565                 default:
2566                     FailAtT(arg1Loc, "invalid region width");
2567                     rgn.set(Region::Width::WI_INVALID);
2568                 }
2569                 rgn.set(ParseRegionHorz());
2570             } else {
2571                 // <V;W,H>
2572                 ConsumeOrFailAfterPrev(SEMI, "expected ;");
2573                 switch(arg1) {
2574                 case  0:
2575                 case  1:
2576                 case  2:
2577                 case  4:
2578                 case  8:
2579                 case 16:
2580                 case 32:
2581                     rgn.v = arg1;
2582                     break;
2583                 default:
2584                     FailAtT(arg1Loc, "invalid region vertical stride");
2585                     rgn.set(Region::Vert::VT_INVALID);
2586                 }
2587                 rgn.set(ParseRegionWidth());
2588                 ConsumeOrFailAfterPrev(COMMA, "expected ,");
2589                 rgn.set(ParseRegionHorz());
2590             }
2591             ConsumeOrFailAfterPrev(RANGLE, "expected >");
2592         } else {
2593             rgn = Region::SRC110;
2594         }
2595         return rgn;
2596     }
2597 
2598 
ParseRegionVert()2599     Region::Vert ParseRegionVert() {
2600         Loc loc = NextLoc();
2601         int x;
2602         ConsumeIntLitOrFail(x, "syntax error in region (vertical stride)");
2603         Region::Vert vs;
2604         switch(x) {
2605         case  0: vs = Region::Vert::VT_0; break;
2606         case  1: vs = Region::Vert::VT_1; break;
2607         case  2: vs = Region::Vert::VT_2; break;
2608         case  4: vs = Region::Vert::VT_4; break;
2609         case  8: vs = Region::Vert::VT_8; break;
2610         case 16: vs = Region::Vert::VT_16; break;
2611         case 32: vs = Region::Vert::VT_32; break;
2612         default:
2613             FailAtT(loc, "invalid region vertical stride");
2614             vs = Region::Vert::VT_INVALID;
2615         }
2616         return vs;
2617     }
2618 
2619 
ParseRegionWidth()2620     Region::Width ParseRegionWidth() {
2621         Loc loc = NextLoc();
2622         int x;
2623         ConsumeIntLitOrFail(x, "syntax error in region (width)");
2624         Region::Width wi;
2625         switch(x) {
2626         case  1: wi = Region::Width::WI_1; break;
2627         case  2: wi = Region::Width::WI_2; break;
2628         case  4: wi = Region::Width::WI_4; break;
2629         case  8: wi = Region::Width::WI_8; break;
2630         case 16: wi = Region::Width::WI_16; break;
2631         default:
2632             FailAtT(loc, "invalid region width");
2633             wi = Region::Width::WI_INVALID;
2634         }
2635         return wi;
2636     }
2637 
2638 
ParseRegionHorz()2639     Region::Horz ParseRegionHorz() {
2640         Loc loc = NextLoc();
2641         int x;
2642         ConsumeIntLitOrFail(x, "syntax error in region (horizontal stride)");
2643         Region::Horz hz;
2644         switch(x) {
2645         case  0: hz = Region::Horz::HZ_0; break;
2646         case  1: hz = Region::Horz::HZ_1; break;
2647         case  2: hz = Region::Horz::HZ_2; break;
2648         case  4: hz = Region::Horz::HZ_4; break;
2649         default:
2650             FailAtT(loc, "invalid region horizontal stride");
2651             hz = Region::Horz::HZ_INVALID;
2652         }
2653         return hz;
2654     }
2655 
2656 
CheckLiteralBounds(const Loc & opStart,Type type,const ImmVal & val,int64_t mn,int64_t mx)2657     void CheckLiteralBounds(
2658         const Loc &opStart,
2659         Type type,
2660         const ImmVal &val,
2661         int64_t mn,
2662         int64_t mx)
2663     {
2664         if (val.s64 < mn || val.s64 > mx) {
2665             WarningAtT(opStart,
2666                 "literal is out of bounds for type ", ToSyntax(type));
2667         }
2668     }
2669 
2670 
FinishSrcOpImmValue(int srcOpIx,const Loc & opStart,const Token & valToken,ImmVal & val)2671     void FinishSrcOpImmValue(
2672         int srcOpIx,
2673         const Loc &opStart,
2674         const Token &valToken,
2675         ImmVal &val)
2676     {
2677         // convert to the underlying data type
2678         Type sty = ParseSrcOpTypeWithoutDefault(srcOpIx, true);
2679 
2680         if (sty == Type::BF) {
2681             // ensure there's no BF type imm value
2682             ErrorAtT(opStart, "Imm operand with BF type is not allowed");
2683         }
2684 
2685         switch (sty) {
2686         case Type::B:
2687         case Type::UB:
2688         case Type::W:
2689         case Type::UW:
2690         case Type::D:
2691         case Type::UD:
2692         case Type::Q:
2693         case Type::UQ:
2694         case Type::V:
2695         case Type::UV:
2696         case Type::VF:
2697             if (!val.isS64()) {
2698                 ErrorAtT(opStart,
2699                     "literal must be integral for type ", ToSyntax(sty));
2700             }
2701             break;
2702         case Type::HF:
2703             if (val.isS64()) { // The value was parsed as integral
2704                 if (valToken.lexeme == INTLIT10 && val.u64 != 0) {
2705                     // base10
2706                     // examples: 2:f or (1+2):f
2707                     FailAtT(opStart,
2708                         "immediate integer floating point literals must be "
2709                         "in hex or binary (e.g. 0x7F800000:f)");
2710                 }
2711                 // base2 or base16
2712                 // e.g. 0x1234:hf  (preserve it)
2713                 if (~0xFFFFull & val.u64) {
2714                     ErrorAtT(opStart, "hex literal too big for type");
2715                 }
2716                 // no copy needed, the half float is already encoded as such
2717             } else { // ==ImmVal::F64
2718                      // it's an fp64 literal, we need to narrow to fp16
2719                      //   e.g. "(1.0/10.0):hf"
2720                 uint64_t DROPPED_PAYLOAD = ~((uint64_t)F16_MANT_MASK) &
2721                     (F64_MANT_MASK >> 1);
2722                 if (IS_NAN(val.f64) && (val.u64 & DROPPED_PAYLOAD)) {
2723                     FailAtT(opStart, "NaN payload value overflows");
2724                 }
2725                 // uint64_t orginalValue = val.u64;
2726                 val.u64 = ConvertDoubleToHalf(val.f64); // copy over u64 to clobber all
2727                 val.kind = ImmVal::Kind::F16;
2728                 // uint64_t newValue = FloatToBits(
2729                 //    ConvertFloatToDouble(ConvertHalfToFloat(val.u16)));
2730                 // if (orginalValue != newValue) {
2731                 //    Warning(opStart,
2732                 //        "precision lost in literal conversion to fp16");
2733                 // }
2734             }
2735             break;
2736         case Type::F:
2737         case Type::DF:
2738             if (val.isS64()) {
2739                 if (valToken.lexeme == INTLIT10 && val.u64 != 0) {
2740                     // base10
2741                     // examples: 2:f or (1+2):f
2742                     FailAtT(opStart,
2743                         "immediate integer floating point literals must be "
2744                         "in hex or binary (e.g. 0x7F800000:f)");
2745                 }
2746                 // base2 or base16 float bits
2747                 // examples:
2748                 //   0x3F000000:f (0.5)
2749                 //   0x7F801010:f (qnan(0x01010))
2750                 //   0xFFC00000:f (-qnan(...))
2751                 //   0x7FC00000:f (qnan())
2752                 //   0x7F800000:f (infinity)
2753                 if (sty == Type::F) {
2754                     // preserve the bits
2755                     val.u32 = (uint32_t)val.s64;
2756                     val.kind = ImmVal::Kind::F32;
2757                 } else {
2758                     // leave :df alone, bits are already set
2759                     val.kind = ImmVal::Kind::F64;
2760                 }
2761             } else { // ==ImmVal::F64
2762                 if (sty == Type::F) {
2763                     // parsed as double e.g. "3.1414:f"
2764                     // need to narrow to fp32
2765                     // any NaN payload must fit in the smaller mantissa
2766                     // The bits we will remove
2767                     //   ~((uint64_t)F32_MANT_MASK) &
2768                     //      (F64_MANT_MASK >> 1)
2769                     // the mantissa bits that will get truncated
2770                     uint64_t DROPPED_PAYLOAD = ~((uint64_t)F32_MANT_MASK) &
2771                         (F64_MANT_MASK >> 1);
2772                     if (IS_NAN(val.f64) && (val.u64 & DROPPED_PAYLOAD)) {
2773                         FailS(opStart, "NaN payload value overflows");
2774                     }
2775                     //
2776                     // Use a raw bitwise assignment; some compilers will clear
2777                     // the NaN bit by making an assignment
2778                     val.u64 = ConvertDoubleToFloatBits(val.f64);
2779                     val.kind = ImmVal::Kind::F32;
2780                     // the below would be wrong
2781                     //   val = ConvertDoubleToFloat(val.f64);
2782                     } // else: sty == Type::DF (nothing needed)
2783                 }
2784             break;
2785         default:
2786             break;
2787         }
2788 
2789         // check literal bounds of integral values
2790         // literals may be signed floating in general
2791         switch (sty) {
2792         case Type::B:
2793             // if (val.kind == ImmVal::S64 &&
2794             //     ((val.u64 & 0xFFFFFFFFFFFFFF00ull) == 0xFFFFFFFFFFFFFF00ull))
2795             // {
2796             //     // negative value, but not too negative for 16 bits
2797             //     val.u64 &= 0xFFull;
2798             // }
2799             CheckLiteralBounds(opStart, sty, val, -128, 127);
2800             val.kind = ImmVal::Kind::S8;
2801             val.s64 = val.s8; // sign extend to a 64-bit value
2802             break;
2803         case Type::UB:
2804             // CheckLiteralBounds(opStart, sty, val, 0, 0xFF);
2805             val.kind = ImmVal::Kind::U8;
2806             val.u64 = val.u8; // could &= by 0xFF
2807             break;
2808         case Type::W:
2809             // if (val.kind == ImmVal::S64 &&
2810             //     ((val.u64 & 0xFFFFFFFFFFFF0000ull) == 0xFFFFFFFFFFFF0000ull))
2811             // {
2812             //     // negative value, but not too negative for 16 bits
2813             //    val.u64 &= 0xFFFFull;
2814             // }
2815             val.s64 = val.s16; // sign extend to a 64-bit value
2816             CheckLiteralBounds(opStart, sty, val, -32768, 32767);
2817             val.kind = ImmVal::Kind::S16;
2818             break;
2819         case Type::UW:
2820             // fails ~1:ub
2821             // CheckLiteralBounds(opStart, sty, val, 0, 0xFFFF);
2822             val.kind = ImmVal::Kind::U16;
2823             val.u64 = val.u16; // truncate to 16-bit: // could &= by 0xFFFF
2824             break;
2825         case Type::D:
2826             val.s64 = val.s32; // sign extend to a 64-bit value
2827             CheckLiteralBounds(opStart, sty, val, -2147483648ll, 2147483647ll);
2828             val.kind = ImmVal::Kind::S32;
2829             break;
2830         case Type::UD:
2831             // CheckLiteralBounds(opStart, sty, val, 0, 0xFFFFFFFF);
2832             // only check if input is signed??? Or reject signed input
2833             // if (val.kind == ImmVal::S64 &&
2834             //    ((val.u64 & 0xFFFFFFFF00000000ull) == 0xFFFFFFFF00000000ull))
2835             // {
2836             //     // negative value, but we can reprsent it as u32
2837             // }
2838             // val.u64 &= 0xFFFFFFFF;
2839             val.u64 = val.u32; // truncate top bits
2840             val.kind = ImmVal::Kind::U32;
2841             break;
2842         case Type::Q:
2843             // no conversion needed
2844             val.kind = ImmVal::Kind::S64;
2845             CheckLiteralBounds(opStart, sty, val,
2846                 0x8000000000000000ll, 0x7FFFFFFFFFFFFFFFll);
2847             break;
2848         case Type::UQ:
2849             // no conversion needed
2850             val.kind = ImmVal::Kind::U64;
2851             break;
2852         case Type::HF:
2853             val.kind = ImmVal::Kind::F16;
2854             break;
2855         case Type::F:
2856             val.kind = ImmVal::Kind::F32;
2857             break;
2858         case Type::DF:
2859             val.kind = ImmVal::Kind::F64;
2860             break;
2861         case Type::UV:
2862         case Type::V:
2863         case Type::VF:
2864             val.kind = ImmVal::Kind::U32;
2865             break;
2866         default:
2867             break;
2868         }
2869 
2870         if (m_opSpec->isBranching()) {
2871             if (m_opSpec->isJipAbsolute()) {
2872                 m_builder.InstSrcOpImmLabelAbsolute(
2873                     srcOpIx,
2874                     opStart,
2875                     val.s64,
2876                     sty);
2877             } else {
2878                 m_builder.InstSrcOpImmLabelRelative(
2879                     srcOpIx,
2880                     opStart,
2881                     val.s64,
2882                     sty);
2883             }
2884         } else {
2885             m_builder.InstSrcOpImmValue(srcOpIx, opStart, val, sty);
2886         }
2887     }
2888 
2889 
FinishSrcOpImmLabel(int srcOpIx,const Loc & opStart,const Loc,const std::string & lbl)2890     void FinishSrcOpImmLabel(
2891         int srcOpIx,
2892         const Loc &opStart,
2893         const Loc /* lblLoc */,
2894         const std::string &lbl)
2895     {
2896         Type type = ParseSrcOpTypeWithDefault(srcOpIx, true, true);
2897         m_builder.InstSrcOpImmLabel(srcOpIx, opStart, lbl, type);
2898     }
2899 
2900 
2901     // = '-' | '~' | '(abs)' | '-(abs)' | '~(abs)'
2902     // = or start of "|r3|" like
ParseSrcModifierOpt(bool & pipeAbs)2903     SrcModifier ParseSrcModifierOpt(bool &pipeAbs) {
2904         SrcModifier srcMod = SrcModifier::NONE;
2905         if (Consume(SUB) || Consume(TILDE)) { // same lexeme as -
2906             srcMod = SrcModifier::NEG;
2907         }
2908         pipeAbs = LookingAt(PIPE);
2909         if (Consume(ABS) || Consume(PIPE)) {
2910             srcMod = srcMod == SrcModifier::NEG ?
2911                 SrcModifier::NEG_ABS : SrcModifier::ABS;
2912         }
2913         return srcMod;
2914     }
2915 
2916     // Special handling for >=XE_HP src1 op, we rely on the src1 op format
2917     // to determine if this is the new XE_HP encoding.  If the format is
2918     // r2:4 then should set ExBSO to true later on.
2919     //
2920     // In XeHP: only ExBSO descriptors take Src1.Length out of the descriptor
2921     //         and only if the descriptor is a register (e.g. a0.2)
2922     //
2923     // The syntax:
2924     //   REG
2925     //      bare register: "r13" or "null"
2926     //      given a reg ExDesc means to not use ExBSO
2927     //      NOTE: users are encouraged to use the explicit InstOpt {ExBSO}
2928     //
2929     //   REG (COLON|HASH) INT
2930     //      src one length
2931     //      given a reg ExDesc, this means to use ExBSO
2932     //      NOTE: users are encouraged to use the explicit InstOpt {ExBSO}
2933     //
2934     //   REG COLON TYPE
2935     //      r13:ud or null:ud
2936     //
2937     //   REG COLON TYPE (COLON|HASH) INT
2938     //   REG (COLON|HASH) INT COLON TYPE
2939     //      compatibility with old asm
2940     //      r13:ud:1 or null:ud:0
ParseSendSrc1OpWithOptLen(int & src1Len)2941     void ParseSendSrc1OpWithOptLen(int &src1Len) {
2942         m_srcLocs[1] = NextLoc();
2943         src1Len = -1;
2944         const Token regnameTk = Next();
2945         const RegInfo *regInfo;
2946         int regNum;
2947         bool parsedType = false;
2948 
2949         if (ConsumeReg(regInfo, regNum)) {
2950             // send src1 must not have region and subreg
2951             m_srcKinds[1] = Operand::Kind::DIRECT;
2952             //
2953             // parse the type
2954             Type sty = Type::INVALID;
2955             if (LookingAtSeq(COLON, IDENT)) {
2956                 // this disables expressions for the length sizes
2957                 //   send ... null:(2*0)
2958                 sty = ParseSendOperandTypeWithDefault(1);
2959                 parsedType = true;
2960             } else {
2961                 sty = SendOperandDefaultType(1);
2962             }
2963 
2964             const Token &tk = Next();
2965             if (Consume(COLON) || Consume(HASH)) {
2966                 // e.g. r12:4 or r12#4 (the latter is legacy only)
2967                 // (we added COLON so we could use this with a preproecssor)
2968                 if (platform() < Platform::XE_HP) {
2969                     FailAtT(tk.loc,
2970                       "Send with Imm ExDesc should not have Src1.Length "
2971                       "given on src1 (e.g. r0#4); Src1.Length should be "
2972                       "in ExDesc[10:6]");
2973                 }
2974                 m_sendSrcLenLocs[1] = NextLoc();
2975                 //
2976                 ImmVal v;
2977                 if (!TryParseIntConstExpr(v, "extended descriptor")) {
2978                     FailT("expected extended send descriptor");
2979                 }
2980                 src1Len = (int)v.s64;
2981                 m_sendSrcLens[1] = src1Len;
2982                 if (src1Len < 0 || src1Len > 0x1F) {
2983                     FailAtT(tk.loc, "Src1.Length is out of range");
2984                 }
2985                 if (regNum + src1Len - 1 > 255) {
2986                     FailAtT(tk.loc, "Src1.Length extends past GRF end");
2987                 }
2988             } else if (
2989                 regInfo->regName == RegName::GRF_R &&
2990                 LookingAtSeq(SUB, INTLIT10))
2991             {
2992                 // enabling a range based syntax as well for src1Len
2993                 //   r12-14 => r12#3
2994                 // users that require the C-preprocessor get tripped up using
2995                 // # as a decorator (e.g. since it's a cpp operator)
2996                 Skip();
2997                 int lastReg = 0;
2998                 ConsumeIntLit(lastReg);
2999                 if (lastReg < regNum)
3000                     FailAtT(tk.loc,
3001                         "Src1Len: ending register must be >= start reg");
3002                 else if (lastReg > 255)
3003                     FailAtT(tk.loc,
3004                         "Src1Len: ending register must be <= 255");
3005                 src1Len = lastReg - regNum + 1;
3006             }
3007 
3008             // null#0:ud
3009             if (!parsedType)
3010                 sty = ParseSendOperandTypeWithDefault(1);
3011 
3012             // construct the op directly
3013             m_builder.InstSrcOpRegDirect(
3014                 1,
3015                 m_srcLocs[1],
3016                 SrcModifier::NONE,
3017                 regInfo->regName,
3018                 RegRef(regNum, 0),
3019                 Region::SRC010, // set the default region
3020                 sty);
3021         } else {
3022             FailT("syntax error in send src1");
3023         }
3024     }
3025 
ParseSendSrc1OpWithLen()3026     int ParseSendSrc1OpWithLen() {
3027         const Token regnameTk = Next();
3028         const RegInfo *regInfo;
3029         int regNum = 0;
3030         if (!ConsumeReg(regInfo, regNum)) {
3031             FailT("expected Src1 register");
3032         }
3033         // send src1 must not have region and subreg
3034         m_srcKinds[1] = Operand::Kind::DIRECT;
3035 
3036         if (!Consume(COLON) && !Consume(HASH)) {
3037             FailT("expected ':' (Src1Length must suffix Src1 payload)");
3038         }
3039         const auto src1LenLoc = Next().loc;
3040         ImmVal v;
3041         if (!TryParseIntConstExprPrimary(v, "src1 length")) {
3042             FailT("failed to parse src1 length");
3043         } else if (v.s64 < 0 || v.s64 > 32) {
3044             FailAtT(src1LenLoc, "invalid src1 length");
3045         }
3046         int src1Len = (int)v.s64;
3047         m_sendSrcLens[1] = src1Len;
3048         if (regInfo->regName == RegName::ARF_NULL && src1Len != 0) {
3049             FailAtT(src1LenLoc, "Src1Len must be 0 for null register");
3050         } else if (regInfo->regName == RegName::GRF_R &&
3051             regNum + src1Len > regInfo->getNumReg())
3052         {
3053             FailAtT(src1LenLoc,
3054                 "Src1Len: ending register must be <= ", regInfo->getNumReg());
3055         }
3056 
3057         // construct the op directly
3058         Type t = SendOperandDefaultType(1);
3059         m_builder.InstSrcOpRegDirect(
3060             1,
3061             m_srcLocs[1],
3062             SrcModifier::NONE,
3063             regInfo->regName,
3064             RegRef(regNum, 0),
3065             Region::SRC010, // set the default region
3066             t);
3067 
3068         return src1Len;
3069     }
3070 
3071     // e.g. "r13" or "r13:f"
ParseSendSrcOp(int srcOpIx,bool enableImplicitOperand)3072     void ParseSendSrcOp(int srcOpIx, bool enableImplicitOperand) {
3073         m_srcLocs[srcOpIx] = NextLoc();
3074 
3075         const RegInfo *regInfo;
3076         int regNum;
3077 
3078         // For XE send now supports src1; sends goes away
3079         // To support some older syntax floating around
3080         // underneath IGA will insert a null src1 if needed
3081         if (enableImplicitOperand) {
3082             bool isSuccess = PeekReg(regInfo, regNum);
3083             if (!isSuccess || regInfo->regName == RegName::ARF_A) {
3084                 m_builder.InstSrcOpRegDirect(
3085                     srcOpIx,
3086                     m_srcLocs[srcOpIx],
3087                     SrcModifier::NONE,
3088                     RegName::ARF_NULL,
3089                     REGREF_ZERO_ZERO,
3090                     Region::SRC010,
3091                     Type::INVALID);
3092                 return;
3093             }
3094         }
3095 
3096 #ifndef DISABLE_SENDx_IND_SRC_OPND
3097         ParseSrcOp(srcOpIx);
3098 #else
3099         if (!ConsumeReg(regInfo, regNum)) {
3100             Fail("expected send operand");
3101         }
3102         auto dotLoc = NextLoc();
3103         if (Consume(DOT)) {
3104             int i;
3105             ConsumeIntLitOrFail(i, "expected subregister");
3106             if (m_parseOpts.deprecatedSyntaxWarnings) {
3107                 Warning(dotLoc, "send instructions may not have subregisters");
3108             }
3109         }
3110         RegRef reg {uint16_t(regNum), 0};
3111         // gets the implicit region and warns against using explicit regioning
3112         Region rgn = ParseSrcOpRegionVWH(*regInfo, srcOpIx, false);
3113         // because we are trying to phase out source operands on send
3114         // instructions we handle them explicitly here
3115         Type sty = ParseSendOperandTypeWithDefault(srcOpIx);
3116         m_handler.InstSrcOpRegDirect(
3117             srcOpIx,
3118             m_srcLocs[srcOpIx],
3119             SrcModifier::NONE,
3120             *regInfo,
3121             reg,
3122             rgn,
3123             sty);
3124 #endif
3125     }
3126 
3127 
3128 
3129 
ParseDstOpTypeWithDefault()3130     Type ParseDstOpTypeWithDefault() {
3131         if (m_opSpec->hasImplicitDstType()) {
3132             if (LookingAt(COLON)) {
3133                 if (m_opts.deprecatedSyntaxWarnings)
3134                     WarningT("implicit type on dst should be omitted");
3135                 // parse the type but ignore it
3136                 ParseOpTypeWithDefault(DST_TYPES, "expected destination type");
3137             }
3138             // use the implicit type anyway
3139             return m_opSpec->implicitDstType();
3140         }
3141         return ParseOpTypeWithDefault(DST_TYPES, "expected destination type");
3142     }
3143 
3144 
ParseSrcOpTypeWithDefault(int srcOpIx,bool immOrLbl,bool isLabel=false)3145     Type ParseSrcOpTypeWithDefault(
3146         int srcOpIx, bool immOrLbl, bool isLabel = false)
3147     {
3148         if (m_opSpec->hasImplicitSrcType(srcOpIx, immOrLbl)) {
3149             if (LookingAt(COLON)) {
3150                 if (m_opts.deprecatedSyntaxWarnings)
3151                     WarningT(
3152                         "implicit type on src", srcOpIx, " should be omitted");
3153                 // parse the type but ignore it
3154                 ParseOpTypeWithDefault(SRC_TYPES, "expected source type");
3155             }
3156             // use the implicit type anyway
3157             return
3158                 m_opSpec->implicitSrcType(srcOpIx, immOrLbl);
3159         } else if (m_opSpec->op == Op::MOV && immOrLbl && isLabel) {
3160             // support mov label without giving label's type
3161             return Type::UD;
3162         }
3163 
3164         return ParseOpTypeWithDefault(SRC_TYPES, "expected source type");
3165     }
ParseSrcOpTypeWithoutDefault(int srcOpIx,bool immOrLbl)3166     Type ParseSrcOpTypeWithoutDefault(int srcOpIx, bool immOrLbl) {
3167         if (m_opSpec->hasImplicitSrcType(srcOpIx, immOrLbl)) {
3168             if (LookingAt(COLON)) {
3169                 if (m_opts.deprecatedSyntaxWarnings)
3170                     WarningT(
3171                         "implicit type on src", srcOpIx, " should be omitted");
3172                 // parse the type but ignore it
3173                 TryParseOpType(SRC_TYPES);
3174             }
3175             // use the implicit type anyway
3176             return m_opSpec->implicitSrcType(srcOpIx, immOrLbl);
3177         }
3178         Type t = TryParseOpType(SRC_TYPES);
3179         if (t == Type::INVALID &&
3180             !(m_opSpec->isBranching() &&
3181                 !m_model.supportsSimplifiedBranches()))
3182         {
3183             FailT("expected source type");
3184         }
3185         return t;
3186     }
ParseOpTypeWithDefault(const IdentMap<Type> types,const char * expectedErr)3187     Type ParseOpTypeWithDefault(
3188         const IdentMap<Type> types, const char *expectedErr)
3189     {
3190         auto t = TryParseOpType(types);
3191         if (t == Type::INVALID) {
3192             if (m_defaultRegisterType != Type::INVALID) {
3193                 t = m_defaultRegisterType;
3194             } else if (m_opSpec->isSendOrSendsFamily()) {
3195                 t = Type::UD;
3196             } else if (m_opSpec->isBranching() &&
3197                 m_model.supportsSimplifiedBranches())
3198             {
3199                 // no more types for branching
3200                 t = Type::UD;
3201             } else if (m_opSpec->is(Op::SYNC)) {
3202                 // we allow implicit type for sync reg32 (grf or null)
3203                 t = Type::UB;
3204             } else {
3205                 FailT(expectedErr);
3206             }
3207         }
3208         return t;
3209     }
TryParseOpType(const IdentMap<Type> types)3210     Type TryParseOpType(const IdentMap<Type> types) {
3211         if (!LookingAt(COLON)) {
3212             return Type::INVALID;
3213         }
3214         Type type = Type::INVALID;
3215         if (IdentLookupFrom(1, types, type)) {
3216             Skip(2);
3217         }
3218         return type;
3219     }
3220 
3221     // (INTEXPR|AddrRegRef) (INTEXPR|AddrRegRef)
3222     //
3223     // This function is the same as ParseSendDescs, except for it's handling
3224     // of ExBSO and Src1Len.
ParseSendDescsWithOptSrc1Len(int src1Length)3225     void ParseSendDescsWithOptSrc1Len(int src1Length) {
3226         const Loc exDescLoc = NextLoc();
3227         SendDesc exDesc;
3228         RegName regName = RegName::INVALID;
3229         if (ParseAddrRegRefOpt(exDesc.reg, regName)) { // ExDesc is register
3230             IGA_ASSERT(regName == RegName::ARF_A, "Indirect send exDesc must be ARF_A");
3231             exDesc.type = SendDesc::Kind::REG32A;
3232             if (src1Length >= 0) {
3233                 m_implicitExBSO = true;
3234                 m_builder.InstOptAdd(InstOpt::EXBSO);
3235             }
3236         } else { // ExDesc is imm
3237             exDesc.type = SendDesc::Kind::IMM;
3238 
3239             // constant integral expression
3240             ImmVal v;
3241             if (!TryParseIntConstExpr(v, "extended descriptor")) {
3242                 FailT("expected extended send descriptor");
3243             }
3244             exDesc.imm = (uint32_t)v.s64;
3245 
3246             if (src1Length >= 0) {
3247                 // In XeHP, immediate descriptors still treat Src1.Length as
3248                 // ExDesc[10:6]
3249                 if (platform() == Platform::XE_HP) {
3250                     WarningT(
3251                         "Send with immediate ExDesc should not have "
3252                         "Src1.Length given on src1 (e.g. r10:4) "
3253                         "on this platform");
3254                     exDesc.imm |= ((uint32_t)src1Length << 6) & 0x1F;
3255                 }
3256             }
3257             if (platform() >= Platform::XE_HPG) {
3258                 // XeHPG+: Src1.Length is not part of ExDesc for ExDesc.IsImm,
3259                 // but are explicit bits in the EU ISA.
3260                 int exDescSrc1Len = (int)(exDesc.imm >> 6) & 0x1F;
3261                 if (src1Length < 0) {
3262                     WarningAtT(exDescLoc,
3263                         "Src1.Length should suffix src1 register (e.g. r10:4)");
3264                     // take it from the descriptor
3265                     src1Length = exDescSrc1Len;
3266                 } else if (exDescSrc1Len != 0) { // src1Length >= 0
3267                     // they set Src1.Length as part of the descriptor
3268                     WarningAtT(exDescLoc,
3269                         "Send ExDesc[10:6] is not Src1.Length; suffix length "
3270                         "on src1 reg  (e.g. r10:4)");
3271                     // if Src1.Length was also set on the register, then ensure
3272                     // that it at least matches the descriptor value
3273                     if (src1Length >= 0) {
3274                         // throw a fit if they mismatch
3275                         if (exDescSrc1Len != src1Length)
3276                             FailAtT(exDescLoc,
3277                                 "mismatch of Src1.Length suffix and "
3278                                 "ExDesc[10:6]");
3279                         // else we
3280                     } else {
3281                         // take it from the descriptor
3282                         src1Length = exDescSrc1Len;
3283                     }
3284                 } // else: Src1Length >= 0 && exDescSrc1Len == 0 (prefer reg op)
3285 
3286                 if (exDesc.imm & 0x7FF) {
3287                     WarningAtT(exDescLoc, "ExDesc[10:0] must be zero");
3288                     exDesc.imm &= ~0x7FF; // [10:0] MBZ
3289                 }
3290             } // else <=XeHPG (everything is in the imm descriptor)
3291 
3292             if (platform() >= Platform::XE && (exDesc.imm & 0xF)) {
3293                 FailAtT(exDescLoc,
3294                     "ExDesc[3:0] must be 0's; SFID is expressed "
3295                     "as a function control value (e.g. send.dc0 ...)");
3296             }
3297         }
3298         if (LookingAt(COLON)) {
3299             FailAtT(NextLoc(), "extended message descriptor is typeless");
3300         }
3301         //
3302         const Loc descLoc = NextLoc();
3303         SendDesc desc;
3304         if (ParseAddrRegRefOpt(desc.reg, regName)) {
3305             IGA_ASSERT(regName == RegName::ARF_A, "Indirect send desc must be ARF_A");
3306             desc.type = SendDesc::Kind::REG32A;
3307         } else {
3308             // constant integral expression
3309             ImmVal v;
3310             if (!TryParseConstExpr(v)) {
3311                 FailT("expected extended send descriptor");
3312             }
3313             if (!v.isI64()) {
3314                 FailAtT(descLoc,
3315                     "immediate descriptor expression must be integral");
3316             }
3317             desc.imm = (uint32_t)v.s64;
3318             desc.type = SendDesc::Kind::IMM;
3319         }
3320         if (LookingAt(COLON)) {
3321             FailT("Message Descriptor should not have a type");
3322         }
3323         //
3324         m_builder.InstSendDescs(exDescLoc, exDesc, descLoc, desc);
3325         //
3326         m_builder.InstSendSrc1Length(src1Length);
3327     }
3328 
3329 
ParseDesc(const char * which)3330     SendDesc ParseDesc(const char *which) {
3331         SendDesc sd;
3332         RegName regName = RegName::INVALID;
3333         if (ParseAddrRegRefOpt(sd.reg, regName)) { // ExDesc is register
3334             IGA_ASSERT(regName == RegName::ARF_A, "Indirect send Desc must be ARF_A");
3335             sd.type = SendDesc::Kind::REG32A;
3336 
3337         } else { // ExDesc is imm
3338             sd.type = SendDesc::Kind::IMM;
3339 
3340             // constant integral expression
3341             ImmVal v;
3342             if (!TryParseIntConstExprPrimary(ExprParseOpts(), v, which)) {
3343                 FailT("expected ", which);
3344             }
3345             sd.imm = (uint32_t)v.s64;
3346         }
3347         return sd;
3348     }
3349 
3350 
3351 
3352     //
3353     // (INTEXPR|AddrRegRef) (INTEXPR|AddrRegRef)
ParseSendDescsLegacy()3354     void ParseSendDescsLegacy() {
3355         IGA_ASSERT(platform() <= Platform::XE,
3356             "wrong platform for function");
3357 
3358 
3359         const Loc exDescLoc = NextLoc();
3360         SendDesc exDesc;
3361         RegName regName = RegName::INVALID;
3362         if (ParseAddrRegRefOpt(exDesc.reg, regName)) {
3363             IGA_ASSERT(regName == RegName::ARF_A, "Indirect send exDesc must be ARF_A");
3364             exDesc.type = SendDesc::Kind::REG32A;
3365             if (platform() < Platform::XE)
3366                 m_builder.InstSubfunction(SFID::A0REG);
3367             // subfunc is already set as part of opcode (e.g. send.gtwy)
3368         } else {
3369             // constant integral expression
3370             ImmVal v;
3371             if (!TryParseConstExpr(v)) {
3372                 FailT("expected extended send descriptor");
3373             }
3374             if (!v.isI64()) {
3375                 FailAtT(exDescLoc,
3376                     "immediate descriptor expression must be integral");
3377             }
3378             exDesc.imm = (uint32_t)v.s64;
3379             exDesc.type = SendDesc::Kind::IMM;
3380             if (platform() >= Platform::XE && (exDesc.imm & 0xF)) {
3381                 FailAtT(exDescLoc,
3382                     "ExDesc[3:0] must be 0's; SFID is expressed "
3383                     "as a function control value (e.g. send.dc0 ...)");
3384             }
3385             if (platform() < Platform::XE) {
3386                 SFID sfid = sfidFromEncoding(platform(), exDesc.imm & 0xF);
3387                 m_builder.InstSubfunction(sfid);
3388             }
3389         }
3390 
3391         if (LookingAt(COLON)) {
3392             FailT("extended message descriptor is typeless");
3393         }
3394 
3395         const Loc descLoc = NextLoc();
3396         SendDesc desc;
3397         if (ParseAddrRegRefOpt(desc.reg, regName)) {
3398             IGA_ASSERT(regName == RegName::ARF_A, "Indirect send Desc must be ARF_A");
3399             desc.type = SendDesc::Kind::REG32A;
3400         } else {
3401             // constant integral expression
3402             ImmVal v;
3403             if (!TryParseConstExpr(v)) {
3404                 FailT("expected extended send descriptor");
3405             }
3406             if (!v.isI64()) {
3407                 FailAtT(descLoc,
3408                     "immediate descriptor expression must be integral");
3409             }
3410             desc.imm = (uint32_t)v.s64;
3411             desc.type = SendDesc::Kind::IMM;
3412         }
3413 
3414         m_builder.InstSendDescs(exDescLoc, exDesc, descLoc, desc);
3415 
3416         if (LookingAt(COLON)) {
3417             FailT("Message Descriptor is typeless");
3418         }
3419     }
3420 
3421     // ('{' InstOptOrDepInfo (',' IDENT)* '}')?
3422     // InstOptOrDepInfo = DepInfo | InstOpt
3423     //   InstOpt = 'AccWrEn' | 'Atomic' | ...
3424     //   DepInfo = 'NoDDChk' | 'NoDDClr' | ...
ParseInstOpts()3425     void ParseInstOpts() {
3426         InstOptSet instOpts;
3427         instOpts.clear();
3428 
3429         if (Consume(LBRACE)) {
3430             if (!LookingAt(RBRACE))
3431                 ParseInstOptOrFail(instOpts); // else: could be empty list "{}"
3432             while (Consume(COMMA)) {
3433                 ParseInstOptOrFail(instOpts);
3434             }
3435             ConsumeOrFail(RBRACE, "expected }");
3436         }
3437 
3438         // TODO: sink this into the instop parsing once we remerge
3439         // that with KernelParser... (after we rip out the legacy ld/st tables)
3440         bool src1LengthSuffixSet = m_sendSrcLens[1] != -1;
3441         if (instOpts.contains(InstOpt::EXBSO) && !src1LengthSuffixSet
3442             ) {
3443             // GOOD:  send ... r10:2  a0.# ... {ExBSO}
3444             // ERROR: send ... r10    a0.# ... {ExBSO}
3445             //   Src1.Length comes from EU bits a0.# holds 26b offset
3446             //   We throw a tantrum if length is absent
3447             auto loc = m_srcLocs[1].isValid() ? m_srcLocs[1] : m_mnemonicLoc;
3448             FailAtT(loc, "send with ExBSO option should have "
3449                 "Src1.Length suffixing parameter (e.g. r10:4)");
3450         ////////////////////////////
3451         //  else if:
3452         //    platform() >= Platform::XE_HPG
3453         //    m_builder.getExDesc().isImm() &&
3454         //    src1LengthSuffixSet
3455         // GOOD:  send ... r10:2  IMM ... {}
3456         // GOOD:  send ... r10    a0.# ... {}
3457         // this is already checked in ExDesc parser
3458         //
3459         } else if (instOpts.contains(InstOpt::EXBSO) &&
3460             m_builder.getExDesc().isImm())
3461         {
3462             // ERROR: send ... r10    IMM ... {ExBSO}
3463             // ExBSO only makes sense when using a0.#
3464             auto loc = m_srcLocs[1].isValid() ? m_srcLocs[1] : m_mnemonicLoc;
3465             FailAtT(loc, "send with immediate exdesc forbids ExBSO");
3466         }
3467 
3468         m_builder.InstOptsAdd(instOpts);
3469 
3470         if (m_implicitExBSO && !instOpts.contains(InstOpt::EXBSO)
3471             ) {
3472             WarningAtT(m_mnemonicLoc, "send src1 length implicitly added "
3473                 "(include {ExBSO})");
3474         }
3475     }
3476 
ParseInstOptOrFail(InstOptSet & instOpts)3477     void ParseInstOptOrFail(InstOptSet &instOpts) {
3478         auto loc = NextLoc();
3479         if (!tryParseInstOptToken(instOpts) &&
3480             !tryParseInstOptDepInfo(instOpts))
3481         {
3482             FailAtT(loc, "invalid instruction option");
3483         }
3484     }
3485 
tryParseInstOptToken(InstOptSet & instOpts)3486     bool tryParseInstOptToken(InstOptSet &instOpts) {
3487         auto loc = NextLoc();
3488         InstOpt newOpt = InstOpt::ACCWREN;
3489         if (ConsumeIdentEq("AccWrEn")) {
3490             newOpt = InstOpt::ACCWREN;
3491             if (platform() >= Platform::XE_HPC) {
3492                 FailAtT(loc, "AccWrEn not supported on this platform");
3493             }
3494         } else if (ConsumeIdentEq("Atomic")) {
3495             if (platform() < Platform::GEN7) {
3496                 FailAtT(loc, "Atomic mot supported on given platform");
3497             }
3498             newOpt = InstOpt::ATOMIC;
3499             if (instOpts.contains(InstOpt::SWITCH)) {
3500                 FailAtT(loc, "Atomic mutually exclusive with Switch");
3501             } else if (instOpts.contains(InstOpt::NOPREEMPT)) {
3502                 FailAtT(loc, "Atomic mutually exclusive with NoPreempt");
3503             }
3504         } else if (ConsumeIdentEq("Breakpoint")) {
3505             newOpt = InstOpt::BREAKPOINT;
3506         } else if (ConsumeIdentEq("Compacted")) {
3507             newOpt = InstOpt::COMPACTED;
3508             if (instOpts.contains(InstOpt::NOCOMPACT)) {
3509                 FailAtT(loc, "Compacted mutually exclusive with "
3510                     "Uncompacted/NoCompact");
3511             }
3512         } else if (ConsumeIdentEq("EOT")) {
3513             newOpt = InstOpt::EOT;
3514             if (!m_opSpec->isSendOrSendsFamily()) {
3515                 FailAtT(loc, "EOT is only allowed on send instructions");
3516             }
3517         } else if (ConsumeIdentEq("NoCompact") ||
3518             ConsumeIdentEq("Uncompacted"))
3519         {
3520             newOpt = InstOpt::NOCOMPACT;
3521             if (instOpts.contains(InstOpt::COMPACTED)) {
3522                 FailAtT(loc, "Uncomapcted/NoCompact mutually exclusive "
3523                     "with Compacted");
3524             }
3525         } else if (ConsumeIdentEq("Serialize")) {
3526             newOpt = InstOpt::SERIALIZE;
3527         } else if (ConsumeIdentEq("NoMask")) {
3528             FailAtT(loc, "NoMask goes precedes predication as (W) for WrEn: "
3529                 "e.g. (W) op (..) ...   or    (W&f0.0) op (..) ..");
3530         } else if (ConsumeIdentEq("H1")) {
3531             FailAtT(loc, "H1 is obsolete; use M0 in execution offset: "
3532                 "e.g. op (16|M0) ...");
3533         } else if (ConsumeIdentEq("H2")) {
3534             FailAtT(loc, "H2 is obsolete; use M16 in execution offset: "
3535                 "e.g. op (16|M16) ...");
3536         } else if (ConsumeIdentEq("Q1")) {
3537             FailAtT(loc, "Q1 is obsolete; use M0 in execution offset: "
3538                 "e.g. op (8|M0) ...");
3539         } else if (ConsumeIdentEq("Q2")) {
3540             FailAtT(loc, "Q2 is obsolete; use M8 in execution offset: "
3541                 "e.g. op (8|M8) ...");
3542         } else if (ConsumeIdentEq("Q3")) {
3543             FailAtT(loc, "Q3 is obsolete; use M16 in execution offset: "
3544                 "e.g. op (8|M16) ...");
3545         } else if (ConsumeIdentEq("Q4")) {
3546             FailAtT(loc, "Q4 is obsolete; use M24 in execution offset: "
3547                 "e.g. op (8|M24) ...");
3548         } else if (ConsumeIdentEq("N1")) {
3549             FailAtT(loc, "N1 is obsolete; use M0 in execution offset: "
3550                 "e.g. op (4|M0) ...");
3551         } else if (ConsumeIdentEq("N2")) {
3552             FailAtT(loc, "N2 is obsolete; use M4 in execution offset: "
3553                 "e.g. op (4|M4) ...");
3554         } else if (ConsumeIdentEq("N3")) {
3555             FailAtT(loc, "N3 is obsolete; use M8 in execution offset: "
3556                 "e.g. op (4|M8) ...");
3557         } else if (ConsumeIdentEq("N4")) {
3558             FailAtT(loc, "N4 is obsolete; use M12 in execution offset: "
3559                 "e.g. op (4|M12) ...");
3560         } else if (ConsumeIdentEq("N5")) {
3561             FailAtT(loc, "N5 is obsolete; use M16 in execution offset: "
3562                 "e.g. op (4|M16) ...");
3563         } else if (ConsumeIdentEq("N6")) {
3564             FailAtT(loc, "N6 is obsolete; use M20 in execution offset: "
3565                 "e.g. op (4|M20) ...");
3566         } else if (ConsumeIdentEq("N7")) {
3567             FailAtT(loc, "N7 is obsolete; use M24 in execution offset: "
3568                 "e.g. op (4|M24) ...");
3569         } else if (ConsumeIdentEq("N8")) {
3570             FailAtT(loc, "N8 is obsolete; use M28 in execution offset: "
3571                 "e.g. op (4|M28) ...");
3572         } else {
3573             return false;
3574         }
3575 
3576         if (!instOpts.add(newOpt)) {
3577             // adding the option doesn't change the set... (it's duplicate)
3578             FailAtT(loc, "duplicate instruction options");
3579         }
3580         return true;
3581     }
3582 
tryParseInstOptDepInfo(InstOptSet & instOpts)3583     bool tryParseInstOptDepInfo(InstOptSet &instOpts) {
3584         auto loc = NextLoc();
3585         if (LookingAt(IDENT)) {
3586             InstOpt newOpt = InstOpt::ACCWREN;
3587             bool isSwsbOpt = false;
3588             // classic instruction option that affects instruction dependency
3589             // scheduling etc...
3590             if (ConsumeIdentEq("NoDDChk")) {
3591                 newOpt = InstOpt::NODDCHK;
3592                 if (!m_model.supportsHwDeps()) {
3593                     FailAtT(loc, "NoDDChk not supported on given platform");
3594                 }
3595             } else if (ConsumeIdentEq("NoDDClr")) {
3596                 newOpt = InstOpt::NODDCLR;
3597                 if (!m_model.supportsHwDeps()) {
3598                     FailAtT(loc, "NoDDClr not supported on given platform");
3599                 }
3600             } else if (ConsumeIdentEq("NoPreempt")) {
3601                 if (m_model.supportsNoPreempt()) {
3602                     newOpt = InstOpt::NOPREEMPT;
3603                 } else {
3604                     FailAtT(loc, "NoPreempt not supported on given platform");
3605                 }
3606             } else if (ConsumeIdentEq("NoSrcDepSet")) {
3607                 if (m_model.supportNoSrcDepSet()) {
3608                     newOpt = InstOpt::NOSRCDEPSET;
3609                 } else {
3610                     FailAtT(loc, "NoSrcDep not supported on given platform");
3611                 }
3612             } else if (ConsumeIdentEq("Switch")) {
3613                 newOpt = InstOpt::SWITCH;
3614                 if (!m_model.supportsHwDeps()) {
3615                     WarningAtT(loc,
3616                         "ignoring unsupported instruction option {Switch}");
3617                 }
3618             } else if (ConsumeIdentEq("ExBSO")) {
3619                 if (platform() < Platform::XE_HP) {
3620                     FailAtT(loc, "ExBSO not supported on this platform");
3621                 } else if (!m_opSpec->isSendOrSendsFamily()) {
3622                     FailAtT(loc, "ExBSO is not allowed for non-send instructions");
3623                 }
3624                 newOpt = InstOpt::EXBSO;
3625             } else if (ConsumeIdentEq("CPS")) {
3626                 if (platform() < Platform::XE_HP
3627                     )
3628                 {
3629                     FailAtT(loc, "CPS not supported on this platform");
3630                 } else if (!m_opSpec->isSendOrSendsFamily()) {
3631                     FailAtT(loc, "CPS is not allowed for non-send instructions");
3632                 }
3633                 newOpt = InstOpt::CPS;
3634             } else if (ConsumeIdentEq("NoAccSBSet")) {
3635                 // try swsb special token: NoAccSBSet
3636                 m_builder.InstDepInfoSpecialToken(loc, SWSB::SpecialToken::NOACCSBSET);
3637                 isSwsbOpt = true;
3638             } else if (m_opts.swsbEncodeMode >= SWSB_ENCODE_MODE::ThreeDistPipe) {
3639                 // swsb XE encoding: the distance could be A@, F@, L@, I@, M@
3640                 // same pipe dist (e.g. @4) will parse further down
3641                 auto tryParsePipe = [&] (
3642                     const char *distPipeSymbol, SWSB::DistType distPipe)
3643                 {
3644                     if (LookingAtSeq(IDENT, AT) &&
3645                         ConsumeIdentEq(distPipeSymbol) && Consume(AT))
3646                     {
3647                         int32_t regDist = 0;
3648                         Loc distLoc = NextLoc();
3649                         ConsumeIntLitOrFail(
3650                             regDist, "expected register distance value");
3651                         m_builder.InstDepInfoDist(distLoc, distPipe, regDist);
3652                         return true;
3653                     } else {
3654                         return false;
3655                     }
3656                 };
3657                 //
3658                 bool parsedNamedPipe =
3659                     tryParsePipe("F", SWSB::DistType::REG_DIST_FLOAT)  ||
3660                     tryParsePipe("I", SWSB::DistType::REG_DIST_INT)    ||
3661                     tryParsePipe("L", SWSB::DistType::REG_DIST_LONG)   ||
3662                     tryParsePipe("A", SWSB::DistType::REG_DIST_ALL)    ||
3663                     tryParsePipe("M", SWSB::DistType::REG_DIST_MATH);
3664                 if (parsedNamedPipe && m_model.supportsHwDeps()) {
3665                     FailAtT(loc,
3666                         "software dependencies not supported on this platform");
3667                 }
3668 
3669                 isSwsbOpt = true;
3670                 if (!parsedNamedPipe)
3671                     return false; // unrecognized swsb setting
3672             } else {
3673                 return false; // unrecognized option
3674             }
3675             if (!isSwsbOpt && !instOpts.add(newOpt)) {
3676                 // adding the option doesn't change the set... (it's a duplicate)
3677                 FailAtT(loc, "duplicate instruction options");
3678             }
3679         } else if (Consume(DOLLAR)) {
3680             // sbid directive
3681             if (m_model.supportsHwDeps()) {
3682                 FailAtT(loc, "software dependencies not supported on this platform");
3683             }
3684             int32_t sbid;
3685             ConsumeIntLitOrFail(sbid, "expected SBID token");
3686             // $4
3687             if (Consume(DOT)) {
3688                 if (ConsumeIdentEq("dst")) {
3689                     // $4.dst
3690                     m_builder.InstDepInfoSBidDst(loc, sbid);
3691                 } else if (ConsumeIdentEq("src")) {
3692                     // $4.src
3693                     m_builder.InstDepInfoSBidSrc(loc, sbid);
3694                 } else {
3695                     FailT("invalid SBID directive expecting 'dst' or 'src'");
3696                 }
3697             } else if (!LookingAtAnyOf(COMMA,RBRACE)) {
3698                 FailT("syntax error in SBID directive"
3699                     " (expected '.' ',' or '}')");
3700             } else {
3701                 // $4 (allocate/.set)
3702                 m_builder.InstDepInfoSBidAlloc(loc, sbid);
3703             }
3704         } else if (Consume(AT)) {
3705             // min dependency distance @3
3706             if (m_model.supportsHwDeps()) {
3707                 FailAtT(loc,
3708                     "software dependencies not supported on this platform");
3709             }
3710             int32_t dist;
3711             auto loc = NextLoc();
3712             ConsumeIntLitOrFail(dist,"expected register min distance value");
3713             m_builder.InstDepInfoDist(loc, SWSB::DistType::REG_DIST, dist);
3714         } else {
3715             return false;
3716         }
3717         return true;
3718     }
3719 
3720     // FlagRegRef = ('f0'|'f1'|'f2'|'f3') ('.' ('0'|'1'))?
ParseFlagRegRef(RegRef & freg)3721     void ParseFlagRegRef(RegRef &freg) {
3722         if (!LookingAt(IDENT)) {
3723             FailT("expected flag register");
3724         }
3725 
3726         if (ConsumeIdentEq("f0")) {
3727             freg.regNum = 0;
3728         } else if (ConsumeIdentEq("f1")) {
3729             freg.regNum = 1;
3730         } else if (ConsumeIdentEq("f2")) {
3731             freg.regNum = 2;
3732         } else if (ConsumeIdentEq("f3")) {
3733             freg.regNum = 3;
3734         } else {
3735             FailT("unexpected flag register number");
3736         }
3737 
3738         if (LookingAtSeq(DOT, INTLIT10)) {
3739             // e.g. f0.1
3740             //        ^
3741             // protects predication's use in short predication code
3742             // (f0.any2h)
3743             Skip();
3744             auto srLoc = NextLoc();
3745             ConsumeIntLitOrFail(freg.subRegNum, "expected flag subregister");
3746             if (freg.subRegNum > 1) {
3747                 ErrorAtT(srLoc, "flag sub-register out of bounds");
3748             }
3749         } else {
3750             // e.g. f0.any2h (treat as f0.0.any2h)
3751             freg.subRegNum = 0;
3752         }
3753     }
3754 
3755 
ConsumeDstType(Type & dty)3756     bool ConsumeDstType(Type &dty) {
3757         return ConsumeIdentOneOf(DST_TYPES, dty);
3758     }
3759 
3760 
ConsumeSrcType(Type & sty)3761     bool ConsumeSrcType(Type &sty) {
3762         return ConsumeIdentOneOf(SRC_TYPES, sty);
3763     }
3764 
3765 
3766     // See LookingAtLabelDef for definition of a label
ConsumeLabelDef(std::string & label)3767     bool ConsumeLabelDef(std::string &label) {
3768         if (LookingAtLabelDef()) {
3769             label = GetTokenAsString();
3770             (void)Skip(2);
3771             return true;
3772         }
3773         return false;
3774     }
3775 
3776 
3777     // Label = IDENT ':' (not followed by identifier)
3778     //   mov:ud (16) -- not a label
3779     //
3780     //   mov:       -- label
3781     //      ud (16) ...
3782     //   mov:       -- label
3783     //     (f0) ud (16) ...
3784     //
3785     //   lbl1:      -- label
3786     //   lbl2:
3787     //
3788     // For now if we get IDENT COLON IDENT, we resolve by:
3789     //  * if on same line, then it's an instruction
3790     //  * otherwise it's a label
LookingAtLabelDef()3791     bool LookingAtLabelDef() {
3792         if (LookingAtSeq(IDENT,COLON)) {
3793             // ensure not a uniform type
3794             //   mov:ud (..)
3795             const Token &t2 = Next(2);
3796             if (t2.lexeme != IDENT || Next(0).loc.line != t2.loc.line) {
3797                 return true;
3798             }
3799         }
3800         return false;
3801     }
3802 
3803     //
3804     // DPAS-specific parsing functions (XE+)
3805     //
3806     // common function for non-standard operands such as
3807     //   r13:d (type) or r13:u1 (OperandPrecision)
3808     // Both dst & src uses this function. 'isDst' tells if
3809     // it is a dst or src.
ParseDpasOp(int opIx,bool isDst)3810     void ParseDpasOp(int opIx, bool isDst) {
3811         const Loc regStart = NextLoc(0);
3812         if (!isDst) {
3813             m_srcLocs[opIx] = regStart;
3814         }
3815 
3816         if (isDst) {
3817             // ParseSatOpt increments m_offset.
3818             if (ParseSatOpt()) {
3819                 m_builder.InstDstOpSaturate();
3820             }
3821         }
3822 
3823         // make sure indirect reg addressing is not allowed.
3824         if (LookingAtIdentEq("r")) {
3825             FailT("Indirect register addressing not allowed");
3826         }
3827 
3828         // Match grf such as r10
3829         const RegInfo *ri = nullptr;
3830         int regNum = 0;
3831         if (!ConsumeReg(ri, regNum)) {
3832             if (isDst)
3833                 FailT("invalid dst");
3834             else
3835                 FailT("invalid src", opIx);
3836         }
3837         if (ri->regName != RegName::GRF_R) {
3838             // dpas must be GRF except src0, which can be null (meaning +0)
3839             if (opIx != 0 || ri->regName != RegName::ARF_NULL)
3840                 FailT("src", opIx, ": invalid register",
3841                     opIx == 0 ? " (must be GRF or null)" :  " (must be GRF)");
3842         } else if (!ri->isRegNumberValid(regNum)) {
3843             FailT("src", opIx, "register number too large",
3844                 " (", ri->syntax, " only has ", ri->numRegs,
3845                 " on this platform)");
3846         }
3847 
3848         Loc subRegLoc = NextLoc();
3849         int subregNum = 0;
3850         // parse subreg for src2
3851         //
3852         // all operands except src2 have an implicit subreg 0.  No explicit
3853         // subreg is allowed.  For src2, its subreg can be either implicit
3854         // or explicit.  If it is explicit, the subreg must be half-grf
3855         // aligned.
3856         if (LookingAt(DOT)) {
3857             Skip();
3858             ConsumeIntLitOrFail(subregNum, "expected subregister");
3859         }
3860 
3861         // check for reg region, which is not forbidden.
3862         if (LookingAt(LANGLE)) {
3863             FailT("instruction does not support regioning");
3864         }
3865 
3866         // match :<Type> or :<SubBytePrecision>
3867         Loc colonLoc = NextLoc();
3868         Type ty = TryParseOpType(SRC_TYPES);
3869         if (ty == Type::INVALID) {
3870             FailT("invalid type");
3871         }
3872         if (opIx == 1 && subregNum != 0) {
3873             // dpas  src1 subreg must be 0
3874             WarningAtT(subRegLoc,
3875                 "src1 subregister must be GRF aligned for this op");
3876         }
3877         // TODO: we could ensure src2 is half-grf aligned
3878         // int byteOff = SubRegToBytesOffset(subregNum, RegName::GRF_R, ty);
3879 
3880         RegRef reg(regNum, subregNum);
3881         if (isDst) {
3882             m_builder.InstDpasDstOp(regStart, ri->regName, reg, ty);
3883         } else {
3884             m_builder.InstDpasSrcOp(opIx, regStart, ri->regName, reg, ty);
3885         }
3886     }
3887 
3888 }; // class KernelParser
3889 
3890 
3891 ///////////////////////////////////////////////////////////////////////////////
3892 struct ParsedPayloadInfo {
3893     Loc regLoc = Loc::INVALID;
3894     int regNum = -1;
3895     RegName regName = RegName::INVALID;
3896     Loc regLenLoc = Loc::INVALID;
3897     int regLen = 0;
3898     bool regLenWasImplicit = false;
3899 };
3900 struct ParsedAddrOperand {
3901     Loc surfArgLoc = Loc::INVALID;
3902     SendDesc surfArg; // won't be set for flat
3903                          //
3904     Loc scaleLoc = Loc::INVALID;
3905     int scale = 1;
3906     //
3907     ParsedPayloadInfo payload;
3908     //
3909     Loc immOffsetLoc = Loc::INVALID;
3910     int immOffset = 0;
3911 };
3912 
3913 //
3914 // parses a subset of LSC ops
3915 //  EXAMPLES:
3916 //    load.slm                (32|M0)   r10:2:d32    [r20:2]:a32
3917 //    load_strided.ugm.ca.ca  (32|M0)   r10:4:d32x2  [r20:4]:a64
3918 //
3919 //    store.slm       (32|M0)  [r20:2]:a32   r10:2:d32
3920 //    store_quad.slm  (32|M0)  [r20:4]:a64   r10:4:d32.xw
3921 //
3922 //    atomic_fadd.ugm.uc.uc  (32|M0)   r10:2:d32  [r20:4]:a64  null
3923 //
ParseLdStOpControls(Loc mneLoc,const SendOpDefinition & opInfo,VectorMessageArgs & vma)3924 void KernelParser::ParseLdStOpControls(
3925     Loc mneLoc, const SendOpDefinition &opInfo, VectorMessageArgs &vma)
3926 {
3927     auto tryParseAddrSize = [&] (std::string ctrl) {
3928         if (ctrl == "a16") {
3929             vma.addrSize = 16;
3930         } else if (ctrl == "a32") {
3931             vma.addrSize = 32;
3932         } else if (ctrl == "a64") {
3933             vma.addrSize = 64;
3934         } else {
3935             return false;
3936         }
3937         return true;
3938     };
3939     //
3940     auto tryParseDataType = [&] (Loc dtLoc, std::string dt) {
3941         if (dt.size() < 2 || dt[0] != 'd' || !isdigit(dt[1])) {
3942             return false;
3943         }
3944         //
3945         auto errorAtOffset =
3946             [&] (size_t off, std::string msg) {
3947             dtLoc.col += (uint32_t)off;
3948             dtLoc.offset += (PC)off;
3949             FailS(dtLoc, msg);
3950         };
3951         size_t ix = 1;
3952         auto skip = [&] (size_t i) {
3953             ix = std::max<size_t>(i + ix, dt.size());
3954         };
3955         auto lookingAt = [&] (const char *str) {
3956             return dt.find(str, ix) == ix;
3957         };
3958         auto consumeIf = [&] (const char *str) {
3959             if (lookingAt(str)) {
3960                 skip(strlen(str));
3961                 return true;
3962             }
3963             return false;
3964         };
3965         //////////////////////////////////////////////////////////
3966         // data size
3967         //    e.g. d32... or d8u32...
3968         int dataSize = 0;
3969         for (; ix < dt.size() && isdigit(dt[ix]); ix++) {
3970             dataSize = 10*dataSize + dt[ix] - '0';
3971         }
3972         switch (dataSize) {
3973         case  8: case 16: case 32: case 64: case 128: case 256:
3974             break;
3975         default:
3976             errorAtOffset(0, "invalid data type size");
3977         }
3978         // could be d8u32
3979         vma.dataSizeExpandHigh = false;
3980         vma.dataSizeReg = vma.dataSizeMem = dataSize;
3981         if (consumeIf("u32h")) {
3982             vma.dataSizeReg = 32;
3983             vma.dataSizeExpandHigh = true;
3984         } else if (consumeIf("u32")) {
3985             vma.dataSizeReg = 32;
3986         }
3987 
3988         //////////////////////////////////////////////////////////
3989         // vector size
3990         //  ...x4 or ...x64t or ....xyw (for quad)
3991         if (opInfo.hasChMask()) {
3992             if (ix != dt.size())
3993                 errorAtOffset(ix, "unexpected data type");
3994             // e.g. d32.xz
3995             //          ^
3996             if (LookingAtSeq(DOT, IDENT)) {
3997                 Skip();
3998                 auto cmask = ConsumeIdentOrFail("expected op control symbols");
3999                 for (size_t i = 0; i < cmask.size(); i++) {
4000                     auto setElem = [&] (int off) {
4001                         if (vma.dataComponentMask & (1<<off)) {
4002                             errorAtOffset(ix + i,
4003                                 "duplicate component mask element");
4004                         }
4005                         vma.dataComponentMask |= (1<<off);
4006                     };
4007                     if (cmask[i] == 'x') {
4008                         setElem(0);
4009                     } else if (cmask[i] == 'y') {
4010                         setElem(1);
4011                     } else if (cmask[i] == 'z') {
4012                         setElem(2);
4013                     } else if (cmask[i] == 'w') {
4014                         setElem(3);
4015                     } else {
4016                         errorAtOffset(ix + i, "invalid component mask symbol");
4017                     }
4018                 }
4019             } else {
4020                 FailS(NextLoc(), "expected component mask");
4021             }
4022         } else {
4023             // regular vector type
4024             // e.g. d8, d8u32 or d32x16t
4025             auto vix = ix;
4026             int vecSize = 1;
4027             if (ix < dt.size() - 1 && dt[ix] == 'x' && isdigit(dt[ix+1])) {
4028                 ix++;
4029                 vecSize = 0;
4030                 for (; ix < dt.size() && isdigit(dt[ix]); ix++)
4031                     vecSize = 10*vecSize + dt[ix] - '0';
4032                 switch (vecSize) {
4033                 case  1: case  2: case  3: case  4:
4034                 case  8: case 16: case 32: case 64:
4035                     break;
4036                 default: errorAtOffset(vix, "invalid vector size"); break;
4037                 }
4038             }
4039             vma.dataVectorSize = (short)vecSize;
4040             vma.dataTranspose = false;
4041             //
4042             // maybe a 't' following
4043             for (; ix < dt.size(); ix++) {
4044                 if (dt[ix] == 't') {
4045                     if (vma.dataTranspose)
4046                         errorAtOffset(ix, "transpose already set");
4047                     vma.dataTranspose = true;
4048                 } else if (dt[ix] == 'v') {
4049                     if (vma.dataVnni) {
4050                         errorAtOffset(ix, "vnni already set");
4051                     } else if (vma.op != SendOp::LOAD_BLOCK2D) {
4052                         errorAtOffset(ix,
4053                             "vnni only permitted on load_block2d");
4054                     } else {
4055                         vma.dataVnni = true;
4056                     }
4057                 } else {
4058                     errorAtOffset(ix, "malformed data type suffix");
4059                 }
4060             } // data type suffix
4061         }
4062         //
4063         return true;
4064     };
4065     //
4066     auto tryCachingSymbol = [] (std::string cc, CacheOpt &co) {
4067         if (cc == "df") {
4068             co = CacheOpt::DEFAULT;
4069         } else if (cc == "ri") {
4070             co = CacheOpt::READINVALIDATE;
4071         } else if (cc == "ca") {
4072             co = CacheOpt::CACHED;
4073         } else if (cc == "uc") {
4074             co = CacheOpt::UNCACHED;
4075         } else if (cc == "st") {
4076             co = CacheOpt::STREAMING;
4077         } else if (cc == "wb") {
4078             co = CacheOpt::WRITEBACK;
4079         } else if (cc == "wt") {
4080             co = CacheOpt::WRITETHROUGH;
4081         } else {
4082             return false;
4083         }
4084         return true;
4085     };
4086     //
4087     Loc cacheControlLoc = mneLoc;
4088     vma.cachingL1 = CacheOpt::DEFAULT;
4089     vma.cachingL3 = CacheOpt::DEFAULT;
4090     //
4091     bool cachingSet = false;
4092     bool isAddrSizeSet = false;
4093     bool isDataSizeSet = false;
4094     //
4095     while (Consume(DOT)) {
4096         Loc ctrlLoc = NextLoc();
4097         auto ctrl = ConsumeIdentOrFail("expected op control symbol");
4098         if (tryParseAddrSize(ctrl)) {
4099             if (isAddrSizeSet) {
4100                 FailAtT(ctrlLoc, "duplciate address size");
4101             }
4102             isAddrSizeSet = true;
4103         } else if (tryCachingSymbol(ctrl, vma.cachingL1)) {
4104             if (cachingSet) {
4105                 FailAtT(ctrlLoc, "duplicate caching options");
4106             }
4107             cachingSet = true;
4108             cacheControlLoc = ctrlLoc;
4109             if (!LookingAtSeq(DOT, IDENT)) {
4110                 FailT("expected .");
4111             }
4112             Skip();
4113             auto l3sym = GetTokenAsString();
4114             if (!tryCachingSymbol(l3sym, vma.cachingL3)) {
4115                 FailT("expected caching option");
4116             }
4117             Skip();
4118         } else if (tryParseDataType(ctrlLoc, ctrl)) {
4119             if (isDataSizeSet)
4120                 FailS(ctrlLoc, "data size already set");
4121             isDataSizeSet = true;
4122         } else {
4123             FailT("invalid ld/st option");
4124         }
4125     }
4126     if (!isAddrSizeSet) {
4127         FailAtT(mneLoc, "address size not set");
4128     }
4129     if (!isDataSizeSet) {
4130         FailAtT(mneLoc, "data size not set");
4131     }
4132     bool isDft =
4133         vma.cachingL1 != CacheOpt::DEFAULT &&
4134         vma.cachingL3 != CacheOpt::DEFAULT;
4135     if (isDft && vma.sfid == SFID::SLM) {
4136         FailAtT(cacheControlLoc, "SLM forbids cache control options");
4137     }
4138 }
4139 
4140 
ParseLdStInst()4141 bool KernelParser::ParseLdStInst()
4142 {
4143     if (!LookingAtSeq(IDENT, DOT, IDENT)) {
4144         return false;
4145     }
4146 
4147     VectorMessageArgs vma; // vma {}; crashes VS2019 without dft constructor!
4148     //
4149     const auto mneLoc = NextLoc();
4150     auto mne = GetTokenAsString();
4151     const SendOpDefinition &opInfo = lookupSendOp(mne.c_str());
4152     if (!opInfo.isValid())
4153         return false; // doesn't match a known op
4154     vma.op = opInfo.op;
4155     //
4156     const auto sfidSym = GetTokenAsString(Next(2));
4157     vma.sfid = FromSyntax<SFID>(sfidSym);
4158     if (!sendOpSupportsSyntax(platform(), vma.op, vma.sfid)) {
4159         FailS(NextLoc(), "op not yet supported for ld/st parse");
4160     }
4161     // IGA op
4162     m_opSpec = &m_model.lookupOpSpec(
4163         platform() < Platform::XE ? Op::SENDS : Op::SEND);
4164     if (!m_opSpec->isValid()) {
4165         FailS(NextLoc(), "INTERNAL ERROR: unable to lookup iga::Op");
4166     }
4167     m_builder.InstOp(m_opSpec);
4168     m_builder.InstSubfunction(vma.sfid);
4169     //
4170     Skip(3);
4171     //
4172     ParseLdStOpControls(mneLoc, opInfo, vma);
4173     //
4174     ChannelOffset chOff = ChannelOffset::M0; // sets m_execSize
4175     const auto execSizeLoc = NextLoc();
4176     ParseExecInfo(m_defaultExecutionSize, m_execSize, chOff);
4177     vma.execSize = static_cast<int>(m_execSize);
4178     //
4179     ///////////////////////////////////////////////////////////////////////////
4180     vma.addrType = AddrType::FLAT;
4181     //
4182     // r13 or null
4183     auto parseReg = [&] (ParsedPayloadInfo &ppi) {
4184         ppi.regLoc = NextLoc();
4185         const RegInfo *regInfo;
4186         int regNum;
4187         if (!ConsumeReg(regInfo, regNum)) {
4188             FailT("expected register");
4189             return;
4190         } else if (regInfo->regName != RegName::GRF_R &&
4191             regInfo->regName != RegName::ARF_NULL)
4192         {
4193             FailAtT(ppi.regLoc, "register must be GRF or null");
4194             return;
4195         }
4196         ppi.regName = regInfo->regName;
4197         ppi.regNum = (uint8_t)regNum;
4198     };
4199 
4200     // r13:0, r13 (meaning r13:1)
4201     // or null:0, or null meaning (null:0)
4202     auto parsePayload = [&] (ParsedPayloadInfo &ppi) {
4203         auto regLoc = NextLoc();
4204         parseReg(ppi);
4205         if (Consume(COLON)) {
4206             auto tk = Next();
4207             ppi.regLenLoc = tk.loc;
4208             ImmVal val;
4209             if (!TryParseIntConstExprPrimary(
4210                 val, "payload suffix expression"))
4211             {
4212                 FailAtT(tk.loc, "expected integral payload suffix expression");
4213             }
4214             ensureIntegral(tk, val);
4215             if (val.s64 < 0 || val.s64 > 0x1F) {
4216                 FailAtT(tk.loc, "payload size overflow");
4217             }
4218             ppi.regLen = (int)val.s64;
4219         } else {
4220             // implicitly 0 or 1 depending on register
4221             ppi.regLen = ppi.regName == RegName::ARF_NULL ? 0 : 1;
4222             ppi.regLenLoc = regLoc.endOfToken();
4223             ppi.regLenWasImplicit = true;
4224         }
4225     };
4226 
4227     //   the formatter deduces the length just to be nice to the reader
4228     auto parsePayloadDst = [&] (ParsedPayloadInfo &ppi) {
4229         parsePayload(ppi);
4230         auto rgn = m_opSpec->hasImplicitDstRegion(m_builder.isMacroOp()) ?
4231             m_opSpec->implicitDstRegion(m_builder.isMacroOp()).getHz() :
4232             Region::Horz::HZ_1;
4233         auto ty = SendOperandDefaultType(-1);
4234         m_builder.InstDstOpRegDirect(
4235             ppi.regLoc, ppi.regName, ppi.regNum, rgn, ty);
4236     };
4237 
4238     auto parsePayloadSrc1 = [&] (ParsedPayloadInfo &ppi) {
4239         parsePayload(ppi);
4240         m_sendSrcLens[1] = ppi.regLen;
4241         m_sendSrcLenLocs[1] = ppi.regLenLoc;
4242         //
4243         const auto rgn = defaultSendOperandRegion(ppi.regName, 1);
4244         const auto ty = SendOperandDefaultType(1);
4245         m_builder.InstSrcOpRegDirect(
4246             1, ppi.regLoc, ppi.regName, (int)ppi.regNum, rgn, ty);
4247     };
4248 
4249     auto parseA0RegOrImm = [&] () {
4250         SendDesc surf;
4251         ConsumeOrFail(LBRACK, "expected [");
4252         RegName regName = RegName::INVALID;
4253         if (ParseAddrRegRefOpt(surf.reg, regName)) {
4254             IGA_ASSERT(regName == RegName::ARF_A, "Indirect send Desc must be ARF_A");
4255             // surface is a register
4256             surf.type = SendDesc::Kind::REG32A;
4257             if (surf.reg.subRegNum & 1) {
4258                 FailT("a0 subregister must be even (values are word aligned)");
4259             }
4260         } else {
4261             // surface is a constant integral expression
4262             auto loc = NextLoc();
4263             surf.type = SendDesc::Kind::IMM;
4264             ImmVal v;
4265             if (!TryParseIntConstExpr(v, "extended descriptor")) {
4266                 FailT("expected surface state offset");
4267             }
4268             if (v.s64 >= 0x03FFFFFF) { // 26b
4269                 FailAtT(loc, "immediate surface state offset is out of bounds");
4270             }
4271             surf.imm = (uint32_t)v.s64;
4272         }
4273         ConsumeOrFail(RBRACK, "expected ]");
4274         return surf;
4275     };
4276 
4277     Loc exDescLoc = mneLoc;
4278     auto parseAddrOperand = [&] (ParsedAddrOperand &addr) {
4279         addr.surfArgLoc = exDescLoc = NextLoc();
4280         if (ConsumeIdentEq("bti")) {
4281             vma.addrType = AddrType::BTI;
4282             vma.addrSurface = parseA0RegOrImm();
4283         } else if (ConsumeIdentEq("bss")) {
4284             vma.addrType = AddrType::BSS;
4285             vma.addrSurface = parseA0RegOrImm();
4286         } else if (ConsumeIdentEq("ss")) {
4287             vma.addrType = AddrType::SS;
4288             vma.addrSurface = parseA0RegOrImm();
4289         } else {
4290             // the "flat" token is optional
4291             // (if addrtype is absent, we assume flat)
4292             if (!ConsumeIdentEq("flat") && !LookingAt(LBRACK)) {
4293                 FailT("expected address type or [");
4294             }
4295             vma.addrType = AddrType::FLAT;
4296             vma.addrSurface = 0x0;
4297         }
4298         //
4299         Consume(LBRACK);
4300         //
4301         ImmVal v;
4302         //
4303         parsePayload(addr.payload);
4304         m_sendSrcLens[0] = addr.payload.regLen;
4305         m_sendSrcLenLocs[0] = addr.payload.regLenLoc;
4306         //
4307         vma.addrOffset = 0x0;
4308         //
4309         Consume(RBRACK);
4310         //
4311         const auto rgn = defaultSendOperandRegion(addr.payload.regName, 0);
4312         const auto ty = SendOperandDefaultType(0);
4313         m_builder.InstSrcOpRegDirect(
4314             0, addr.surfArgLoc,
4315             addr.payload.regName, (int)addr.payload.regNum,
4316             rgn, ty);
4317     };
4318 
4319     auto setOpToNull =
4320         [&] (int opIx, ParsedPayloadInfo &ppi, Loc loc) {
4321             ppi.regLen = 0;
4322             ppi.regLoc = ppi.regLenLoc = loc;
4323             ppi.regName = RegName::ARF_NULL;
4324             ppi.regNum = 0;
4325             //
4326             const auto rgn = defaultSendOperandRegion(ppi.regName, opIx);
4327             const auto ty = SendOperandDefaultType(opIx);
4328             if (opIx < 0) {
4329                 m_builder.InstDstOpRegDirect(
4330                     ppi.regLoc, ppi.regName, ppi.regNum,
4331                     rgn.getHz(), ty);
4332             } else {
4333                 m_builder.InstSrcOpRegDirect(
4334                     opIx, ppi.regLoc, ppi.regName, ppi.regNum,
4335                     rgn, ty);
4336                 m_sendSrcLens[opIx] = 0;
4337                 m_sendSrcLenLocs[opIx] = loc;
4338             }
4339         };
4340 
4341     ///////////////////////////////////////////////////////////////////////////
4342     // actual parsing
4343     ParsedPayloadInfo dstData;
4344     ParsedAddrOperand src0Addr;
4345     ParsedPayloadInfo src1Data;
4346     if (vma.isLoad()) {
4347         parsePayloadDst(dstData);
4348         parseAddrOperand(src0Addr);
4349         setOpToNull(1, src1Data, mneLoc);
4350     } else if (vma.isStore()) {
4351         setOpToNull(-1, dstData, mneLoc); // build a null operand
4352         parseAddrOperand(src0Addr);
4353         parsePayloadSrc1(src1Data);
4354     } else if (vma.isAtomic()) {
4355         parsePayloadDst(dstData);
4356         parseAddrOperand(src0Addr);
4357         parsePayloadSrc1(src1Data);
4358     } else {
4359         FailAtT(mneLoc, "unsupported operation for load/store syntax");
4360     }
4361 
4362     const int GRF_BYTES = platform() >= Platform::XE_HPC ? 64 : 32;
4363     const int DEFAULT_SIMD = GRF_BYTES / 2;
4364 
4365     int expectedDataRegs = -1;
4366     int expectedAddrRegs = -1;
4367     if (vma.op == SendOp::LOAD_BLOCK2D || vma.op == SendOp::STORE_BLOCK2D) {
4368         expectedAddrRegs = 1;
4369         // expectedDataRegs = unknown (function of the payload)
4370     } else
4371     if (vma.dataTranspose) {
4372         // a block operation with a single register payload
4373         // technically execSize on transpose will be 1, but the
4374         // logical extension for higher SIMD's exists too;
4375         // for now we reject it
4376         if (vma.dataTranspose && vma.execSize != 1) {
4377             FailAtT(execSizeLoc, "transpose messages must be SIMD1");
4378         }
4379         int totalBitsPerSimd = vma.dataSizeReg*vma.elementsPerAddress();
4380         expectedDataRegs =
4381             vma.execSize*std::max<int>(totalBitsPerSimd/8/GRF_BYTES, 1);
4382         expectedAddrRegs = 1;
4383     } else {
4384         // e.g. SIMD4 rounds up to SIMD8 if messages are SIMD16 by default
4385         int execElems = std::max<int>(DEFAULT_SIMD/2, vma.execSize);
4386         int grfsPerComponent =
4387             std::max<int>(vma.dataSizeReg*execElems/8/GRF_BYTES, 1);
4388         expectedDataRegs = grfsPerComponent*vma.elementsPerAddress();
4389         if (opInfo.isAddressScalar()) {
4390             expectedAddrRegs = 1;
4391         } else {
4392             expectedAddrRegs = std::max<int>(execElems*vma.addrSize/8/GRF_BYTES, 1);
4393         }
4394     }
4395     ///////////////////////////////////////////////////////////////////////////
4396     // for almost all messages we can deduce the number of address registers
4397     // needed by the message; one exception is in typed, which uses the address
4398     // register count to determine which of U, V, R, and LOD are included
4399     bool checkAddrPayloadSize = true;
4400     bool hasUvrl =
4401         vma.sfid == SFID::TGM &&
4402         (vma.op == SendOp::LOAD_QUAD || vma.op == SendOp::STORE_QUAD ||
4403             vma.isAtomic());
4404     hasUvrl |=
4405         vma.sfid == SFID::TGM && vma.op == SendOp::STORE_UNCOMPRESSED_QUAD;
4406     bool addrLenIsCorrect = src0Addr.payload.regLen == expectedAddrRegs;
4407     addrLenIsCorrect |= hasUvrl &&
4408         (src0Addr.payload.regLen != 2 * expectedAddrRegs ||
4409          src0Addr.payload.regLen != 3 * expectedAddrRegs ||
4410          src0Addr.payload.regLen != 4 * expectedAddrRegs);
4411     if (checkAddrPayloadSize && !addrLenIsCorrect) {
4412         WarningAtT(
4413             src0Addr.payload.regLoc,
4414             "Src0.Length: should be ", expectedAddrRegs);
4415         if (src0Addr.payload.regLenWasImplicit) {
4416             src0Addr.payload.regLen = expectedAddrRegs;
4417         }
4418     }
4419     //
4420     if (vma.isLoad() || vma.isAtomic()) {
4421         // if prefetch or atomic with no return, then
4422         int expectDstRegs =
4423             dstData.regName == RegName::ARF_NULL ? 0 : expectedDataRegs;
4424         if (!dstData.regLenWasImplicit &&
4425             expectedDataRegs >= 0 &&
4426             dstData.regLen != expectDstRegs)
4427         {
4428             // if given an explicit number of dst regs, ensure it is correct
4429             WarningAtT(
4430                 dstData.regLenLoc, "Dst.Length: should be ", expectDstRegs);
4431         } else if (dstData.regLenWasImplicit) {
4432             if (dstData.regName != RegName::ARF_NULL) {
4433                 // assign it, but issue a warning
4434                 dstData.regLen = expectDstRegs;
4435                 WarningAtT(
4436                     dstData.regLenLoc,
4437                     "Dst.Length: :", expectDstRegs," should suffix operand");
4438             } else {
4439                 dstData.regLen = 0;
4440             }
4441         }
4442     }
4443     if (vma.isStore() || vma.isAtomic()) {
4444         int expectSrc1Regs =
4445             src1Data.regName == RegName::ARF_NULL ? 0 : expectedDataRegs;
4446         if (vma.isAtomic())
4447             expectSrc1Regs *= opInfo.numAtomicArgs();
4448         if (!src1Data.regLenWasImplicit &&
4449             expectSrc1Regs >= 0 &&
4450             src1Data.regLen != expectSrc1Regs)
4451         {
4452             WarningAtT(
4453                 src1Data.regLenLoc, "Src1.Length: should be ", expectSrc1Regs);
4454         } else if (src1Data.regLenWasImplicit &&
4455             src1Data.regName != RegName::ARF_NULL)
4456         {
4457             bool hasSrc1LenSfx = true;
4458             if (hasSrc1LenSfx && vma.addrSurface.isReg()) {
4459                 // with a0 exdesc must omit Src1.Length
4460                 // if ExBSO is set
4461                 expectSrc1Regs = -1;
4462             } else {
4463                 WarningAtT(
4464                     src1Data.regLenLoc,
4465                     "Src1.Length: :", expectSrc1Regs,
4466                     " should suffix operand");
4467             }
4468             src1Data.regLen = expectSrc1Regs;
4469         }
4470     }
4471 
4472     SendDesc exDesc(0x0), desc(0x0);
4473 
4474     std::string err;
4475     if (!encodeDescriptors(
4476         platform(),
4477         vma,
4478         exDesc, desc,
4479         err))
4480     {
4481         if (err.empty())
4482             err = "unknown error translating load/store op";
4483         FailS(mneLoc, err);
4484     }
4485 
4486     desc.imm |= ((uint32_t)src0Addr.payload.regLen & 0xF) << 25;
4487     desc.imm |= ((uint32_t)dstData.regLen & 0x1F) << 20;
4488     if (platform() < Platform::XE_HP && exDesc.isImm()) {
4489         // ExDesc[10:6] on <= XE_HP
4490         exDesc.imm |= (((uint32_t)src1Data.regLen << 6) & 0x1F);
4491     }
4492 
4493     m_builder.InstSendSrc0Length(src0Addr.payload.regLen);
4494     m_builder.InstSendSrc1Length(src1Data.regLen);
4495 
4496     m_builder.InstSendDescs(mneLoc,
4497         exDesc, mneLoc, desc);
4498 
4499     return true;
4500 } // KernelParser::ParseLscOp
4501 
4502 
4503 
ParseGenKernel(const Model & m,const char * inp,iga::ErrorHandler & e,const ParseOpts & popts)4504 Kernel *iga::ParseGenKernel(
4505     const Model &m,
4506     const char *inp,
4507     iga::ErrorHandler &e,
4508     const ParseOpts &popts)
4509 {
4510     Kernel *k = new Kernel(m);
4511 
4512     InstBuilder h(k, e);
4513     if (popts.swsbEncodeMode != SWSB_ENCODE_MODE::SWSBInvalidMode)
4514         h.setSWSBEncodingMode(popts.swsbEncodeMode);
4515 
4516     KernelParser p(m, h, inp, e, popts);
4517     try {
4518         p.ParseListing();
4519     } catch (SyntaxError) {
4520         // no need to handle it (error handler has already recorded the errors)
4521         delete k;
4522         return nullptr;
4523     }
4524 
4525     auto &insts = h.getInsts();
4526     auto blockStarts = Block::inferBlocks(
4527         e,
4528         k->getMemManager(),
4529         insts);
4530     int id = 1;
4531     for (auto bitr : blockStarts) {
4532         bitr.second->setID(id++);
4533         k->appendBlock(bitr.second);
4534     }
4535 
4536 #if 0
4537     std::stringstream ss;
4538     ss << "PARSED BLOCKS\n\n";
4539     for (auto b : k->getBlockList()) {
4540         ss << "BLOCK[" << b->getID() << "] at pc " <<
4541             b->getPC() << ":\n";
4542         ss << "  targeted by {";
4543         int totalTargets = 0;
4544         for (auto b2 : k->getBlockList()) {
4545             for (auto i2 : b2->getInstList()) {
4546                 for (unsigned srcIx = 0; srcIx < i2->getSourceCount(); srcIx++) {
4547                     const iga::Operand &src = i2->getSource(srcIx);
4548                     if (src.getTargetBlock() == b) {
4549                         if (totalTargets++ > 0)
4550                           ss << ", ";
4551                         ss << "." << i2->getPC();
4552                         break;
4553                     }
4554                 }
4555             }
4556         }
4557         ss << "}\n";
4558         for (auto i : b->getInstList()) {
4559             ss << "  ." << i->getPC() << " is " <<
4560                 i->getOpSpec().fullMnemonic << "\n";
4561         }
4562         ss << "\n";
4563     }
4564     std::cout << ss.str();
4565 #endif
4566 
4567     return k;
4568 }
4569