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 #include "iga.h"
10 #include "igad.h"
11 #include "iga.hpp"
12 // IGA headers
13 #include "../Backend/GED/Interface.hpp"
14 #include "../Backend/Native/Interface.hpp"
15 #include "../ErrorHandler.hpp"
16 #include "../Frontend/Formatter.hpp"
17 #include "../Frontend/KernelParser.hpp"
18 #include "../IR/DUAnalysis.hpp"
19 #include "../IR/Checker/IRChecker.hpp"
20 #include "../Models/Models.hpp"
21 #include "../strings.hpp"
22 #include "../version.hpp"
23 #include "common/secure_string.h"
24 #include "common/secure_mem.h"
25
26 // external dependencies
27 #include <algorithm>
28 #include <cstring>
29 #include <map>
30 #include <ostream>
31 #include <sstream>
32 #include <unordered_map>
33 #include <vector>
34
35
36 using namespace iga;
37
38 //
39 // BINARY COMPATIBILITY:
40 // We can add stuff to the assemble/diasseemble options without forcing users
41 // to recompile. E.g. the user compiles with an older header and has:
42 // struct opts {
43 // uint32_t cb; // = sizeof(opts)
44 // uint32_t opt;
45 // };
46 // #define INIT_OPTS(A) {sizeof(struct opts), A}
47 //
48 // Later, we internally add new options (features) and make the structure
49 // struct opts {
50 // uint32_t cb; // = sizeof(opts)
51 // uint32_t opt;
52 // uint32_t newOpt;
53 // uint32_t _reserved; // to pad out the structure on 64-bit compilers
54 // };
55 // #define INIT_OPTS(A1,A2) {sizeof(struct opts), A1, A2, 0}
56 //
57 // WARNING: ensure you pad the structure to 64 bit alignment.
58 // When IGA copies in the user structure it is careful to honor the user's
59 // cb field to determine how much of the structure to read in. We use a memcpy
60 // to copy the structure in safely.
61 //
62 // void iga_api(struct opts *opts)
63 // {
64 // struct opts optsInternal = INIT_OPTS(default1, default2);
65 // memcpy(&optsInternal, opts, opts->cb);
66 // use(optsInternal);
67 // }
68 //
69 // The above strategy allows us to add to the options as long as the new
70 // option has a sensible default.
71
72
iga_status_to_string(iga_status_t st)73 const char *iga_status_to_string(iga_status_t st) {
74 switch (st) {
75 case IGA_SUCCESS: return "succeeded";
76 case IGA_ERROR: return "unknown error";
77 case IGA_INVALID_ARG: return "invalid argument";
78 case IGA_OUT_OF_MEM: return "out of memory";
79 case IGA_DECODE_ERROR: return "decode error";
80 case IGA_ENCODE_ERROR: return "encode error";
81 case IGA_PARSE_ERROR: return "parse error";
82 case IGA_VERSION_ERROR: return "version mismatch";
83 case IGA_INVALID_OBJECT: return "invalid object";
84 case IGA_INVALID_STATE: return "invalid state";
85 case IGA_UNSUPPORTED_PLATFORM: return "unsupported platform";
86 case IGA_DIFF_FAILURE: return "differences encountered";
87 default: return "invalid error code";
88 }
89 }
90
91 // suppress -Wmissing-declarations
92 iga::Platform ToPlatform(iga_gen_t gen);
93
94 // Conversion to an internal platform
95 // we could just re-interpret the bits but this checks for garbage
96 // (validates the enum)
97 // This is not static so that it can be used by other compilation units.
ToPlatform(iga_gen_t gen)98 iga::Platform ToPlatform(iga_gen_t gen)
99 {
100 // for binary compatibilty we accept the enum values from pre Xe-renaming
101 // platforms (e.g IGA_GEN12p1 is GEN_VER(12,1), but we now name XE_VER(1,0)
102 switch (gen) {
103 case iga_gen_t::IGA_GEN12p1: gen = iga_gen_t::IGA_XE; break;
104 default:
105 break;
106 }
107
108 const auto *m = iga::Model::LookupModel(iga::Platform(gen));
109 return m ? m->platform : iga::Platform::INVALID;
110 }
111
iga_platforms_list(size_t gens_length_bytes,iga_gen_t * gens,size_t * gens_length_bytes_required)112 iga_status_t iga_platforms_list(
113 size_t gens_length_bytes,
114 iga_gen_t *gens,
115 size_t *gens_length_bytes_required)
116 {
117 if (gens_length_bytes != 0 && gens == nullptr)
118 return IGA_INVALID_ARG;
119
120 const size_t MAX_SPACE_NEEDED = ALL_MODELS_LEN*sizeof(iga_gen_t);
121 if (gens_length_bytes_required)
122 *gens_length_bytes_required = MAX_SPACE_NEEDED;
123 if (gens) {
124 for (size_t i = 0;
125 i < std::min(gens_length_bytes,MAX_SPACE_NEEDED)/sizeof(iga_gen_t);
126 i++)
127 {
128 gens[i] = static_cast<iga_gen_t>(ALL_MODELS[i]->platform);
129 }
130 }
131 return IGA_SUCCESS;
132 }
133
134 // We run into a minor annoyance here. We must return IGA memory that
135 // that never gets cleaned up by the user. Thus we use module global
136 // memory. On DLL attach this initializer will run and on DLL detach it
137 // should be cleaned up.
138 //
139 //
140 // c.f. iga_platform_names and iga_platform_symbol_suffix
141 struct PlatformNameMap {
142 std::unordered_map<iga::Platform,std::vector<std::string>> names;
143 std::unordered_map<iga::Platform,std::string> exts;
144
PlatformNameMapPlatformNameMap145 PlatformNameMap() {
146 for (size_t i = 0; i < ALL_MODELS_LEN; i++) {
147 const Model &me = *ALL_MODELS[i];
148 std::vector<std::string> nmlist;
149 for (const auto &mn : me.names)
150 {
151 std::string str = mn.str();
152 if (str.empty())
153 break;
154 nmlist.push_back(str);
155 }
156 names[me.platform] = nmlist;
157 exts[me.platform] = me.extension.str();
158 }
159 }
160 };
161 //
162 static const PlatformNameMap s_names;
163
iga_platform_symbol_suffix(iga_gen_t gen,const char ** suffix)164 iga_status_t iga_platform_symbol_suffix(
165 iga_gen_t gen,
166 const char **suffix)
167 {
168 if (suffix == nullptr)
169 return IGA_INVALID_ARG;
170 auto itr = s_names.exts.find(ToPlatform(gen));
171 if (itr == s_names.exts.end()) {
172 *suffix = nullptr;
173 return IGA_INVALID_ARG;
174 }
175 *suffix = itr->second.c_str();
176
177 return IGA_SUCCESS;
178 }
179
iga_platform_names(iga_gen_t gen,size_t names_bytes,const char ** names,size_t * names_bytes_needed)180 iga_status_t iga_platform_names(
181 iga_gen_t gen,
182 size_t names_bytes,
183 const char **names,
184 size_t *names_bytes_needed)
185 {
186 if (names_bytes != 0 && names == nullptr)
187 return IGA_INVALID_ARG;
188
189 auto itr = s_names.names.find(ToPlatform(gen));
190 if (itr == s_names.names.end()) {
191 return IGA_INVALID_ARG;
192 }
193 const auto &pltNames = itr->second;
194 if (names_bytes_needed)
195 *names_bytes_needed = pltNames.size()*sizeof(const char*);
196 const int n_copy = std::min<int>(
197 (int)pltNames.size(), (int)names_bytes/sizeof(const char *));
198 for (int ni = 0; ni < n_copy; ni++)
199 names[ni] = pltNames[ni].c_str();
200
201 return IGA_SUCCESS;
202 }
203
204 class IGAContext {
205 private:
206 // set to a magic constant when the object is valid (live)
207 // this helps detection such as people passing the wrong context
208 // e.g. iga_release_context(&ctx) instead of iga_release_context(ctx)
209 uint64_t m_validToken;
210 // the options the context was created with
211 iga_context_options_t m_opts;
212 // the model corresponding to the gen options
213 const Model& m_model;
214 // a cached copy of the assembled bits
215 // we free this upon destruction
216 void *m_assemble_bits;
217 // a cached copy of the last disassembled text
218 // we free this upon destruction
219 char *m_disassemble_text;
220 // a reusable empty string to return on errors
221 char m_empty_string[4];
222
223 // diagnostics from the last compile
224 bool m_errorsValid, m_warningsValid;
225 std::vector<iga_diagnostic_t> m_errors, m_warnings;
226 public:
clearDiagnostics(std::vector<iga_diagnostic_t> & api_ds)227 static void clearDiagnostics(std::vector<iga_diagnostic_t> &api_ds) {
228 for (auto &d : api_ds) {
229 free((void*)d.message);
230 memset(&d, 0xDE, sizeof(d));
231 }
232 api_ds.clear();
233 }
234
235
translateDiagnosticList(const std::vector<iga::Diagnostic> & ds,std::vector<iga_diagnostic_t> & api_ds)236 static iga_status_t translateDiagnosticList(
237 const std::vector<iga::Diagnostic> &ds,
238 std::vector<iga_diagnostic_t> &api_ds)
239 {
240 for (const auto &d : ds) {
241 const char *str = strdup(d.message.c_str());
242 if (!str) {
243 return IGA_OUT_OF_MEM;
244 }
245
246 if (d.at.col != 0 && d.at.line != 0) {
247 // col and line
248 iga_diagnostic_t temp = {
249 d.at.line, d.at.col, (uint32_t)d.at.offset, d.at.extent, str};
250 api_ds.push_back(temp);
251 } else {
252 // pc (offset is in bytes)
253 iga_diagnostic_t temp = {0, 0, (uint32_t)d.at.offset, d.at.extent, str};
254 api_ds.push_back(temp);
255 }
256 }
257 return IGA_SUCCESS;
258 }
259
translateDiagnostics(const iga::ErrorHandler & eh)260 iga_status_t translateDiagnostics(const iga::ErrorHandler &eh) {
261 clearDiagnostics(m_errors);
262 clearDiagnostics(m_warnings);
263 m_warningsValid = m_errorsValid = false;
264
265 iga_status_t s1 = translateDiagnosticList(eh.getErrors(), m_errors);
266 if (s1 != IGA_SUCCESS) {
267 clearDiagnostics(m_errors);
268 return s1;
269 }
270
271 iga_status_t s2 = translateDiagnosticList(eh.getWarnings(), m_warnings);
272 if (s2 != IGA_SUCCESS) {
273 clearDiagnostics(m_warnings);
274 clearDiagnostics(m_errors);
275 return s2;
276 }
277
278 m_warningsValid = m_errorsValid = true;
279 return IGA_SUCCESS;
280 }
281
convertPlatform(iga_gen_t gen)282 static const Model &convertPlatform(iga_gen_t gen) {
283 const Model *m = Model::LookupModel(ToPlatform(gen));
284 if (!m) {
285 throw FatalError();
286 }
287 return *m;
288 }
289
290 static const uint64_t VALID_COOKIE = 0xFEDCBA9876543210ull;
291
292 public:
IGAContext(iga_context_options_t opts)293 IGAContext(iga_context_options_t opts)
294 : m_validToken(VALID_COOKIE)
295 , m_opts(opts)
296 , m_model(convertPlatform(opts.gen))
297 , m_assemble_bits(nullptr)
298 , m_disassemble_text(nullptr)
299 , m_errorsValid(false)
300 , m_warningsValid(false)
301 {
302 memset(m_empty_string, 0, sizeof(m_empty_string));
303 }
304
305
~IGAContext()306 ~IGAContext() {
307 m_validToken = 0xDEADDEADDEADDEADull;
308 clearDiagnostics(m_warnings);
309 clearDiagnostics(m_errors);
310
311 if (m_disassemble_text) {
312 free(m_disassemble_text);
313 m_disassemble_text = nullptr;
314 }
315 if (m_assemble_bits) {
316 free(m_assemble_bits);
317 m_assemble_bits = nullptr;
318 }
319 }
320
321
valid() const322 bool valid() const {
323 return m_validToken == VALID_COOKIE;
324 }
325
326
assemble(iga_assemble_options_t & aopts,const char * inp,void ** bits,uint32_t * bitsLen32)327 iga_status_t assemble(
328 iga_assemble_options_t &aopts,
329 const char *inp,
330 void **bits,
331 uint32_t *bitsLen32)
332 {
333 iga::ErrorHandler errHandler;
334 // compatibility for legacy fields
335 bool used_legacy_fields = false;
336 if (aopts._reserved0) { // used to be error_on_compact_fail
337 aopts.encoder_opts |= IGA_ENCODER_OPT_ERROR_ON_COMPACT_FAIL;
338 used_legacy_fields = true;
339 }
340
341 if (aopts._reserved1) { // used to be error_on_compact_fail
342 aopts.encoder_opts |= IGA_ENCODER_OPT_AUTO_DEPENDENCIES;
343 used_legacy_fields = true;
344 }
345
346 if (used_legacy_fields) {
347 errHandler.reportWarning(Loc(1,1,0,0),
348 "iga_assemble call uses deprecated options "
349 " (error_on_compact_fail or autoset_deps); see newest "
350 "iga.h header file for updated fields");
351 }
352
353 // 1. Parse the kernel text
354 ParseOpts popts(m_model);
355 popts.supportLegacyDirectives =
356 (aopts.syntax_opts & IGA_SYNTAX_OPT_LEGACY_SYNTAX) != 0;
357 Kernel *pKernel = iga::ParseGenKernel(m_model, inp, errHandler, popts);
358 if (pKernel && !errHandler.hasErrors() && aopts.enabled_warnings) {
359 // check semantics if we parsed without error && they haven't
360 // disabled all checking (-Wnone)
361 CheckSemantics(*pKernel, errHandler, aopts.enabled_warnings);
362 }
363 if (errHandler.hasErrors()) {
364 *bits = nullptr;
365 *bitsLen32 = 0;
366 iga_status_t st = translateDiagnostics(errHandler);
367 if (pKernel)
368 delete pKernel;
369 return (st != IGA_SUCCESS) ? st : IGA_PARSE_ERROR;
370 } else if (pKernel == nullptr) {
371 // parser returned nullptr for kernel, but with no errors
372 // shouldn't be reachable; implies we have a missing diagnostic
373 if (pKernel)
374 delete pKernel;
375 return IGA_ERROR;
376 }
377
378 // 3. Encode the final IR into bits
379 //
380 // clobber the last assembly's bits
381 if (m_assemble_bits) {
382 free(m_assemble_bits);
383 m_assemble_bits = nullptr;
384 }
385 size_t bitsLen = 0;
386 EncoderOpts eopts(
387 (aopts.encoder_opts & IGA_ENCODER_OPT_AUTO_COMPACT) != 0,
388 (aopts.encoder_opts & IGA_ENCODER_OPT_ERROR_ON_COMPACT_FAIL) == 0,
389 (aopts.encoder_opts & IGA_ENCODER_OPT_FORCE_NO_COMPACT) != 0);
390 eopts.autoDepSet = (aopts.encoder_opts & IGA_ENCODER_OPT_AUTO_DEPENDENCIES) != 0;
391 eopts.sbidCount = aopts.sbid_count;
392 eopts.swsbEncodeMode = aopts.swsb_encode_mode;
393
394 if ((aopts.encoder_opts & IGA_ENCODER_OPT_USE_NATIVE) == 0) {
395 if (!iga::ged::IsEncodeSupported(m_model, eopts)) {
396 delete pKernel;
397 return IGA_UNSUPPORTED_PLATFORM;
398 }
399 iga::ged::Encode(m_model, eopts, errHandler, *pKernel, *bits, bitsLen);
400 } else {
401 if (!iga::native::IsEncodeSupported(m_model, eopts)) {
402 delete pKernel;
403 return IGA_UNSUPPORTED_PLATFORM;
404 }
405 iga::native::Encode(
406 m_model,
407 eopts,
408 errHandler,
409 *pKernel,
410 *bits,
411 bitsLen);
412 }
413 *bitsLen32 = (uint32_t)bitsLen;
414 if (errHandler.hasErrors()) {
415 // failed encoding
416 delete pKernel;
417 iga_status_t st = translateDiagnostics(errHandler);
418 return st == IGA_SUCCESS ? IGA_ENCODE_ERROR : st;
419 }
420
421 // 4. Copy out the result
422 // encoding succeeded, copy the bits out
423 m_assemble_bits = (void *)malloc(*bitsLen32);
424 if (!m_assemble_bits) {
425 delete pKernel;
426 return IGA_OUT_OF_MEM;
427 }
428 memcpy_s(m_assemble_bits, *bitsLen32, * bits, *bitsLen32);
429 *bits = m_assemble_bits;
430 delete pKernel;
431 return translateDiagnostics(errHandler);
432 }
433
formatterOpts(const iga_disassemble_options_t & dopts,const char * (* formatLabel)(int32_t,void *),void * formatLabelEnv,SWSB_ENCODE_MODE swsbEnMod=SWSB_ENCODE_MODE::SWSBInvalidMode)434 FormatOpts formatterOpts(
435 const iga_disassemble_options_t &dopts,
436 const char *(*formatLabel)(int32_t, void *),
437 void *formatLabelEnv,
438 // swsb encoding mode, if not specified, the encoding mode will
439 // be derived from platform by SWSB::getEncdoeMode
440 SWSB_ENCODE_MODE swsbEnMod = SWSB_ENCODE_MODE::SWSBInvalidMode)
441 {
442 FormatOpts fopts(
443 m_model,
444 formatLabel,
445 formatLabelEnv);
446 fopts.addApiOpts(dopts.formatting_opts);
447 if (swsbEnMod == SWSB_ENCODE_MODE::SWSBInvalidMode)
448 fopts.setSWSBEncodingMode(m_model.getSWSBEncodeMode());
449 else
450 fopts.setSWSBEncodingMode(swsbEnMod);
451
452 return fopts;
453 }
454
checkForLegacyFields(iga_disassemble_options_t & dopts,iga::ErrorHandler & errHandler)455 void checkForLegacyFields(
456 iga_disassemble_options_t &dopts,
457 iga::ErrorHandler &errHandler)
458 {
459 // crude compatibility for legacy fields
460 bool used_legacy_fields = false;
461 if (dopts._reserved0) { // used to be hex_floats
462 dopts.formatting_opts |= IGA_FORMATTING_OPT_PRINT_HEX_FLOATS;
463 used_legacy_fields = true;
464 }
465 if (dopts._reserved1) { // used to be hex_floats
466 dopts.formatting_opts |= IGA_FORMATTING_OPT_PRINT_PC;
467 used_legacy_fields = true;
468 }
469 if (used_legacy_fields) {
470 errHandler.reportWarning(Loc(1,1,0,0),
471 "iga_disassemble* call uses deprecated options "
472 " (hex_floats or print_pc); see newest "
473 "iga.h header file for updated fields");
474 }
475 }
476
disassembleKernel(iga::ErrorHandler & errHandler,iga_disassemble_options_t & dopts,const void * bits,uint32_t bitsLen,Kernel * & k)477 iga_status_t disassembleKernel(
478 iga::ErrorHandler &errHandler,
479 iga_disassemble_options_t &dopts,
480 const void *bits,
481 uint32_t bitsLen,
482 Kernel *&k)
483 {
484 k = nullptr;
485 checkForLegacyFields(dopts, errHandler);
486 DecoderOpts dopts2(
487 (dopts.formatting_opts & IGA_FORMATTING_OPT_NUMERIC_LABELS) != 0);
488 if ((dopts.decoder_opts & IGA_DECODING_OPT_NATIVE) == 0) {
489 if (!iga::ged::IsDecodeSupported(m_model,dopts2)) {
490 return IGA_UNSUPPORTED_PLATFORM;
491 }
492 k = iga::ged::Decode(
493 m_model, dopts2, errHandler, bits, (size_t)bitsLen);
494 } else {
495 if (!iga::native::IsDecodeSupported(m_model,dopts2)) {
496 return IGA_UNSUPPORTED_PLATFORM;
497 }
498 k = iga::native::Decode(
499 m_model, dopts2, errHandler, bits, (size_t)bitsLen);
500 }
501 return k == nullptr ? IGA_DECODE_ERROR : IGA_SUCCESS;
502 }
503
disassemble(iga_disassemble_options_t & dopts,const void * bits,uint32_t bitsLen,const char * (* formatLbl)(int32_t,void *),void * formatLblEnv,char ** output)504 iga_status_t disassemble(
505 iga_disassemble_options_t &dopts,
506 const void *bits,
507 uint32_t bitsLen,
508 const char *(*formatLbl)(int32_t, void *),
509 void *formatLblEnv,
510 char **output)
511 {
512 if (output)
513 *output = &m_empty_string[0];
514
515 iga::Kernel *k = nullptr;
516 iga::ErrorHandler errHandler;
517 iga_status_t st = IGA_ERROR;
518
519 st = disassembleKernel(
520 errHandler,
521 dopts,
522 bits,
523 bitsLen,
524 k);
525 if (k != nullptr) {
526 // we succeeded in decoding; now format the output to text
527 std::stringstream ss;
528 FormatOpts fopts = formatterOpts(dopts, formatLbl, formatLblEnv);
529 DepAnalysis la;
530 if (dopts.formatting_opts & IGA_FORMATTING_OPT_PRINT_DEFS) {
531 la = ComputeDepAnalysis(k);
532 fopts.liveAnalysis = &la;
533 }
534 FormatKernel(errHandler, ss, fopts, *k, bits);
535
536 // copy the text out
537 if (m_disassemble_text) {
538 // previous disassemble clobbers new disassemble
539 free(m_disassemble_text);
540 }
541 size_t slen = (size_t)ss.tellp();
542 m_disassemble_text = (char *)malloc(1 + slen);
543 if (!m_disassemble_text) {
544 // bail out
545 delete k;
546 return IGA_OUT_OF_MEM;
547 }
548 ss.read(m_disassemble_text, slen);
549 m_disassemble_text[slen] = 0;
550 if(output) {
551 *output = m_disassemble_text;
552 }
553
554 delete k;
555 } // k non-null
556
557 st = translateDiagnostics(errHandler);
558 if (errHandler.hasErrors()) {
559 return IGA_DECODE_ERROR;
560 }
561 return st;
562 }
563
564
disassembleInstruction(iga_disassemble_options_t & dopts,const void * bits,const char * (* formatLbl)(int32_t,void *),void * formatLblEnv,char ** output)565 iga_status_t disassembleInstruction(
566 iga_disassemble_options_t &dopts,
567 const void *bits,
568 const char *(*formatLbl)(int32_t, void *),
569 void *formatLblEnv,
570 char **output)
571 {
572 if (output)
573 *output = &m_empty_string[0];
574
575 // infer the length based on compaction bit
576 size_t bitsLen = ((const MInst *)bits)->isCompact() ? 8 : 16;
577
578 iga::Kernel *k = nullptr;
579 iga::ErrorHandler errHandler;
580 iga_status_t st = IGA_ERROR;
581
582 // force decoding opts to use numeric labels
583 dopts.formatting_opts |= IGA_FORMATTING_OPT_NUMERIC_LABELS;
584
585 st = disassembleKernel(
586 errHandler,
587 dopts,
588 bits,
589 (uint32_t)bitsLen,
590 k);
591 if (k != nullptr) {
592 if (m_disassemble_text) {
593 // previous disassemble clobbers new disassemble
594 free(m_disassemble_text);
595 }
596
597 const Instruction *firstInst = nullptr;
598 const iga::BlockList &bl = k->getBlockList();
599 for (const auto &b : bl) {
600 if (!b->getInstList().empty()) {
601 firstInst = b->getInstList().front();
602 break;
603 }
604 }
605 if (!firstInst) {
606 delete k;
607 return IGA_ERROR; // should be unreachable
608 }
609 std::stringstream ss;
610 FormatOpts fopts = formatterOpts(dopts,formatLbl,formatLblEnv);
611 FormatInstruction(errHandler, ss, fopts, *firstInst);
612
613 size_t slen = (size_t)ss.tellp();
614 m_disassemble_text = (char *)malloc(1 + slen);
615 if (!m_disassemble_text) {
616 delete k;
617 return IGA_OUT_OF_MEM;
618 }
619 ss.read(m_disassemble_text, slen);
620 m_disassemble_text[slen] = 0;
621 if (output) {
622 *output = m_disassemble_text;
623 }
624
625 delete k;
626 } // k non-null
627
628 st = translateDiagnostics(errHandler);
629 if (errHandler.hasErrors()) {
630 return IGA_DECODE_ERROR;
631 }
632 return st;
633 }
634
635
getErrors(const iga_diagnostic_t ** ds,uint32_t * ds_len) const636 iga_status_t getErrors(
637 const iga_diagnostic_t **ds, uint32_t *ds_len) const
638 {
639 if (!m_errorsValid) {
640 *ds = nullptr;
641 *ds_len = 0;
642 return IGA_INVALID_STATE;
643 }
644 *ds_len = (uint32_t)m_errors.size();
645 *ds = *ds_len ? &m_errors[0] : nullptr;
646 return IGA_SUCCESS;
647 }
648
649
getWarnings(const iga_diagnostic_t ** ds,uint32_t * ds_len) const650 iga_status_t getWarnings(
651 const iga_diagnostic_t **ds, uint32_t *ds_len) const
652 {
653 if (!m_warningsValid) {
654 *ds = nullptr;
655 *ds_len = 0;
656 return IGA_INVALID_STATE;
657 }
658 *ds_len = (uint32_t)m_warnings.size();
659 *ds = *ds_len ? &m_warnings[0] : nullptr;
660 return IGA_SUCCESS;
661 }
662 }; // class IGAContext
663
664
665 #define RETURN_INVALID_ARG_ON_NULL(X) \
666 do { if (!(X)) return IGA_INVALID_ARG; } while (0)
667
668 #define CAST_CONTEXT(ID,C) \
669 IGAContext *ID = (IGAContext *)(C); \
670 if (!ID->valid()) { \
671 return IGA_INVALID_OBJECT; \
672 }
673
674
iga_version_string()675 const char *iga_version_string()
676 {
677 return IGA_VERSION_PREFIX_STRING IGA_VERSION_SUFFIX;
678 }
679
680
iga_context_create(const iga_context_options_t * opts,iga_context_t * ctx)681 iga_status_t iga_context_create(
682 const iga_context_options_t *opts,
683 iga_context_t *ctx)
684 {
685 RETURN_INVALID_ARG_ON_NULL(ctx);
686 RETURN_INVALID_ARG_ON_NULL(opts);
687
688 iga_context_options_t coptsInternal =
689 IGA_CONTEXT_OPTIONS_INIT(IGA_GEN_INVALID);
690 if (opts->cb > sizeof(iga_context_options_t)) {
691 return IGA_VERSION_ERROR;
692 }
693 memcpy_s(&coptsInternal, opts->cb, opts, opts->cb);
694
695 iga::Platform p = ToPlatform(opts->gen);
696 if (p == iga::Platform::INVALID) {
697 return IGA_UNSUPPORTED_PLATFORM;
698 }
699
700 IGAContext *ctx_obj = nullptr;
701 try {
702 ctx_obj = new IGAContext(*opts);
703 } catch (std::bad_alloc &) {
704 return IGA_OUT_OF_MEM;
705 }
706
707 *ctx = (iga_context_t *)ctx_obj;
708
709 return IGA_SUCCESS;
710 }
iga_create_context(const iga_context_options_t * opts,iga_context_t * ctx)711 iga_status_t iga_create_context(
712 const iga_context_options_t *opts,
713 iga_context_t *ctx)
714 {
715 return iga_context_create(opts, ctx);
716 }
717
iga_context_release(iga_context_t ctx)718 iga_status_t iga_context_release(iga_context_t ctx)
719 {
720 RETURN_INVALID_ARG_ON_NULL(ctx);
721
722 CAST_CONTEXT(ctx_obj, ctx);
723 delete ctx_obj;
724
725 return IGA_SUCCESS;
726 }
iga_release_context(iga_context_t ctx)727 iga_status_t iga_release_context(iga_context_t ctx)
728 {
729 return iga_context_release(ctx);
730 }
731
iga_context_assemble(iga_context_t ctx,const iga_assemble_options_t * aopts,const char * kernel_text,void ** output,uint32_t * output_size)732 iga_status_t iga_context_assemble(
733 iga_context_t ctx,
734 const iga_assemble_options_t *aopts,
735 const char *kernel_text,
736 void **output,
737 uint32_t *output_size)
738 {
739 RETURN_INVALID_ARG_ON_NULL(ctx);
740 RETURN_INVALID_ARG_ON_NULL(aopts);
741 RETURN_INVALID_ARG_ON_NULL(kernel_text);
742 RETURN_INVALID_ARG_ON_NULL(output);
743 RETURN_INVALID_ARG_ON_NULL(output_size);
744 // see note at the top of the file about binary compatibility
745 if (aopts->cb > sizeof(*aopts)) {
746 return IGA_VERSION_ERROR;
747 }
748 iga_assemble_options_t aoptsInternal = IGA_ASSEMBLE_OPTIONS_INIT();
749 memcpy_s(&aoptsInternal, aopts->cb, aopts, aopts->cb);
750
751 CAST_CONTEXT(ctx_obj, ctx);
752 return ctx_obj->assemble(
753 aoptsInternal,
754 kernel_text,
755 output,
756 output_size);
757 }
iga_assemble(iga_context_t ctx,const iga_assemble_options_t * opts,const char * kernel_text,void ** output,uint32_t * output_size)758 IGA_API iga_status_t iga_assemble(
759 iga_context_t ctx,
760 const iga_assemble_options_t *opts,
761 const char *kernel_text,
762 void **output,
763 uint32_t *output_size)
764 {
765 return iga_context_assemble(ctx, opts, kernel_text, output, output_size);
766 }
767
iga_context_disassemble(iga_context_t ctx,const iga_disassemble_options_t * dopts,const void * input,uint32_t input_size,const char * (* fmt_label_name)(int32_t,void *),void * fmt_label_ctx,char ** kernel_text)768 iga_status_t iga_context_disassemble(
769 iga_context_t ctx,
770 const iga_disassemble_options_t *dopts,
771 const void *input,
772 uint32_t input_size,
773 const char * (*fmt_label_name)(int32_t, void *),
774 void *fmt_label_ctx,
775 char **kernel_text)
776 {
777 RETURN_INVALID_ARG_ON_NULL(ctx);
778 RETURN_INVALID_ARG_ON_NULL(dopts);
779 if (input == nullptr && input_size != 0)
780 return IGA_INVALID_ARG;
781 RETURN_INVALID_ARG_ON_NULL(kernel_text);
782 if (dopts->cb > sizeof(*dopts)) {
783 return IGA_VERSION_ERROR;
784 }
785 iga_disassemble_options_t doptsInternal = IGA_DISASSEMBLE_OPTIONS_INIT();
786 memcpy_s(&doptsInternal, dopts->cb, dopts, dopts->cb);
787
788 CAST_CONTEXT(ctx_obj, ctx);
789 return ctx_obj->disassemble(
790 doptsInternal,
791 input,
792 input_size,
793 fmt_label_name,
794 fmt_label_ctx,
795 kernel_text);
796 }
iga_disassemble(iga_context_t ctx,const iga_disassemble_options_t * dopts,const void * input,uint32_t input_size,const char * (* fmt_label_name)(int32_t,void *),void * fmt_label_ctx,char ** kernel_text)797 iga_status_t iga_disassemble(
798 iga_context_t ctx,
799 const iga_disassemble_options_t *dopts,
800 const void *input,
801 uint32_t input_size,
802 const char * (*fmt_label_name)(int32_t, void *),
803 void *fmt_label_ctx,
804 char **kernel_text)
805 {
806 return iga_context_disassemble(
807 ctx, dopts, input, input_size, fmt_label_name, fmt_label_ctx, kernel_text);
808 }
809
iga_disassemble_instruction(iga_context_t ctx,const iga_disassemble_options_t * dopts,const void * input,const char * (* fmt_label_name)(int32_t,void *),void * fmt_label_ctx,char ** kernel_text)810 iga_status_t iga_disassemble_instruction(
811 iga_context_t ctx,
812 const iga_disassemble_options_t *dopts,
813 const void *input,
814 const char * (*fmt_label_name)(int32_t, void *),
815 void *fmt_label_ctx,
816 char **kernel_text)
817 {
818 return iga_context_disassemble_instruction(
819 ctx, dopts, input, fmt_label_name, fmt_label_ctx, kernel_text);
820 }
iga_context_disassemble_instruction(iga_context_t ctx,const iga_disassemble_options_t * dopts,const void * input,const char * (* fmt_label_name)(int32_t,void *),void * fmt_label_ctx,char ** kernel_text)821 iga_status_t iga_context_disassemble_instruction(
822 iga_context_t ctx,
823 const iga_disassemble_options_t *dopts,
824 const void *input,
825 const char * (*fmt_label_name)(int32_t, void *),
826 void *fmt_label_ctx,
827 char **kernel_text)
828 {
829 RETURN_INVALID_ARG_ON_NULL(ctx);
830 RETURN_INVALID_ARG_ON_NULL(dopts);
831 RETURN_INVALID_ARG_ON_NULL(input);
832 RETURN_INVALID_ARG_ON_NULL(kernel_text);
833 if (dopts->cb > sizeof(*dopts)) {
834 return IGA_VERSION_ERROR;
835 }
836 iga_disassemble_options_t doptsInternal =
837 IGA_DISASSEMBLE_OPTIONS_INIT_NUMERIC_LABELS();
838 memcpy_s(&doptsInternal, dopts->cb, dopts, dopts->cb);
839
840 CAST_CONTEXT(ctx_obj, ctx);
841 return ctx_obj->disassembleInstruction(
842 doptsInternal,
843 input,
844 fmt_label_name,
845 fmt_label_ctx,
846 kernel_text);
847 }
848
849
iga_context_get_errors(iga_context_t ctx,const iga_diagnostic_t ** ds,uint32_t * ds_len)850 iga_status_t iga_context_get_errors(
851 iga_context_t ctx,
852 const iga_diagnostic_t **ds,
853 uint32_t *ds_len)
854 {
855 RETURN_INVALID_ARG_ON_NULL(ctx);
856 RETURN_INVALID_ARG_ON_NULL(ds);
857 RETURN_INVALID_ARG_ON_NULL(ds_len);
858
859 CAST_CONTEXT(ctx_obj, ctx);
860 return ctx_obj->getErrors(ds,ds_len);
861 }
iga_get_errors(iga_context_t ctx,const iga_diagnostic_t ** ds,uint32_t * ds_len)862 iga_status_t iga_get_errors(
863 iga_context_t ctx,
864 const iga_diagnostic_t **ds,
865 uint32_t *ds_len)
866 {
867 return iga_context_get_errors(ctx, ds, ds_len);
868 }
869
iga_context_get_warnings(iga_context_t ctx,const iga_diagnostic_t ** ds,uint32_t * ds_len)870 iga_status_t iga_context_get_warnings(
871 iga_context_t ctx,
872 const iga_diagnostic_t **ds,
873 uint32_t *ds_len)
874 {
875 RETURN_INVALID_ARG_ON_NULL(ctx);
876 RETURN_INVALID_ARG_ON_NULL(ds);
877 RETURN_INVALID_ARG_ON_NULL(ds_len);
878
879 CAST_CONTEXT(ctx_obj, ctx);
880 return ctx_obj->getWarnings(ds,ds_len);
881 }
iga_get_warnings(iga_context_t ctx,const iga_diagnostic_t ** ds,uint32_t * ds_len)882 iga_status_t iga_get_warnings(
883 iga_context_t ctx,
884 const iga_diagnostic_t **ds,
885 uint32_t *ds_len)
886 {
887 return iga_context_get_warnings(ctx, ds, ds_len);
888 }
889
iga_diagnostic_get_message(const iga_diagnostic_t * d,const char ** message)890 iga_status_t iga_diagnostic_get_message(
891 const iga_diagnostic_t *d,
892 const char **message)
893 {
894 RETURN_INVALID_ARG_ON_NULL(d);
895 RETURN_INVALID_ARG_ON_NULL(message);
896
897 *message = d->message;
898
899 return IGA_SUCCESS;
900 }
901
902
iga_diagnostic_get_offset(const iga_diagnostic_t * d,uint32_t * offset)903 iga_status_t iga_diagnostic_get_offset(
904 const iga_diagnostic_t *d,
905 uint32_t *offset)
906 {
907 RETURN_INVALID_ARG_ON_NULL(d);
908 RETURN_INVALID_ARG_ON_NULL(offset);
909
910 *offset = d->offset;
911
912 return IGA_SUCCESS;
913 }
914
915 // static const uint32_t IGA_DIAGNOSTIC_BINARY_MASK = 0x80000000;
916 /* #define IGA_DIAGNOSTIC_IS_BINARY(D) \
917 ((D)->column & IGA_DIAGNOSTIC_BINARY_MASK)
918 for now we use line == col == 0 to mean binary
919 */
920 #define IGA_DIAGNOSTIC_IS_BINARY(D) \
921 ((D)->column == 0 && (D)->line == 0)
922
923
iga_diagnostic_get_type(const iga_diagnostic_t * d,iga_diagnostic_type_t * dt)924 iga_status_t iga_diagnostic_get_type(
925 const iga_diagnostic_t *d,
926 iga_diagnostic_type_t *dt)
927 {
928 RETURN_INVALID_ARG_ON_NULL(d);
929 RETURN_INVALID_ARG_ON_NULL(dt);
930
931 *dt = IGA_DIAGNOSTIC_IS_BINARY(d) ?
932 IGA_DIAGNOSTIC_BINARY :
933 IGA_DIAGNOSTIC_TEXT;
934
935 return IGA_SUCCESS;
936 }
937
938
iga_diagnostic_get_text_line(const iga_diagnostic_t * d,uint32_t * line)939 iga_status_t iga_diagnostic_get_text_line(
940 const iga_diagnostic_t *d,
941 uint32_t *line)
942 {
943 RETURN_INVALID_ARG_ON_NULL(d);
944 RETURN_INVALID_ARG_ON_NULL(line);
945
946 if (IGA_DIAGNOSTIC_IS_BINARY(d))
947 return IGA_INVALID_ARG;
948
949 *line = d->line;
950
951 return IGA_SUCCESS;
952 }
953
954
iga_diagnostic_get_text_column(const iga_diagnostic_t * d,uint32_t * col)955 iga_status_t iga_diagnostic_get_text_column(
956 const iga_diagnostic_t *d,
957 uint32_t *col)
958 {
959 RETURN_INVALID_ARG_ON_NULL(d);
960 RETURN_INVALID_ARG_ON_NULL(col);
961
962 if (IGA_DIAGNOSTIC_IS_BINARY(d))
963 return IGA_INVALID_ARG;
964 *col = d->column; // & ~IGA_DIAGNOSTIC_BINARY_MASK;
965
966 return IGA_SUCCESS;
967 }
968
969
iga_diagnostic_get_text_extent(const iga_diagnostic_t * d,uint32_t * ext)970 iga_status_t iga_diagnostic_get_text_extent(
971 const iga_diagnostic_t *d,
972 uint32_t *ext)
973 {
974 RETURN_INVALID_ARG_ON_NULL(d);
975 RETURN_INVALID_ARG_ON_NULL(ext);
976
977 if (IGA_DIAGNOSTIC_IS_BINARY(d))
978 return IGA_INVALID_ARG;
979 *ext = d->extent;
980
981 return IGA_SUCCESS;
982 }
983
984 ///////////////////////////////////////////////////////////////////////////////
985 // //
986 // IGA OPSPEC API //
987 // //
988 ///////////////////////////////////////////////////////////////////////////////
989
990 #define IGA_COPY_OUT(DST, DST_LEN_PTR, SRC, SRC_LEN) \
991 do { \
992 if ((DST) != nullptr) { \
993 size_t _CPLEN = *(DST_LEN_PTR) < (SRC_LEN) ? *(DST_LEN_PTR) : (SRC_LEN); \
994 memcpy_s( \
995 (DST), _CPLEN * sizeof(*(DST)), (SRC), _CPLEN * sizeof(*(DST))); \
996 } \
997 *(DST_LEN_PTR) = (SRC_LEN); \
998 } while (0)
999
1000 // same as above, but for a string
1001 #define IGA_COPY_OUT_STR(DST, DST_LEN_PTR, SRC) \
1002 do { \
1003 size_t SRC_LEN = strlen(SRC) + 1; \
1004 if ((DST) != nullptr) { \
1005 size_t _CPLEN = *(DST_LEN_PTR) < (SRC_LEN) ? *(DST_LEN_PTR) : (SRC_LEN); \
1006 memcpy_s( \
1007 (DST), _CPLEN * sizeof(*(DST)), (SRC), _CPLEN * sizeof(*(DST))); \
1008 DST[_CPLEN - 1] = 0; \
1009 } \
1010 *(DST_LEN_PTR) = (SRC_LEN); \
1011 } while (0)
1012
1013
1014 // In the opaque pointer returned, we flip some top bits.
1015 // Should the user clobber their stack or accidentially send this pointer
1016 // of to be written, this will hopefully trap (assuming we are in user space)
1017 // immediately rather than corrupting our internal data structures.
1018 // x86 takes either 0x80000000 or 0xC000000 up to 0xFFFFFFFF as system space
1019 //
1020 // TODO: another method would be to store this as a relative address...
1021 // relative to something near the instspec....
1022 // (or store as Platform x Op)
1023 // That would translate to fairly small integer that should be in the
1024 // no access range (near 0)
opspec_to_handle(const OpSpec * os)1025 static iga_opspec_t opspec_to_handle(const OpSpec *os) {
1026 const uintptr_t TOP_BIT = (sizeof(void *) == 4) ?
1027 0xC0000000 : 0x8000000000000000;
1028 return (iga_opspec_t)((uintptr_t)os ^ TOP_BIT);
1029 }
opspec_from_handle(iga_opspec_t os)1030 static const OpSpec *opspec_from_handle(iga_opspec_t os) {
1031 const uintptr_t TOP_BIT = (sizeof(void *) == 4) ?
1032 0xC0000000 : 0x8000000000000000;
1033 return (const OpSpec *)((uintptr_t)os ^ TOP_BIT);
1034 }
1035
1036
iga_opspec_enumerate(iga_gen_t gen,iga_opspec_t * ops_arr,size_t * ops_arr_len)1037 iga_status_t iga_opspec_enumerate(
1038 iga_gen_t gen,
1039 iga_opspec_t *ops_arr,
1040 size_t *ops_arr_len)
1041 {
1042 RETURN_INVALID_ARG_ON_NULL(ops_arr_len);
1043
1044 const Model *m = Model::LookupModel(ToPlatform(gen));
1045 if (!m) {
1046 return IGA_UNSUPPORTED_PLATFORM;
1047 }
1048 std::vector<iga_opspec_t> ops;
1049 ops.reserve(128);
1050 for (const OpSpec *os : m->ops()) {
1051 ops.emplace_back(opspec_to_handle(os));
1052 }
1053
1054 IGA_COPY_OUT(ops_arr, ops_arr_len, ops.data(), ops.size());
1055 return IGA_SUCCESS;
1056 }
1057
1058
iga_opspec_from_op(iga_gen_t gen,uint32_t op_enum,iga_opspec_t * op)1059 iga_status_t iga_opspec_from_op(
1060 iga_gen_t gen,
1061 uint32_t op_enum,
1062 iga_opspec_t *op)
1063 {
1064 RETURN_INVALID_ARG_ON_NULL(op);
1065 const Model *m = Model::LookupModel(ToPlatform(gen));
1066 if (!m) {
1067 return IGA_UNSUPPORTED_PLATFORM;
1068 }
1069 const OpSpec *os = &m->lookupOpSpec(static_cast<iga::Op>(op_enum));
1070 *op = opspec_to_handle(os);
1071 return IGA_SUCCESS;
1072 }
1073
1074
iga_opspec_mnemonic(iga_opspec_t op,char * mnemonic,size_t * mnemonic_len)1075 iga_status_t iga_opspec_mnemonic(
1076 iga_opspec_t op,
1077 char *mnemonic,
1078 size_t *mnemonic_len)
1079 {
1080 RETURN_INVALID_ARG_ON_NULL(op);
1081 RETURN_INVALID_ARG_ON_NULL(mnemonic_len);
1082
1083 const OpSpec *os = opspec_from_handle(op);
1084 IGA_COPY_OUT_STR(mnemonic, mnemonic_len, os->mnemonic.str().c_str());
1085 return IGA_SUCCESS;
1086 }
1087
1088
iga_opspec_name(iga_opspec_t op,char * name,size_t * name_len)1089 iga_status_t iga_opspec_name(
1090 iga_opspec_t op,
1091 char *name,
1092 size_t *name_len)
1093 {
1094 RETURN_INVALID_ARG_ON_NULL(op);
1095 RETURN_INVALID_ARG_ON_NULL(name_len);
1096
1097 const OpSpec *os = opspec_from_handle(op);
1098 IGA_COPY_OUT_STR(name, name_len, os->name.str().c_str());
1099 return IGA_SUCCESS;
1100 }
1101
1102
iga_opspec_description(iga_opspec_t op,char * desc,size_t * desc_len)1103 iga_status_t iga_opspec_description(
1104 iga_opspec_t op,
1105 char *desc,
1106 size_t *desc_len)
1107 {
1108 RETURN_INVALID_ARG_ON_NULL(op);
1109 RETURN_INVALID_ARG_ON_NULL(desc_len);
1110
1111 IGA_COPY_OUT_STR(desc, desc_len, "<description unsupported>");
1112 return IGA_SUCCESS;
1113 }
1114
1115
iga_opspec_op(iga_opspec_t op,uint32_t * opcode)1116 iga_status_t iga_opspec_op(
1117 iga_opspec_t op,
1118 uint32_t *opcode)
1119 {
1120 RETURN_INVALID_ARG_ON_NULL(op);
1121 RETURN_INVALID_ARG_ON_NULL(opcode);
1122
1123 *opcode = static_cast<uint32_t>(opspec_from_handle(op)->op);
1124 return IGA_SUCCESS;
1125 }
1126
1127
iga_opspec_op_encoding(iga_opspec_t op,uint32_t * opcode)1128 iga_status_t iga_opspec_op_encoding(
1129 iga_opspec_t op,
1130 uint32_t *opcode)
1131 {
1132 RETURN_INVALID_ARG_ON_NULL(op);
1133 RETURN_INVALID_ARG_ON_NULL(opcode);
1134
1135 *opcode = static_cast<uint32_t>(opspec_from_handle(op)->opcode);
1136 return IGA_SUCCESS;
1137 }
1138
1139
1140 ///////////////////////////////////////////////////////////////////////////////
1141 // RETURNS ALL THE FUNCTIONS IN A TABLE
1142 ///////////////////////////////////////////////////////////////////////////////
1143
iga_get_interface(iga_functions_t * funcs)1144 iga_status_t iga_get_interface(iga_functions_t *funcs)
1145 {
1146 RETURN_INVALID_ARG_ON_NULL(funcs);
1147
1148 funcs->iga_version_string = iga_version_string;
1149 funcs->iga_status_to_string = iga_status_to_string;
1150
1151 funcs->iga_context_create = &iga_context_create;
1152 funcs->iga_context_release = &iga_context_release;
1153 funcs->iga_context_assemble = &iga_context_assemble;
1154 funcs->iga_context_disassemble = &iga_context_disassemble;
1155 funcs->iga_context_disassemble_instruction =
1156 &iga_context_disassemble_instruction;
1157 funcs->iga_context_get_errors = &iga_context_get_errors;
1158 funcs->iga_context_get_warnings = &iga_context_get_warnings;
1159
1160 funcs->iga_diagnostic_get_message = &iga_diagnostic_get_message;
1161 funcs->iga_diagnostic_get_offset = &iga_diagnostic_get_offset;
1162 funcs->iga_diagnostic_get_type = &iga_diagnostic_get_type;
1163 funcs->iga_diagnostic_get_text_line = &iga_diagnostic_get_text_line;
1164 funcs->iga_diagnostic_get_text_column = &iga_diagnostic_get_text_column;
1165 funcs->iga_diagnostic_get_text_extent = &iga_diagnostic_get_text_extent;
1166
1167 funcs->iga_opspec_enumerate = &iga_opspec_enumerate;
1168 funcs->iga_opspec_mnemonic = &iga_opspec_mnemonic;
1169 funcs->iga_opspec_name = &iga_opspec_name;
1170 funcs->iga_opspec_description = &iga_opspec_description;
1171 funcs->iga_opspec_op = &iga_opspec_op;
1172
1173 return IGA_SUCCESS;
1174 }
1175
1176
1177
1178