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