1 /*========================== begin_copyright_notice ============================
2 
3 Copyright (C) 2020-2021 Intel Corporation
4 
5 SPDX-License-Identifier: MIT
6 
7 ============================= end_copyright_notice ===========================*/
8 
9 #ifndef IGA_MESSAGE_INFO_HPP
10 #define IGA_MESSAGE_INFO_HPP
11 
12 #include "../api/iga_bxml_enums.hpp"
13 #include "../Backend/Native/Field.hpp"
14 #include "../IR/Types.hpp"
15 #include "../IR/Loc.hpp"
16 
17 #include <cstdint>
18 #include <string>
19 #include <tuple>
20 #include <vector>
21 
22 namespace iga
23 {
24     struct DescField {
DescFieldiga::DescField25         DescField(int _off, int _len) : off(_off), len(_len) { }
26         int off, len;
27     };
28 
29     using DiagnosticList = std::vector<std::pair<DescField,std::string>>;
30     using DecodedDescFields =
31         std::vector<std::tuple<Fragment,uint32_t,std::string>>;
32 
33     // This is generic list of send operations.  One or more messages may map
34     // to the same element (hence why "generic").  In other words, two
35     // different units (maybe different hardware generate the same message).
36     enum class SendOp {
37         INVALID,
38 #define DEFINE_SEND_OP(E, M, D, A) E,
39 #include "EnumSendOpInfo.hpp"
40 #undef DEFINE_SEND_OP
41     };
42 
43     struct SendOpDefinition {
44         SendOp op;
45         const char *mnemonic; // e.g. "load" for SendOp::LOAD
46         const char *description; // a longer freer description
47 
48         // typesafe bitset of send op attributes
49         enum class Attr {
50             NONE = 0x0,
51             //
52             // the may have a destination (e.g. loads or atomics)
53             // certain control ops may or may not have destinations
54             HAS_DST = 0x1,
55             //
56             // is the address a scalar register (e.g. load_block)
57             IS_SCALAR_ADDR = 0x2,
58             //
59             // if the operation supports a ChMask (e.g. load_quad)
60             HAS_CMASK = 0x4,
61             //
62             // the op is a load, store, or atomic
63             GROUP_LOAD   = 0x10,
64             GROUP_STORE  = 0x20,
65             GROUP_ATOMIC = 0x40,
66             GROUP_OTHER  = 0x80, // e.g. fence, barrier, etc...
67 
68             // for atomics, the number of data arguments in src1
69             ATOMIC_UNARY   = 0x100, // none (atomic_iinc)
70             ATOMIC_BINARY  = 0x200, // one (atomic_fadd)
71             ATOMIC_TERNARY = 0x400, // two (atomic_icas)
72         };
73         Attr attrs;
74 
SendOpDefinitioniga::SendOpDefinition75         constexpr SendOpDefinition(
76             SendOp o,
77             const char *mne,
78             const char *desc,
79             Attr _attrs = Attr::NONE)
80             : op(o), mnemonic(mne), description(desc), attrs(_attrs) { }
81         constexpr SendOpDefinition(const SendOpDefinition&) = delete;
82         constexpr SendOpDefinition& operator=(const SendOpDefinition&) = delete;
83 
isValidiga::SendOpDefinition84         bool isValid() const {return op != SendOp::INVALID;}
hasAttriga::SendOpDefinition85         bool hasAttr(Attr a) const {return (((int)attrs & (int)a)) != 0;}
86 
hasDstiga::SendOpDefinition87         bool hasDst() const {return hasAttr(Attr::HAS_DST);}
hasChMaskiga::SendOpDefinition88         bool hasChMask() const {return hasAttr(Attr::HAS_CMASK);}
isAddressScalariga::SendOpDefinition89         bool isAddressScalar() const {return hasAttr(Attr::IS_SCALAR_ADDR);}
90 
isLoadiga::SendOpDefinition91         bool isLoad() const {return hasAttr(Attr::GROUP_LOAD);}
isStoreiga::SendOpDefinition92         bool isStore() const {return hasAttr(Attr::GROUP_STORE);}
isAtomiciga::SendOpDefinition93         bool isAtomic() const {return hasAttr(Attr::GROUP_ATOMIC);}
isOtheriga::SendOpDefinition94         bool isOther() const {return hasAttr(Attr::GROUP_OTHER);}
95 
numAtomicArgsiga::SendOpDefinition96         int numAtomicArgs() const {
97             return hasAttr(Attr::ATOMIC_UNARY) ? 0 :
98                 hasAttr(Attr::ATOMIC_BINARY) ? 1 :
99                 hasAttr(Attr::ATOMIC_TERNARY) ? 2 :
100                 -1;
101         }
102     }; // SendOpDefinition
operator |(SendOpDefinition::Attr a1,SendOpDefinition::Attr a2)103     static inline constexpr SendOpDefinition::Attr operator|(
104         SendOpDefinition::Attr a1,
105         SendOpDefinition::Attr a2)
106     {
107         return SendOpDefinition::Attr(int(a1) | int(a2));
108     }
109 
110 
111     const SendOpDefinition &lookupSendOp(SendOp op);
112     const SendOpDefinition &lookupSendOp(const char *mnemonic);
113 
114     std::string ToSyntax(SendOp op);
115 
116     enum class CacheOpt {
117         // Invalid value (not invalidate or anything)
118         INVALID = 0,
119         //
120         // the default caching state from MOCS or wherever
121         DEFAULT,
122         //
123         // a read is the last use and the lines should be invalidated
124         //   e.g. bit 13 in some HDC messages
125         READINVALIDATE,
126         //
127         // indicates a load should be cached
128         //
129         // e.g. in the BTI system 0xFD is used for this (non-coherent)
130         CACHED,
131         //
132         // indicates a message that bypasses the cache (and evicts the entry
133         // if present)
134         //
135         // e.g. in the BTI system 0xFF is used for this (coherent)
136         UNCACHED,
137         //
138         // a streaming access (line goes into LRU)
139         STREAMING,
140         //
141         // invalidates and writes back lines to the backing store
142         WRITEBACK,
143         //
144         // indicates lines should be written back to the backing store as well
145         // as retained in all levels
146         WRITETHROUGH,
147     };
148     std::string ToSymbol(CacheOpt op);
149 
150     enum class AddrType {
151         // the invalid value
152         INVALID = 0,
153         // stateless
154         FLAT,
155         // stateful: bindless surface state (UMD)
156         BSS,
157         // stateful: surface state (KMD)
158         SS,
159         // stateful: binding table interface
160         BTI,
161     };
162     std::string ToSymbol(AddrType op);
163 
164     ///////////////////////////////////////////////////////////////////////////
165     // Generic processing of basic buffer messages.   This includes data port
166     // vector reads and writes as well block and atomic messages.  There may be
167     // certain obscure or legacy operations that are unsupported.
168     //
169     struct MessageInfo {
170         enum class Attr : uint32_t {
171             NONE = 0x0,
172             //
173             // Set on atomic operations that return data
174             ATOMIC_RETURNS  = 1 << 0,
175             //
176             // For messages that uncompress data, expand into the high unit
177             // instead of the low.
178             EXPAND_HIGH     = 1 << 1,
179             //
180             // If the message supports a channel mask for vector size
181             // (data elements)
182             HAS_CHMASK      = 1 << 2,
183             //
184             // Indicates the message is has U, V, R, and LOD coordinates
185             // as part of the address payloads.
186             HAS_UVRLOD      = 1 << 3,
187             //
188             // Indicates a scratch message
189             SCRATCH         = 1 << 4,
190             //
191             // Indicates SLM
192             SLM             = 1 << 5,
193             //
194             // Indicates the data is transposed during load or store
195             TRANSPOSED      = 1 << 6,
196             //
197             // Indicates the message is a typed operation
198             TYPED           = 1 << 7,
199             //
200             VALID = 0x80000000,
201         };
202 
203         //
204         // Queries a boolean property of this message.
205         bool hasAttr(Attr attr) const;
206         // Adds attributes to the attribute set.
207         void addAttr(Attr attr);
208 
209         // The specific operation
210         SendOp       op = SendOp::INVALID;
211 
212         // A bitset of the above attributes
213         Attr         attributeSet = MessageInfo::Attr::NONE;
214 
215         // Size (in bits) of one address
216         int          addrSizeBits = 0;
217         //
218         // The size (in bits) of each element once stored in the register file.
219         int          elemSizeBitsRegFile = 0;
220         //
221         // The size (in bits) of each element in memory.
222         //
223         // This can differ from elemSizeRegisterFile if there's any compression
224         // effects.  For example, a byte gathering read will load one, two,
225         // four bytes into each 32b slot leaving the upper bits for the one-
226         // and two-byte case.
227         //
228         int          elemSizeBitsMemory = 0;
229         //
230         // The number of vector elems or active channels in the cmask.
231         // This is the number of elements each address loads.
232         //
233         // For example a legacy untyped load with a channel mask of XYZ
234         // would be 3.  Some scatter/gather messages have a SIMT vector
235         // component similar in function.
236         int          elemsPerAddr = 0;
237         //
238         // For LOAD_QUAD and STORE_QUAD, this holds a bit mask of up to four
239         // bits with which channels are *enabled* (not disabled).
240         //
241         // The elemsPerAddr field will still be set
242         // (with the set cardinality here).
243         int          channelsEnabled = 0;
244         //
245         // The number of channels in the size of the operation.
246         // (The "SIMD" size.)
247         int          execWidth = 0;
248 
249         // Caching options for the L1 cache (if supported)
250         CacheOpt     cachingL1 = CacheOpt::INVALID;
251         //
252         // Caching options for the L3 cache (if supported)
253         // In some HDC messages this is bit 13.  Some parts don't
254         // implement it though.
255         CacheOpt     cachingL3 = CacheOpt::INVALID;
256         //
257         // The surface addressing model
258         AddrType     addrType = AddrType::INVALID;
259         //
260         // The surface identifier (if applicable).  E.g. BTI.
261         SendDesc     surfaceId;
262         //
263         // possible immediate offset if the encoding supports it
264         int immediateOffset = 0;
265         //
266         // same as the above but only for block2d (mutally exclusive with above)
267         int immediateOffsetBlock2dX = 0, immediateOffsetBlock2dY = 0;
268         //
269         // A symbol value for this message (syntax).
270         // The syntax is not complete and IGA will not consume it directly
271         // (since some information isn't in necessarily in the descriptors).
272         std::string  symbol;
273         //
274         //
275         // This may hold a useful string telling the user the exact message
276         // matching this specification up decode (if it exists).  This is
277         // indended for for debugging only; the value and behavior are subject
278         // to change at any time. The nullptr is a possible value.
279         std::string  description;
280 
281         const char *docs = nullptr;
282         //
283         // A block message
isBlockiga::MessageInfo284         bool isBlock() const {
285             return execWidth == 1 && (isLoad() || isStore());
286         }
287         //
isLoadiga::MessageInfo288         bool isLoad() const {return lookupSendOp(op).isLoad();}
isStoreiga::MessageInfo289         bool isStore() const {return lookupSendOp(op).isStore();}
isAtomiciga::MessageInfo290         bool isAtomic() const {return lookupSendOp(op).isAtomic();}
291         //
292         // An atomic that returns a value
isAtomicLoadiga::MessageInfo293         bool isAtomicLoad() const {return hasAttr(Attr::ATOMIC_RETURNS);}
294         //
295         // A transpose data layout
isTransposediga::MessageInfo296         bool isTransposed() const {return hasAttr(Attr::TRANSPOSED);}
297 
298         // Enables the operation to be used within a predicate assignment.
operator booliga::MessageInfo299         operator bool() const {return hasAttr(Attr::VALID);}
300     }; // class MessageInfo
301 
operator |(MessageInfo::Attr a,MessageInfo::Attr b)302     static inline MessageInfo::Attr operator |(
303         MessageInfo::Attr a, MessageInfo::Attr b)
304     {
305         return MessageInfo::Attr(int(a) | int(b));
306     }
operator |=(MessageInfo::Attr & a,MessageInfo::Attr b)307     static inline MessageInfo::Attr &operator |=(
308         MessageInfo::Attr &a, MessageInfo::Attr b)
309     {
310         return (a = a | b);
311     }
operator &(MessageInfo::Attr a,MessageInfo::Attr b)312     static inline bool operator &(MessageInfo::Attr a, MessageInfo::Attr b) {
313         return (int(a) & int(b)) != 0;
314     }
hasAttr(Attr attr) const315     inline bool MessageInfo::hasAttr(Attr attr) const {
316         return attributeSet & attr;
317     }
addAttr(Attr attr)318     inline void MessageInfo::addAttr(Attr attr) {
319         attributeSet = attributeSet | attr;
320     }
321 
322     // for legacy encodings where the SFID comes from bits
323     SFID sfidFromEncoding(Platform p, uint32_t sfidBits);
324 
325     // The payload lengths for a given message.
326     // Negative values indicate that the value is unknown or could be variable.
327     struct PayloadLengths {
328         int dstLen = -1;
329         int src0Len = -1, src1Len = -1;
330         bool uvrlod = false;
331 
PayloadLengthsiga::PayloadLengths332         PayloadLengths() { }
333 
334         // This attempts to deduce payload lengths from the given inputs.
335         PayloadLengths(
336             Platform p,
337             SFID sfid,
338             ExecSize execSize,
339             uint32_t desc);
340 
341         // Same as above, but given a Op::SEND*
342         //
343         // For older platforms this will extract the SFID from exDesc
344         PayloadLengths(
345             Platform p,
346             ExecSize execSize,
347             uint32_t desc,
348             uint32_t exDesc);
349     };
350 
351 
352     // Organizes various parts of syntax during decoding.  This enables
353     // formatters to interleave real registers.
354     //
355     // LOAD: load, load_strided, load_cmask, fence
356     //    MNEMONIC SFID CONTROLS
357     //         $datareg DATATYPE
358     //         SURFACE [ SCALE $addrreg IMMOFFSET ] ADDRTYPE
359     // ATOMIC: atomic_iinc
360     //    MNEMONIC SFID CONTROLS
361     //         $datareg DATATYPE
362     //         SURFACE [ SCALE $addrreg IMMOFFSET ] ADDRTYPE
363     //         $atomic-arg
364     // STORE: store, store_strided
365     //    MNEMONIC SFID CONTROLS
366     //         SURFACE [ SCALE $addrreg IMMOFFSET ] ADDRTYPE
367     //         $datareg DATATYPE
368     // CONTROL: e.g. barrier
369     //    MNEMONIC SFID CONTROLS
370     //         SURFACE [ SCALE $addrreg IMMOFFSET ] ADDRTYPE
371     //         $datareg DATATYPE
372     //
373     // Examples:
374     //   load.ugm.ca.ca (32)          r10:d32x4    bti<0x02>[r20-0x40]:a32
375     //   load.ugm.ca.ca (1)           r10:d32x4t   flat[r20+0x40]:a64
376     //   load.slm (1)                 r10:d32x4t   flat[r20+0x40]:a64
377     //   store_strided.ugm.wt.wb (16) bss<a0.4>[r20+0x40]:a32   r10:d32x4
378     //   atomic_iinc.ugm.un.un (32)   r10:d32   flat[r20-0x40]:a64   null
379     struct MessageSyntax {
380         enum class Layout {
381             // unset
382             INVALID = 0,
383             // e.g. load, load_strided, load_quad, load_blocks2d, ...
384             LOAD,
385             // e.g. store, store_quad, store_strided, ...
386             STORE,
387             // e.g. atomic_iinc, atomic_fadd
388             ATOMIC,
389             // e.g. barrier or eot
390             CONTROL
391         };
392         Layout layout = Layout::INVALID;
393 
isValidiga::MessageSyntax394         bool isValid() const {return layout != Layout::INVALID;}
395         //
isLoadiga::MessageSyntax396         bool isLoad() const {return layout == Layout::LOAD;}
isStoreiga::MessageSyntax397         bool isStore() const {return layout == Layout::STORE;}
isAtomiciga::MessageSyntax398         bool isAtomic() const {return layout == Layout::ATOMIC;}
isControliga::MessageSyntax399         bool isControl() const {return layout == Layout::CONTROL;}
400 
401         std::string   mnemonic; // e.g. "load" or "load_block"
402         // SFID          sfid;     // e.g. "ugm"
403         std::string   controls; // e.g. ".ugm.d32.a64.ca.ca"
404 
405         // address stuff
406         std::string   surface;     // e.g. "bti[0x2]", "bti[a0.4]", or "" (flat)
407         std::string   scale;       // e.g. 4*...
408         std::string   immOffset;   // e.g. -0x40
409 
410         // emits rough syntax
411         std::string str(
412             std::string execInfo,
413             std::string dataReg,
414             std::string addrReg,
415             std::string atmoicArgReg) const;
416 
417         // orders the operands in a symbolic form
418         // (suffixes surface, scaling and offset)
419         std::string sym() const;
420     }; // MessageSyntax
421 
422     //
423     // Returns the resulting information of a message decode
424     struct DecodeResult {
425         MessageInfo        info;
426         MessageSyntax      syntax;
427         DiagnosticList     warnings;
428         DiagnosticList     errors;
429         DecodedDescFields  fields;
430 
operator booliga::DecodeResult431         operator bool() const {return errors.empty();}
432     };
433 
434     // Attempts to decode the descriptor for
435     DecodeResult tryDecode(
436         Platform p, SFID sfid, ExecSize execSize,
437         SendDesc exDesc, SendDesc desc,
438         DecodedDescFields *fields);
439 
440     //
441     // returns true if the SFID on a given platform is eligible for symbolic
442     // translate for load/store syntax; this function enables us to
443     // incrementally enable load/store syntax
444     bool       sendOpSupportsSyntax(Platform p, SendOp op, SFID sfid);
445     //
446     bool       isLscSFID(Platform p, SFID sfid);
447     //
448     struct VectorMessageArgs {
449         SFID           sfid = SFID::INVALID;
450         SendOp         op = SendOp::INVALID;
451         int            execSize = 0;
452         CacheOpt       cachingL1 = CacheOpt::DEFAULT;
453         CacheOpt       cachingL3 = CacheOpt::DEFAULT;
454         //
455         AddrType       addrType = AddrType::FLAT;
456         SendDesc       addrSurface = 0; // a0 or imm
457         int            addrScale = 1; // must be 1x, V*D, 2*V*2, or 4*V*D
458         int            addrSize = 0;
459         int            addrOffset = 0; // imm only
460         int            addrOffsetX = 0; // block2d only
461         int            addrOffsetY = 0; // block2d only
462         int            dataSizeReg = 0;
463         int            dataSizeMem = 0;
464         bool           dataSizeExpandHigh = 0; // d8u32h
465         union {
466             struct {
467                 short  dataVectorSize;
468                 bool   dataTranspose;
469                 bool   dataVnni;
470             };
471             //
472             // used for LOAD_QUAD, STORE_QUAD, ...
473             // e.g. this might map to to dc1 untyped read/write ops
474             uint32_t   dataComponentMask = 0; // x,y,z,w (x-enabled is bit 0, ...)
475         };
476 
477         ///////////////////////////////////////////////////////////////////////
478         // creates an initial empty message
VectorMessageArgsiga::VectorMessageArgs479         constexpr VectorMessageArgs() { }
480 
481         // returns the logical count of elements per address
482         // for simple vector messages, it's dataVectorSize, but for
483         // messages with a component mask, it's the number of enabled channels
elementsPerAddressiga::VectorMessageArgs484         int elementsPerAddress() const {
485             if (lookupSendOp(op).hasChMask()) {
486                 int n = 0;
487                 for (int i = 0; i < 4; i++)
488                     n += (((1 << i) & dataComponentMask) ? 1 : 0);
489                 return n;
490             } else {
491                 return dataVectorSize;
492             }
493         }
494 
isLoadiga::VectorMessageArgs495         bool isLoad() const {return lookupSendOp(op).isLoad();}
isStoreiga::VectorMessageArgs496         bool isStore() const {return lookupSendOp(op).isStore();}
isAtomiciga::VectorMessageArgs497         bool isAtomic() const {return lookupSendOp(op).isAtomic();}
hasCMaskiga::VectorMessageArgs498         bool hasCMask() const {return lookupSendOp(op).hasChMask();}
499     }; // VectorMessageArgs
500 
501     bool encodeDescriptors(
502         Platform p,
503         const VectorMessageArgs &vma,
504         SendDesc &exDesc,
505         SendDesc &desc,
506         std::string &err);
507 } // iga::
508 
509 #endif
510