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