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 <iostream>
10 #include <fstream>
11 #include <string>
12 
13 
14 #include "visa_igc_common_header.h"
15 #include "Common_ISA_framework.h"
16 #include "VISAKernel.h"
17 #include "Option.h"
18 #include "Common_ISA.h"
19 #include "BinaryCISAEmission.h"
20 #include "Timer.h"
21 
22 #include "DebugInfo.h"
23 
24 #ifndef DLL_MODE
25 #include "EnumFiles.hpp"
26 #endif
27 
28 using namespace std;
29 
30 ///
31 /// Reads byte code and calls the builder API as it does so.
32 ///
33 extern bool readIsaBinaryNG(const char *buf, CISA_IR_Builder *builder,
34                             vector<VISAKernel *> &kernels,
35                             const char *kernelName, unsigned int majorVersion,
36                             unsigned int minorVersion);
37 
38 #ifndef DLL_MODE
39 void parseText(
40     std::string fileName,
41     int argc, const char *argv[], Options &opt);
42 #endif
43 
44 // default size of the physical reg pool mem manager in bytes
45 #define PHY_REG_MEM_SIZE   (16*1024)
46 
47 // default size of the kernel mem manager in bytes
48 #define KERNEL_MEM_SIZE    (4*1024*1024)
49 
50 #define JIT_SUCCESS                     0
51 #define JIT_INVALID_INPUT               1
52 #define JIT_CISA_ERROR                  3
53 #define JIT_INVALID_PLATFORM            5
54 
55 #ifndef DLL_MODE
parseBinary(std::string fileName,int argc,const char * argv[],Options & opt)56 void parseBinary(
57     std::string fileName,
58     int argc, const char *argv[], Options &opt)
59 {
60     // read in common isa binary file
61     int c;
62     char *buf;
63     FILE *commonISAInput = NULL;
64     long file_size;
65     unsigned byte_pos = 0;
66     common_isa_header commonISAHeader;
67     vISA::Mem_Manager globalMem(KERNEL_MEM_SIZE);
68 
69     // open common isa file
70     if ((commonISAInput = fopen(fileName.c_str(), "rb")) == NULL)
71     {
72         std::cerr << fileName << ": cannot open file\n";
73         exit(EXIT_FAILURE);
74     }
75 
76     fseek(commonISAInput, 0, SEEK_END);
77     file_size = ftell(commonISAInput);
78     fseek(commonISAInput, 0, SEEK_SET);
79 
80     buf = (char*)globalMem.alloc(file_size + 1);
81     char *buf_ptr = buf;
82     while ((c = fgetc(commonISAInput)) != EOF) {
83         *buf_ptr = c;
84         buf_ptr++;
85     }
86 
87     processCommonISAHeader(commonISAHeader, byte_pos, buf, &globalMem);
88     fclose(commonISAInput);
89     vISA::Mem_Manager mem(4096);
90 
91     /// Try opening the file.
92     FILE* isafile = fopen(fileName.c_str(), "rb");
93     if (!isafile) {
94         std::cerr << fileName << ": cannot open file\n";
95         exit(EXIT_FAILURE);
96     }
97 
98     /// Calculate file size.
99     fseek(isafile, 0, SEEK_END);
100     long isafilesize = ftell(isafile);
101     rewind(isafile);
102 
103     /// Reading file into buffer.
104     char* isafilebuf = (char*)mem.alloc(isafilesize);
105     if (isafilesize != fread(isafilebuf, 1, isafilesize, isafile))
106     {
107         cerr << "Unable to read entire file into buffer." << endl;
108         exit(EXIT_FAILURE);
109     }
110     fclose(isafile);
111 
112     TARGET_PLATFORM platform = getGenxPlatform();
113     VISA_BUILDER_OPTION builderOption =
114         (platform == GENX_NONE) ? VISA_BUILDER_VISA : VISA_BUILDER_BOTH;
115     CISA_IR_Builder* cisa_builder = NULL;
116 
117     CISA_IR_Builder::CreateBuilder(cisa_builder, vISA_DEFAULT, builderOption, platform, argc, argv);
118     MUST_BE_TRUE(cisa_builder, "cisa_builder is NULL.");
119 
120     vector<VISAKernel*> kernels;
121     readIsaBinaryNG(isafilebuf, cisa_builder, kernels, NULL, COMMON_ISA_MAJOR_VER, COMMON_ISA_MINOR_VER);
122     std::string binFileName;
123 
124     if (cisa_builder->m_options.getOption(vISA_OutputvISABinaryName))
125     {
126         const char* cisaBinaryName = NULL;
127         cisa_builder->m_options.getOption(vISA_GetvISABinaryName, cisaBinaryName);
128         binFileName = cisaBinaryName;
129     }
130 
131     int result = cisa_builder->Compile((char*)binFileName.c_str());
132     CISA_IR_Builder::DestroyBuilder(cisa_builder);
133     if (result != VISA_SUCCESS)
134     {
135         exit(1);
136     }
137 }
138 #endif
139 
JITCompileAllOptions(const char * kernelName,const void * kernelIsa,unsigned int kernelIsaSize,void * & genBinary,unsigned int & genBinarySize,const char * platform,int majorVersion,int minorVersion,int numArgs,const char * args[],char * errorMsg,FINALIZER_INFO * jitInfo,void * gtpin_init)140 int JITCompileAllOptions(const char* kernelName,
141     const void* kernelIsa,
142     unsigned int kernelIsaSize,
143     void* &genBinary,
144     unsigned int& genBinarySize,
145     const char* platform,
146     int majorVersion,
147     int minorVersion,
148     int numArgs,
149     const char* args[],
150     char* errorMsg,
151     FINALIZER_INFO* jitInfo,
152     void* gtpin_init)
153 {
154     // This function becomes the new entry point even for JITCompile clients.
155     if (kernelName == NULL || kernelIsa == NULL || strlen(kernelName) > COMMON_ISA_MAX_KERNEL_NAME_LEN)
156     {
157         return JIT_INVALID_INPUT;
158     }
159     // This must be done before processing the options,
160     // as some options depend on the platform
161     if (SetVisaPlatform(platform) != 0)
162     {
163         return JIT_INVALID_PLATFORM;
164     }
165 
166     genBinary = NULL;
167     genBinarySize = 0;
168 
169     char* isafilebuf = (char*)kernelIsa;
170     CISA_IR_Builder* cisa_builder = NULL;
171     // HW mode: default: GEN path; if dump/verify: Both path
172     VISA_BUILDER_OPTION builderOption = VISA_BUILDER_GEN;
173 
174     CISA_IR_Builder::CreateBuilder(cisa_builder, vISA_DEFAULT, builderOption, getGenxPlatform(), numArgs, args);
175     cisa_builder->setGtpinInit(gtpin_init);
176 
177     if (!cisa_builder)
178     {
179         return JIT_CISA_ERROR;
180     }
181 
182     vector<VISAKernel*> kernels;
183     bool passed = readIsaBinaryNG(isafilebuf, cisa_builder, kernels, kernelName, majorVersion, minorVersion);
184 
185     if (!passed)
186     {
187         return JIT_CISA_ERROR;
188     }
189 
190     cisa_builder->Compile("");
191 
192     VISAKernel* kernel = kernels[0];
193     FINALIZER_INFO* tempJitInfo = NULL;
194     void* genxBinary = NULL;
195     int size = 0;
196     kernel->GetJitInfo(tempJitInfo);
197     kernel->GetGenxDebugInfo(tempJitInfo->genDebugInfo, tempJitInfo->genDebugInfoSize);
198 
199     if (gtpin_init)
200     {
201         // Return free GRF info
202         kernel->GetGTPinBuffer(tempJitInfo->freeGRFInfo, tempJitInfo->freeGRFInfoSize);
203     }
204 
205     if (jitInfo != NULL && tempJitInfo != NULL)
206         memcpy_s(jitInfo, sizeof(FINALIZER_INFO), tempJitInfo, sizeof(FINALIZER_INFO));
207 
208     if (!(0 == kernel->GetGenxBinary(genxBinary, size) && genxBinary != NULL))
209     {
210         return JIT_INVALID_INPUT;
211     }
212     genBinary = genxBinary;
213     genBinarySize = size;
214 
215     CISA_IR_Builder::DestroyBuilder(cisa_builder);
216     return JIT_SUCCESS;
217 }
218 
219 /**
220   * This is the main entry point for CM.
221   */
JITCompile(const char * kernelName,const void * kernelIsa,unsigned int kernelIsaSize,void * & genBinary,unsigned int & genBinarySize,const char * platform,int majorVersion,int minorVersion,int numArgs,const char * args[],char * errorMsg,FINALIZER_INFO * jitInfo)222 DLL_EXPORT int JITCompile(const char* kernelName,
223                           const void* kernelIsa,
224                           unsigned int kernelIsaSize,
225                           void* &genBinary,
226                           unsigned int& genBinarySize,
227                           const char* platform,
228                           int majorVersion,
229                           int minorVersion,
230                           int numArgs,
231                           const char* args[],
232                           char* errorMsg,
233                           FINALIZER_INFO* jitInfo)
234 {
235     // JITCompile will invoke the other JITCompile API that supports relocation.
236     // Via this path, relocs will be NULL. This way we can share a single
237     // implementation of JITCompile.
238 
239     return JITCompileAllOptions(kernelName, kernelIsa, kernelIsaSize, genBinary, genBinarySize,
240         platform, majorVersion, minorVersion, numArgs, args, errorMsg, jitInfo, nullptr);
241 }
242 
JITCompile_v2(const char * kernelName,const void * kernelIsa,unsigned int kernelIsaSize,void * & genBinary,unsigned int & genBinarySize,const char * platform,int majorVersion,int minorVersion,int numArgs,const char * args[],char * errorMsg,FINALIZER_INFO * jitInfo,void * gtpin_init)243 DLL_EXPORT int JITCompile_v2(const char* kernelName,
244     const void* kernelIsa,
245     unsigned int kernelIsaSize,
246     void* &genBinary,
247     unsigned int& genBinarySize,
248     const char* platform,
249     int majorVersion,
250     int minorVersion,
251     int numArgs,
252     const char* args[],
253     char* errorMsg,
254     FINALIZER_INFO* jitInfo,
255     void* gtpin_init)
256 {
257     // JITCompile will invoke the other JITCompile API that supports relocation.
258     // Via this path, relocs will be NULL. This way we can share a single
259     // implementation of JITCompile.
260     return JITCompileAllOptions(kernelName, kernelIsa, kernelIsaSize, genBinary, genBinarySize,
261         platform, majorVersion, minorVersion, numArgs, args, errorMsg, jitInfo, gtpin_init);
262 }
263 
getJITVersion(unsigned int & majorV,unsigned int & minorV)264 DLL_EXPORT void getJITVersion(unsigned int& majorV, unsigned int& minorV)
265 {
266     majorV = COMMON_ISA_MAJOR_VER;
267     minorV = COMMON_ISA_MINOR_VER;
268 }
269 
270 #ifndef  DLL_MODE
271 
272 /// Returns true if a string ends with an expected suffix.
endsWith(const std::string & str,const std::string & suf)273 static bool endsWith(const std::string &str, const std::string &suf)
274 {
275     if (str.length() < suf.length())
276         return false;
277     return 0 == str.compare(str.length() - suf.length(), suf.length(), suf);
278 }
279 
main(int argc,const char * argv[])280 int main(int argc, const char *argv[])
281 {
282     char fileName[256];
283     cout << argv[0];
284     for (int i = 1; i < argc; i++)
285         cout << " " << argv[i];
286     cout << std::endl;
287 
288 #if 0
289     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
290     //_crtBreakAlloc = 4763;
291 #endif
292 
293 
294     // here we let the tool quit instead of crashing
295     if (argc < 2)
296     {
297         Options::showUsage(COUT_ERROR);
298         return 1;
299     }
300 
301     int startPos = 1;
302     bool parserMode = false;
303     if (endsWith(argv[startPos], ".visaasm") || endsWith(argv[startPos], ".isaasm"))
304     {
305         startPos++;
306         parserMode = true;
307     }
308     else if (endsWith(argv[startPos], ".isa"))
309     {
310         startPos++;
311     }
312 
313     // Note that we process options twice in offline mode (once here and once in createBuilder),
314     // since we have to get some essential options such as platform and stepping
315     Options opt;
316     if (!opt.parseOptions(argc - startPos, &argv[startPos]))
317     {
318         return 1;
319     }
320     if (opt.getOptionCstr(vISA_DecodeDbg))
321     {
322         const char* dbgName;
323         opt.getOption(vISA_DecodeDbg, dbgName);
324         decodeAndDumpDebugInfo((char*)dbgName);
325         exit(0);
326     }
327 
328     bool platformIsSet = false;
329     bool dumpCommonIsa = false;
330     bool generateBinary = false;
331     opt.getOption(vISA_PlatformIsSet, platformIsSet);
332     opt.getOption(vISA_GenerateISAASM, dumpCommonIsa);
333 
334     if (opt.getOption(vISA_isParseMode))
335         parserMode = true;
336 
337     opt.getOption(vISA_GenerateBinary, generateBinary);
338     if (!platformIsSet && ((!dumpCommonIsa && !parserMode) || generateBinary))
339     {
340         std::cerr << "USAGE: must specify platform\n";
341         Options::showUsage(COUT_ERROR);
342         return 1;
343     }
344 
345     //
346     // for debug print lex results to stdout (default)
347     // for release open "lex.out" and redirect lex results
348     //
349     std::list<std::string> filesList;
350     if (parserMode && opt.getOption(vISA_IsaasmNamesFileUsed))
351     {
352         const char* isaasmNamesFile;
353         opt.getOption(vISA_ISAASMNamesFile, isaasmNamesFile);
354         strcpy_s(fileName, 256, isaasmNamesFile);
355         filesList.push_back(fileName);
356     }
357     else
358     {
359         //
360         // allow input filename with any suffix or no suffix
361         //
362         std::string cmdLine = argv[1];
363         if (!PrepareInput(cmdLine, filesList)) {
364             std::cerr << "ERROR: Unable to open input file(s)." << std::endl;
365             return 1;
366         }
367         std::string::size_type testNameEnd = cmdLine.find_last_of(".");
368         std::string testName;
369         if (testNameEnd != std::string::npos)
370             testName = cmdLine.substr(0, testNameEnd);
371         else
372             testName = cmdLine;
373 
374         std::string::size_type numChars = cmdLine.copy(fileName, std::string::npos);
375         fileName[numChars] = '\0';
376     }
377 
378     // holds storage for file names that we automatically deduce in the loop
379     // below; memory can be deallocated later
380     std::vector<std::string> asmFileRoots;
381 
382     for (auto fName : filesList)
383     {
384         // If the file name is not set by the user, then use the same
385         // file name (drop path)
386         // we do not include an extension as other logic suffixes that later
387         // depending on the desired
388         //   e.g. foo/bar.visaasm ==> ./bar
389         if (!opt.isOptionSetByUser(VISA_AsmFileName)) {
390             auto extOff = fName.rfind('.');
391             if (extOff == std::string::npos) {
392                 std::cerr << fName << ": cannot find file extension";
393                 return 1;
394             }
395             size_t fileStartOff = 0;
396             for (int i = (int)extOff; i >= 0; i--) {
397                 if (fName[i] == '\\' || fName[i] == '/') {
398                     fileStartOff = (size_t)i + 1;
399                     break;
400                 }
401             }
402             auto baseRoot = fName.substr(fileStartOff, extOff - fileStartOff);
403             asmFileRoots.emplace_back(baseRoot);
404             opt.setOptionInternally(VISA_AsmFileName,
405                 asmFileRoots.back().c_str());
406         }
407 
408         if (parserMode)
409         {
410             parseText(fName, argc - startPos, &argv[startPos], opt);
411         }
412         else
413         {
414             parseBinary(fName, argc - startPos, &argv[startPos], opt);
415         }
416     }
417 
418 
419 #ifdef COLLECT_ALLOCATION_STATS
420 #if 0
421     cout << "# allocation: " << numAllocations << endl;
422     cout << "total allocation size: " << (totalAllocSize / 1024) << " KB" << endl;
423     cout << "# mallocs: " << numMallocCalls << endl;
424     cout << "total malloc size: " << (totalMallocSize / 1024) << " KB" << endl;
425     cout << "# memory managers: " << numMemManagers << endl;
426     cout << "Max Arena list length: " << maxArenaLength << endl;
427 #else
428     cout << numAllocations << "\t" << (totalAllocSize / 1024) << "\t" <<
429         numMallocCalls << "\t" << (totalMallocSize / 1024) << "\t" << numMemManagers <<
430         "\t" << maxArenaLength << endl;
431 #endif
432 #endif
433     return 0;
434 }
435 #endif
436 
freeBlock(void * ptr)437 DLL_EXPORT void freeBlock(void* ptr)
438 {
439     if (ptr != NULL)
440     {
441         free(ptr);
442     }
443 }
444 
allocCodeBlock(size_t sz)445 void* allocCodeBlock(size_t sz)
446 {
447     // allocate buffer for the Gen kernel binary.
448     // for now just use malloc, but eventually runtime should provide this function
449     return (char*) malloc(sz * sizeof(char));
450 }
451 
452 #ifndef DLL_MODE
453 
454 extern int CISAparse(CISA_IR_Builder* builder);
455 
parseText(std::string fileName,int argc,const char * argv[],Options & opt)456 void parseText(std::string fileName, int argc, const char *argv[], Options &opt)
457 {
458     int num_kernels = 0;
459     std::string testName;
460 
461     std::list<std::string> file_names;
462     bool isaasmNamesFileUsed =  false;
463 
464     opt.getOption(vISA_IsaasmNamesFileUsed, isaasmNamesFileUsed);
465     if (isaasmNamesFileUsed) {
466         std::ifstream os;
467         os.open(fileName, std::ios::in);
468         if (!os.is_open()) {
469             std::cerr << "could not open an isaasm names input file.\n";
470             exit(1);
471         }
472 
473         std::string line;
474         while (!os.eof()) {
475             std::getline(os, line);
476             if (line == "")
477                 continue;
478             file_names.push_back(line);
479             num_kernels++;
480         }
481         os.close();
482     }
483     else {
484         num_kernels = 1;
485         file_names.push_back(fileName);
486     }
487 
488     //used to ignore duplicate file names
489     std::map<std::string, bool> files_parsed;
490 
491     TARGET_PLATFORM platform = getGenxPlatform();
492     VISA_BUILDER_OPTION builderOption =
493         (platform == GENX_NONE) ? VISA_BUILDER_VISA : VISA_BUILDER_BOTH;
494 
495     CISA_IR_Builder *cisa_builder = nullptr;
496 
497     CISA_IR_Builder::CreateBuilder(cisa_builder, vISA_ASM_READER, builderOption, getGenxPlatform(), argc, argv);
498 
499     for (int i = 0; i < num_kernels; i++)
500     {
501         if (files_parsed.find(file_names.front()) != files_parsed.end())
502         {
503             file_names.pop_front();
504             continue;
505         }
506         else
507         {
508             files_parsed[file_names.front()] = true;
509         }
510 
511         auto vISAFileName = file_names.front();
512         CISAin = fopen(vISAFileName.c_str(), "r");
513         if (!CISAin)
514         {
515             std::cerr <<  vISAFileName << ": cannot open vISA assembly file\n";
516             exit(1);
517         }
518 
519         std::string::size_type testNameEnd = vISAFileName.find_last_of('.');
520         std::string::size_type testNameStart = vISAFileName.find_last_of('\\');
521 
522         if (testNameStart != std::string::npos)
523             testNameStart++;
524         else
525             testNameStart = 0;
526 
527         if (testNameEnd != std::string::npos)
528             testName = vISAFileName.substr(testNameStart, testNameEnd);
529         else
530             testName = vISAFileName;
531 
532         CISAdebug = 0;
533         int fail = CISAparse(cisa_builder);
534         fclose(CISAin);
535         if (fail)
536         {
537             if (cisa_builder->HasParseError()) {
538                 std::cerr << cisa_builder->GetParseError() << "\n";
539             } else {
540                 std::cerr << "error during parsing: CISAparse() returned " << fail << "\n";
541             }
542             exit(1);
543         }
544 
545         // If the input text lacks "OutputAsmPath" (and it should),
546         // then we can override it with the implied name here.
547         auto k = cisa_builder->get_kernel();
548         if (k->getOutputAsmPath().empty()) {
549             const char *outputPrefix = opt.getOptionCstr(VISA_AsmFileName);
550             k->setOutputAsmPath(outputPrefix);
551             cisa_builder->getOptions()->setOptionInternally(VISA_AsmFileName, outputPrefix);
552         }
553 
554         file_names.pop_front();
555     }
556 
557     std::string binFileName = testName + ".isa";
558 
559     bool outputCISABinaryName = false;
560     opt.getOption(vISA_OutputvISABinaryName, outputCISABinaryName);
561     if (outputCISABinaryName)
562     {
563         char const* cisaBinaryName;
564         opt.getOption(vISA_GetvISABinaryName, cisaBinaryName);
565         binFileName = cisaBinaryName;
566     }
567 
568     cisa_builder->Compile(binFileName.c_str());
569     CISA_IR_Builder::DestroyBuilder(cisa_builder);
570 }
571 #endif
572