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 ®nameLoc,
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 ®nameLoc,
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