yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
4721b6ef2
master
1#include "clang/Basic/Stack.h" 2#include "clang/Basic/TargetOptions.h" 3#include "clang/Basic/Version.h" 4#include "clang/CodeGen/CodeGenAction.h" 5#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" 6#include "clang/Config/config.h" 7#include "clang/Driver/DriverDiagnostic.h" 8#include "clang/Driver/Options.h" 9#include "clang/Frontend/CompilerInstance.h" 10#include "clang/Frontend/CompilerInvocation.h" 11#include "clang/Frontend/FrontendAction.h" 12#include "clang/Frontend/FrontendDiagnostic.h" 13#include "clang/Frontend/TextDiagnosticBuffer.h" 14#include "clang/Frontend/TextDiagnosticPrinter.h" 15#include "clang/Frontend/Utils.h" 16#include "clang/FrontendTool/Utils.h" 17#include "clang/Lex/PreprocessorOptions.h" 18#include "llvm/ADT/Statistic.h" 19#include "llvm/Config/llvm-config.h" 20#include "llvm/LinkAllPasses.h" 21#include "llvm/Option/Arg.h" 22#include "llvm/Option/ArgList.h" 23#include "llvm/Option/OptTable.h" 24#include "llvm/Support/BuryPointer.h" 25#include "llvm/Support/Compiler.h" 26#include "llvm/Support/ErrorHandling.h" 27#include "llvm/Support/ManagedStatic.h" 28#include "llvm/Support/Path.h" 29#include "llvm/Support/Process.h" 30#include "llvm/Support/Signals.h" 31#include "llvm/Support/TargetSelect.h" 32#include "llvm/Support/TimeProfiler.h" 33#include "llvm/Support/Timer.h" 34#include "llvm/Support/raw_ostream.h" 35#include "llvm/Target/TargetMachine.h" 36 37// Jit 38#include "llvm/ExecutionEngine/JITEventListener.h" 39#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" 40#include "llvm/ExecutionEngine/JITSymbol.h" 41#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 42#include "llvm/ExecutionEngine/Orc/LLJIT.h" 43#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h" 44#include "llvm/IR/LLVMContext.h" 45#include "llvm/IRReader/IRReader.h" 46 47// Slang 48 49#include "slang-com-helper.h" 50#include "slang-com-ptr.h" 51#include "slang.h" 52 53#include <compiler-core/slang-artifact-associated-impl.h> 54#include <compiler-core/slang-artifact-desc-util.h> 55#include <compiler-core/slang-downstream-compiler.h> 56#include <compiler-core/slang-slice-allocator.h> 57#include <core/slang-com-object.h> 58#include <core/slang-hash.h> 59#include <core/slang-list.h> 60#include <core/slang-shared-library.h> 61#include <core/slang-string-util.h> 62#include <core/slang-string.h> 63#include <stdio.h> 64 65// We want to make math functions available to the JIT 66#if SLANG_GCC_FAMILY && __GNUC__ < 6 67#include <cmath> 68#define SLANG_LLVM_STD std:: 69#else 70#include <math.h> 71#define SLANG_LLVM_STD 72#endif 73 74#if SLANG_OSX 75// For memset_pattern functions 76// https://www.unix.com/man-page/osx/3/memset_pattern16/ 77#include <string.h> 78#endif 79 80#if SLANG_WINDOWS_FAMILY 81 82/* 83It's not clear if this function is needed for ARM WIN targets, but we'll assume it does for now. 84 85https://learn.microsoft.com/en-us/windows/win32/devnotes/-win32-chkstk 86https://www.betaarchive.com/wiki/index.php/Microsoft_KB_Archive/100775 87https://codywu2010.wordpress.com/2010/10/04/__chkstk-and-stack-overflow/ 88*/ 89 90#if SLANG_PROCESSOR_X86 91extern "C" void /* __declspec(naked)*/ __cdecl_chkstk (); 92#else 93extern "C" void /* __declspec(naked)*/ __cdecl__chkstk (); 94#endif 95#endif 96 97// Predeclare. We'll use this symbol to lookup timestamp, if we don't have a hash. 98extern "C" SLANG_DLL_EXPORT SlangResult 99createLLVMDownstreamCompiler_V4 (const SlangUUID & intfGuid ,Slang ::IDownstreamCompiler ** out ); 100 101namespace slang_llvm 102{ 103 104using namespace clang ; 105 106using namespace llvm::opt ; 107using namespace llvm ; 108using namespace llvm::orc ; 109 110using namespace Slang ; 111 112class LLVMDownstreamCompiler :public ComBaseObject ,public IDownstreamCompiler 113{ 114public : 115typedef ComBaseObject Super ; 116 117// IUnknown 118SLANG_COM_BASE_IUNKNOWN_ALL 119 120// ICastable 121virtual SLANG_NO_THROW void * SLANG_MCALL castAs (const Guid & guid )SLANG_OVERRIDE ; 122 123// IDownstreamCompiler 124virtual SLANG_NO_THROW const Desc & SLANG_MCALL getDesc ()SLANG_OVERRIDE {return m_desc ; } 125virtual SLANG_NO_THROW SlangResult SLANG_MCALL 126compile (const CompileOptions & options ,IArtifact ** outArtifact )SLANG_OVERRIDE ; 127virtual SLANG_NO_THROW bool SLANG_MCALL 128canConvert (const ArtifactDesc & from ,const ArtifactDesc & to )SLANG_OVERRIDE ; 129virtual SLANG_NO_THROW SlangResult SLANG_MCALL 130convert (IArtifact * from ,const ArtifactDesc & to ,IArtifact ** outArtifact )SLANG_OVERRIDE ; 131virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased ()SLANG_OVERRIDE {return false; } 132virtual SLANG_NO_THROW SlangResult SLANG_MCALL getVersionString (slang::IBlob ** outVersionString ) 133SLANG_OVERRIDE ; 134virtual SLANG_NO_THROW SlangResult SLANG_MCALL 135validate (const uint32_t * contents ,int contentsSize )SLANG_OVERRIDE 136 { 137SLANG_UNUSED (contents ); 138SLANG_UNUSED (contentsSize ); 139return SLANG_FAIL ; 140 } 141virtual SLANG_NO_THROW SlangResult SLANG_MCALL 142disassemble (const uint32_t * contents ,int contentsSize )SLANG_OVERRIDE 143 { 144SLANG_UNUSED (contents ); 145SLANG_UNUSED (contentsSize ); 146return SLANG_FAIL ; 147 } 148virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassembleWithResult ( 149const uint32_t * contents , 150int contentsSize , 151String & outString )SLANG_OVERRIDE 152 { 153SLANG_UNUSED (contents ); 154SLANG_UNUSED (contentsSize ); 155SLANG_UNUSED (outString ); 156return SLANG_FAIL ; 157 } 158 159LLVMDownstreamCompiler () 160 :m_desc ( 161SLANG_PASS_THROUGH_LLVM , 162SemanticVersion (LLVM_VERSION_MAJOR ,LLVM_VERSION_MINOR ,LLVM_VERSION_PATCH )) 163 { 164 } 165 166void * getInterface (const Guid & guid ); 167void * getObject (const Guid & guid ); 168 169Desc m_desc ; 170}; 171 172 173/* !!!!!!!!!!!!!!!!!!!!! LLVMJITSharedLibrary !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ 174 175/* This implementation uses atomic ref counting to ensure the shared libraries lifetime can outlive 176the LLVMDownstreamCompileResult and the compilation that created it */ 177class LLVMJITSharedLibrary :public ComBaseObject ,public ISlangSharedLibrary 178{ 179public : 180// ISlangUnknown 181SLANG_COM_BASE_IUNKNOWN_ALL 182 183/// ICastable 184virtual SLANG_NO_THROW void * SLANG_MCALL castAs (const Guid & guid )SLANG_OVERRIDE ; 185 186// ISlangSharedLibrary impl 187virtual SLANG_NO_THROW void * SLANG_MCALL findSymbolAddressByName (char const * name ) 188SLANG_OVERRIDE ; 189 190LLVMJITSharedLibrary (std::unique_ptr < llvm::orc::LLJIT > jit ) 191 :m_jit (std::move (jit )) 192 { 193 } 194 195protected : 196ISlangUnknown * getInterface (const SlangUUID & uuid ); 197void * getObject (const SlangUUID & uuid ); 198 199 std::unique_ptr < llvm::orc::LLJIT > m_jit ; 200}; 201 202ISlangUnknown * LLVMJITSharedLibrary ::getInterface (const SlangUUID & guid ) 203{ 204if (guid == ISlangUnknown ::getTypeGuid ()|| guid == ISlangCastable ::getTypeGuid ()|| 205guid == ISlangSharedLibrary ::getTypeGuid ()) 206 { 207return static_cast < ISlangSharedLibrary *> (this ); 208 } 209return nullptr ; 210} 211 212void * LLVMJITSharedLibrary ::getObject (const SlangUUID & uuid ) 213{ 214SLANG_UNUSED (uuid ); 215return nullptr ; 216} 217 218void * LLVMJITSharedLibrary ::castAs (const Guid & guid ) 219{ 220if (auto ptr = getInterface (guid )) 221 { 222return ptr ; 223 } 224return getObject (guid ); 225} 226 227void * LLVMJITSharedLibrary ::findSymbolAddressByName (char const * name ) 228{ 229auto fnExpected = m_jit -> lookup (name ); 230if (fnExpected ) 231 { 232auto fn = std::move (* fnExpected ); 233return (void * )fn .getAddress (); 234 } 235return nullptr ; 236} 237 238 239static void _ensureSufficientStack () {} 240 241static void _llvmErrorHandler (void * userData ,const char * message ,bool genCrashDiag ) 242{ 243// DiagnosticsEngine& diags = *static_cast<DiagnosticsEngine*>(userData); 244// diags.Report(diag::err_fe_error_backend) << message; 245 246printf ("Clang/LLVM fatal error: %s\n" ,message ); 247 248// Run the interrupt handlers to make sure any special cleanups get done, in 249// particular that we remove files registered with RemoveFileOnSignal. 250 llvm::sys::RunInterruptHandlers (); 251 252// We cannot recover from llvm errors. (!) 253// 254// Returning nothing, will still cause LLVM to exit the process. 255} 256 257static Slang ::ArtifactDiagnostic ::Severity _getSeverity (DiagnosticsEngine ::Level level ) 258{ 259typedef ArtifactDiagnostic ::Severity Severity ; 260typedef DiagnosticsEngine ::Level Level ; 261switch (level ) 262 { 263default : 264case Level ::Ignored : 265case Level ::Note : 266case Level ::Remark : 267 { 268return Severity ::Info ; 269 } 270case Level ::Warning : 271 { 272return Severity ::Warning ; 273 } 274case Level ::Error : 275case Level ::Fatal : 276 { 277return Severity ::Error ; 278 } 279 } 280} 281 282class BufferedDiagnosticConsumer :public clang::DiagnosticConsumer 283{ 284public : 285BufferedDiagnosticConsumer (IArtifactDiagnostics * diagnostics ) 286 :m_diagnostics (diagnostics ) 287 { 288 } 289 290void HandleDiagnostic (DiagnosticsEngine ::Level level ,const Diagnostic & info )override 291 { 292SmallString < 100 > text ; 293info .FormatDiagnostic (text ); 294 295ArtifactDiagnostic diagnostic ; 296diagnostic .severity = _getSeverity (level ); 297diagnostic .stage = ArtifactDiagnostic ::Stage ::Compile ; 298diagnostic .text = TerminatedCharSlice (text .c_str (),Count (text .size ())); 299 300auto location = info .getLocation (); 301 302// Work out what the location is 303auto & sourceManager = info .getSourceManager (); 304 305// Gets the file/line number 306const bool useLineDirectives = true; 307const PresumedLoc presumedLoc = sourceManager .getPresumedLoc (location ,useLineDirectives ); 308 309diagnostic .location .line = presumedLoc .getLine (); 310diagnostic .filePath = TerminatedCharSlice (presumedLoc .getFilename ()); 311 312m_diagnostics -> add (diagnostic ); 313 } 314 315bool hasError ()const 316 { 317return m_diagnostics -> getCountAtLeastSeverity (ArtifactDiagnostic ::Severity ::Error )> 0 ; 318 } 319 320ComPtr < IArtifactDiagnostics > m_diagnostics ; 321}; 322 323/* 324* A question is how to make the prototypes available for these functions. They would need to be 325* defined before the the prelude - or potentially in the prelude. 326* 327* I could just define the prototypes in the prelude, and only impl, if needed. Here though I 328* require that all the functions implemented here, use C style names (ie unmanagled) to simplify 329* lookup. 330*/ 331 332struct NameAndFunc 333{ 334typedef void (* Func )(); 335 336const char * name ; 337Func func ; 338}; 339 340#define SLANG_LLVM_EXPAND (x ) x 341 342#define SLANG_LLVM_FUNC (name ,cppName ,retType ,paramTypes ) \ 343 NameAndFunc{ \ 344 #name, \ 345 (NameAndFunc::Func) static_cast<retType(*) paramTypes>(&SLANG_LLVM_EXPAND(cppName))}, 346 347// Implementations of maths functions available to JIT 348static float F32_frexp (float x ,int * e ) 349{ 350float m = ::frexpf (x ,e ); 351return m ; 352} 353 354static double F64_frexp (double x ,int * e ) 355{ 356double m = ::frexp (x ,e ); 357return m ; 358} 359 360static void assertFailed (const char * msg ) 361{ 362printf ("Assert failed: %s\n" ,msg ); 363SLANG_BREAKPOINT (0 ); 364} 365 366#if SLANG_OSX 367 368namespace OSXSpecific 369{ 370 371static void bzero (void * dst ,size_t size ) 372{ 373 ::memset (dst ,0 ,size ); 374} 375 376}// namespace OSXSpecific 377#endif 378 379#if SLANG_VC && SLANG_PTR_IS_32 380 381namespace WinSpecific 382{ 383 384// NOTE! These are functions used in 32 bit windows to enable 64 bit maths. This set is probably 385// *not* complete. Check: 386 387// https://source.winehq.org/source/dlls/ntdll/large_int.c 388 389static int64_t __stdcall_alldiv (int64_t a ,int64_t b ) 390{ 391return a /b ; 392} 393 394static int64_t __stdcall_allrem (int64_t a ,int64_t b ) 395{ 396return a %b ; 397} 398 399static uint64_t __stdcall_aullrem (uint64_t a ,uint64_t b ) 400{ 401return a %b ; 402} 403 404static uint64_t __stdcall_aulldiv (uint64_t a ,uint64_t b ) 405{ 406return a /b ; 407} 408 409}// namespace WinSpecific 410 411#endif 412 413 414// These are only the functions that cannot be implemented with 'reasonable performance' in the 415// prelude. It is assumed that calling from JIT to C function whilst not super expensive, is an 416// issue. 417 418// name, cppName, retType, paramTypes 419// clang-format off 420#define SLANG_LLVM_FUNCS (x ) \ 421 x(F64_ceil, ceil, double, (double)) \ 422 x(F64_floor, floor, double, (double)) \ 423 x(F64_round, round, double, (double)) \ 424 x(F64_abs, fabs, double, (double)) \ 425 x(F64_sin, sin, double, (double)) \ 426 x(F64_cos, cos, double, (double)) \ 427 x(F64_tan, tan, double, (double)) \ 428 x(F64_asin, asin, double, (double)) \ 429 x(F64_acos, acos, double, (double)) \ 430 x(F64_atan, atan, double, (double)) \ 431 x(F64_sinh, sinh, double, (double)) \ 432 x(F64_cosh, cosh, double, (double)) \ 433 x(F64_tanh, tanh, double, (double)) \ 434 x(F64_log2, log2, double, (double)) \ 435 x(F64_log, log, double, (double)) \ 436 x(F64_log10, log10, double, (double)) \ 437 x(F64_exp2, exp2, double, (double)) \ 438 x(F64_exp, exp, double, (double)) \ 439 x(F64_fabs, fabs, double, (double)) \ 440 x(F64_trunc, trunc, double, (double)) \ 441 x(F64_sqrt, sqrt, double, (double)) \ 442 \ 443 x(F64_isnan, SLANG_LLVM_STD isnan, bool, (double)) \ 444 x(F64_isfinite, SLANG_LLVM_STD isfinite, bool, (double)) \ 445 x(F64_isinf, SLANG_LLVM_STD isinf, bool, (double)) \ 446 \ 447 x(F64_atan2, atan2, double, (double, double)) \ 448 \ 449 x(F64_frexp, F64_frexp, double, (double, int*)) \ 450 x(F64_pow, pow, double, (double, double)) \ 451 \ 452 x(F64_modf, modf, double, (double, double*)) \ 453 x(F64_fmod, fmod, double, (double, double)) \ 454 x(F64_remainder, remainder, double, (double, double)) \ 455 \ 456 x(F32_ceil, ceilf, float, (float)) \ 457 x(F32_floor, floorf, float, (float)) \ 458 x(F32_round, roundf, float, (float)) \ 459 x(F32_abs, fabsf, float, (float)) \ 460 x(F32_sin, sinf, float, (float)) \ 461 x(F32_cos, cosf, float, (float)) \ 462 x(F32_tan, tanf, float, (float)) \ 463 x(F32_asin, asinf, float, (float)) \ 464 x(F32_acos, acosf, float, (float)) \ 465 x(F32_atan, atanf, float, (float)) \ 466 x(F32_sinh, sinhf, float, (float)) \ 467 x(F32_cosh, coshf, float, (float)) \ 468 x(F32_tanh, tanhf, float, (float)) \ 469 x(F32_log2, log2f, float, (float)) \ 470 x(F32_log, logf, float, (float)) \ 471 x(F32_log10, log10f, float, (float)) \ 472 x(F32_exp2, exp2f, float, (float)) \ 473 x(F32_exp, expf, float, (float)) \ 474 x(F32_fabs, fabsf, float, (float)) \ 475 x(F32_trunc, truncf, float, (float)) \ 476 x(F32_sqrt, sqrtf, float, (float)) \ 477 \ 478 x(F32_isnan, SLANG_LLVM_STD isnan, bool, (float)) \ 479 x(F32_isfinite, SLANG_LLVM_STD isfinite, bool, (float)) \ 480 x(F32_isinf, SLANG_LLVM_STD isinf, bool, (float)) \ 481 \ 482 x(F32_atan2, atan2f, float, (float, float)) \ 483 \ 484 x(F32_frexp, F32_frexp, float, (float, int*)) \ 485 x(F32_pow, powf, float, (float, float)) \ 486 \ 487 x(F32_modf, modff, float, (float, float*)) \ 488 x(F32_fmod, fmodf, float, (float, float)) \ 489 x(F32_remainder, remainderf, float, (float, float)) \ 490 \ 491 x(assertFailed, assertFailed, void, (const char*)) \ 492 \ 493 x(memcpy, memcpy, void*, (void*, const void*, size_t)) \ 494 x(memmove, memmove, void*, (void*, const void*, size_t)) \ 495 x(memcmp, memcmp, int, (const void*, const void*, size_t)) \ 496 x(memset, memset, void*, (void*, int, size_t)) 497 498#if SLANG_OSX 499# define SLANG_PLATFORM_FUNCS (x ) \ 500 x(memset_pattern4, memset_pattern4, void, (void*, const void*, size_t)) \ 501 x(memset_pattern8, memset_pattern8, void, (void*, const void*, size_t)) \ 502 x(memset_pattern16, memset_pattern16, void, (void*, const void*, size_t)) \ 503 \ 504 x(__bzero, OSXSpecific::bzero, void, (void*, size_t)) 505#endif 506// clang-format on 507 508#if SLANG_WINDOWS_FAMILY 509#if SLANG_PROCESSOR_X86 510#define SLANG_PLATFORM_FUNCS (x ) x(_chkstk, _chkstk, void, ()) 511#else 512#define SLANG_PLATFORM_FUNCS (x ) x(__chkstk, __chkstk, void, ()) 513#endif 514#endif 515 516#ifndef SLANG_PLATFORM_FUNCS 517#define SLANG_PLATFORM_FUNCS (x ) 518#endif 519 520static int _getOptimizationLevel (DownstreamCompileOptions ::OptimizationLevel level ) 521{ 522typedef DownstreamCompileOptions ::OptimizationLevel OptimizationLevel ; 523switch (level ) 524 { 525case OptimizationLevel ::None : 526return 0 ; 527default : 528case OptimizationLevel ::Default : 529return 1 ; 530case OptimizationLevel ::High : 531return 2 ; 532case OptimizationLevel ::Maximal : 533return 3 ; 534 } 535} 536 537static SlangResult _initLLVM () 538{ 539// Initialize targets first, so that --version shows registered targets. 540#if 0 541 llvm::InitializeAllTargets (); 542 llvm::InitializeAllTargetMCs (); 543 llvm::InitializeAllAsmPrinters (); 544 llvm::InitializeAllAsmParsers (); 545#else 546// Just initialize items needed for this target. 547 548 llvm::InitializeNativeTarget (); 549 llvm::InitializeNativeTargetAsmPrinter (); 550 llvm::InitializeNativeTargetAsmParser (); 551 552 llvm::InitializeNativeTargetDisassembler (); 553#endif 554 555// Set an error handler, so that any LLVM backend diagnostics go through our 556// error handler. 557// llvm::install_fatal_error_handler(_llvmErrorHandler, 558// static_cast<void*>(&clang->getDiagnostics())); 559// NOTE! Can only be set once. 560 llvm::install_fatal_error_handler (_llvmErrorHandler ,nullptr ); 561 562return SLANG_OK ; 563} 564 565 566bool LLVMDownstreamCompiler ::canConvert (const ArtifactDesc & from ,const ArtifactDesc & to ) 567{ 568return false; 569} 570 571SlangResult LLVMDownstreamCompiler ::convert ( 572IArtifact * from , 573const ArtifactDesc & to , 574IArtifact ** outArtifact ) 575{ 576return SLANG_E_NOT_IMPLEMENTED ; 577} 578 579SlangResult LLVMDownstreamCompiler ::getVersionString (slang::IBlob ** outVersionString ) 580{ 581StringBuilder versionString ; 582// Append the version 583m_desc .version .append (versionString ); 584 585// Really we should have a hash to identify the specific version. 586// For now we'll fall back to just using the timestamp 587 588 { 589// If we don't have the commitHash, we use the library timestamp, to uniquely identify. 590versionString <<" " 591 <<SharedLibraryUtils ::getSharedLibraryTimestamp ( 592 (void * )createLLVMDownstreamCompiler_V4 ); 593 } 594 595* outVersionString = StringBlob ::moveCreate (versionString ).detach (); 596return SLANG_OK ; 597} 598 599void * LLVMDownstreamCompiler ::castAs (const Guid & guid ) 600{ 601if (auto ptr = getInterface (guid )) 602 { 603return ptr ; 604 } 605return getObject (guid ); 606} 607 608void * LLVMDownstreamCompiler ::getInterface (const Guid & guid ) 609{ 610if (guid == ISlangUnknown ::getTypeGuid ()|| guid == ICastable ::getTypeGuid ()|| 611guid == IDownstreamCompiler ::getTypeGuid ()) 612 { 613return static_cast < IDownstreamCompiler *> (this ); 614 } 615return nullptr ; 616} 617 618void * LLVMDownstreamCompiler ::getObject (const Guid & guid ) 619{ 620SLANG_UNUSED (guid ); 621return nullptr ; 622} 623 624SlangResult LLVMDownstreamCompiler ::compile ( 625const CompileOptions & inOptions , 626IArtifact ** outArtifact ) 627{ 628if (!isVersionCompatible (inOptions )) 629 { 630// Not possible to compile with this version of the interface. 631return SLANG_E_NOT_IMPLEMENTED ; 632 } 633 634CompileOptions options = getCompatibleVersion (& inOptions ); 635 636// Currently supports single source file 637if (options .sourceArtifacts .count != 1 ) 638 { 639return SLANG_FAIL ; 640 } 641IArtifact * sourceArtifact = options .sourceArtifacts [0 ]; 642 643_ensureSufficientStack (); 644 645static const SlangResult initLLVMResult = _initLLVM (); 646SLANG_RETURN_ON_FAIL (initLLVMResult ); 647 648 std::unique_ptr < CompilerInstance > clang (new CompilerInstance ()); 649IntrusiveRefCntPtr < DiagnosticIDs > diagID (new DiagnosticIDs ()); 650 651// Register the support for object-file-wrapped Clang modules. 652auto pchOps = clang -> getPCHContainerOperations (); 653pchOps -> registerWriter (std::make_unique < ObjectFilePCHContainerWriter > ()); 654pchOps -> registerReader (std::make_unique < ObjectFilePCHContainerReader > ()); 655 656IntrusiveRefCntPtr < DiagnosticOptions > diagOpts = new DiagnosticOptions (); 657 658ComPtr < IArtifactDiagnostics > diagnostics (new ArtifactDiagnostics ); 659 660 661// TODO(JS): We might just want this to talk directly to the listener. 662// For now we just buffer up. 663BufferedDiagnosticConsumer diagsBuffer (diagnostics ); 664 665IntrusiveRefCntPtr < DiagnosticsEngine > diags = 666new DiagnosticsEngine (diagID ,diagOpts ,& diagsBuffer , false); 667 668ComPtr < ISlangBlob > sourceBlob ; 669SLANG_RETURN_ON_FAIL (sourceArtifact -> loadBlob (ArtifactKeep ::Yes ,sourceBlob .writeRef ())); 670 671const auto sourceSlice = StringUtil ::getSlice (sourceBlob ); 672StringRef sourceStringRef (sourceSlice .begin (),sourceSlice .getLength ()); 673 674auto sourceBuffer = llvm::MemoryBuffer ::getMemBuffer (sourceStringRef ); 675 676auto & invocation = clang -> getInvocation (); 677 678 std::string verboseOutputString ; 679 680// Capture all of the verbose output into a buffer, so not writen to stdout 681clang -> setVerboseOutputStream (std::make_unique < llvm::raw_string_ostream > (verboseOutputString )); 682 683SmallVector < char > output ; 684clang -> setOutputStream (std::make_unique < llvm::raw_svector_ostream > (output )); 685 686 frontend::ActionKind action = frontend::ActionKind ::EmitLLVMOnly ; 687 688// EmitCodeGenOnly doesn't appear to actually emit anything 689// EmitLLVM outputs LLVM assembly 690// EmitLLVMOnly doesn't 'emit' anything, but the IR that is produced is accessible, from the 691// 'action'. 692 693action = frontend::ActionKind ::EmitLLVMOnly ; 694 695// action = frontend::ActionKind::EmitBC; 696// action = frontend::ActionKind::EmitLLVM; 697// 698// action = frontend::ActionKind::EmitCodeGenOnly; 699// action = frontend::ActionKind::EmitObj; 700// action = frontend::ActionKind::EmitAssembly; 701 702Language language ; 703LangStandard ::Kind langStd ; 704switch (options .sourceLanguage ) 705 { 706case SLANG_SOURCE_LANGUAGE_CPP : 707 { 708language = Language ::CXX ; 709langStd = LangStandard ::Kind ::lang_cxx17 ; 710break ; 711 } 712case SLANG_SOURCE_LANGUAGE_C : 713 { 714language = Language ::C ; 715langStd = LangStandard ::Kind ::lang_c17 ; 716break ; 717 } 718default : 719 { 720return SLANG_E_NOT_AVAILABLE ; 721 } 722 } 723 724const InputKind inputKind (language ,InputKind ::Format ::Source ); 725 726 { 727auto & opts = invocation .getFrontendOpts (); 728 729// Add the source 730// TODO(JS): For the moment this kind of include does *NOT* show a input source filename 731// not super surprising as one isn't set, but it's not clear how one would be set when the 732// input is a memory buffer. For Slang usage, this probably isn't an issue, because it's 733// *output* typically holds #line directives. 734 { 735 736FrontendInputFile inputFile (* sourceBuffer ,inputKind ); 737opts .Inputs .push_back (inputFile ); 738 } 739 740opts .ProgramAction = action ; 741 } 742 743 { 744auto & opts = invocation .getPreprocessorOpts (); 745 746// Add definition so that 'LLVM/Clang' compilations can be recognized 747opts .addMacroDef ("SLANG_LLVM" ); 748 749for (const auto & define :options .defines ) 750 { 751const Index index = asStringSlice (define .nameWithSig ).indexOf ('(' ); 752if (index >=0 ) 753 { 754// Interface does not support having a signature. 755return SLANG_E_NOT_AVAILABLE ; 756 } 757 758// TODO(JS): NOTE! The options do not support setting a *value* just that a macro is 759// defined. So strictly speaking, we should probably have a warning/error if the value 760// is not appropriate 761opts .addMacroDef (define .nameWithSig .begin ()); 762 } 763 } 764 765 766 llvm::Triple targetTriple ; 767 { 768auto & opts = invocation .getTargetOpts (); 769 770opts .Triple = LLVM_DEFAULT_TARGET_TRIPLE ; 771 772// A code model isn't set by default, "default" seems to fit the bill here 773opts .CodeModel = "default" ; 774 775targetTriple = llvm::Triple (opts .Triple ); 776 } 777 778 { 779auto opts = invocation .getLangOpts (); 780 781 std::vector < std::string > includes ; 782for (const auto & includePath :options .includePaths ) 783 { 784includes .push_back (includePath .begin ()); 785 } 786 787 clang::CompilerInvocation ::setLangDefaults ( 788* opts , 789inputKind , 790targetTriple , 791includes , 792langStd ); 793 794if (options .floatingPointMode == DownstreamCompileOptions ::FloatingPointMode ::Fast ) 795 { 796opts -> FastMath = true; 797 } 798 } 799 800 { 801auto & opts = invocation .getHeaderSearchOpts (); 802 803// These only work if the resource directory is setup (or a virtual file system points to 804// it) 805opts .UseBuiltinIncludes = true; 806opts .UseStandardSystemIncludes = true; 807opts .UseStandardCXXIncludes = true; 808 809/// Use libc++ instead of the default libstdc++. 810// opts.UseLibcxx = true; 811 } 812 813 814 { 815auto & opts = invocation .getCodeGenOpts (); 816 817// Set to -O optimization level 818opts .OptimizationLevel = _getOptimizationLevel (options .optimizationLevel ); 819 820// Copy over the targets CodeModel 821opts .CodeModel = invocation .getTargetOpts ().CodeModel ; 822 } 823 824// const llvm::opt::OptTable& opts = clang::driver::getDriverOptTable(); 825 826// TODO(JS): Need a way to find in system search paths, for now we just don't bother 827// 828// The system search paths are for includes for compiler intrinsics it seems. 829// Infer the builtin include path if unspecified. 830#if 0 831 { 832auto & searchOpts = clang -> getHeaderSearchOpts (); 833if (searchOpts .UseBuiltinIncludes && searchOpts .ResourceDir .empty ()) 834 { 835// TODO(JS): Hack - hard coded path such that we can test out the 836// resource directory functionality. 837 838StringRef binaryPath = "F:/dev/llvm-12.0/llvm-project-llvmorg-12.0.1/build.vs/Release/bin" ; 839 840// Dir is bin/ or lib/, depending on where BinaryPath is. 841 842// On Windows, libclang.dll is in bin/. 843// On non-Windows, libclang.so/.dylib is in lib/. 844// With a static-library build of libclang, LibClangPath will contain the 845// path of the embedding binary, which for LLVM binaries will be in bin/. 846// ../lib gets us to lib/ in both cases. 847SmallString < 128 > path = llvm::sys::path::parent_path (binaryPath ); 848 llvm::sys::path::append (path ,Twine ("lib" )+ CLANG_LIBDIR_SUFFIX ,"clang" ,CLANG_VERSION_STRING ); 849 850searchOpts .ResourceDir = path .c_str (); 851 } 852 } 853#endif 854 855// Create the actual diagnostics engine. 856clang -> createDiagnostics (); 857clang -> setDiagnostics (diags .get ()); 858 859if (!clang -> hasDiagnostics ()) 860return SLANG_FAIL ; 861 862// 863clang -> createFileManager (); 864clang -> createSourceManager (clang -> getFileManager ()); 865 866 867 std::unique_ptr < LLVMContext > llvmContext = std::make_unique < LLVMContext > (); 868 869 clang::CodeGenAction * codeGenAction = nullptr ; 870 std::unique_ptr < FrontendAction > act ; 871 872 { 873// If we are going to just emit IR, we need to have access to the underlying type 874if (action == frontend::ActionKind ::EmitLLVMOnly ) 875 { 876EmitLLVMOnlyAction * llvmOnlyAction = new EmitLLVMOnlyAction (llvmContext .get ()); 877codeGenAction = llvmOnlyAction ; 878// Make act the owning ptr 879act = std::unique_ptr < FrontendAction > (llvmOnlyAction ); 880 } 881else 882 { 883act = CreateFrontendAction (* clang ); 884 } 885 886if (!act ) 887 { 888return SLANG_FAIL ; 889 } 890 891const bool compileSucceeded = clang -> ExecuteAction (* act ); 892 893// If the compilation failed make sure, we have an error 894if (!compileSucceeded ) 895 { 896diagnostics -> requireErrorDiagnostic (); 897 } 898 899if (!compileSucceeded || diagsBuffer .hasError ()) 900 { 901diagnostics -> setResult (SLANG_FAIL ); 902 903auto artifact = ArtifactUtil ::createArtifact ( 904ArtifactDesc ::make (ArtifactKind ::None ,ArtifactPayload ::None )); 905ArtifactUtil ::addAssociated (artifact ,diagnostics ); 906 907* outArtifact = artifact .detach (); 908return SLANG_OK ; 909 } 910 } 911 912 std::unique_ptr < llvm::Module > module ; 913 914switch (action ) 915 { 916case frontend::ActionKind ::EmitLLVM : 917 { 918// LLVM output is text, that must be zero terminated 919output .push_back (char (0 )); 920 921StringRef identifier ; 922StringRef data (output .begin (),output .size ()- 1 ); 923 924MemoryBufferRef memoryBufferRef (data ,identifier ); 925 926SMDiagnostic err ; 927module = llvm::parseIR (memoryBufferRef ,err ,* llvmContext ); 928break ; 929 } 930case frontend::ActionKind ::EmitBC : 931 { 932StringRef identifier ; 933StringRef data (output .begin (),output .size ()); 934 935MemoryBufferRef memoryBufferRef (data ,identifier ); 936 937SMDiagnostic err ; 938module = llvm::parseIR (memoryBufferRef ,err ,* llvmContext ); 939break ; 940 } 941case frontend::ActionKind ::EmitLLVMOnly : 942 { 943// Get the module produced by the action 944module = codeGenAction -> takeModule (); 945break ; 946 } 947 } 948 949switch (options .targetType ) 950 { 951// TODO(JS): Shared library may not be appropriate, but as long as the 'shared library' is 952// never accessed as a blob all is good. 953case SLANG_SHADER_SHARED_LIBRARY : 954 955// TODO(JS): 956// Hmm. What does this even mean? 957// I guess the idea is it's 'SHADER' style, but is runnable on the host. 958case SLANG_SHADER_HOST_CALLABLE : 959 { 960// Try running something in the module on the JIT 961 std::unique_ptr < llvm::orc::LLJIT > jit ; 962 { 963// Create the JIT 964 965LLJITBuilder jitBuilder ; 966 967Expected < std::unique_ptr < llvm::orc::LLJIT >> expectJit = jitBuilder .create (); 968if (!expectJit ) 969 { 970/* JS: NOTE! 971 972It is worth saying there can be some odd issues around creating the JIT - if 973LLVM-C is linked against. 974 975If it is then LLVM will likely startup saying LLVM-C isn't found. 976BUT if you have LLVM *installed* on your system (as is reasonable to do from a 977LLVM distro, then at startup it *MIGHT* find a LLVM-C dll in that installation 978(ie nothing to do with the version of LLVM linked with). This will likely lead 979to an odd error saying the 'triple can't be found' and that no targets are 980registered. 981 982Also note that the behavior *may* be different with Debug/Release - because of 983how the linked resolves symbols that are multiply defined. 984 985If there are problems creating the JIT, check that LLVM-C is not linked against 986(it should be disabled in the premake). 987*/ 988 989auto err = expectJit .takeError (); 990 991 std::string jitErrorString ; 992 llvm::raw_string_ostream jitErrorStream (jitErrorString ); 993 994jitErrorStream <<err ; 995 996ArtifactDiagnostic diagnostic ; 997 998StringBuilder buf ; 999buf <<"Unable to create JIT engine: " <<jitErrorString .c_str (); 1000 1001diagnostic .severity = ArtifactDiagnostic ::Severity ::Error ; 1002diagnostic .stage = ArtifactDiagnostic ::Stage ::Link ; 1003diagnostic .text = TerminatedCharSlice (buf .getBuffer (),buf .getLength ()); 1004 1005// Add the error 1006diagnostics -> add (diagnostic ); 1007diagnostics -> setResult (SLANG_FAIL ); 1008 1009auto artifact = ArtifactUtil ::createArtifact ( 1010ArtifactDesc ::make (ArtifactKind ::None ,ArtifactPayload ::None )); 1011ArtifactUtil ::addAssociated (artifact ,diagnostics ); 1012 1013* outArtifact = artifact .detach (); 1014return SLANG_OK ; 1015 } 1016jit = std::move (* expectJit ); 1017 } 1018 1019// Used the following link to test this out 1020// https://www.llvm.org/docs/ORCv2.html 1021// https://www.llvm.org/docs/ORCv2.html#processandlibrarysymbols 1022 1023 { 1024auto & es = jit -> getExecutionSession (); 1025 1026const DataLayout & dl = jit -> getDataLayout (); 1027MangleAndInterner mangler (es ,dl ); 1028 1029// The name of the lib must be unique. Should be here as we are only thing adding 1030// libs 1031auto stdcLibExpected = es .createJITDylib ("stdc" ); 1032 1033if (stdcLibExpected ) 1034 { 1035auto & stdcLib = * stdcLibExpected ; 1036 1037// Add all the symbolmap 1038SymbolMap symbolMap ; 1039 1040// symbolMap.insert(std::make_pair(mangler("sin"), 1041// JITEvaluatedSymbol::fromPointer(static_cast<double (*)(double)>(&sin)))); 1042 1043 { 1044static const NameAndFunc funcs []= {SLANG_LLVM_FUNCS ( 1045SLANG_LLVM_FUNC )SLANG_PLATFORM_FUNCS (SLANG_LLVM_FUNC )}; 1046 1047for (auto & func :funcs ) 1048 { 1049symbolMap .insert (std::make_pair ( 1050mangler (func .name ), 1051JITEvaluatedSymbol ::fromPointer (func .func ))); 1052 } 1053 } 1054 1055#if SLANG_PTR_IS_32 && SLANG_VC 1056 { 1057// https://docs.microsoft.com/en-us/windows/win32/devnotes/-win32-alldiv 1058symbolMap .insert (std::make_pair ( 1059mangler ("_alldiv" ), 1060JITEvaluatedSymbol ::fromPointer (WinSpecific ::_alldiv ))); 1061symbolMap .insert (std::make_pair ( 1062mangler ("_allrem" ), 1063JITEvaluatedSymbol ::fromPointer (WinSpecific ::_allrem ))); 1064symbolMap .insert (std::make_pair ( 1065mangler ("_aullrem" ), 1066JITEvaluatedSymbol ::fromPointer (WinSpecific ::_aullrem ))); 1067symbolMap .insert (std::make_pair ( 1068mangler ("_aulldiv" ), 1069JITEvaluatedSymbol ::fromPointer (WinSpecific ::_aulldiv ))); 1070 } 1071#endif 1072 1073if (auto err = stdcLib .define (absoluteSymbols (symbolMap ))) 1074 { 1075return SLANG_FAIL ; 1076 } 1077 1078// Required or the symbols won't be found 1079jit -> getMainJITDylib ().addToLinkOrder (stdcLib ); 1080 } 1081 } 1082 1083ThreadSafeModule threadSafeModule (std::move (module ), std::move (llvmContext )); 1084 1085if (auto err = jit -> addIRModule (std::move (threadSafeModule ))) 1086 { 1087return SLANG_FAIL ; 1088 } 1089 1090if (auto err = jit -> initialize (jit -> getMainJITDylib ())) 1091 { 1092return SLANG_FAIL ; 1093 } 1094 1095// Create the shared library 1096ComPtr < ISlangSharedLibrary > sharedLibrary (new LLVMJITSharedLibrary (std::move (jit ))); 1097 1098// Work out the ArtifactDesc 1099const auto targetDesc = ArtifactDescUtil ::makeDescForCompileTarget (options .targetType ); 1100 1101auto artifact = ArtifactUtil ::createArtifact (targetDesc ); 1102ArtifactUtil ::addAssociated (artifact ,diagnostics ); 1103 1104artifact -> addRepresentation (sharedLibrary ); 1105 1106* outArtifact = artifact .detach (); 1107return SLANG_OK ; 1108 } 1109 } 1110 1111return SLANG_FAIL ; 1112} 1113 1114}// namespace slang_llvm 1115 1116extern "C" SLANG_DLL_EXPORT SlangResult 1117createLLVMDownstreamCompiler_V4 (const SlangUUID & intfGuid ,Slang ::IDownstreamCompiler ** out ) 1118{ 1119Slang ::ComPtr < slang_llvm::LLVMDownstreamCompiler > compiler ( 1120new slang_llvm::LLVMDownstreamCompiler ); 1121 1122if (auto ptr = compiler -> castAs (intfGuid )) 1123 { 1124compiler .detach (); 1125* out = (Slang ::IDownstreamCompiler * )ptr ; 1126return SLANG_OK ; 1127 } 1128 1129return SLANG_E_NO_INTERFACE ; 1130}