yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Sam EstepUpdate LLVM from 13.0.1 to 14.0.6 (#8031)4721b6ef2

master
35.2 KiB1130 linesraw
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:
115    typedef ComBaseObject Super;
116
117    // IUnknown
118    SLANG_COM_BASE_IUNKNOWN_ALL
119
120    // ICastable
121    virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) SLANG_OVERRIDE;
122
123    // IDownstreamCompiler
124    virtual SLANG_NO_THROW const Desc& SLANG_MCALL getDesc() SLANG_OVERRIDE { return m_desc; }
125    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
126    compile(const CompileOptions& options, IArtifact** outArtifact) SLANG_OVERRIDE;
127    virtual SLANG_NO_THROW bool SLANG_MCALL
128    canConvert(const ArtifactDesc& from, const ArtifactDesc& to) SLANG_OVERRIDE;
129    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
130    convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) SLANG_OVERRIDE;
131    virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased() SLANG_OVERRIDE { return false; }
132    virtual SLANG_NO_THROW SlangResult SLANG_MCALL getVersionString(slang::IBlob** outVersionString)
133        SLANG_OVERRIDE;
134    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
135    validate(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE
136    {
137        SLANG_UNUSED(contents);
138        SLANG_UNUSED(contentsSize);
139        return SLANG_FAIL;
140    }
141    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
142    disassemble(const uint32_t* contents, int contentsSize) SLANG_OVERRIDE
143    {
144        SLANG_UNUSED(contents);
145        SLANG_UNUSED(contentsSize);
146        return SLANG_FAIL;
147    }
148    virtual SLANG_NO_THROW SlangResult SLANG_MCALL disassembleWithResult(
149        const uint32_t* contents,
150        int contentsSize,
151        String& outString) SLANG_OVERRIDE
152    {
153        SLANG_UNUSED(contents);
154        SLANG_UNUSED(contentsSize);
155        SLANG_UNUSED(outString);
156        return SLANG_FAIL;
157    }
158
159    LLVMDownstreamCompiler()
160        : m_desc(
161              SLANG_PASS_THROUGH_LLVM,
162              SemanticVersion(LLVM_VERSION_MAJOR, LLVM_VERSION_MINOR, LLVM_VERSION_PATCH))
163    {
164    }
165
166    void* getInterface(const Guid& guid);
167    void* getObject(const Guid& guid);
168
169    Desc 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
181    SLANG_COM_BASE_IUNKNOWN_ALL
182
183    /// ICastable
184    virtual SLANG_NO_THROW void* SLANG_MCALL castAs(const Guid& guid) SLANG_OVERRIDE;
185
186    // ISlangSharedLibrary impl
187    virtual SLANG_NO_THROW void* SLANG_MCALL findSymbolAddressByName(char const* name)
188        SLANG_OVERRIDE;
189
190    LLVMJITSharedLibrary(std::unique_ptr<llvm::orc::LLJIT> jit)
191        : m_jit(std::move(jit))
192    {
193    }
194
195protected:
196    ISlangUnknown* getInterface(const SlangUUID& uuid);
197    void* getObject(const SlangUUID& uuid);
198
199    std::unique_ptr<llvm::orc::LLJIT> m_jit;
200};
201
202ISlangUnknown* LLVMJITSharedLibrary::getInterface(const SlangUUID& guid)
203{
204    if (guid == ISlangUnknown::getTypeGuid() || guid == ISlangCastable::getTypeGuid() ||
205        guid == ISlangSharedLibrary::getTypeGuid())
206    {
207        return static_cast<ISlangSharedLibrary*>(this);
208    }
209    return nullptr;
210}
211
212void* LLVMJITSharedLibrary::getObject(const SlangUUID& uuid)
213{
214    SLANG_UNUSED(uuid);
215    return nullptr;
216}
217
218void* LLVMJITSharedLibrary::castAs(const Guid& guid)
219{
220    if (auto ptr = getInterface(guid))
221    {
222        return ptr;
223    }
224    return getObject(guid);
225}
226
227void* LLVMJITSharedLibrary::findSymbolAddressByName(char const* name)
228{
229    auto fnExpected = m_jit->lookup(name);
230    if (fnExpected)
231    {
232        auto fn = std::move(*fnExpected);
233        return (void*)fn.getAddress();
234    }
235    return 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
246    printf("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{
259    typedef ArtifactDiagnostic::Severity Severity;
260    typedef DiagnosticsEngine::Level Level;
261    switch (level)
262    {
263    default:
264    case Level::Ignored:
265    case Level::Note:
266    case Level::Remark:
267        {
268            return Severity::Info;
269        }
270    case Level::Warning:
271        {
272            return Severity::Warning;
273        }
274    case Level::Error:
275    case Level::Fatal:
276        {
277            return Severity::Error;
278        }
279    }
280}
281
282class BufferedDiagnosticConsumer : public clang::DiagnosticConsumer
283{
284public:
285    BufferedDiagnosticConsumer(IArtifactDiagnostics* diagnostics)
286        : m_diagnostics(diagnostics)
287    {
288    }
289
290    void HandleDiagnostic(DiagnosticsEngine::Level level, const Diagnostic& info) override
291    {
292        SmallString<100> text;
293        info.FormatDiagnostic(text);
294
295        ArtifactDiagnostic diagnostic;
296        diagnostic.severity = _getSeverity(level);
297        diagnostic.stage = ArtifactDiagnostic::Stage::Compile;
298        diagnostic.text = TerminatedCharSlice(text.c_str(), Count(text.size()));
299
300        auto location = info.getLocation();
301
302        // Work out what the location is
303        auto& sourceManager = info.getSourceManager();
304
305        // Gets the file/line number
306        const bool useLineDirectives = true;
307        const PresumedLoc presumedLoc = sourceManager.getPresumedLoc(location, useLineDirectives);
308
309        diagnostic.location.line = presumedLoc.getLine();
310        diagnostic.filePath = TerminatedCharSlice(presumedLoc.getFilename());
311
312        m_diagnostics->add(diagnostic);
313    }
314
315    bool hasError() const
316    {
317        return m_diagnostics->getCountAtLeastSeverity(ArtifactDiagnostic::Severity::Error) > 0;
318    }
319
320    ComPtr<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{
334    typedef void (*Func)();
335
336    const char* name;
337    Func 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{
350    float m = ::frexpf(x, e);
351    return m;
352}
353
354static double F64_frexp(double x, int* e)
355{
356    double m = ::frexp(x, e);
357    return m;
358}
359
360static void assertFailed(const char* msg)
361{
362    printf("Assert failed: %s\n", msg);
363    SLANG_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{
391    return a / b;
392}
393
394static int64_t __stdcall _allrem(int64_t a, int64_t b)
395{
396    return a % b;
397}
398
399static uint64_t __stdcall _aullrem(uint64_t a, uint64_t b)
400{
401    return a % b;
402}
403
404static uint64_t __stdcall _aulldiv(uint64_t a, uint64_t b)
405{
406    return 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{
522    typedef DownstreamCompileOptions::OptimizationLevel OptimizationLevel;
523    switch (level)
524    {
525    case OptimizationLevel::None:
526        return 0;
527    default:
528    case OptimizationLevel::Default:
529        return 1;
530    case OptimizationLevel::High:
531        return 2;
532    case OptimizationLevel::Maximal:
533        return 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
562    return SLANG_OK;
563}
564
565
566bool LLVMDownstreamCompiler::canConvert(const ArtifactDesc& from, const ArtifactDesc& to)
567{
568    return false;
569}
570
571SlangResult LLVMDownstreamCompiler::convert(
572    IArtifact* from,
573    const ArtifactDesc& to,
574    IArtifact** outArtifact)
575{
576    return SLANG_E_NOT_IMPLEMENTED;
577}
578
579SlangResult LLVMDownstreamCompiler::getVersionString(slang::IBlob** outVersionString)
580{
581    StringBuilder versionString;
582    // Append the version
583    m_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.
590        versionString << " "
591                      << SharedLibraryUtils::getSharedLibraryTimestamp(
592                             (void*)createLLVMDownstreamCompiler_V4);
593    }
594
595    *outVersionString = StringBlob::moveCreate(versionString).detach();
596    return SLANG_OK;
597}
598
599void* LLVMDownstreamCompiler::castAs(const Guid& guid)
600{
601    if (auto ptr = getInterface(guid))
602    {
603        return ptr;
604    }
605    return getObject(guid);
606}
607
608void* LLVMDownstreamCompiler::getInterface(const Guid& guid)
609{
610    if (guid == ISlangUnknown::getTypeGuid() || guid == ICastable::getTypeGuid() ||
611        guid == IDownstreamCompiler::getTypeGuid())
612    {
613        return static_cast<IDownstreamCompiler*>(this);
614    }
615    return nullptr;
616}
617
618void* LLVMDownstreamCompiler::getObject(const Guid& guid)
619{
620    SLANG_UNUSED(guid);
621    return nullptr;
622}
623
624SlangResult LLVMDownstreamCompiler::compile(
625    const CompileOptions& inOptions,
626    IArtifact** outArtifact)
627{
628    if (!isVersionCompatible(inOptions))
629    {
630        // Not possible to compile with this version of the interface.
631        return SLANG_E_NOT_IMPLEMENTED;
632    }
633
634    CompileOptions options = getCompatibleVersion(&inOptions);
635
636    // Currently supports single source file
637    if (options.sourceArtifacts.count != 1)
638    {
639        return SLANG_FAIL;
640    }
641    IArtifact* sourceArtifact = options.sourceArtifacts[0];
642
643    _ensureSufficientStack();
644
645    static const SlangResult initLLVMResult = _initLLVM();
646    SLANG_RETURN_ON_FAIL(initLLVMResult);
647
648    std::unique_ptr<CompilerInstance> clang(new CompilerInstance());
649    IntrusiveRefCntPtr<DiagnosticIDs> diagID(new DiagnosticIDs());
650
651    // Register the support for object-file-wrapped Clang modules.
652    auto pchOps = clang->getPCHContainerOperations();
653    pchOps->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());
654    pchOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
655
656    IntrusiveRefCntPtr<DiagnosticOptions> diagOpts = new DiagnosticOptions();
657
658    ComPtr<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.
663    BufferedDiagnosticConsumer diagsBuffer(diagnostics);
664
665    IntrusiveRefCntPtr<DiagnosticsEngine> diags =
666        new DiagnosticsEngine(diagID, diagOpts, &diagsBuffer, false);
667
668    ComPtr<ISlangBlob> sourceBlob;
669    SLANG_RETURN_ON_FAIL(sourceArtifact->loadBlob(ArtifactKeep::Yes, sourceBlob.writeRef()));
670
671    const auto sourceSlice = StringUtil::getSlice(sourceBlob);
672    StringRef sourceStringRef(sourceSlice.begin(), sourceSlice.getLength());
673
674    auto sourceBuffer = llvm::MemoryBuffer::getMemBuffer(sourceStringRef);
675
676    auto& invocation = clang->getInvocation();
677
678    std::string verboseOutputString;
679
680    // Capture all of the verbose output into a buffer, so not writen to stdout
681    clang->setVerboseOutputStream(std::make_unique<llvm::raw_string_ostream>(verboseOutputString));
682
683    SmallVector<char> output;
684    clang->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
693    action = 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
702    Language language;
703    LangStandard::Kind langStd;
704    switch (options.sourceLanguage)
705    {
706    case SLANG_SOURCE_LANGUAGE_CPP:
707        {
708            language = Language::CXX;
709            langStd = LangStandard::Kind::lang_cxx17;
710            break;
711        }
712    case SLANG_SOURCE_LANGUAGE_C:
713        {
714            language = Language::C;
715            langStd = LangStandard::Kind::lang_c17;
716            break;
717        }
718    default:
719        {
720            return SLANG_E_NOT_AVAILABLE;
721        }
722    }
723
724    const InputKind inputKind(language, InputKind::Format::Source);
725
726    {
727        auto& 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
736            FrontendInputFile inputFile(*sourceBuffer, inputKind);
737            opts.Inputs.push_back(inputFile);
738        }
739
740        opts.ProgramAction = action;
741    }
742
743    {
744        auto& opts = invocation.getPreprocessorOpts();
745
746        // Add definition so that 'LLVM/Clang' compilations can be recognized
747        opts.addMacroDef("SLANG_LLVM");
748
749        for (const auto& define : options.defines)
750        {
751            const Index index = asStringSlice(define.nameWithSig).indexOf('(');
752            if (index >= 0)
753            {
754                // Interface does not support having a signature.
755                return 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
761            opts.addMacroDef(define.nameWithSig.begin());
762        }
763    }
764
765
766    llvm::Triple targetTriple;
767    {
768        auto& opts = invocation.getTargetOpts();
769
770        opts.Triple = LLVM_DEFAULT_TARGET_TRIPLE;
771
772        // A code model isn't set by default, "default" seems to fit the bill here
773        opts.CodeModel = "default";
774
775        targetTriple = llvm::Triple(opts.Triple);
776    }
777
778    {
779        auto opts = invocation.getLangOpts();
780
781        std::vector<std::string> includes;
782        for (const auto& includePath : options.includePaths)
783        {
784            includes.push_back(includePath.begin());
785        }
786
787        clang::CompilerInvocation::setLangDefaults(
788            *opts,
789            inputKind,
790            targetTriple,
791            includes,
792            langStd);
793
794        if (options.floatingPointMode == DownstreamCompileOptions::FloatingPointMode::Fast)
795        {
796            opts->FastMath = true;
797        }
798    }
799
800    {
801        auto& opts = invocation.getHeaderSearchOpts();
802
803        // These only work if the resource directory is setup (or a virtual file system points to
804        // it)
805        opts.UseBuiltinIncludes = true;
806        opts.UseStandardSystemIncludes = true;
807        opts.UseStandardCXXIncludes = true;
808
809        /// Use libc++ instead of the default libstdc++.
810        // opts.UseLibcxx = true;
811    }
812
813
814    {
815        auto& opts = invocation.getCodeGenOpts();
816
817        // Set to -O optimization level
818        opts.OptimizationLevel = _getOptimizationLevel(options.optimizationLevel);
819
820        // Copy over the targets CodeModel
821        opts.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    {
832        auto& searchOpts = clang->getHeaderSearchOpts();
833        if (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
838            StringRef 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.
847            SmallString<128> path = llvm::sys::path::parent_path(binaryPath);
848            llvm::sys::path::append(path, Twine("lib") + CLANG_LIBDIR_SUFFIX, "clang", CLANG_VERSION_STRING);
849
850            searchOpts.ResourceDir = path.c_str();
851        }
852    }
853#endif
854
855    // Create the actual diagnostics engine.
856    clang->createDiagnostics();
857    clang->setDiagnostics(diags.get());
858
859    if (!clang->hasDiagnostics())
860        return SLANG_FAIL;
861
862    //
863    clang->createFileManager();
864    clang->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
874        if (action == frontend::ActionKind::EmitLLVMOnly)
875        {
876            EmitLLVMOnlyAction* llvmOnlyAction = new EmitLLVMOnlyAction(llvmContext.get());
877            codeGenAction = llvmOnlyAction;
878            // Make act the owning ptr
879            act = std::unique_ptr<FrontendAction>(llvmOnlyAction);
880        }
881        else
882        {
883            act = CreateFrontendAction(*clang);
884        }
885
886        if (!act)
887        {
888            return SLANG_FAIL;
889        }
890
891        const bool compileSucceeded = clang->ExecuteAction(*act);
892
893        // If the compilation failed make sure, we have an error
894        if (!compileSucceeded)
895        {
896            diagnostics->requireErrorDiagnostic();
897        }
898
899        if (!compileSucceeded || diagsBuffer.hasError())
900        {
901            diagnostics->setResult(SLANG_FAIL);
902
903            auto artifact = ArtifactUtil::createArtifact(
904                ArtifactDesc::make(ArtifactKind::None, ArtifactPayload::None));
905            ArtifactUtil::addAssociated(artifact, diagnostics);
906
907            *outArtifact = artifact.detach();
908            return SLANG_OK;
909        }
910    }
911
912    std::unique_ptr<llvm::Module> module;
913
914    switch (action)
915    {
916    case frontend::ActionKind::EmitLLVM:
917        {
918            // LLVM output is text, that must be zero terminated
919            output.push_back(char(0));
920
921            StringRef identifier;
922            StringRef data(output.begin(), output.size() - 1);
923
924            MemoryBufferRef memoryBufferRef(data, identifier);
925
926            SMDiagnostic err;
927            module = llvm::parseIR(memoryBufferRef, err, *llvmContext);
928            break;
929        }
930    case frontend::ActionKind::EmitBC:
931        {
932            StringRef identifier;
933            StringRef data(output.begin(), output.size());
934
935            MemoryBufferRef memoryBufferRef(data, identifier);
936
937            SMDiagnostic err;
938            module = llvm::parseIR(memoryBufferRef, err, *llvmContext);
939            break;
940        }
941    case frontend::ActionKind::EmitLLVMOnly:
942        {
943            // Get the module produced by the action
944            module = codeGenAction->takeModule();
945            break;
946        }
947    }
948
949    switch (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.
953    case 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.
958    case 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
965                LLJITBuilder jitBuilder;
966
967                Expected<std::unique_ptr<llvm::orc::LLJIT>> expectJit = jitBuilder.create();
968                if (!expectJit)
969                {
970                    /* JS: NOTE!
971
972                    It is worth saying there can be some odd issues around creating the JIT - if
973                    LLVM-C is linked against.
974
975                    If it is then LLVM will likely startup saying LLVM-C isn't found.
976                    BUT if you have LLVM *installed* on your system (as is reasonable to do from a
977                    LLVM 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
979                    to an odd error saying the 'triple can't be found' and that no targets are
980                    registered.
981
982                    Also note that the behavior *may* be different with Debug/Release - because of
983                    how the linked resolves symbols that are multiply defined.
984
985                    If there are problems creating the JIT, check that LLVM-C is not linked against
986                    (it should be disabled in the premake).
987                    */
988
989                    auto err = expectJit.takeError();
990
991                    std::string jitErrorString;
992                    llvm::raw_string_ostream jitErrorStream(jitErrorString);
993
994                    jitErrorStream << err;
995
996                    ArtifactDiagnostic diagnostic;
997
998                    StringBuilder buf;
999                    buf << "Unable to create JIT engine: " << jitErrorString.c_str();
1000
1001                    diagnostic.severity = ArtifactDiagnostic::Severity::Error;
1002                    diagnostic.stage = ArtifactDiagnostic::Stage::Link;
1003                    diagnostic.text = TerminatedCharSlice(buf.getBuffer(), buf.getLength());
1004
1005                    // Add the error
1006                    diagnostics->add(diagnostic);
1007                    diagnostics->setResult(SLANG_FAIL);
1008
1009                    auto artifact = ArtifactUtil::createArtifact(
1010                        ArtifactDesc::make(ArtifactKind::None, ArtifactPayload::None));
1011                    ArtifactUtil::addAssociated(artifact, diagnostics);
1012
1013                    *outArtifact = artifact.detach();
1014                    return SLANG_OK;
1015                }
1016                jit = 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            {
1024                auto& es = jit->getExecutionSession();
1025
1026                const DataLayout& dl = jit->getDataLayout();
1027                MangleAndInterner mangler(es, dl);
1028
1029                // The name of the lib must be unique. Should be here as we are only thing adding
1030                // libs
1031                auto stdcLibExpected = es.createJITDylib("stdc");
1032
1033                if (stdcLibExpected)
1034                {
1035                    auto& stdcLib = *stdcLibExpected;
1036
1037                    // Add all the symbolmap
1038                    SymbolMap symbolMap;
1039
1040                    // symbolMap.insert(std::make_pair(mangler("sin"),
1041                    // JITEvaluatedSymbol::fromPointer(static_cast<double (*)(double)>(&sin))));
1042
1043                    {
1044                        static const NameAndFunc funcs[] = {SLANG_LLVM_FUNCS(
1045                            SLANG_LLVM_FUNC) SLANG_PLATFORM_FUNCS(SLANG_LLVM_FUNC)};
1046
1047                        for (auto& func : funcs)
1048                        {
1049                            symbolMap.insert(std::make_pair(
1050                                mangler(func.name),
1051                                JITEvaluatedSymbol::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
1058                        symbolMap.insert(std::make_pair(
1059                            mangler("_alldiv"),
1060                            JITEvaluatedSymbol::fromPointer(WinSpecific::_alldiv)));
1061                        symbolMap.insert(std::make_pair(
1062                            mangler("_allrem"),
1063                            JITEvaluatedSymbol::fromPointer(WinSpecific::_allrem)));
1064                        symbolMap.insert(std::make_pair(
1065                            mangler("_aullrem"),
1066                            JITEvaluatedSymbol::fromPointer(WinSpecific::_aullrem)));
1067                        symbolMap.insert(std::make_pair(
1068                            mangler("_aulldiv"),
1069                            JITEvaluatedSymbol::fromPointer(WinSpecific::_aulldiv)));
1070                    }
1071#endif
1072
1073                    if (auto err = stdcLib.define(absoluteSymbols(symbolMap)))
1074                    {
1075                        return SLANG_FAIL;
1076                    }
1077
1078                    // Required or the symbols won't be found
1079                    jit->getMainJITDylib().addToLinkOrder(stdcLib);
1080                }
1081            }
1082
1083            ThreadSafeModule threadSafeModule(std::move(module), std::move(llvmContext));
1084
1085            if (auto err = jit->addIRModule(std::move(threadSafeModule)))
1086            {
1087                return SLANG_FAIL;
1088            }
1089
1090            if (auto err = jit->initialize(jit->getMainJITDylib()))
1091            {
1092                return SLANG_FAIL;
1093            }
1094
1095            // Create the shared library
1096            ComPtr<ISlangSharedLibrary> sharedLibrary(new LLVMJITSharedLibrary(std::move(jit)));
1097
1098            // Work out the ArtifactDesc
1099            const auto targetDesc = ArtifactDescUtil::makeDescForCompileTarget(options.targetType);
1100
1101            auto artifact = ArtifactUtil::createArtifact(targetDesc);
1102            ArtifactUtil::addAssociated(artifact, diagnostics);
1103
1104            artifact->addRepresentation(sharedLibrary);
1105
1106            *outArtifact = artifact.detach();
1107            return SLANG_OK;
1108        }
1109    }
1110
1111    return SLANG_FAIL;
1112}
1113
1114} // namespace slang_llvm
1115
1116extern "C" SLANG_DLL_EXPORT SlangResult
1117createLLVMDownstreamCompiler_V4(const SlangUUID& intfGuid, Slang::IDownstreamCompiler** out)
1118{
1119    Slang::ComPtr<slang_llvm::LLVMDownstreamCompiler> compiler(
1120        new slang_llvm::LLVMDownstreamCompiler);
1121
1122    if (auto ptr = compiler->castAs(intfGuid))
1123    {
1124        compiler.detach();
1125        *out = (Slang::IDownstreamCompiler*)ptr;
1126        return SLANG_OK;
1127    }
1128
1129    return SLANG_E_NO_INTERFACE;
1130}