@@ -25,6 +25,7 @@ SPDX-License-Identifier: MIT
2525
2626#include " llvm/Support/Path.h"
2727#include " llvm/Support/FileSystem.h"
28+ #include " llvm/Support/JSON.h"
2829
2930#if !defined(_MSC_VER) && !defined(__MINGW32__)
3031#include < unistd.h>
@@ -37,8 +38,6 @@ namespace iOpenCL
3738
3839extern RETVAL g_cInitRetValue;
3940
40- const char * uniqueElfFileNamePart = " _%%%%%%%%" ;
41-
4241ShaderHash CGen8OpenCLProgram::CLProgramCtxProvider::getProgramHash () const {
4342 return m_Context.hash ;
4443}
@@ -365,78 +364,98 @@ void dumpOCLCos(const IGC::COpenCLKernel *Kernel, const std::string &stateDebugM
365364 IGC::Debug::DumpUnlock ();
366365}
367366
368- // Build a name for an ELF temporary file. If uniqueLockFileName contains any % characters then
369- // these characters are replaced with random characters 0-9 or a-f, and such a uniquely named
370- // file, where this name is returned in resultUniqueLockFileName, is created in a system temporary
371- // directory to reserve this name.
372- bool createElfFileName (std::string &name, unsigned int maxNameLen, SIMDMode simdMode, int id,
373- SmallVectorImpl<char > &uniqueLockFileName, SmallVectorImpl<char > &resultUniqueLockFileName,
374- std::string& resultFullElfFileNameStr)
367+ static std::string getKernelDumpName (const IGC::COpenCLKernel* kernel)
375368{
376- SmallString< 64 > tempDir; // Do not worry about its size, because system_temp_directory() appends if needed.
377- bool retValue = true ;
369+ std::string kernelName (kernel-> m_kernelInfo . m_kernelName );
370+ kernel-> getShaderFileName (kernelName) ;
378371
379- // Location of temporary input and output files required by the linker of ELF files
380- // is a system temporary directory
381- llvm::sys::path::system_temp_directory (true , tempDir);
382- #if defined(_WIN64) || defined(_WIN32)
383- tempDir.append (" \\ " );
384- #else
385- tempDir.append (" /" );
386- #endif // defined(_WIN64) || defined(_WIN32)
387-
388- std::string pidStr =
389- #if LLVM_ON_UNIX
390- std::to_string (getpid ());
391- #elif defined(_WIN32)
392- std::to_string (::GetCurrentProcessId ());
393- #else
394- " " ;
395- #endif
372+ auto * context = kernel->GetContext ();
373+ auto name = IGC::Debug::DumpName (IGC::Debug::GetShaderOutputName ())
374+ .Hash (context->hash )
375+ .Type (ShaderType::OPENCL_SHADER)
376+ .PostFix (kernelName)
377+ .SIMDSize (kernel->m_SIMDSize )
378+ .Extension (" elf" );
379+ return name.RelativePath ();
380+ }
396381
397- resultFullElfFileNameStr. append (tempDir. c_str ());
398- resultFullElfFileNameStr. append (name. substr ( 0 , maxNameLen - 1 ). c_str ());
399- resultFullElfFileNameStr. append ( " _simd " ) ;
400- resultFullElfFileNameStr. append ( to_string (simdMode == SIMDMode::SIMD8 ? 8 : simdMode == SIMDMode::SIMD16 ? 16 : 32 )) ;
401- resultFullElfFileNameStr. append ( " _ " );
402- resultFullElfFileNameStr. append ( to_string (id) );
403- if (!pidStr. empty ())
404- {
405- resultFullElfFileNameStr. append ( " _ " );
406- resultFullElfFileNameStr. append (pidStr) ;
382+ static std::string getTempFileName ( const IGC::COpenCLKernel* kernel)
383+ {
384+ SmallString< 128 > FileName ;
385+ std::error_code EC ;
386+
387+ EC = sys::fs::getPotentiallyUniqueTempFileName ( " ze " , " elf " , FileName );
388+ if (EC) {
389+ IGC::CodeGenContext* ctx = kernel-> GetContext ();
390+ ctx-> EmitError (( std::string ( " unable to create temporary file for kernel: " ) + kernel-> m_kernelInfo . m_kernelName ). c_str (), nullptr );
391+ return " " ;
407392 }
393+ return FileName.c_str ();
394+ }
408395
409- std::string uniqueLockFileNameStr = uniqueLockFileName.data ();
410- if (uniqueLockFileNameStr.find (' %' ) < uniqueLockFileNameStr.size ())
396+ void CGen8OpenCLProgramBase::addElfKernelMapping (const std::string& elfFileName, const std::string& kernelName)
397+ {
398+ if (!elfMapEntries)
411399 {
412- int uniqueLockFileID = 0 ;
413- // Every '%' will be replaced with a random character (0-9 or a-f), taking care of multithreaded compilations
414- if (std::error_code EC = sys::fs::createUniqueFile (
415- uniqueLockFileName, uniqueLockFileID, resultUniqueLockFileName))
416- {
417- IGC_ASSERT_MESSAGE (false , " A uniquely named file not created" );
418- retValue = false ;
419- }
400+ elfMapEntries = std::make_unique<llvm::json::Array>();
420401 }
421- else
402+ llvm::json::Value elfKernelMapping = llvm::json::Object
422403 {
423- resultUniqueLockFileName = uniqueLockFileName;
404+ {" filename" , elfFileName},
405+ {" kernel" , kernelName}
406+ };
407+ elfMapEntries->push_back (elfKernelMapping);
408+ }
409+
410+ bool CGen8OpenCLProgramBase::createElfKernelMapFile (const llvm::Twine& FilePath)
411+ {
412+ int writeFD = 0 ;
413+ sys::fs::CreationDisposition disp = sys::fs::CreationDisposition::CD_CreateAlways;
414+ sys::fs::OpenFlags flags = sys::fs::OpenFlags::OF_None;
415+ unsigned int mode = sys::fs::all_read | sys::fs::all_write;
416+ auto EC = sys::fs::openFileForReadWrite (FilePath, writeFD, disp, flags, mode);
417+ if (!EC)
418+ {
419+ raw_fd_ostream OS (writeFD, true , true ); // shouldClose=true, unbuffered=true
420+ OS << llvm::formatv (" {0:2}" , llvm::json::Value (std::move (*elfMapEntries.release ())));
421+ // close(writeFD) is not required due to shouldClose parameter in ostream
422+ return true ;
424423 }
424+ return false ;
425+ }
425426
426- resultFullElfFileNameStr.append (resultUniqueLockFileName.data ());
427- resultFullElfFileNameStr.append (" .elf" );
427+ bool CGen8OpenCLProgramBase::dumpElfKernelMapFile (IGC::CodeGenContext* Ctx)
428+ {
429+ if (IGC_IS_FLAG_DISABLED (ElfTempDumpEnable))
430+ {
431+ // Silently skip JSON generation
432+ return true ;
433+ }
428434
429- return retValue;
435+ SmallString<64 > elfMapPath;
436+ std::error_code EC;
437+ EC = sys::fs::getPotentiallyUniqueTempFileName (" ze_map" , " json" , elfMapPath);
438+ if (EC)
439+ {
440+ if (Ctx)
441+ {
442+ Ctx->EmitError (" unable to create temporary file for kernel mapping" , nullptr );
443+ }
444+ return false ;
445+ }
446+ return createElfKernelMapFile (elfMapPath);
430447}
431448
432- void CGen8OpenCLProgram::GetZEBinary (
449+ bool CGen8OpenCLProgram::GetZEBinary (
433450 llvm::raw_pwrite_stream& programBinary,
434451 unsigned pointerSizeInBytes,
435452 const char * spv, uint32_t spvSize,
436453 const char * metrics, uint32_t metricsSize,
437454 const char * buildOptions, uint32_t buildOptionsSize)
438455{
439456 std::vector<std::unique_ptr<llvm::MemoryBuffer>> elfStorage;
457+ bool elfTmpFilesError = false ; // error creating temp files
458+ bool retValue = true ;
440459
441460 ZEBinaryBuilder zebuilder (m_Platform, pointerSizeInBytes == 8 ,
442461 m_Context.m_programInfo ,
@@ -446,25 +465,23 @@ void CGen8OpenCLProgram::GetZEBinary(
446465 zebuilder.setProductFamily (m_Platform.eProductFamily );
447466 zebuilder.setGfxCoreFamily (m_Platform.eRenderCoreFamily );
448467
468+ //
469+ // Creating ZE binary requires linking individual ELF files,
470+ // containing kernel code and debug data (DWARF). Due to usual
471+ // naming convention of the kernels, we may exceed the size of
472+ // the file name (usually ~250 chars). So, each ELF file is
473+ // created using LLVM's TempFile functionality and a JSON file,
474+ // containing mapping between temp name and 'usual' kernel name.
475+ //
476+ // JSON file is created if IGC_ElfTempDumpEnable is enabled.
449477 std::list<string> elfVecNames; // List of parameters for the linker, contains in/out ELF file names and params
450478 std::vector<const char *> elfVecPtrs; // Vector of pointers to the elfVecNames vector elements
451479 SIMDMode simdMode = SIMDMode::SIMD8; // Currently processed kernel's SIMD
452480
453- SmallString<64 > tempDir; // Do not worry its size, because system_temp_directory() appends if needed.
454- // Location of temporary input and output files required by the linker is a system temporary directory
455- llvm::sys::path::system_temp_directory (true , tempDir);
456- #if defined(_WIN64) || defined(_WIN32)
457- tempDir.append (" \\ " );
458- #else
459- tempDir.append (" /" );
460- #endif // defined(_WIN64) || defined(_WIN32)
461-
462481 const unsigned int maxElfFileNameLength = 512 ;
463482 std::string elfLinkerLogName = " lldLinkLogName" ; // First parameter for the linker, just a log name or a program name if this linker would be external.
464- SmallString<16 > uniqueLockFilePartStr (uniqueElfFileNamePart);
465483 std::string linkedElfFileNameStr = " " ; // Output file name created and filled by the linker
466484 int kernelID = 0 ; // Index of kernels; inserted into temporary ELF files names
467- bool elfTmpFilesError = false ; // Set if something goes wrong with temporary files or the linker.
468485
469486 // If a single kernel in a program then neither temporary files are created nor the linker is in use,
470487 // hence ELF data is taken directly from the first found kernel's output (i.e. from m_debugData).
@@ -540,9 +557,6 @@ void CGen8OpenCLProgram::GetZEBinary(
540557 isDebugInfo = false ;
541558 if (IGC_IS_FLAG_ENABLED (ZeBinCompatibleDebugging) && isDebugInfo)
542559 {
543- const unsigned int rsrvdForAllButFullName = 64 ; // characters will be used for temporary ELF file names.
544- unsigned int spaceAvailableForKernelName = maxElfFileNameLength - rsrvdForAllButFullName - tempDir.size ();
545-
546560 if (m_ShaderProgramList.size () == 1 && kernelVec.size () == 1 )
547561 {
548562 // The first and only kernel found.
@@ -556,38 +570,28 @@ void CGen8OpenCLProgram::GetZEBinary(
556570 // - build for the linker a name of an output file, and
557571 // - add a log name, as a first element, to a vector of the linker parameters.
558572
559- // Build a temporary output file name (with a path) for the linker
573+ ctx = kernel-> GetContext (); // Remember context for future usage regarding warning emission (if needed).
560574
561- if (!createElfFileName (
562- kernel->m_kernelInfo .m_kernelName ,
563- spaceAvailableForKernelName, // maxNameLen
564- simdMode,
565- 0 , // kernel ID
566- uniqueLockFilePartStr,
567- uniqueLockFilePartStr, // unique part only result, i.e. _%%%%%%%% replaced with unique sequence of characters
568- linkedElfFileNameStr)) // full name with a path result
575+ // Build a temporary output file name (with a path) for the linker
576+ std::string elfFileNameStr = getTempFileName (kernel);
577+ if (elfFileNameStr.empty ())
569578 {
570- IGC_ASSERT_MESSAGE (false , " A unique name for a linked ELF file not created" );
579+ elfTmpFilesError = true ; // Handle this error at the end of this function
580+ break ;
571581 }
582+ linkedElfFileNameStr = elfFileNameStr;
572583 elfVecNames.push_back (elfLinkerLogName); // 1st element in this vector of names; required by the linker
573584 elfVecPtrs.push_back (elfVecNames.back ().c_str ());
574-
575- ctx = kernel->GetContext (); // Remember context for future usage regarding warning emission (if needed).
576585 }
577586
578- std::string elfFileNameStr = " " ;
579587 // Build a temporary input file name (with a path) for the linker
580- if (!createElfFileName (
581- kernel->m_kernelInfo .m_kernelName ,
582- spaceAvailableForKernelName, // maxNameLen
583- simdMode,
584- kernelID,
585- uniqueLockFilePartStr, // unique part is the same unique sequence of characters got earlier for a linked file
586- uniqueLockFilePartStr, // unique part only result should remain the same as the previous parameter
587- elfFileNameStr)) // full name with a path result
588+ std::string elfFileNameStr = getTempFileName (kernel);
589+ if (elfFileNameStr.empty ())
588590 {
589- IGC_ASSERT_MESSAGE (false , " A unique name for an input ELF file not created" );
591+ elfTmpFilesError = true ; // Handle this error at the end of this function
592+ break ;
590593 }
594+ addElfKernelMapping (elfFileNameStr, getKernelDumpName (kernel));
591595
592596 int writeFD = 0 ;
593597 sys::fs::CreationDisposition disp = sys::fs::CreationDisposition::CD_CreateAlways;
@@ -606,7 +610,7 @@ void CGen8OpenCLProgram::GetZEBinary(
606610 }
607611 else
608612 {
609- ctx->EmitError (" ELF file opening error " , nullptr );
613+ ctx->EmitError (( std::string ( " failed to open ELF file: " ) + elfFileNameStr). c_str () , nullptr );
610614 elfTmpFilesError = true ; // Handle this error at the end of this function
611615 break ;
612616 }
@@ -735,6 +739,7 @@ void CGen8OpenCLProgram::GetZEBinary(
735739 {
736740 // Nothing to do with the linker when any error with temporary files occured.
737741 ctx->EmitError (" ZeBinary will not contain correct debug info due to an ELF temporary files error" , nullptr );
742+ retValue = false ;
738743 }
739744
740745 if (IGC_IS_FLAG_DISABLED (ElfTempDumpEnable))
@@ -753,13 +758,15 @@ void CGen8OpenCLProgram::GetZEBinary(
753758
754759 // Also remove a temporary linked output ELF file...
755760 sys::fs::remove (Twine (linkedElfFileNameStr), true ); // true=ignore non-existing
756-
757- // ...and finally a unique locked file.
758- sys::fs::remove (Twine (uniqueLockFilePartStr), true ); // true=ignore non-existing
761+ }
762+ else
763+ {
764+ retValue &= dumpElfKernelMapFile (ctx);
759765 }
760766 }
761767
762768 zebuilder.getBinaryObject (programBinary);
769+ return retValue;
763770}
764771
765772void CGen8OpenCLProgram::CreateKernelBinaries ()
0 commit comments