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 /*
10  * Experimental IGA C++ wrapper interface
11  */
12 #ifndef _IGAX_HPP
13 #define _IGAX_HPP
14 
15 #include "common/secure_mem.h"
16 #include "iga.h"
17 #include "iga_bxml_ops.hpp"
18 
19 #include <cstdarg>
20 #include <cstdlib>
21 #include <cstring>
22 #include <exception>
23 #include <iomanip>
24 #include <ostream>
25 #include <sstream>
26 #include <string>
27 #include <tuple>
28 #include <vector>
29 
30 namespace igax {
31 
32 enum class Platform {
33       GEN_INVALID   = IGA_GEN_INVALID
34     , GEN7          = IGA_GEN7
35     , GEN7P5        = IGA_GEN7p5
36     , GEN8          = IGA_GEN8
37     , GEN8LP        = IGA_GEN8lp
38     , GEN9          = IGA_GEN9
39     , GEN9LP        = IGA_GEN9lp
40     , GEN9P5        = IGA_GEN9p5
41     , GEN10         = IGA_GEN10
42     , GEN11         = IGA_GEN11
43     , XE            = IGA_XE
44     , XE_HP         = IGA_XE_HP
45     , XE_HPG        = IGA_XE_HPG
46     , XE_HPC        = IGA_XE_HPC
47 };
48 
49 struct PlatformInfo {
50     Platform                   platform;
51     std::string                suffix;
52     std::vector<std::string>   names;
53 
toGenigax::PlatformInfo54     iga_gen_t toGen() const {return static_cast<iga_gen_t>(platform);}
55 };
56 static std::vector<PlatformInfo> QueryPlatforms();
57 
58 struct Diagnostic {
59     std::string message; // diagnostic message
60     int line, col;       // line and column (text only)
61     int off;             // byte offset (text or binary diagnostics)
62     int ext;             // length of region (text only?)
63 
Diagnosticigax::Diagnostic64     Diagnostic(const char *msg, int ln, int cl, int o, int e)
65         : message(msg), line(ln), col(cl), off(o), ext(e) {}
66 
67     // emits the error diagnostic to an output stream
68     // includes emitLoc, emitContext, and some other info
69     void emit(
70         std::ostream &os,
71         const std::string &source = "",
72         const void *bits = nullptr,
73         size_t bitsLen = 0) const;
74     // emits just the location
75     void emitLoc(std::ostream &os) const;
76     // emits information about the location (e.g. marked source line)
77     void emitContext(std::ostream &os,
78         const std::string &source = "",
79         const void *bits = nullptr,
80         size_t bitsLen = 0) const;
81 
82     // converts the error diagnostic to a string
83     // (emit() using std::stringstream)
84     // No context info is passed to emit
strigax::Diagnostic85     std::string str() const { return str(""); }
86     // Passes source the context
87     std::string str(const std::string &source) const;
88     // Passes the binary source to the context
89     std::string str(const void *bits, size_t bitsLen) const;
90 };
91 
92 // Product type grouping an output result output with all the warnings
93 // returned in its creation.
94 template <typename T>
95 struct Result {
96     T                           value;
97     std::vector<Diagnostic>     warnings;
98 };
99 typedef std::vector<unsigned char> Bits;
100 struct AsmResult : Result<Bits> { };
101 struct DisResult : Result<std::string> { };
102 
103 // a context manages memory and state across the module boundary
104 class Context {
105     iga_context_t context;
106     Platform platform;
107 
108   public:
109     Context(const iga_context_options_t &copts);
Context(Platform p)110     Context(Platform p) : Context(static_cast<iga_gen_t>(p)) {}
Context(iga_gen_t p)111     Context(iga_gen_t p) : Context(IGA_CONTEXT_OPTIONS_INIT(p)) {}
112     ~Context();
113 
getPlatform() const114     Platform getPlatform() const { return platform; }
115 
116     // assembles a string into bits, returns warning if applicable
117     // failure will throw an igax::AssembleError of some sort
118     // (see subclasses below)
119     AsmResult assembleFromString(
120         const std::string &text,
121         const iga_assemble_options_t &opts = IGA_ASSEMBLE_OPTIONS_INIT());
122     // Disassembles a sequence of bits to a string
123     DisResult disassembleToString(
124         const void *bits,
125         const size_t bitsLen,
126         const iga_disassemble_options_t &opts = IGA_DISASSEMBLE_OPTIONS_INIT());
127 };
128 
129 // parent class for all IGA API errors
130 // subclasses contain more specific information
131 // some obscure errors (e.g. out of memory) lack a subclass and
132 // will be instances of this class, typically you shouldn't handle these
133 struct Error : std::exception {
134     iga_status_t status; // the error code returned
135     const char *api; // the C-API that was called
136 
Errorigax::Error137     Error(iga_status_t _status, const char *_api)
138         : status(_status), api(_api) {}
139 
140     // emits the error to an output stream
141     // sub-errors override this to offer more specific information
142     virtual void emit(std::ostream &os) const;
143 
144     // converts the error to a string message (via emit and std::stringstream)
145     std::string str() const;
146 };
147 
148 // indicates a failure to assemble (typically a parse error or encoding error)
149 struct AssembleError : Error {
150     std::vector<Diagnostic> errors;
151     std::string source;
152 
AssembleErrorigax::AssembleError153     AssembleError(
154         iga_status_t st,
155         const char *api,
156         const std::vector<Diagnostic> &errs,
157         const std::string &src)
158         : Error(st, api), errors(errs), source(src) {}
159 
160     // overrides default implementation to emit the diagnostics instead
161     virtual void emit(std::ostream &os) const;
162 };
163 // thrown when a syntax error(s) are encountered during assembly
164 struct SyntaxError : AssembleError {
SyntaxErrorigax::SyntaxError165     SyntaxError(
166         const char *api,
167         const std::vector<Diagnostic> &errs,
168         const std::string &src)
169             : AssembleError(IGA_PARSE_ERROR, api, errs, src) { }
170 };
171 // thrown when assembly encounters an encoding error
172 // E.g. we cannot compact an instruction
173 struct EncodeError : AssembleError {
EncodeErrorigax::EncodeError174     EncodeError(
175         const char *api,
176         std::vector<Diagnostic> errs,
177         const std::string src)
178             : AssembleError(IGA_ENCODE_ERROR, api, errs, src) { }
179 };
180 
181 
182 // indicates a failure to disassemble (malformed bits?)
183 struct DisassembleError : Error {
184     std::vector<Diagnostic> errors;
185     const void *bits;
186     size_t bitsLen;
187     std::string outputText;
188 
DisassembleErrorigax::DisassembleError189     DisassembleError(
190         iga_status_t st,
191         const char *api,
192         const std::vector<Diagnostic> &errs,
193         const void *_bits,
194         size_t _bitsLen,
195         std::string& text)
196             : Error(st, api), errors(errs), bits(_bits), bitsLen(_bitsLen), outputText(text) {}
197 
198     virtual void emit(std::ostream &os) const;
199 };
200 // most specific case that one wants to react to in DisassembleError
201 // something is wrong with the bits being decoded
202 struct DecodeError : DisassembleError {
DecodeErrorigax::DecodeError203     DecodeError(
204         const char *api,
205         const std::vector<Diagnostic> &errs,
206         const void *_bits,
207         size_t _bitsLen,
208         std::string& text)
209             : DisassembleError(IGA_DECODE_ERROR, api, errs, _bits, _bitsLen, text) { }
210 };
211 
212 
213 ///////////////////////////////////////////////
214 // IGA OPSPEC API
215 //
216 // Lists information about ops for a given platform
217 class OpSpec {
218     Platform         m_platform;
219     iga_opspec_t     m_op;
220 public:
OpSpec(Platform p,iga_opspec_t op)221     OpSpec(Platform p, iga_opspec_t op) : m_platform(p), m_op(op) { }
222     iga::Op op() const;
223     std::string menmonic() const;
224     std::string name() const;
225     std::string description() const;
226 
227     // enumerates all the operations for a given platform
228     static std::vector<OpSpec> enumerate(Platform p);
229 };
230 
231 
232 ///////////////////////////////////////////////////////////////////////////////
233 /////////////////////////////// IMPLEMENTATION ////////////////////////////////
234 ///////////////////////////////////////////////////////////////////////////////
235 // The API user shouldn't have to directly interact with this code.  It might
236 // prove helpful in debugging, but our hope is that you can treat this as
237 // an abstraction.
238 
239 #define IGA_CHECKED_CALL(API, ...)           \
240     do {                                     \
241         iga_status_t _st = API(__VA_ARGS__); \
242         if (_st != IGA_SUCCESS)              \
243             throw Error(_st, #API);          \
244     } while (0)
245 
QueryPlatforms()246 static inline std::vector<PlatformInfo> QueryPlatforms()
247 {
248     size_t nps = 0;
249     IGA_CHECKED_CALL(iga_platforms_list, 0, nullptr, &nps);
250     std::vector<iga_gen_t> gens;
251     gens.resize(nps/sizeof(iga_gen_t));
252     IGA_CHECKED_CALL(iga_platforms_list,
253         gens.size()*sizeof(iga_gen_t), gens.data(), nullptr);
254     //
255     std::vector<PlatformInfo> pis;
256     pis.reserve(gens.size());
257     for (size_t piIx = 0; piIx < gens.size(); piIx++) {
258         const char *suffix = nullptr;
259         IGA_CHECKED_CALL(iga_platform_symbol_suffix, gens[piIx], &suffix);
260         size_t names_bytes_needed = 0;
261         IGA_CHECKED_CALL(
262             iga_platform_names, gens[piIx], 0, nullptr, &names_bytes_needed);
263         std::vector<const char *> names_cstr;
264         names_cstr.resize(names_bytes_needed/sizeof(const char *));
265         IGA_CHECKED_CALL(iga_platform_names,
266             gens[piIx],
267             names_cstr.size()*sizeof(const char *),
268             names_cstr.data(),
269             nullptr);
270         std::vector<std::string> names;
271         names.reserve(names_cstr.size());
272         for (const char *nm : names_cstr)
273             names.push_back(nm);
274         //
275         pis.emplace_back();
276         auto &pi = pis.back();
277         pi.platform = static_cast<igax::Platform>(gens[piIx]);
278         pi.suffix = suffix;
279         pi.names = names;
280     }
281     //
282     return pis;
283 }
284 
getDiagnostics(const iga_context_t & context,bool errs)285 static inline std::vector<Diagnostic> getDiagnostics(
286     const iga_context_t &context, bool errs)
287 {
288     std::vector<Diagnostic> out;
289     const iga_diagnostic_t *ds = nullptr;
290     uint32_t dsLen             = 0;
291     if (errs) {
292         IGA_CHECKED_CALL(iga_get_errors, context, &ds, &dsLen);
293     } else {
294         IGA_CHECKED_CALL(iga_get_warnings, context, &ds, &dsLen);
295     }
296     for (uint32_t i = 0; i < dsLen; i++) {
297         const char *msg;
298         IGA_CHECKED_CALL(iga_diagnostic_get_message, ds + i, &msg);
299         uint32_t off;
300         IGA_CHECKED_CALL(iga_diagnostic_get_offset, ds + i, &off);
301 
302         iga_diagnostic_type_t dt;
303         IGA_CHECKED_CALL(iga_diagnostic_get_type, ds + i, &dt);
304         if (dt == IGA_DIAGNOSTIC_TEXT) {
305             uint32_t line, col, ext;
306             IGA_CHECKED_CALL(iga_diagnostic_get_text_line, ds + i, &line);
307             IGA_CHECKED_CALL(iga_diagnostic_get_text_column, ds + i, &col);
308             IGA_CHECKED_CALL(iga_diagnostic_get_text_extent, ds + i, &ext);
309             out.emplace_back(msg, line, col, off, ext);
310         } else { // binary
311             out.emplace_back(msg, 0, 0, off, 0);
312         }
313     }
314     return out;
315 }
getWarnings(const iga_context_t & ctx)316 static inline std::vector<Diagnostic> getWarnings(const iga_context_t &ctx)
317 {
318     return igax::getDiagnostics(ctx, false);
319 }
getErrors(const iga_context_t & ctx)320 static inline std::vector<Diagnostic> getErrors(const iga_context_t &ctx)
321 {
322     return igax::getDiagnostics(ctx, true);
323 }
324 
Context(const iga_context_options_t & copts)325 inline Context::Context(const iga_context_options_t &copts)
326     : context(nullptr), platform(static_cast<igax::Platform>(copts.gen))
327 {
328     IGA_CHECKED_CALL(iga_create_context, &copts, &context);
329 }
330 
~Context()331 inline Context::~Context() {
332     (void)iga_release_context(context);
333     context = nullptr;
334 }
335 
assembleFromString(const std::string & text,const iga_assemble_options_t & opts)336 inline AsmResult Context::assembleFromString(
337     const std::string &text,
338     const iga_assemble_options_t &opts)
339 {
340     AsmResult result;
341     void *bits;
342     uint32_t bitsLen;
343 
344     iga_status_t st =
345         iga_assemble(context, &opts, text.c_str(), &bits, &bitsLen);
346     if (st != IGA_SUCCESS) {
347         if (st == IGA_UNSUPPORTED_PLATFORM) {
348             std::vector<Diagnostic> errs;
349             throw AssembleError(st, "iga_assemble", errs, text);
350         }
351         std::vector<Diagnostic> errs = igax::getErrors(context);
352         if (st == IGA_PARSE_ERROR) {
353             throw SyntaxError("iga_assemble", errs, text);
354         } else if (st == IGA_ENCODE_ERROR) {
355             throw EncodeError("iga_assemble", errs, text);
356         } else {
357             throw AssembleError(st, "iga_assemble", errs, text);
358         }
359     }
360 
361     // copy out the warnings
362     result.warnings = igax::getWarnings(context);
363     // copy out the bits
364     if (bitsLen > 0) {
365         result.value.resize(bitsLen);
366         memcpy_s(result.value.data(), bitsLen, bits, bitsLen);
367     }
368 
369     return result;
370 }
371 
disassembleToString(const void * bits,const size_t bitsLen,const iga_disassemble_options_t & opts)372 inline DisResult Context::disassembleToString(
373     const void *bits,
374     const size_t bitsLen,
375     const iga_disassemble_options_t &opts)
376 {
377     char *text;
378     iga_status_t st = iga_disassemble(
379         context,
380         &opts,
381         bits,
382         (uint32_t)bitsLen,
383         nullptr,
384         nullptr,
385         &text);
386     if (st != IGA_SUCCESS) {
387         if (st == IGA_UNSUPPORTED_PLATFORM) {
388             std::vector<Diagnostic> errs;
389             std::string text_str = text;
390             throw DisassembleError(st, "iga_disassemble", errs, bits, bitsLen, text_str);
391         }
392         std::vector<Diagnostic> errs = igax::getErrors(context);
393         if (st == IGA_DECODE_ERROR) {
394             std::string text_str = text;
395             throw DecodeError("iga_disassemble", errs, bits, bitsLen, text_str);
396         } else {
397             std::string text_str = text;
398             throw DisassembleError(st, "iga_disassemble", errs, bits, bitsLen, text_str);
399         }
400     }
401 
402     DisResult result;
403     result.value = text;
404     result.warnings = getWarnings(context);
405     return result;
406 }
407 
emit(std::ostream & os) const408 inline void Error::emit(std::ostream &os) const {
409     os << api << ": " << iga_status_to_string(status);
410 }
str() const411 inline std::string Error::str() const {
412     std::stringstream ss;
413     emit(ss);
414     return ss.str();
415 }
416 
emit(std::ostream & os) const417 inline void AssembleError::emit(std::ostream &os) const {
418     if (status != IGA_PARSE_ERROR && status != IGA_ENCODE_ERROR) {
419         os << api << ": " << iga_status_to_string(status) << "\n";
420     }
421     for (auto &e : errors) {
422         e.emit(os, source);
423     }
424 }
425 
emit(std::ostream & os) const426 inline void DisassembleError::emit(std::ostream &os) const {
427     if (status != IGA_DECODE_ERROR) {
428         os << api << ": " << iga_status_to_string(status) << "\n";
429     }
430     for (auto &e : errors) {
431         e.emit(os, "", bits, bitsLen);
432     }
433 }
434 
emitLoc(std::ostream & os) const435 inline void Diagnostic::emitLoc(
436     std::ostream &os) const
437 {
438     if (line > 0) { // text error
439         os << std::dec << "line " << line << "." << col << ":";
440     } else { // binary error
441         os << std::hex << "byte offset 0x" << off << ":";
442     }
443 }
444 
emitContext(std::ostream & os,const std::string & src,const void * bytes,size_t bytesLen) const445 inline void Diagnostic::emitContext(
446     std::ostream &os,
447     const std::string &src,
448     const void *bytes,
449     size_t bytesLen) const
450 {
451     if (line > 0) {
452         // text error
453         if (off <= (int)src.length()) {
454             // we have context too, print that
455             int ix = off;
456             while (ix > 0 && src[ix - 1] != '\r' && src[ix - 1] != '\n') {
457                 ix--;
458             }
459             do {
460                 os << src[ix++];
461             } while (ix < (int)src.size() && src[ix] != '\r' &&
462                      src[ix] != '\n');
463             os << "\n";
464             for (int i = 1; i < col; i++) {
465                 os << ' ';
466             }
467             os << "^\n";
468         }
469     } else {
470         // binary offset
471         const unsigned char *bits = (const unsigned char *)bytes;
472         if (bits && off + 3 < (int)bytesLen) {
473             size_t iLen = (bits[off + 3] & 0x20) ? 8 : 16;
474             if ((size_t)off + iLen < (int)bytesLen) {
475                 for (size_t i = 0; i < iLen && i + off < bytesLen; i++) {
476                     if (i > 0)
477                         os << ' ';
478                     os << std::hex << std::setw(2) << std::setfill('0')
479                               << (unsigned)bits[off + i];
480                     if (i % 4 == 3)
481                         os << ' ';
482                 }
483                 os << "\n";
484             }
485         }
486     }
487 }
emit(std::ostream & os,const std::string & src,const void * bytes,size_t bytesLen) const488 inline void Diagnostic::emit(
489     std::ostream &os,
490     const std::string &src,
491     const void *bytes,
492     size_t bytesLen) const
493 {
494     emitLoc(os);
495     os << " " << message << "\n";
496     emitContext(os, src, bytes, bytesLen);
497 }
498 
str(const std::string & src) const499 inline std::string Diagnostic::str(const std::string &src) const {
500     std::stringstream ss;
501     emit(ss, src);
502     return ss.str();
503 }
str(const void * bytes,size_t bytesLen) const504 inline std::string Diagnostic::str(const void *bytes, size_t bytesLen) const {
505     std::stringstream ss;
506     emit(ss, "", bytes, bytesLen);
507     return ss.str();
508 }
509 
enumerate(Platform p)510 inline std::vector<OpSpec> OpSpec::enumerate(Platform p)
511 {
512     iga_opspec_t arrAuto[128];
513     iga_opspec_t *arrPtr = &arrAuto[0];
514     size_t arrLen = sizeof(arrAuto)/sizeof(arrAuto[0]);
515     IGA_CHECKED_CALL(iga_opspec_enumerate, (iga_gen_t)p, arrPtr, &arrLen);
516     if (arrLen > sizeof(arrAuto)/sizeof(arrAuto[0])) {
517         arrPtr = (iga_opspec_t *)alloca(arrLen * sizeof(iga_opspec_t));
518         IGA_CHECKED_CALL(iga_opspec_enumerate, (iga_gen_t)p, arrPtr, &arrLen);
519     }
520     std::vector<OpSpec> vec;
521     vec.reserve(arrLen);
522     for (size_t i = 0; i < arrLen; i++) {
523         vec.emplace_back(p, arrPtr[i]);
524     }
525     return vec;
526 }
527 
op() const528 inline iga::Op OpSpec::op() const
529 {
530     uint32_t iga_op;
531     IGA_CHECKED_CALL(iga_opspec_op, m_op, &iga_op);
532     return static_cast<iga::Op>(iga_op);
533 }
534 
535 
536 #define IGA_OPSPEC_STRING_GETTER(API, INITSIZE) {         \
537         char _staticBuf[INITSIZE];                        \
538         char *strPtr  = &_staticBuf[0];                   \
539         size_t strCap = sizeof(_staticBuf);               \
540         IGA_CHECKED_CALL(API, m_op, strPtr, &strCap);     \
541         if (strCap > sizeof(_staticBuf)) {                \
542             strPtr = (char *)malloc(strCap);              \
543             IGA_CHECKED_CALL(API, m_op, strPtr, &strCap); \
544             std::string res(strPtr);                      \
545             free(strPtr);                                 \
546             return res;                                   \
547         }                                                 \
548         return std::string(strPtr);                       \
549     }
550 inline std::string OpSpec::menmonic() const
551 IGA_OPSPEC_STRING_GETTER(iga_opspec_mnemonic, 16);
552 inline std::string OpSpec::name() const
553 IGA_OPSPEC_STRING_GETTER(iga_opspec_name, 32);
554 inline std::string OpSpec::description() const
555 IGA_OPSPEC_STRING_GETTER(iga_opspec_description, 128);
556 
557 #undef IGA_OPSPEC_STRING_GETTER
558 
559 } // igax::
560 
561 #endif /* _IGAX_HPP */
562