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 #ifndef IGA_BACKEND_NATIVE_FIELD_HPP 10 #define IGA_BACKEND_NATIVE_FIELD_HPP 11 12 #include "../../api/iga_bxml_ops.hpp" 13 #include "../../asserts.hpp" 14 #include "../../bits.hpp" 15 #include "../../strings.hpp" 16 17 #include <cstdint> 18 #include <cstring> 19 #include <iomanip> 20 #include <ostream> 21 #include <string> 22 23 #if (__cplusplus >= 201402L) || (defined(_MSC_VER) && (_MSVC_LANG >= 201402L)) 24 // We assume we are at least C++11, but here we're trying to ensure we 25 // are C++14 or better. Various constexpr functions below will then be able 26 // to opt-in based if our compiler is C++14 or better. C++11 allows us to 27 // return constexpr expressions only, but C++14 allows more statements. 28 // 29 // MSVS defines __cplusplus as 199711L, but they do use _MSVC_LANG in those 30 // release. Maybe there's a better way. 31 #define CONSTEXPR_IF_CPP14 constexpr 32 #else 33 #define CONSTEXPR_IF_CPP14 34 #endif 35 36 namespace iga 37 { 38 #define INVALID_FIELD Field() 39 40 // Defines a fragment of a field. A field consists of one or 41 // more fragments. See the struct below for more definition. 42 struct Fragment 43 { 44 static const int NO_OFFSET = -1; 45 46 enum class Kind { 47 INVALID = 0, 48 ENCODED, 49 ZERO_FILL, // takes no space in encodings 50 ZERO_WIRES, // takes up encoding space, but must be zero 51 // ONES_FILL (only enable if needed) 52 // other more complicated functions (e.g. clone) 53 } kind = Kind::ENCODED; 54 const char *name; // e.g. "Dst.Subreg[4:3]" 55 int offset; // offset in the instruction 56 int length; // number of bits in this fragment 57 Fragmentiga::Fragment58 constexpr Fragment( 59 const char *_name, 60 int _offset, 61 int _length, 62 Kind _kind = Kind::ENCODED) 63 : name(_name), offset(_offset), length(_length), kind(_kind) 64 { 65 } Fragmentiga::Fragment66 constexpr Fragment() : 67 Fragment(nullptr, NO_OFFSET, 0, Kind::INVALID) { } 68 69 // We could lower this to C++11 if we really tried. 70 // (it's a pure boolean expression underneath) overlapsiga::Fragment71 CONSTEXPR_IF_CPP14 bool overlaps(const Fragment &f) const { 72 if (!isEncoded() || !f.isEncoded()) 73 return false; // pseudo-fragments (unmapped) don't collide 74 75 // arrange intervals as: smaller or equal, and larger 76 const Fragment &smaller = length <= f.length ? *this : f; 77 const Fragment &larger = length <= f.length ? f : *this; 78 int sLo = smaller.offset, 79 sHi = smaller.offset + smaller.length - 1; 80 int lLo = larger.offset, 81 lHi = larger.offset + larger.length - 1; 82 // determine if the ends of smaller fit in larger 83 return (sLo >= lLo && sLo <= lHi) || 84 (sHi >= lLo && sHi <= lHi); 85 } 86 isEncodediga::Fragment87 constexpr bool isEncoded() const {return kind == Kind::ENCODED;} isZeroFilliga::Fragment88 constexpr bool isZeroFill() const {return kind == Kind::ZERO_FILL;} isZeroWiresiga::Fragment89 constexpr bool isZeroWires() const {return kind == Kind::ZERO_WIRES;} isInvalidiga::Fragment90 constexpr bool isInvalid() const {return kind == Kind::INVALID;} 91 getMaskiga::Fragment92 constexpr uint64_t getMask() const { 93 return getFieldMaskUnshifted<uint64_t>(length); 94 } 95 }; 96 97 98 // A field represents a set of bits encoded in an instruction that a 99 // processor uses. E.g. the opcode, a register number, .... 100 // 101 // Typically, fields are contiguous, but some are fragmented in 102 // non-contiguous parts in the instruction. Moreover, some of these 103 // fragments aren't even encoded, but are intrinically defined 104 // (implicitly defined). 105 // 106 // One example of a fragmented field is ExDesc in the send instruction. 107 // which has fragments scattered all over the 128b encoding. 108 // 109 // Another pattern is intrinsic fragments. That is, a field has bits 110 // that are not encoding in the instruction, but are implicitly some 111 // value (usually 0). The .kind field will be set to KIND::ZERO_FILL, 112 // for example. 113 // 114 // E.g. in TGL a ternary dst register encodes Dst.Subreg[4:3]. 115 // Dst.Subreg[2:0] don't get encoded in the instruction and are 116 // implicitly defined as zeros. This is logically the same as 117 // decoding the two bits containing Dst.Subreg[4:3] and 118 // shifting it left 3 bits. In this scheme we'd define this as 119 // A field consistening of an ENCODED fragment for [4:3] and a 120 // 121 // 122 struct Field { 123 static const int MAX_FRAGMENTS_PER_FIELD = 4; 124 125 // the field name; e.g. "Dst.Reg" 126 const char *name; 127 128 // in order of low to high 129 Fragment fragments[MAX_FRAGMENTS_PER_FIELD]; 130 131 // an undefined field (zero fragments) Fieldiga::Field132 constexpr Field() 133 : name(nullptr) 134 { 135 } 136 // a simple encoded field (single contiguous) Fieldiga::Field137 constexpr Field(const char *_name, int offset, int length) 138 : name(_name) 139 , fragments{Fragment(_name, offset, length)} 140 { 141 } 142 // a zero fill field or must-be-zero field Fieldiga::Field143 constexpr Field(const char *_name, int length, 144 Fragment::Kind kind = Fragment::Kind::ZERO_FILL) 145 : name(_name) 146 , fragments{Fragment(_name, Fragment::NO_OFFSET, length, kind)} 147 { 148 149 } 150 // a field with two fragments Fieldiga::Field151 constexpr Field( 152 const char *_name, 153 const char *name0, int offset0, int length0, Fragment::Kind kind0, 154 const char *name1, int offset1, int length1, Fragment::Kind kind1) 155 : name(_name) 156 , fragments{ 157 Fragment(name0, offset0, length0, kind0), 158 Fragment(name1, offset1, length1, kind1) 159 } 160 { 161 } 162 // a field with three fragments Fieldiga::Field163 constexpr Field( 164 const char *_name, 165 const char *name0, int offset0, int length0, Fragment::Kind kind0, 166 const char *name1, int offset1, int length1, Fragment::Kind kind1, 167 const char *name2, int offset2, int length2, Fragment::Kind kind2) 168 : name(_name) 169 , fragments { 170 Fragment(name0, offset0, length0, kind0), 171 Fragment(name1, offset1, length1, kind1), 172 Fragment(name2, offset2, length2, kind2), 173 } 174 { 175 } 176 // a field with two encoded fragments Fieldiga::Field177 constexpr Field( 178 const char *_name, 179 const char *name0, int offset0, int length0, 180 const char *name1, int offset1, int length1) 181 : Field(_name, 182 name0, offset0, length0, Fragment::Kind::ENCODED, 183 name1, offset1, length1, Fragment::Kind::ENCODED) 184 { 185 } 186 // a field with three encoded fragments Fieldiga::Field187 constexpr Field( 188 const char *_name, 189 const char *name0, int offset0, int length0, 190 const char *name1, int offset1, int length1, 191 const char *name2, int offset2, int length2) 192 : Field(_name, 193 name0, offset0, length0, Fragment::Kind::ENCODED, 194 name1, offset1, length1, Fragment::Kind::ENCODED, 195 name2, offset2, length2, Fragment::Kind::ENCODED) 196 { 197 } 198 // a field a fragment of intrinsically defined 0's and top bits encoded Fieldiga::Field199 constexpr Field( 200 const char *_name, 201 const char *name0, int shift, 202 const char *name1, int offset1, int length1) 203 : Field(_name, 204 name0, Fragment::NO_OFFSET, shift, Fragment::Kind::ZERO_FILL, 205 name1, offset1, length1, Fragment::Kind::ENCODED) 206 { 207 } 208 209 // returns the field length lengthiga::Field210 CONSTEXPR_IF_CPP14 int length() const { 211 return decodedLength(); 212 } 213 214 // returns the decoded length of the entire field decodedLengthiga::Field215 CONSTEXPR_IF_CPP14 int decodedLength() const { 216 int len = 0; 217 for (const auto &fr : fragments) { 218 if (fr.isInvalid()) 219 break; 220 len += fr.length; 221 } 222 return len; 223 } 224 225 // returns the decoded length of the entire field encodedLengthiga::Field226 CONSTEXPR_IF_CPP14 int encodedLength() const { 227 int len = 0; 228 for (const auto &fr : fragments) { 229 if (fr.isInvalid()) 230 break; 231 else if (fr.isEncoded() || fr.isZeroWires()) 232 len += fr.length; 233 } 234 return len; 235 } 236 isAtomiciga::Field237 constexpr bool isAtomic() const { 238 return 239 fragments[1].isInvalid() && 240 // if this is MBZ field 241 (fragments[0].isEncoded() || 242 fragments[0].isZeroFill() || 243 fragments[0].isZeroWires()); 244 } 245 246 // returns a reference the first fragment 247 // asserts that the fragment is atomic atomFragmentiga::Field248 const Fragment &atomFragment() const { 249 IGA_ASSERT(isAtomic(), "fragment is not atomic"); 250 return fragments[0]; 251 } 252 253 // implicit conversion to fragment operator const Fragment&iga::Field254 operator const Fragment &() const { 255 return atomFragment(); 256 } 257 fragmentToFieldiga::Field258 constexpr Field fragmentToField(int ix) const { 259 return Field( 260 fragments[ix].name, 261 fragments[ix].offset, 262 fragments[ix].length); 263 } 264 }; // Field 265 operator ==(const Fragment & f1,const Fragment & f2)266 static inline bool operator== (const Fragment &f1, const Fragment &f2) 267 { 268 return 269 f1.kind == f2.kind && 270 f1.offset == f2.offset && 271 f1.length == f2.length && 272 strcmp(f1.name, f2.name) == 0; 273 } operator <(const Fragment & f1,const Fragment & f2)274 static inline bool operator< (const Fragment &f1, const Fragment &f2) 275 { 276 if (f1.kind != f2.kind) { 277 return f1.kind < f2.kind; 278 } else if (f1.offset != f2.offset) { 279 return f1.offset < f2.offset; 280 } else if (f1.length != f2.length) { 281 return f1.length < f2.length; 282 } else { 283 return strcmp(f1.name, f2.name) < 0; 284 } 285 } 286 287 ////////////////////////////////////////////////////////////////////////// 288 // enables us to group various operands together 289 struct OperandFields { 290 const char *name; 291 const Field &fTYPE; 292 const Field *pfSRCMODS; 293 const Field &fREGFILE; 294 295 const Field &fREG; 296 const Field &fSUBREG; 297 const Field &fSPCACC; 298 299 const Field *pfRGNVT; 300 const Field *pfRGNWI; 301 const Field &fRGNHZ; 302 303 const Field *pfADDRMODE; 304 const Field *pfADDRREG; 305 const Field *pfADDROFF; 306 307 const Field *pfSRCISIMM; 308 const Field *pfSRCIMM32L; // also IMM16 309 const Field *pfSRCIMM32H; 310 }; 311 312 enum OpIx { 313 IX_DST = 0x40, // must not overlap with other bits 314 IX_SRC0 = 0x10, 315 IX_SRC1 = 0x20, 316 IX_SRC2 = 0x30, 317 OP_IX_TYPE_MASK = 0xF0, 318 319 OP_IX_MASK = 0x0F, // masks table index (unique across bas. and trn.) 320 321 BAS = 0x100, 322 BAS_DST = BAS | IX_DST | 0, 323 BAS_SRC0 = BAS | IX_SRC0 | 1, 324 BAS_SRC1 = BAS | IX_SRC1 | 2, 325 326 TER = 0x200, 327 TER_DST = TER | IX_DST | 3, 328 TER_SRC0 = TER | IX_SRC0 | 4, 329 TER_SRC1 = TER | IX_SRC1 | 5, 330 TER_SRC2 = TER | IX_SRC2 | 6 331 }; IsTernary(OpIx IX)332 static inline bool IsTernary(OpIx IX) {return (IX & OpIx::TER) == OpIx::TER;} IsDst(OpIx IX)333 static inline bool IsDst(OpIx IX) {return (IX & OpIx::IX_DST) == OpIx::IX_DST;} ToSrcIndex(OpIx IX)334 static inline int ToSrcIndex(OpIx IX) { 335 IGA_ASSERT(!IsDst(IX),"ToSrcIndex(OpIx) on dst index"); 336 return ((IX & 0xF0)>>4) - 1; 337 } ToFieldOperandArrayIndex(OpIx IX)338 static inline int ToFieldOperandArrayIndex(OpIx IX) { 339 return (IX & OpIx::OP_IX_MASK); 340 } ToStringOpIx(OpIx ix)341 static inline std::string ToStringOpIx(OpIx ix) { 342 switch (ix & OpIx::OP_IX_TYPE_MASK) { 343 case OpIx::IX_DST: return "dst"; 344 case OpIx::IX_SRC0: return "src0"; 345 case OpIx::IX_SRC1: return "src1"; 346 case OpIx::IX_SRC2: return "src2"; 347 default: return "?"; 348 } 349 } 350 351 352 // a grouping of compaction information 353 struct CompactionMapping { 354 const Field &index; 355 const uint64_t *values; 356 size_t numValues; 357 const Field **mappings; 358 size_t numMappings; 359 const char **meanings; // length is numValues 360 // this is used for "what would this compaction word mean" 361 std::string (*format)(Op,uint64_t); 362 363 // sums up with width of all the fields mapped countNumBitsMappediga::CompactionMapping364 size_t countNumBitsMapped() const { 365 size_t entrySizeBits = 0; 366 for (size_t k = 0; k < numMappings; k++) 367 entrySizeBits += mappings[k]->length(); 368 return entrySizeBits; 369 } isSrcImmFieldiga::CompactionMapping370 bool isSrcImmField() const {return mappings == nullptr;} 371 372 // emits output such as "0`001`1`0`001" 373 // for SrcImm compacted fields it just emits the value emitBinaryiga::CompactionMapping374 void emitBinary(std::ostream &os, uint64_t val) const { 375 if (mappings == nullptr) { 376 // srcimm field 377 os << std::setw(16) << "(" << fmtHex(val) << ")"; 378 } else { 379 int bitOff = (int)countNumBitsMapped(); 380 for (int mIx = 0; mIx < (int)numMappings; ++mIx) { 381 if (mIx != 0) { 382 os << "`"; 383 } 384 const Field *mf = mappings[mIx]; 385 bitOff -= mf->length(); 386 auto bs = iga::getBits(val, bitOff, mf->length()); 387 fmtBinaryDigits(os, bs, mf->length()); 388 } 389 } 390 } 391 }; 392 393 // groups all CompactionMappings for a given platform 394 struct CompactionScheme { 395 const CompactionMapping &CTRLIX_2SRC; 396 const CompactionMapping &DTIX_2SRC; 397 const CompactionMapping &SRIX_2SRC; 398 const CompactionMapping &SRC0IX_2SRC; 399 const CompactionMapping &SRC1IX_2SRC; 400 // 401 const CompactionMapping &CTRLIX_3SRC; 402 const CompactionMapping &SRCIX_3SRC; 403 const CompactionMapping &SRIX_3SRC; 404 }; 405 406 // Assumes existence of: 407 // constexpr static Field CMP_ ## SYM ## _ ## PLATFORM = ... 408 // std::string [namespace::]Format_ ## SYM ## _ ## PLATFORM (Op, uint64_t) 409 // const const Field SYM ## _MAPPINGS_ ## PLATFORM [M] {...} 410 // const const uint64_t SYM ## _VALUES_ ## PLATFORM [N] {...} 411 // const const const char * SYM ## _MEANINGS_ ## PLATFORM [N] {...} 412 // 413 // For example: 414 // MAKE_COMPACTION_MAPPING(CTRLIX_2SRC, TGL); 415 // 416 // utilizes all of the following: 417 // 418 // constexpr static Field iga::xe::CMP_CTRLIX_2SRC_TGL {...} 419 // std::string iga::xe::Format_CTRLIX_2SRC_TGL (Op, uint64_t) 420 // const const Field iga::xe::CTRLIX_2SRC_MAPPINGS_TGL [M] {...} 421 // const const uint64_t iga::xe::CTRLIX_2SRC_VALUES_TGL [N] {...} 422 // const const const char * iga::xe::CTRLIX_2SRC_MEANINGS_TGL [N] {...} 423 #define MAKE_COMPACTION_MAPPING(SYM, PLATFORM)\ 424 extern std::string Format_ ## SYM ## _ ## PLATFORM (Op, uint64_t);\ 425 \ 426 static_assert(sizeof(SYM ## _VALUES_ ## PLATFORM)/sizeof(SYM ## _VALUES_ ## PLATFORM [0]) == \ 427 sizeof(SYM ## _MEANINGS_ ## PLATFORM)/sizeof(SYM ## _MEANINGS_ ## PLATFORM [0]), \ 428 "mismatch in table sizes");\ 429 static const CompactionMapping CM_ ## SYM ## _ ## PLATFORM {\ 430 CMP_ ## SYM ## _ ## PLATFORM, \ 431 SYM ## _VALUES_ ## PLATFORM, \ 432 sizeof(SYM ## _VALUES_ ## PLATFORM)/sizeof(SYM ## _VALUES_ ## PLATFORM [0]),\ 433 SYM ## _MAPPINGS_ ## PLATFORM, \ 434 sizeof(SYM ## _MAPPINGS_ ## PLATFORM)/sizeof(SYM ## _MAPPINGS_ ## PLATFORM [0]),\ 435 SYM ## _MEANINGS_ ## PLATFORM,\ 436 &(Format_ ## SYM ## _ ## PLATFORM),\ 437 } 438 } // namespace 439 440 #endif /* IGA_BACKEND_NATIVE_FIELD_HPP */ 441