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