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