1 /*
2  * Copyright (C) 2018-2021 Intel Corporation
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  */
7 
8 #include "offline_compiler.h"
9 
10 #include "shared/offline_compiler/source/queries.h"
11 #include "shared/offline_compiler/source/utilities/get_git_version_info.h"
12 #include "shared/source/compiler_interface/intermediate_representations.h"
13 #include "shared/source/debug_settings/debug_settings_manager.h"
14 #include "shared/source/device_binary_format/device_binary_formats.h"
15 #include "shared/source/device_binary_format/elf/elf_encoder.h"
16 #include "shared/source/device_binary_format/elf/ocl_elf.h"
17 #include "shared/source/helpers/compiler_hw_info_config.h"
18 #include "shared/source/helpers/compiler_options_parser.h"
19 #include "shared/source/helpers/debug_helpers.h"
20 #include "shared/source/helpers/file_io.h"
21 #include "shared/source/helpers/hw_helper.h"
22 #include "shared/source/helpers/hw_info.h"
23 #include "shared/source/helpers/string.h"
24 #include "shared/source/helpers/validators.h"
25 #include "shared/source/os_interface/os_inc_base.h"
26 #include "shared/source/os_interface/os_library.h"
27 
28 #include "cif/common/cif_main.h"
29 #include "cif/helpers/error.h"
30 #include "cif/import/library_api.h"
31 #include "compiler_options.h"
32 #include "igfxfmid.h"
33 #include "ocl_igc_interface/code_type.h"
34 #include "ocl_igc_interface/fcl_ocl_device_ctx.h"
35 #include "ocl_igc_interface/igc_ocl_device_ctx.h"
36 #include "ocl_igc_interface/platform_helper.h"
37 
38 #include <algorithm>
39 #include <iomanip>
40 #include <iostream>
41 #include <list>
42 
43 #ifdef _WIN32
44 #include <direct.h>
45 #define MakeDirectory _mkdir
46 #define GetCurrentWorkingDirectory _getcwd
47 #else
48 #include <sys/stat.h>
49 #define MakeDirectory(dir) mkdir(dir, 0777)
50 #define GetCurrentWorkingDirectory getcwd
51 #endif
52 
53 namespace NEO {
54 
55 CIF::CIFMain *createMainNoSanitize(CIF::CreateCIFMainFunc_t createFunc);
56 
convertToPascalCase(const std::string & inString)57 std::string convertToPascalCase(const std::string &inString) {
58     std::string outString;
59     bool capitalize = true;
60 
61     for (unsigned int i = 0; i < inString.length(); i++) {
62         if (isalpha(inString[i]) && capitalize == true) {
63             outString += toupper(inString[i]);
64             capitalize = false;
65         } else if (inString[i] == '_') {
66             capitalize = true;
67         } else {
68             outString += inString[i];
69         }
70     }
71     return outString;
72 }
73 
74 OfflineCompiler::OfflineCompiler() = default;
~OfflineCompiler()75 OfflineCompiler::~OfflineCompiler() {
76     pBuildInfo.reset();
77     delete[] irBinary;
78     delete[] genBinary;
79     delete[] debugDataBinary;
80 }
81 
create(size_t numArgs,const std::vector<std::string> & allArgs,bool dumpFiles,int & retVal,OclocArgHelper * helper)82 OfflineCompiler *OfflineCompiler::create(size_t numArgs, const std::vector<std::string> &allArgs, bool dumpFiles, int &retVal, OclocArgHelper *helper) {
83     retVal = SUCCESS;
84     auto pOffCompiler = new OfflineCompiler();
85 
86     if (pOffCompiler) {
87         pOffCompiler->argHelper = helper;
88         retVal = pOffCompiler->initialize(numArgs, allArgs, dumpFiles);
89     }
90 
91     if (retVal != SUCCESS) {
92         delete pOffCompiler;
93         pOffCompiler = nullptr;
94     }
95 
96     return pOffCompiler;
97 }
98 
printQueryHelp(OclocArgHelper * helper)99 void printQueryHelp(OclocArgHelper *helper) {
100     helper->printf(OfflineCompiler::queryHelp.data());
101 }
102 
query(size_t numArgs,const std::vector<std::string> & allArgs,OclocArgHelper * helper)103 int OfflineCompiler::query(size_t numArgs, const std::vector<std::string> &allArgs, OclocArgHelper *helper) {
104     if (allArgs.size() != 3) {
105         helper->printf("Error: Invalid command line. Expected ocloc query <argument>");
106         return INVALID_COMMAND_LINE;
107     }
108 
109     auto retVal = SUCCESS;
110     auto &arg = allArgs[2];
111 
112     if (Queries::queryNeoRevision == arg) {
113         auto revision = NEO::getRevision();
114         helper->saveOutput(Queries::queryNeoRevision.data(), revision.c_str(), revision.size() + 1);
115     } else if (Queries::queryOCLDriverVersion == arg) {
116         auto driverVersion = NEO::getOclDriverVersion();
117         helper->saveOutput(Queries::queryOCLDriverVersion.data(), driverVersion.c_str(), driverVersion.size() + 1);
118     } else if ("--help" == arg) {
119         printQueryHelp(helper);
120     } else {
121         helper->printf("Error: Invalid command line. Uknown argument %s.", arg.c_str());
122         retVal = INVALID_COMMAND_LINE;
123     }
124 
125     return retVal;
126 }
127 
128 struct OfflineCompiler::buildInfo {
129     std::unique_ptr<CIF::Builtins::BufferLatest, CIF::RAII::ReleaseHelper<CIF::Builtins::BufferLatest>> fclOptions;
130     std::unique_ptr<CIF::Builtins::BufferLatest, CIF::RAII::ReleaseHelper<CIF::Builtins::BufferLatest>> fclInternalOptions;
131     std::unique_ptr<IGC::OclTranslationOutputTagOCL, CIF::RAII::ReleaseHelper<IGC::OclTranslationOutputTagOCL>> fclOutput;
132     IGC::CodeType::CodeType_t intermediateRepresentation;
133 };
134 
buildIrBinary()135 int OfflineCompiler::buildIrBinary() {
136     int retVal = SUCCESS;
137     UNRECOVERABLE_IF(fclDeviceCtx == nullptr);
138     pBuildInfo->intermediateRepresentation = useLlvmText ? IGC::CodeType::llvmLl
139                                                          : (useLlvmBc ? IGC::CodeType::llvmBc : preferredIntermediateRepresentation);
140 
141     //sourceCode.size() returns the number of characters without null terminated char
142     CIF::RAII::UPtr_t<CIF::Builtins::BufferLatest> fclSrc = nullptr;
143     pBuildInfo->fclOptions = CIF::Builtins::CreateConstBuffer(fclMain.get(), options.c_str(), options.size());
144     pBuildInfo->fclInternalOptions = CIF::Builtins::CreateConstBuffer(fclMain.get(), internalOptions.c_str(), internalOptions.size());
145     auto err = CIF::Builtins::CreateConstBuffer(fclMain.get(), nullptr, 0);
146 
147     auto srcType = IGC::CodeType::undefined;
148     std::vector<uint8_t> tempSrcStorage;
149     if (this->argHelper->hasHeaders()) {
150         srcType = IGC::CodeType::elf;
151 
152         NEO::Elf::ElfEncoder<> elfEncoder(true, true, 1U);
153         elfEncoder.getElfFileHeader().type = NEO::Elf::ET_OPENCL_SOURCE;
154         elfEncoder.appendSection(NEO::Elf::SHT_OPENCL_SOURCE, "CLMain", sourceCode);
155 
156         for (const auto &header : this->argHelper->getHeaders()) {
157             ArrayRef<const uint8_t> headerData(header.data, header.length);
158             ConstStringRef headerName = header.name;
159 
160             elfEncoder.appendSection(NEO::Elf::SHT_OPENCL_HEADER, headerName, headerData);
161         }
162         tempSrcStorage = elfEncoder.encode();
163         fclSrc = CIF::Builtins::CreateConstBuffer(fclMain.get(), tempSrcStorage.data(), tempSrcStorage.size());
164     } else {
165         srcType = IGC::CodeType::oclC;
166         fclSrc = CIF::Builtins::CreateConstBuffer(fclMain.get(), sourceCode.c_str(), sourceCode.size() + 1);
167     }
168 
169     auto fclTranslationCtx = fclDeviceCtx->CreateTranslationCtx(srcType, pBuildInfo->intermediateRepresentation, err.get());
170 
171     if (true == NEO::areNotNullptr(err->GetMemory<char>())) {
172         updateBuildLog(err->GetMemory<char>(), err->GetSizeRaw());
173         retVal = BUILD_PROGRAM_FAILURE;
174         return retVal;
175     }
176 
177     if (false == NEO::areNotNullptr(fclSrc.get(), pBuildInfo->fclOptions.get(), pBuildInfo->fclInternalOptions.get(),
178                                     fclTranslationCtx.get())) {
179         retVal = OUT_OF_HOST_MEMORY;
180         return retVal;
181     }
182 
183     pBuildInfo->fclOutput = fclTranslationCtx->Translate(fclSrc.get(), pBuildInfo->fclOptions.get(),
184                                                          pBuildInfo->fclInternalOptions.get(), nullptr, 0);
185 
186     if (pBuildInfo->fclOutput == nullptr) {
187         retVal = OUT_OF_HOST_MEMORY;
188         return retVal;
189     }
190 
191     UNRECOVERABLE_IF(pBuildInfo->fclOutput->GetBuildLog() == nullptr);
192     UNRECOVERABLE_IF(pBuildInfo->fclOutput->GetOutput() == nullptr);
193 
194     if (pBuildInfo->fclOutput->Successful() == false) {
195         updateBuildLog(pBuildInfo->fclOutput->GetBuildLog()->GetMemory<char>(), pBuildInfo->fclOutput->GetBuildLog()->GetSizeRaw());
196         retVal = BUILD_PROGRAM_FAILURE;
197         return retVal;
198     }
199 
200     storeBinary(irBinary, irBinarySize, pBuildInfo->fclOutput->GetOutput()->GetMemory<char>(), pBuildInfo->fclOutput->GetOutput()->GetSizeRaw());
201     isSpirV = pBuildInfo->intermediateRepresentation == IGC::CodeType::spirV;
202 
203     updateBuildLog(pBuildInfo->fclOutput->GetBuildLog()->GetMemory<char>(), pBuildInfo->fclOutput->GetBuildLog()->GetSizeRaw());
204 
205     return retVal;
206 }
207 
validateInputType(const std::string & input,bool isLlvm,bool isSpirv)208 std::string OfflineCompiler::validateInputType(const std::string &input, bool isLlvm, bool isSpirv) {
209     auto asBitcode = ArrayRef<const uint8_t>::fromAny(input.data(), input.size());
210     if (isSpirv) {
211         if (NEO::isSpirVBitcode(asBitcode)) {
212             return "";
213         }
214         return "Warning : file does not look like spirv bitcode (wrong magic numbers)";
215     }
216 
217     if (isLlvm) {
218         if (NEO::isLlvmBitcode(asBitcode)) {
219             return "";
220         }
221         return "Warning : file does not look like llvm bitcode (wrong magic numbers)";
222     }
223 
224     if (NEO::isSpirVBitcode(asBitcode)) {
225         return "Warning : file looks like spirv bitcode (based on magic numbers) - please make sure proper CLI flags are present";
226     }
227 
228     if (NEO::isLlvmBitcode(asBitcode)) {
229         return "Warning : file looks like llvm bitcode (based on magic numbers) - please make sure proper CLI flags are present";
230     }
231 
232     return "";
233 }
234 
buildSourceCode()235 int OfflineCompiler::buildSourceCode() {
236     int retVal = SUCCESS;
237 
238     do {
239         if (sourceCode.empty()) {
240             retVal = INVALID_PROGRAM;
241             break;
242         }
243         UNRECOVERABLE_IF(igcDeviceCtx == nullptr);
244         auto inputTypeWarnings = validateInputType(sourceCode, inputFileLlvm, inputFileSpirV);
245         this->argHelper->printf(inputTypeWarnings.c_str());
246 
247         CIF::RAII::UPtr_t<IGC::OclTranslationOutputTagOCL> igcOutput;
248         bool inputIsIntermediateRepresentation = inputFileLlvm || inputFileSpirV;
249         if (false == inputIsIntermediateRepresentation) {
250             retVal = buildIrBinary();
251             if (retVal != SUCCESS)
252                 break;
253 
254             auto igcTranslationCtx = igcDeviceCtx->CreateTranslationCtx(pBuildInfo->intermediateRepresentation, IGC::CodeType::oclGenBin);
255             igcOutput = igcTranslationCtx->Translate(pBuildInfo->fclOutput->GetOutput(), pBuildInfo->fclOptions.get(),
256                                                      pBuildInfo->fclInternalOptions.get(),
257                                                      nullptr, 0);
258 
259         } else {
260             storeBinary(irBinary, irBinarySize, sourceCode.c_str(), sourceCode.size());
261             isSpirV = inputFileSpirV;
262             auto igcSrc = CIF::Builtins::CreateConstBuffer(igcMain.get(), sourceCode.c_str(), sourceCode.size());
263             auto igcOptions = CIF::Builtins::CreateConstBuffer(igcMain.get(), options.c_str(), options.size());
264             auto igcInternalOptions = CIF::Builtins::CreateConstBuffer(igcMain.get(), internalOptions.c_str(), internalOptions.size());
265             auto igcTranslationCtx = igcDeviceCtx->CreateTranslationCtx(inputFileSpirV ? IGC::CodeType::spirV : IGC::CodeType::llvmBc, IGC::CodeType::oclGenBin);
266             igcOutput = igcTranslationCtx->Translate(igcSrc.get(), igcOptions.get(), igcInternalOptions.get(), nullptr, 0);
267         }
268         if (igcOutput == nullptr) {
269             retVal = OUT_OF_HOST_MEMORY;
270             break;
271         }
272         UNRECOVERABLE_IF(igcOutput->GetBuildLog() == nullptr);
273         UNRECOVERABLE_IF(igcOutput->GetOutput() == nullptr);
274         updateBuildLog(igcOutput->GetBuildLog()->GetMemory<char>(), igcOutput->GetBuildLog()->GetSizeRaw());
275 
276         if (igcOutput->GetOutput()->GetSizeRaw() != 0) {
277             storeBinary(genBinary, genBinarySize, igcOutput->GetOutput()->GetMemory<char>(), igcOutput->GetOutput()->GetSizeRaw());
278         }
279         if (igcOutput->GetDebugData()->GetSizeRaw() != 0) {
280             storeBinary(debugDataBinary, debugDataBinarySize, igcOutput->GetDebugData()->GetMemory<char>(), igcOutput->GetDebugData()->GetSizeRaw());
281         }
282         retVal = igcOutput->Successful() ? SUCCESS : BUILD_PROGRAM_FAILURE;
283     } while (0);
284 
285     return retVal;
286 }
287 
build()288 int OfflineCompiler::build() {
289     int retVal = SUCCESS;
290     if (isOnlySpirV()) {
291         retVal = buildIrBinary();
292     } else {
293         retVal = buildSourceCode();
294     }
295     generateElfBinary();
296     if (dumpFiles) {
297         writeOutAllFiles();
298     }
299 
300     return retVal;
301 }
302 
updateBuildLog(const char * pErrorString,const size_t errorStringSize)303 void OfflineCompiler::updateBuildLog(const char *pErrorString, const size_t errorStringSize) {
304     std::string errorString = (errorStringSize && pErrorString) ? std::string(pErrorString, pErrorString + errorStringSize) : "";
305     if (errorString[0] != '\0') {
306         if (buildLog.empty()) {
307             buildLog.assign(errorString.c_str());
308         } else {
309             buildLog.append("\n");
310             buildLog.append(errorString.c_str());
311         }
312     }
313 }
314 
getBuildLog()315 std::string &OfflineCompiler::getBuildLog() {
316     return buildLog;
317 }
318 
setFamilyType()319 void OfflineCompiler::setFamilyType() {
320     familyNameWithType.clear();
321     familyNameWithType.append(familyName[hwInfo.platform.eRenderCoreFamily]);
322     familyNameWithType.append(hwInfo.capabilityTable.platformType);
323 }
324 
initHardwareInfo(std::string deviceName)325 int OfflineCompiler::initHardwareInfo(std::string deviceName) {
326     int retVal = INVALID_DEVICE;
327 
328     if (deviceName.empty()) {
329         return retVal;
330     }
331 
332     if (argHelper->isFatbinary()) {
333         argHelper->setHwInfoForFatbinaryTarget(hwInfo);
334         setFamilyType();
335         retVal = SUCCESS;
336         return retVal;
337     }
338 
339     overridePlatformName(deviceName);
340     std::transform(deviceName.begin(), deviceName.end(), deviceName.begin(), ::tolower);
341     const char hexPrefix = 2;
342     std::string product("");
343 
344     auto numeration = argHelper->getMajorMinorRevision(deviceName);
345     if (!numeration.empty()) {
346         uint32_t productConfig = argHelper->getProductConfig(numeration);
347 
348         if (argHelper->getHwInfoForProductConfig(productConfig, hwInfo)) {
349             deviceConfig = static_cast<PRODUCT_CONFIG>(productConfig);
350             setFamilyType();
351             retVal = SUCCESS;
352             return retVal;
353         }
354         argHelper->printf("Could not determine target based on product config: %s\n", deviceName.c_str());
355         return retVal;
356     } else if (deviceName.find(".") != std::string::npos) {
357         argHelper->printf("Could not determine target based on product config: %s\n", deviceName.c_str());
358         return retVal;
359     }
360 
361     if (deviceName.substr(0, hexPrefix) == "0x" && std::all_of(deviceName.begin() + hexPrefix, deviceName.end(), (::isxdigit))) {
362         product = argHelper->returnProductNameForDevice(stoi(deviceName, 0, 16));
363         if (!product.empty()) {
364             argHelper->printf("Auto-detected target based on %s device id: %s\n", deviceName.c_str(), product.c_str());
365             deviceName = product;
366         } else {
367             argHelper->printf("Could not determine target based on device id: %s\n", deviceName.c_str());
368             return retVal;
369         }
370     }
371 
372     for (unsigned int productId = 0; productId < IGFX_MAX_PRODUCT; ++productId) {
373         if (hardwarePrefix[productId] && (0 == strcmp(deviceName.c_str(), hardwarePrefix[productId]))) {
374             if (hardwareInfoTable[productId]) {
375                 hwInfo = *hardwareInfoTable[productId];
376                 if (revisionId != -1) {
377                     hwInfo.platform.usRevId = revisionId;
378                 }
379 
380                 auto hwInfoConfig = defaultHardwareInfoConfigTable[hwInfo.platform.eProductFamily];
381                 setHwInfoValuesFromConfig(hwInfoConfig, hwInfo);
382                 hardwareInfoSetup[hwInfo.platform.eProductFamily](&hwInfo, true, hwInfoConfig);
383                 setFamilyType();
384                 retVal = SUCCESS;
385                 break;
386             }
387         }
388     }
389 
390     return retVal;
391 }
392 
getStringWithinDelimiters(const std::string & src)393 std::string OfflineCompiler::getStringWithinDelimiters(const std::string &src) {
394     size_t start = src.find("R\"===(");
395     size_t stop = src.find(")===\"");
396 
397     DEBUG_BREAK_IF(std::string::npos == start);
398     DEBUG_BREAK_IF(std::string::npos == stop);
399 
400     start += strlen("R\"===(");
401     size_t size = stop - start;
402 
403     std::string dst(src, start, size + 1);
404     dst[size] = '\0'; // put null char at the end
405 
406     return dst;
407 }
408 
initialize(size_t numArgs,const std::vector<std::string> & allArgs,bool dumpFiles)409 int OfflineCompiler::initialize(size_t numArgs, const std::vector<std::string> &allArgs, bool dumpFiles) {
410     this->dumpFiles = dumpFiles;
411     int retVal = SUCCESS;
412     const char *source = nullptr;
413     std::unique_ptr<char[]> sourceFromFile;
414     size_t sourceFromFileSize = 0;
415     this->pBuildInfo = std::make_unique<buildInfo>();
416     retVal = parseCommandLine(numArgs, allArgs);
417     if (showHelp) {
418         printUsage();
419         return retVal;
420     } else if (retVal != SUCCESS) {
421         return retVal;
422     }
423 
424     if (options.empty()) {
425         // try to read options from file if not provided by commandline
426         size_t ext_start = inputFile.find_last_of(".");
427         if (ext_start != std::string::npos) {
428             std::string oclocOptionsFileName = inputFile.substr(0, ext_start);
429             oclocOptionsFileName.append("_ocloc_options.txt");
430 
431             std::string oclocOptionsFromFile;
432             bool oclocOptionsRead = readOptionsFromFile(oclocOptionsFromFile, oclocOptionsFileName, argHelper);
433             if (oclocOptionsRead && !isQuiet()) {
434                 argHelper->printf("Building with ocloc options:\n%s\n", oclocOptionsFromFile.c_str());
435             }
436 
437             if (oclocOptionsRead) {
438                 std::istringstream iss(allArgs[0] + " " + oclocOptionsFromFile);
439                 std::vector<std::string> tokens{
440                     std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};
441 
442                 retVal = parseCommandLine(tokens.size(), tokens);
443                 if (retVal != SUCCESS) {
444                     return retVal;
445                 }
446             }
447 
448             std::string optionsFileName = inputFile.substr(0, ext_start);
449             optionsFileName.append("_options.txt");
450 
451             bool optionsRead = readOptionsFromFile(options, optionsFileName, argHelper);
452             if (optionsRead && !isQuiet()) {
453                 argHelper->printf("Building with options:\n%s\n", options.c_str());
454             }
455 
456             std::string internalOptionsFileName = inputFile.substr(0, ext_start);
457             internalOptionsFileName.append("_internal_options.txt");
458 
459             std::string internalOptionsFromFile;
460             bool internalOptionsRead = readOptionsFromFile(internalOptionsFromFile, internalOptionsFileName, argHelper);
461             if (internalOptionsRead && !isQuiet()) {
462                 argHelper->printf("Building with internal options:\n%s\n", internalOptionsFromFile.c_str());
463             }
464             CompilerOptions::concatenateAppend(internalOptions, internalOptionsFromFile);
465         }
466     }
467 
468     retVal = deviceName.empty() ? SUCCESS : initHardwareInfo(deviceName.c_str());
469     if (retVal != SUCCESS) {
470         argHelper->printf("Error: Cannot get HW Info for device %s.\n", deviceName.c_str());
471         return retVal;
472     }
473 
474     if (CompilerOptions::contains(options, CompilerOptions::generateDebugInfo.str())) {
475         if (hwInfo.platform.eRenderCoreFamily >= IGFX_GEN9_CORE) {
476             internalOptions = CompilerOptions::concatenate(internalOptions, CompilerOptions::debugKernelEnable);
477         }
478     }
479 
480     if (deviceName.empty()) {
481         internalOptions = CompilerOptions::concatenate("-ocl-version=300 -cl-ext=-all,+cl_khr_3d_image_writes", internalOptions);
482         CompilerOptions::concatenateAppend(internalOptions, CompilerOptions::enableImageSupport);
483     } else {
484         appendExtensionsToInternalOptions(hwInfo, options, internalOptions);
485         appendExtraInternalOptions(hwInfo, internalOptions);
486     }
487 
488     parseDebugSettings();
489 
490     // set up the device inside the program
491     sourceFromFile = argHelper->loadDataFromFile(inputFile, sourceFromFileSize);
492     if (sourceFromFileSize == 0) {
493         retVal = INVALID_FILE;
494         return retVal;
495     }
496 
497     if (inputFileLlvm || inputFileSpirV) {
498         // use the binary input "as is"
499         sourceCode.assign(sourceFromFile.get(), sourceFromFileSize);
500     } else {
501         // for text input, we also accept files used as runtime builtins
502         source = strstr((const char *)sourceFromFile.get(), "R\"===(");
503         sourceCode = (source != nullptr) ? getStringWithinDelimiters(sourceFromFile.get()) : sourceFromFile.get();
504     }
505 
506     if ((inputFileSpirV == false) && (inputFileLlvm == false)) {
507         auto fclLibFile = OsLibrary::load(Os::frontEndDllName);
508 
509         if (fclLibFile == nullptr) {
510             argHelper->printf("Error: Failed to load %s\n", Os::frontEndDllName);
511             return OUT_OF_HOST_MEMORY;
512         }
513 
514         this->fclLib.reset(fclLibFile);
515         if (this->fclLib == nullptr) {
516             return OUT_OF_HOST_MEMORY;
517         }
518 
519         auto fclCreateMain = reinterpret_cast<CIF::CreateCIFMainFunc_t>(this->fclLib->getProcAddress(CIF::CreateCIFMainFuncName));
520         if (fclCreateMain == nullptr) {
521             return OUT_OF_HOST_MEMORY;
522         }
523 
524         this->fclMain = CIF::RAII::UPtr(createMainNoSanitize(fclCreateMain));
525         if (this->fclMain == nullptr) {
526             return OUT_OF_HOST_MEMORY;
527         }
528 
529         if (false == this->fclMain->IsCompatible<IGC::FclOclDeviceCtx>()) {
530             argHelper->printf("Incompatible interface in FCL : %s\n", CIF::InterfaceIdCoder::Dec(this->fclMain->FindIncompatible<IGC::FclOclDeviceCtx>()).c_str());
531             DEBUG_BREAK_IF(true);
532             return OUT_OF_HOST_MEMORY;
533         }
534 
535         this->fclDeviceCtx = this->fclMain->CreateInterface<IGC::FclOclDeviceCtxTagOCL>();
536         if (this->fclDeviceCtx == nullptr) {
537             return OUT_OF_HOST_MEMORY;
538         }
539 
540         fclDeviceCtx->SetOclApiVersion(hwInfo.capabilityTable.clVersionSupport * 10);
541         preferredIntermediateRepresentation = fclDeviceCtx->GetPreferredIntermediateRepresentation();
542         if (this->fclDeviceCtx->GetUnderlyingVersion() > 4U) {
543             auto igcPlatform = fclDeviceCtx->GetPlatformHandle();
544             if (nullptr == igcPlatform) {
545                 return OUT_OF_HOST_MEMORY;
546             }
547             IGC::PlatformHelper::PopulateInterfaceWith(*igcPlatform, hwInfo.platform);
548         }
549     } else {
550         if (!isQuiet()) {
551             argHelper->printf("Compilation from IR - skipping loading of FCL\n");
552         }
553         preferredIntermediateRepresentation = IGC::CodeType::spirV;
554     }
555 
556     this->igcLib.reset(OsLibrary::load(Os::igcDllName));
557     if (this->igcLib == nullptr) {
558         return OUT_OF_HOST_MEMORY;
559     }
560 
561     auto igcCreateMain = reinterpret_cast<CIF::CreateCIFMainFunc_t>(this->igcLib->getProcAddress(CIF::CreateCIFMainFuncName));
562     if (igcCreateMain == nullptr) {
563         return OUT_OF_HOST_MEMORY;
564     }
565 
566     this->igcMain = CIF::RAII::UPtr(createMainNoSanitize(igcCreateMain));
567     if (this->igcMain == nullptr) {
568         return OUT_OF_HOST_MEMORY;
569     }
570 
571     std::vector<CIF::InterfaceId_t> interfacesToIgnore = {IGC::OclGenBinaryBase::GetInterfaceId()};
572     if (false == this->igcMain->IsCompatible<IGC::IgcOclDeviceCtx>(&interfacesToIgnore)) {
573         argHelper->printf("Incompatible interface in IGC : %s\n", CIF::InterfaceIdCoder::Dec(this->igcMain->FindIncompatible<IGC::IgcOclDeviceCtx>(&interfacesToIgnore)).c_str());
574         DEBUG_BREAK_IF(true);
575         return OUT_OF_HOST_MEMORY;
576     }
577 
578     CIF::Version_t verMin = 0, verMax = 0;
579     if (false == this->igcMain->FindSupportedVersions<IGC::IgcOclDeviceCtx>(IGC::OclGenBinaryBase::GetInterfaceId(), verMin, verMax)) {
580         argHelper->printf("Patchtoken interface is missing");
581         return OUT_OF_HOST_MEMORY;
582     }
583 
584     this->igcDeviceCtx = this->igcMain->CreateInterface<IGC::IgcOclDeviceCtxTagOCL>();
585     if (this->igcDeviceCtx == nullptr) {
586         return OUT_OF_HOST_MEMORY;
587     }
588     this->igcDeviceCtx->SetProfilingTimerResolution(static_cast<float>(hwInfo.capabilityTable.defaultProfilingTimerResolution));
589     auto igcPlatform = this->igcDeviceCtx->GetPlatformHandle();
590     auto igcGtSystemInfo = this->igcDeviceCtx->GetGTSystemInfoHandle();
591     auto igcFeWa = this->igcDeviceCtx->GetIgcFeaturesAndWorkaroundsHandle();
592     if ((igcPlatform == nullptr) || (igcGtSystemInfo == nullptr) || (igcFeWa == nullptr)) {
593         return OUT_OF_HOST_MEMORY;
594     }
595     IGC::PlatformHelper::PopulateInterfaceWith(*igcPlatform.get(), hwInfo.platform);
596     IGC::GtSysInfoHelper::PopulateInterfaceWith(*igcGtSystemInfo.get(), hwInfo.gtSystemInfo);
597     // populate with features
598 
599     igcFeWa.get()->SetFtrDesktop(hwInfo.featureTable.flags.ftrDesktop);
600     igcFeWa.get()->SetFtrChannelSwizzlingXOREnabled(hwInfo.featureTable.flags.ftrChannelSwizzlingXOREnabled);
601 
602     igcFeWa.get()->SetFtrGtBigDie(hwInfo.featureTable.flags.ftrGtBigDie);
603     igcFeWa.get()->SetFtrGtMediumDie(hwInfo.featureTable.flags.ftrGtMediumDie);
604     igcFeWa.get()->SetFtrGtSmallDie(hwInfo.featureTable.flags.ftrGtSmallDie);
605 
606     igcFeWa.get()->SetFtrGT1(hwInfo.featureTable.flags.ftrGT1);
607     igcFeWa.get()->SetFtrGT1_5(hwInfo.featureTable.flags.ftrGT1_5);
608     igcFeWa.get()->SetFtrGT2(hwInfo.featureTable.flags.ftrGT2);
609     igcFeWa.get()->SetFtrGT3(hwInfo.featureTable.flags.ftrGT3);
610     igcFeWa.get()->SetFtrGT4(hwInfo.featureTable.flags.ftrGT4);
611 
612     igcFeWa.get()->SetFtrIVBM0M1Platform(hwInfo.featureTable.flags.ftrIVBM0M1Platform);
613     igcFeWa.get()->SetFtrGTL(hwInfo.featureTable.flags.ftrGT1);
614     igcFeWa.get()->SetFtrGTM(hwInfo.featureTable.flags.ftrGT2);
615     igcFeWa.get()->SetFtrGTH(hwInfo.featureTable.flags.ftrGT3);
616 
617     igcFeWa.get()->SetFtrSGTPVSKUStrapPresent(hwInfo.featureTable.flags.ftrSGTPVSKUStrapPresent);
618     igcFeWa.get()->SetFtrGTA(hwInfo.featureTable.flags.ftrGTA);
619     igcFeWa.get()->SetFtrGTC(hwInfo.featureTable.flags.ftrGTC);
620     igcFeWa.get()->SetFtrGTX(hwInfo.featureTable.flags.ftrGTX);
621     igcFeWa.get()->SetFtr5Slice(hwInfo.featureTable.flags.ftr5Slice);
622 
623     auto compilerHwInfoConfig = CompilerHwInfoConfig::get(hwInfo.platform.eProductFamily);
624     if (compilerHwInfoConfig) {
625         igcFeWa.get()->SetFtrGpGpuMidThreadLevelPreempt(compilerHwInfoConfig->isMidThreadPreemptionSupported(hwInfo));
626     }
627     igcFeWa.get()->SetFtrIoMmuPageFaulting(hwInfo.featureTable.flags.ftrIoMmuPageFaulting);
628     igcFeWa.get()->SetFtrWddm2Svm(hwInfo.featureTable.flags.ftrWddm2Svm);
629     igcFeWa.get()->SetFtrPooledEuEnabled(hwInfo.featureTable.flags.ftrPooledEuEnabled);
630 
631     igcFeWa.get()->SetFtrResourceStreamer(hwInfo.featureTable.flags.ftrResourceStreamer);
632 
633     return retVal;
634 }
635 
parseCommandLine(size_t numArgs,const std::vector<std::string> & argv)636 int OfflineCompiler::parseCommandLine(size_t numArgs, const std::vector<std::string> &argv) {
637     int retVal = SUCCESS;
638     bool compile32 = false;
639     bool compile64 = false;
640 
641     if (numArgs < 2) {
642         showHelp = true;
643         return INVALID_COMMAND_LINE;
644     }
645 
646     for (uint32_t argIndex = 1; argIndex < numArgs; argIndex++) {
647         const auto &currArg = argv[argIndex];
648         const bool hasMoreArgs = (argIndex + 1 < numArgs);
649         if ("compile" == currArg) {
650             //skip it
651         } else if (("-file" == currArg) && hasMoreArgs) {
652             inputFile = argv[argIndex + 1];
653             argIndex++;
654         } else if (("-output" == currArg) && hasMoreArgs) {
655             outputFile = argv[argIndex + 1];
656             argIndex++;
657         } else if ((CompilerOptions::arch32bit == currArg) || ("-32" == currArg)) {
658             compile32 = true;
659             CompilerOptions::concatenateAppend(internalOptions, CompilerOptions::arch32bit);
660         } else if ((CompilerOptions::arch64bit == currArg) || ("-64" == currArg)) {
661             compile64 = true;
662             CompilerOptions::concatenateAppend(internalOptions, CompilerOptions::arch64bit);
663         } else if (CompilerOptions::greaterThan4gbBuffersRequired == currArg) {
664             CompilerOptions::concatenateAppend(internalOptions, CompilerOptions::greaterThan4gbBuffersRequired);
665         } else if (("-device" == currArg) && hasMoreArgs) {
666             deviceName = argv[argIndex + 1];
667             argIndex++;
668         } else if ("-llvm_text" == currArg) {
669             useLlvmText = true;
670         } else if ("-llvm_bc" == currArg) {
671             useLlvmBc = true;
672         } else if ("-llvm_input" == currArg) {
673             inputFileLlvm = true;
674         } else if ("-spirv_input" == currArg) {
675             inputFileSpirV = true;
676         } else if ("-cpp_file" == currArg) {
677             useCppFile = true;
678         } else if (("-options" == currArg) && hasMoreArgs) {
679             options = argv[argIndex + 1];
680             argIndex++;
681         } else if (("-internal_options" == currArg) && hasMoreArgs) {
682             CompilerOptions::concatenateAppend(internalOptions, argv[argIndex + 1]);
683             argIndex++;
684         } else if ("-options_name" == currArg) {
685             useOptionsSuffix = true;
686         } else if ("-force_stos_opt" == currArg) {
687             forceStatelessToStatefulOptimization = true;
688         } else if (("-out_dir" == currArg) && hasMoreArgs) {
689             outputDirectory = argv[argIndex + 1];
690             argIndex++;
691         } else if ("-q" == currArg) {
692             argHelper->getPrinterRef() = MessagePrinter(true);
693             quiet = true;
694         } else if ("-spv_only" == currArg) {
695             onlySpirV = true;
696         } else if ("-output_no_suffix" == currArg) {
697             outputNoSuffix = true;
698         } else if ("--help" == currArg) {
699             showHelp = true;
700             return SUCCESS;
701         } else if (("-revision_id" == currArg) && hasMoreArgs) {
702             revisionId = std::stoi(argv[argIndex + 1], nullptr, 0);
703             argIndex++;
704         } else if ("-exclude_ir" == currArg) {
705             excludeIr = true;
706         } else {
707             argHelper->printf("Invalid option (arg %d): %s\n", argIndex, argv[argIndex].c_str());
708             retVal = INVALID_COMMAND_LINE;
709             break;
710         }
711     }
712 
713     unifyExcludeIrFlags();
714 
715     if (DebugManager.flags.OverrideRevision.get() != -1) {
716         revisionId = static_cast<unsigned short>(DebugManager.flags.OverrideRevision.get());
717     }
718 
719     if (retVal == SUCCESS) {
720         if (compile32 && compile64) {
721             argHelper->printf("Error: Cannot compile for 32-bit and 64-bit, please choose one.\n");
722             retVal |= INVALID_COMMAND_LINE;
723         }
724 
725         if (deviceName.empty() && (false == onlySpirV)) {
726             argHelper->printf("Error: Device name missing.\n");
727             retVal = INVALID_COMMAND_LINE;
728         }
729 
730         if (inputFile.empty()) {
731             argHelper->printf("Error: Input file name missing.\n");
732             retVal = INVALID_COMMAND_LINE;
733         } else if (!argHelper->fileExists(inputFile)) {
734             argHelper->printf("Error: Input file %s missing.\n", inputFile.c_str());
735             retVal = INVALID_FILE;
736         }
737     }
738 
739     return retVal;
740 }
741 
unifyExcludeIrFlags()742 void OfflineCompiler::unifyExcludeIrFlags() {
743     const auto excludeIrFromZebin{internalOptions.find(CompilerOptions::excludeIrFromZebin.data()) != std::string::npos};
744     if (!excludeIr && excludeIrFromZebin) {
745         excludeIr = true;
746     } else if (excludeIr && !excludeIrFromZebin) {
747         const std::string prefix{"-ze"};
748         CompilerOptions::concatenateAppend(internalOptions, prefix + CompilerOptions::excludeIrFromZebin.data());
749     }
750 }
751 
setStatelessToStatefullBufferOffsetFlag()752 void OfflineCompiler::setStatelessToStatefullBufferOffsetFlag() {
753     bool isStatelessToStatefulBufferOffsetSupported = true;
754     if (!deviceName.empty()) {
755         const auto &compilerHwInfoConfig = *CompilerHwInfoConfig::get(hwInfo.platform.eProductFamily);
756         isStatelessToStatefulBufferOffsetSupported = compilerHwInfoConfig.isStatelessToStatefulBufferOffsetSupported();
757     }
758     if (DebugManager.flags.EnableStatelessToStatefulBufferOffsetOpt.get() != -1) {
759         isStatelessToStatefulBufferOffsetSupported = DebugManager.flags.EnableStatelessToStatefulBufferOffsetOpt.get() != 0;
760     }
761     if (isStatelessToStatefulBufferOffsetSupported) {
762         CompilerOptions::concatenateAppend(internalOptions, CompilerOptions::hasBufferOffsetArg);
763     }
764 }
765 
appendExtraInternalOptions(const HardwareInfo & hwInfo,std::string & internalOptions)766 void OfflineCompiler::appendExtraInternalOptions(const HardwareInfo &hwInfo, std::string &internalOptions) {
767     const auto &compilerHwInfoConfig = *CompilerHwInfoConfig::get(hwInfo.platform.eProductFamily);
768     auto sharedSystemMemCapabilitiesSupported = (hwInfo.capabilityTable.sharedSystemMemCapabilities > 0);
769     if (sharedSystemMemCapabilitiesSupported && !forceStatelessToStatefulOptimization) {
770         CompilerOptions::concatenateAppend(internalOptions, CompilerOptions::greaterThan4gbBuffersRequired);
771     }
772     if (compilerHwInfoConfig.isForceEmuInt32DivRemSPRequired()) {
773         CompilerOptions::concatenateAppend(internalOptions, CompilerOptions::forceEmuInt32DivRemSP);
774     }
775 }
776 
parseDebugSettings()777 void OfflineCompiler::parseDebugSettings() {
778     setStatelessToStatefullBufferOffsetFlag();
779 }
780 
parseBinAsCharArray(uint8_t * binary,size_t size,std::string & fileName)781 std::string OfflineCompiler::parseBinAsCharArray(uint8_t *binary, size_t size, std::string &fileName) {
782     std::string builtinName = convertToPascalCase(fileName);
783     std::ostringstream out;
784 
785     // Convert binary to cpp
786     out << "#include <cstddef>\n";
787     out << "#include <cstdint>\n\n";
788     out << "size_t " << builtinName << "BinarySize_" << familyNameWithType << " = " << size << ";\n";
789     out << "uint32_t " << builtinName << "Binary_" << familyNameWithType << "[" << (size + 3) / 4 << "] = {"
790         << std::endl
791         << "    ";
792 
793     uint32_t *binaryUint = (uint32_t *)binary;
794     for (size_t i = 0; i < (size + 3) / 4; i++) {
795         if (i != 0) {
796             out << ", ";
797             if (i % 8 == 0) {
798                 out << std::endl
799                     << "    ";
800             }
801         }
802         if (i < size / 4) {
803             out << "0x" << std::hex << std::setw(8) << std::setfill('0') << binaryUint[i];
804         } else {
805             uint32_t lastBytes = size & 0x3;
806             uint32_t lastUint = 0;
807             uint8_t *pLastUint = (uint8_t *)&lastUint;
808             for (uint32_t j = 0; j < lastBytes; j++) {
809                 pLastUint[sizeof(uint32_t) - 1 - j] = binary[i * 4 + j];
810             }
811             out << "0x" << std::hex << std::setw(8) << std::setfill('0') << lastUint;
812         }
813     }
814     out << "};" << std::endl;
815 
816     out << std::endl
817         << "#include \"shared/source/built_ins/registry/built_ins_registry.h\"\n"
818         << std::endl;
819     out << "namespace NEO {" << std::endl;
820     out << "static RegisterEmbeddedResource register" << builtinName << "Bin(" << std::endl;
821     out << "    \"" << familyNameWithType << "_0_" << fileName.c_str() << ".builtin_kernel.bin\"," << std::endl;
822     out << "    (const char *)" << builtinName << "Binary_" << familyNameWithType << "," << std::endl;
823     out << "    " << builtinName << "BinarySize_" << familyNameWithType << ");" << std::endl;
824     out << "}" << std::endl;
825 
826     return out.str();
827 }
828 
getFileNameTrunk(std::string & filePath)829 std::string OfflineCompiler::getFileNameTrunk(std::string &filePath) {
830     size_t slashPos = filePath.find_last_of("\\/", filePath.size()) + 1;
831     size_t extPos = filePath.find_last_of(".", filePath.size());
832     if (extPos == std::string::npos) {
833         extPos = filePath.size();
834     }
835 
836     std::string fileTrunk = filePath.substr(slashPos, (extPos - slashPos));
837 
838     return fileTrunk;
839 }
840 
getDevicesTypes()841 std::string getDevicesTypes() {
842     std::list<std::string> prefixes;
843     for (int j = 0; j < IGFX_MAX_PRODUCT; j++) {
844         if (hardwarePrefix[j] == nullptr)
845             continue;
846         prefixes.push_back(hardwarePrefix[j]);
847     }
848 
849     std::ostringstream os;
850     for (auto it = prefixes.begin(); it != prefixes.end(); it++) {
851         if (it != prefixes.begin())
852             os << ", ";
853         os << *it;
854     }
855 
856     return os.str();
857 }
858 
getDevicesFamilies()859 std::string getDevicesFamilies() {
860     std::list<std::string> prefixes;
861     for (unsigned int i = 0; i < IGFX_MAX_CORE; ++i) {
862         if (familyName[i] == nullptr)
863             continue;
864         prefixes.push_back(familyName[i]);
865     }
866 
867     std::ostringstream os;
868     for (auto it = prefixes.begin(); it != prefixes.end(); it++) {
869         if (it != prefixes.begin())
870             os << ", ";
871         os << *it;
872     }
873 
874     return os.str();
875 }
876 
getDevicesConfigs()877 std::string OfflineCompiler::getDevicesConfigs() {
878     std::list<std::string> configNum;
879     auto allSupportedConfigs = argHelper->getAllSupportedProductConfigs();
880 
881     for (auto &config : allSupportedConfigs) {
882         auto numeration = argHelper->parseProductConfigFromValue(config);
883         configNum.push_back(numeration);
884     }
885 
886     std::ostringstream os;
887     for (auto it = configNum.begin(); it != configNum.end(); it++) {
888         if (it != configNum.begin())
889             os << ", ";
890         os << *it;
891     }
892 
893     return os.str();
894 }
895 
printUsage()896 void OfflineCompiler::printUsage() {
897     argHelper->printf(R"===(Compiles input file to Intel Compute GPU device binary (*.bin).
898 Additionally, outputs intermediate representation (e.g. spirV).
899 Different input and intermediate file formats are available.
900 
901 Usage: ocloc [compile] -file <filename> -device <device_type> [-output <filename>] [-out_dir <output_dir>] [-options <options>] [-32|-64] [-internal_options <options>] [-llvm_text|-llvm_input|-spirv_input] [-options_name] [-q] [-cpp_file] [-output_no_suffix] [--help]
902 
903   -file <filename>              The input file to be compiled
904                                 (by default input source format is
905                                 OpenCL C kernel language).
906 
907   -device <device_type>         Target device.
908                                 <device_type> can be: %s, %s or hexadecimal value with 0x prefix - can be single or multiple target devices.
909                                 The <major>[<minor>[.<revision>]] numbers:
910                                 <major> - family of graphics products,
911                                 <minor> - can be omitted, then ocloc will
912                                 compile for all of the <major> matching devices.
913                                 <revision> - can be omitted, then ocloc will
914                                 compile for all of the <major>.<minor> matching
915                                 devices.
916                                 The hexadecimal value represents device ID.
917                                 If such value is provided, ocloc will try to
918                                 match it with corresponding device type.
919                                 For example, 0xFF20 device ID will be translated
920                                 to tgllp.
921                                 If multiple target devices are provided, ocloc
922                                 will compile for each of these targets and will
923                                 create a fatbinary archive that contains all of
924                                 device binaries produced this way.
925                                 Supported -device patterns examples:
926                                 -device 0xFF20   ; will compile 1 target (tgllp)
927                                 -device 12.0.7   ; will compile 1 target (dg1)
928                                 -device 11       ; will compile the architecture
929                                                    (gen11)
930                                 -device 9.0,11.0 ; will compile 2 targets
931                                                    (skl & icllp)
932                                 -device 9.0-11.0 ; will compile all targets
933                                                    in range (inclusive)
934                                 -device 9.0-     ; will compile all targets
935                                                    newer/same as provided
936                                 -device -9.0     ; will compile all targets
937                                                    older/same as provided
938                                 -device *        ; will compile all targets
939                                                    known to ocloc
940 
941                                 Deprecated notation that is still supported:
942                                 <device_type> can be: %s
943                                 - can be single or multiple target devices.
944                                 Supported -device patterns examples:
945                                 -device skl        ; will compile 1 target
946                                 -device skl,icllp  ; will compile 2 targets
947                                 -device skl-icllp  ; will compile all targets
948                                                      in range (inclusive)
949                                 -device skl-       ; will compile all targets
950                                                      newer/same as provided
951                                 -device -skl       ; will compile all targets
952                                                      older/same as provided
953                                 -device gen9       ; will compile all targets
954                                                      matching the same gen
955                                 -device gen9-gen11 ; will compile all targets
956                                                      in range (inclusive)
957                                 -device gen9-      ; will compile all targets
958                                                      newer/same as provided
959                                 -device -gen9      ; will compile all targets
960                                                      older/same as provided
961 
962   -output <filename>            Optional output file base name.
963                                 Default is input file's base name.
964                                 This base name will be used for all output
965                                 files. Proper sufixes (describing file formats)
966                                 will be added automatically.
967 
968   -out_dir <output_dir>         Optional output directory.
969                                 Default is current working directory.
970 
971   -options <options>            Optional OpenCL C compilation options
972                                 as defined by OpenCL specification.
973                                 Special options for Vector Compute:
974                                 -vc-codegen <vc options> compile from SPIRV
975                                 -cmc <cm-options> compile from CM sources
976 
977   -32                           Forces target architecture to 32-bit pointers.
978                                 Default pointer size is inherited from
979                                 ocloc's pointer size.
980                                 This option is exclusive with -64.
981 
982   -64                           Forces target architecture to 64-bit pointers.
983                                 Default pointer size is inherited from
984                                 ocloc's pointer size.
985                                 This option is exclusive with -32.
986 
987   -internal_options <options>   Optional compiler internal options
988                                 as defined by compilers used underneath.
989                                 Check intel-graphics-compiler (IGC) project
990                                 for details on available internal options.
991                                 You also may provide explicit --help to inquire
992                                 information about option, mentioned in -options
993 
994   -llvm_text                    Forces intermediate representation (IR) format
995                                 to human-readable LLVM IR (.ll).
996                                 This option affects only output files
997                                 and should not be used in combination with
998                                 '-llvm_input' option.
999                                 Default IR is spirV.
1000                                 This option is exclusive with -spirv_input.
1001                                 This option is exclusive with -llvm_input.
1002 
1003   -llvm_input                   Indicates that input file is an llvm binary.
1004                                 Default is OpenCL C kernel language.
1005                                 This option is exclusive with -spirv_input.
1006                                 This option is exclusive with -llvm_text.
1007 
1008   -spirv_input                  Indicates that input file is a spirV binary.
1009                                 Default is OpenCL C kernel language format.
1010                                 This option is exclusive with -llvm_input.
1011                                 This option is exclusive with -llvm_text.
1012 
1013   -options_name                 Will add suffix to output files.
1014                                 This suffix will be generated based on input
1015                                 options (useful when rebuilding with different
1016                                 set of options so that results won't get
1017                                 overwritten).
1018                                 This suffix is added always as the last part
1019                                 of the filename (even after file's extension).
1020                                 It does not affect '--output' parameter and can
1021                                 be used along with it ('--output' parameter
1022                                 defines the base name - i.e. prefix).
1023 
1024   -force_stos_opt               Will forcibly enable stateless to stateful optimization,
1025                                 i.e. skip "-cl-intel-greater-than-4GB-buffer-required".
1026 
1027   -q                            Will silence most of output messages.
1028 
1029   -spv_only                     Will generate only spirV file.
1030 
1031   -cpp_file                     Will generate c++ file with C-array
1032                                 containing Intel Compute device binary.
1033 
1034   -output_no_suffix             Prevents ocloc from adding family name suffix.
1035 
1036   --help                        Print this usage message.
1037 
1038   -revision_id <revision_id>    Target stepping. Can be decimal or hexadecimal value.
1039 
1040   -exclude_ir                   Excludes IR from the output binary file.
1041 
1042 Examples :
1043   Compile file to Intel Compute GPU device binary (out = source_file_Gen9core.bin)
1044     ocloc -file source_file.cl -device skl
1045 )===",
1046                       getDevicesConfigs().c_str(),
1047                       NEO::getDevicesFamilies().c_str(),
1048                       NEO::getDevicesTypes().c_str());
1049 }
1050 
storeBinary(char * & pDst,size_t & dstSize,const void * pSrc,const size_t srcSize)1051 void OfflineCompiler::storeBinary(
1052     char *&pDst,
1053     size_t &dstSize,
1054     const void *pSrc,
1055     const size_t srcSize) {
1056     dstSize = 0;
1057 
1058     DEBUG_BREAK_IF(!(pSrc && srcSize > 0));
1059 
1060     delete[] pDst;
1061     pDst = new char[srcSize];
1062 
1063     dstSize = static_cast<uint32_t>(srcSize);
1064     memcpy_s(pDst, dstSize, pSrc, srcSize);
1065 }
1066 
generateElfBinary()1067 bool OfflineCompiler::generateElfBinary() {
1068     if (!genBinary || !genBinarySize) {
1069         return false;
1070     }
1071 
1072     // return "as is" if zebin format
1073     if (isDeviceBinaryFormat<DeviceBinaryFormat::Zebin>(ArrayRef<uint8_t>(reinterpret_cast<uint8_t *>(genBinary), genBinarySize))) {
1074         this->elfBinary = std::vector<uint8_t>(genBinary, genBinary + genBinarySize);
1075         return true;
1076     }
1077 
1078     SingleDeviceBinary binary = {};
1079     binary.buildOptions = this->options;
1080     binary.intermediateRepresentation = ArrayRef<const uint8_t>(reinterpret_cast<const uint8_t *>(this->irBinary), this->irBinarySize);
1081     binary.deviceBinary = ArrayRef<const uint8_t>(reinterpret_cast<const uint8_t *>(this->genBinary), this->genBinarySize);
1082     binary.debugData = ArrayRef<const uint8_t>(reinterpret_cast<const uint8_t *>(this->debugDataBinary), this->debugDataBinarySize);
1083     std::string packErrors;
1084     std::string packWarnings;
1085 
1086     using namespace NEO::Elf;
1087     ElfEncoder<EI_CLASS_64> ElfEncoder;
1088     ElfEncoder.getElfFileHeader().type = ET_OPENCL_EXECUTABLE;
1089     if (binary.buildOptions.empty() == false) {
1090         ElfEncoder.appendSection(SHT_OPENCL_OPTIONS, SectionNamesOpenCl::buildOptions,
1091                                  ArrayRef<const uint8_t>(reinterpret_cast<const uint8_t *>(binary.buildOptions.data()), binary.buildOptions.size()));
1092     }
1093 
1094     if (!binary.intermediateRepresentation.empty() && !excludeIr) {
1095         if (isSpirV) {
1096             ElfEncoder.appendSection(SHT_OPENCL_SPIRV, SectionNamesOpenCl::spirvObject, binary.intermediateRepresentation);
1097         } else {
1098             ElfEncoder.appendSection(SHT_OPENCL_LLVM_BINARY, SectionNamesOpenCl::llvmObject, binary.intermediateRepresentation);
1099         }
1100     }
1101 
1102     if (binary.debugData.empty() == false) {
1103         ElfEncoder.appendSection(SHT_OPENCL_DEV_DEBUG, SectionNamesOpenCl::deviceDebug, binary.debugData);
1104     }
1105 
1106     if (binary.deviceBinary.empty() == false) {
1107         ElfEncoder.appendSection(SHT_OPENCL_DEV_BINARY, SectionNamesOpenCl::deviceBinary, binary.deviceBinary);
1108     }
1109 
1110     this->elfBinary = ElfEncoder.encode();
1111 
1112     return true;
1113 }
1114 
writeOutAllFiles()1115 void OfflineCompiler::writeOutAllFiles() {
1116     std::string fileBase;
1117     std::string fileTrunk = getFileNameTrunk(inputFile);
1118     if (outputNoSuffix) {
1119         if (outputFile.empty()) {
1120             fileBase = fileTrunk;
1121         } else {
1122             fileBase = outputFile;
1123         }
1124     } else {
1125         if (outputFile.empty()) {
1126             fileBase = fileTrunk + "_" + familyNameWithType;
1127         } else {
1128             fileBase = outputFile + "_" + familyNameWithType;
1129         }
1130     }
1131 
1132     if (outputDirectory != "") {
1133         std::list<std::string> dirList;
1134         std::string tmp = outputDirectory;
1135         size_t pos = outputDirectory.size() + 1;
1136 
1137         do {
1138             dirList.push_back(tmp);
1139             pos = tmp.find_last_of("/\\", pos);
1140             tmp = tmp.substr(0, pos);
1141         } while (pos != std::string::npos);
1142 
1143         while (!dirList.empty()) {
1144             MakeDirectory(dirList.back().c_str());
1145             dirList.pop_back();
1146         }
1147     }
1148 
1149     if (irBinary && !inputFileSpirV) {
1150         std::string irOutputFileName = generateFilePathForIr(fileBase) + generateOptsSuffix();
1151 
1152         argHelper->saveOutput(irOutputFileName, irBinary, irBinarySize);
1153     }
1154 
1155     if (genBinary) {
1156         std::string genOutputFile = generateFilePath(outputDirectory, fileBase, ".gen") + generateOptsSuffix();
1157 
1158         argHelper->saveOutput(genOutputFile, genBinary, genBinarySize);
1159 
1160         if (useCppFile) {
1161             std::string cppOutputFile = generateFilePath(outputDirectory, fileBase, ".cpp");
1162             std::string cpp = parseBinAsCharArray((uint8_t *)genBinary, genBinarySize, fileTrunk);
1163             argHelper->saveOutput(cppOutputFile, cpp.c_str(), cpp.size());
1164         }
1165     }
1166 
1167     if (!elfBinary.empty()) {
1168         std::string elfOutputFile;
1169         if (outputNoSuffix) {
1170             elfOutputFile = generateFilePath(outputDirectory, fileBase, "");
1171         } else {
1172             elfOutputFile = generateFilePath(outputDirectory, fileBase, ".bin") + generateOptsSuffix();
1173         }
1174         argHelper->saveOutput(
1175             elfOutputFile,
1176             elfBinary.data(),
1177             elfBinary.size());
1178     }
1179 
1180     if (debugDataBinary) {
1181         std::string debugOutputFile = generateFilePath(outputDirectory, fileBase, ".dbg") + generateOptsSuffix();
1182 
1183         argHelper->saveOutput(
1184             debugOutputFile,
1185             debugDataBinary,
1186             debugDataBinarySize);
1187     }
1188 }
1189 
readOptionsFromFile(std::string & options,const std::string & file,OclocArgHelper * helper)1190 bool OfflineCompiler::readOptionsFromFile(std::string &options, const std::string &file, OclocArgHelper *helper) {
1191     if (!helper->fileExists(file)) {
1192         return false;
1193     }
1194     size_t optionsSize = 0U;
1195     auto optionsFromFile = helper->loadDataFromFile(file, optionsSize);
1196     if (optionsSize > 0) {
1197         // Remove comment containing copyright header
1198         options = optionsFromFile.get();
1199         size_t commentBegin = options.find("/*");
1200         size_t commentEnd = options.rfind("*/");
1201         if (commentBegin != std::string::npos && commentEnd != std::string::npos) {
1202             auto sizeToReplace = commentEnd - commentBegin + 2;
1203             options = options.replace(commentBegin, sizeToReplace, "");
1204             size_t optionsBegin = options.find_first_not_of(" \t\n\r");
1205             if (optionsBegin != std::string::npos) {
1206                 options = options.substr(optionsBegin, options.length());
1207             }
1208         }
1209         auto trimPos = options.find_last_not_of(" \n\r");
1210         options = options.substr(0, trimPos + 1);
1211     }
1212     return true;
1213 }
1214 
generateFilePath(const std::string & directory,const std::string & fileNameBase,const char * extension)1215 std::string generateFilePath(const std::string &directory, const std::string &fileNameBase, const char *extension) {
1216     UNRECOVERABLE_IF(extension == nullptr);
1217 
1218     if (directory.empty()) {
1219         return fileNameBase + extension;
1220     }
1221 
1222     bool hasTrailingSlash = (*directory.rbegin() == '/');
1223     std::string ret;
1224     ret.reserve(directory.size() + (hasTrailingSlash ? 0 : 1) + fileNameBase.size() + strlen(extension) + 1);
1225     ret.append(directory);
1226     if (false == hasTrailingSlash) {
1227         ret.append("/", 1);
1228     }
1229     ret.append(fileNameBase);
1230     ret.append(extension);
1231 
1232     return ret;
1233 }
1234 
1235 } // namespace NEO
1236