yum-mirror/slang

Making it easier to work with shaders

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

aidanfnvAdd arguments for controlling floating point denormal mode (#7461)d50c3f34a

master
27.2 KiB907 linesraw
1// slang-dxc-compiler.cpp
2#include "slang-dxc-compiler.h"
3
4#include "../core/slang-blob.h"
5#include "../core/slang-char-util.h"
6#include "../core/slang-common.h"
7#include "../core/slang-io.h"
8#include "../core/slang-semantic-version.h"
9#include "../core/slang-shared-library.h"
10#include "../core/slang-string-slice-pool.h"
11#include "../core/slang-string-util.h"
12#include "slang-artifact-associated-impl.h"
13#include "slang-artifact-desc-util.h"
14#include "slang-artifact-diagnostic-util.h"
15#include "slang-artifact-util.h"
16#include "slang-com-helper.h"
17#include "slang-include-system.h"
18#include "slang-source-loc.h"
19
20// Enable DXIL by default unless told not to
21#ifndef SLANG_ENABLE_DXIL_SUPPORT
22#if SLANG_APPLE_FAMILY
23#define SLANG_ENABLE_DXIL_SUPPORT 0
24#else
25#define SLANG_ENABLE_DXIL_SUPPORT 1
26#endif
27#endif
28
29// Enable calling through to  `dxc` to
30// generate code on Windows.
31#if SLANG_ENABLE_DXIL_SUPPORT
32
33#ifdef _WIN32
34#include <unknwn.h>
35#include <windows.h>
36#endif
37
38#include "../../external/dxc/dxcapi.h"
39
40#ifndef _WIN32
41#ifdef __uuidof
42// DXC's WinAdapter.h defines __uuidof(T) over types, but the existing
43// usage in this file is over values (both are accepted on MSVC.)
44// We also need to decay through Slang::ComPtr, hence the helper struct
45template<typename T>
46struct StripSlangComPtr
47{
48    using type = T;
49};
50template<typename T>
51struct StripSlangComPtr<Slang::ComPtr<T>>
52{
53    using type = T;
54};
55#undef __uuidof
56#define __uuidof(x) __emulated_uuidof<StripSlangComPtr<std::decay_t<decltype(x)>>::type>()
57#endif
58#endif
59#endif
60
61namespace Slang
62{
63
64#if SLANG_ENABLE_DXIL_SUPPORT
65
66static UnownedStringSlice _getSlice(IDxcBlob* blob)
67{
68    return StringUtil::getSlice((ISlangBlob*)blob);
69}
70
71// IDxcIncludeHandler
72// 7f61fc7d-950d-467f-b3e3-3c02fb49187c
73static const Guid IID_IDxcIncludeHandler =
74    {0x7f61fc7d, 0x950d, 0x467f, {0x3c, 0x02, 0xfb, 0x49, 0x18, 0x7c}};
75
76static UnownedStringSlice _addName(const UnownedStringSlice& inSlice, StringSlicePool& pool)
77{
78    UnownedStringSlice slice = inSlice;
79    if (slice.getLength() == 0)
80    {
81        slice = UnownedStringSlice::fromLiteral("unnamed");
82    }
83
84    StringBuilder buf;
85    const Index length = slice.getLength();
86    buf << slice;
87
88    for (Index i = 0;; ++i)
89    {
90        buf.reduceLength(length);
91
92        if (i > 0)
93        {
94            buf << "_" << i;
95        }
96
97        StringSlicePool::Handle handle;
98        if (!pool.findOrAdd(buf.getUnownedSlice(), handle))
99        {
100            return pool.getSlice(handle);
101        }
102    }
103}
104
105static UnownedStringSlice _addName(IArtifact* artifact, StringSlicePool& pool)
106{
107    return _addName(ArtifactUtil::findName(artifact), pool);
108}
109
110class DxcIncludeHandler : public IDxcIncludeHandler
111{
112public:
113    // Implement IUnknown
114    SLANG_NO_THROW HRESULT SLANG_MCALL QueryInterface(const IID& uuid, void** out) override
115    {
116        ISlangUnknown* intf = getInterface(reinterpret_cast<const Guid&>(uuid));
117        if (intf)
118        {
119            *out = intf;
120            return SLANG_OK;
121        }
122        return SLANG_E_NO_INTERFACE;
123    }
124    SLANG_NO_THROW ULONG SLANG_MCALL AddRef() SLANG_OVERRIDE { return 1; }
125    SLANG_NO_THROW ULONG SLANG_MCALL Release() SLANG_OVERRIDE { return 1; }
126
127    // Implement IDxcIncludeHandler
128    virtual HRESULT SLANG_MCALL LoadSource(LPCWSTR inFilename, IDxcBlob** outSource) SLANG_OVERRIDE
129    {
130        // Hmm DXC does something a bit odd - when it sees a path, it just passes that in with ./ in
131        // front!! NOTE! It doesn't make any difference if it is "" or <> quoted.
132
133        // So we just do a work around where we strip if we see a path starting with ./
134        String filePath = String::fromWString(inFilename);
135
136        // If it starts with ./ then attempt to strip it
137        if (filePath.startsWith("./"))
138        {
139            const String remaining = filePath.getUnownedSlice().tail(2);
140
141            // Okay if we strip ./ and what we have is absolute, then it's the absolute path that we
142            // care about, otherwise we just leave as is.
143            if (Path::isAbsolute(remaining))
144            {
145                filePath = remaining;
146            }
147        }
148
149        ComPtr<ISlangBlob> blob;
150        PathInfo pathInfo;
151        SlangResult res = m_system.findAndLoadFile(filePath, String(), pathInfo, blob);
152
153        // NOTE! This only works because ISlangBlob is *binary compatible* with IDxcBlob, if either
154        // change things could go boom
155        *outSource = (IDxcBlob*)blob.detach();
156        return res;
157    }
158
159    DxcIncludeHandler(
160        SearchDirectoryList* searchDirectories,
161        ISlangFileSystemExt* fileSystemExt,
162        SourceManager* sourceManager = nullptr)
163        : m_system(searchDirectories, fileSystemExt, sourceManager)
164    {
165    }
166
167protected:
168    // Used by QueryInterface for casting
169    ISlangUnknown* getInterface(const Guid& guid)
170    {
171        if (guid == ISlangUnknown::getTypeGuid() || guid == IID_IDxcIncludeHandler)
172        {
173            return (ISlangUnknown*)(static_cast<IDxcIncludeHandler*>(this));
174        }
175        return nullptr;
176    }
177
178    IncludeSystem m_system;
179};
180
181class DXCDownstreamCompiler : public DownstreamCompilerBase
182{
183public:
184    typedef DownstreamCompilerBase Super;
185
186    // IDownstreamCompiler
187    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
188    compile(const CompileOptions& options, IArtifact** outArtifact) SLANG_OVERRIDE;
189    virtual SLANG_NO_THROW bool SLANG_MCALL
190    canConvert(const ArtifactDesc& from, const ArtifactDesc& to) SLANG_OVERRIDE;
191    virtual SLANG_NO_THROW SlangResult SLANG_MCALL
192    convert(IArtifact* from, const ArtifactDesc& to, IArtifact** outArtifact) SLANG_OVERRIDE;
193    virtual SLANG_NO_THROW bool SLANG_MCALL isFileBased() SLANG_OVERRIDE { return false; }
194    virtual SLANG_NO_THROW SlangResult SLANG_MCALL getVersionString(slang::IBlob** outVersionString)
195        SLANG_OVERRIDE;
196
197    /// Must be called before use
198    SlangResult init(ISlangSharedLibrary* library);
199
200    DXCDownstreamCompiler() {}
201
202protected:
203    DxcCreateInstanceProc m_createInstance = nullptr;
204
205    /// The commit hash associated with the DXC dll used
206    /// If 0 length, no hash was found
207    String m_commitHash;
208    /// The commit count. 0 if not set
209    uint32_t m_commitCount = 0;
210
211    ComPtr<ISlangSharedLibrary> m_sharedLibrary;
212};
213
214static String _moveTaskMemAllocatedToString(char* chars)
215{
216    if (chars)
217    {
218        const String str(chars);
219        ::CoTaskMemFree(chars);
220        return str;
221    }
222    return String();
223}
224
225SlangResult DXCDownstreamCompiler::init(ISlangSharedLibrary* library)
226{
227    m_sharedLibrary = library;
228
229    m_createInstance = (DxcCreateInstanceProc)library->findFuncByName("DxcCreateInstance");
230    if (!m_createInstance)
231    {
232        return SLANG_FAIL;
233    }
234
235    // Must be able to create the compiler. We inly do this here, because we want to get the
236    // compiler version.
237    ComPtr<IDxcCompiler> dxcCompiler;
238    SLANG_RETURN_ON_FAIL(m_createInstance(
239        CLSID_DxcCompiler,
240        __uuidof(dxcCompiler),
241        (LPVOID*)dxcCompiler.writeRef()));
242
243    uint32_t major = 0;
244    uint32_t minor = 0;
245    uint32_t patch = 0;
246
247    // Get the version info
248    {
249        ComPtr<IDxcVersionInfo> versionInfo;
250        if (SLANG_SUCCEEDED(dxcCompiler->QueryInterface(versionInfo.writeRef())))
251        {
252            versionInfo->GetVersion(&major, &minor);
253        }
254    }
255
256    // Get the commit hash
257    {
258
259        ComPtr<IDxcVersionInfo2> versionInfo;
260        if (SLANG_SUCCEEDED(dxcCompiler->QueryInterface(versionInfo.writeRef())))
261        {
262            char* commitHash = nullptr;
263            versionInfo->GetCommitInfo(&m_commitCount, &commitHash);
264            m_commitHash = _moveTaskMemAllocatedToString(commitHash);
265        }
266    }
267
268    // Try and get the custom build string, as we can potentially get the patch version from that.
269    if (patch == 0)
270    {
271        ComPtr<IDxcVersionInfo3> versionInfo;
272
273        if (SLANG_SUCCEEDED(dxcCompiler->QueryInterface(versionInfo.writeRef())))
274        {
275            char* customVersionCString = nullptr;
276            versionInfo->GetCustomVersionString(&customVersionCString);
277
278            const String customVersionString = _moveTaskMemAllocatedToString(customVersionCString);
279
280            SemanticVersion semanticVersion(int(major), int(minor), 0);
281            StringBuilder buf;
282            semanticVersion.append(buf);
283
284            if (customVersionString.startsWith(buf) &&
285                customVersionString.getLength() > buf.getLength() + 2 &&
286                customVersionString[buf.getLength()] == '.')
287            {
288                // Get the patch slice
289                UnownedStringSlice patchSlice =
290                    StringUtil::getAtInSplit(customVersionString.getUnownedSlice(), '.', 2);
291
292                Int patchValue;
293                if (SLANG_SUCCEEDED(StringUtil::parseInt(patchSlice, patchValue)) && patchValue > 0)
294                {
295                    patch = uint32_t(patchValue);
296                }
297            }
298        }
299    }
300
301    m_desc = Desc(SLANG_PASS_THROUGH_DXC, SemanticVersion(int(major), int(minor), int(patch)));
302
303    return SLANG_OK;
304}
305
306static SlangResult _parseDiagnosticLine(
307    SliceAllocator& allocator,
308    const UnownedStringSlice& line,
309    List<UnownedStringSlice>& lineSlices,
310    IArtifactDiagnostics::Diagnostic& outDiagnostic)
311{
312    /* tests/diagnostics/syntax-error-intrinsic.slang:14:2: error: expected expression */
313    if (lineSlices.getCount() < 5)
314    {
315        return SLANG_FAIL;
316    }
317
318    outDiagnostic.filePath = allocator.allocate(lineSlices[0]);
319
320    SLANG_RETURN_ON_FAIL(StringUtil::parseInt(lineSlices[1], outDiagnostic.location.line));
321
322    // Int lineCol;
323    // SLANG_RETURN_ON_FAIL(StringUtil::parseInt(lineSlices[2], lineCol));
324
325    UnownedStringSlice severitySlice = lineSlices[3].trim();
326
327    outDiagnostic.severity = ArtifactDiagnostic::Severity::Error;
328    if (severitySlice == UnownedStringSlice::fromLiteral("warning"))
329    {
330        outDiagnostic.severity = ArtifactDiagnostic::Severity::Warning;
331    }
332
333    // The rest of the line
334    outDiagnostic.text = allocator.allocate(lineSlices[4].begin(), line.end());
335    return SLANG_OK;
336}
337
338static SlangResult _handleOperationResult(
339    IDxcOperationResult* dxcResult,
340    IArtifactDiagnostics* diagnostics,
341    ComPtr<IDxcBlob>& outBlob)
342{
343    // Retrieve result.
344    HRESULT resultCode = S_OK;
345    SLANG_RETURN_ON_FAIL(dxcResult->GetStatus(&resultCode));
346
347    // Note: it seems like the dxcompiler interface
348    // doesn't support querying diagnostic output
349    // *unless* the compile failed (no way to get
350    // warnings out!?).
351
352    if (SLANG_SUCCEEDED(diagnostics->getResult()))
353    {
354        diagnostics->setResult(resultCode);
355    }
356
357    // Try getting the error/diagnostics blob
358    ComPtr<IDxcBlobEncoding> dxcErrorBlob;
359    dxcResult->GetErrorBuffer(dxcErrorBlob.writeRef());
360
361    if (dxcErrorBlob)
362    {
363        const UnownedStringSlice diagnosticsSlice = _getSlice(dxcErrorBlob);
364        if (diagnosticsSlice.getLength())
365        {
366            diagnostics->appendRaw(asCharSlice(diagnosticsSlice));
367
368            SliceAllocator allocator;
369            List<IArtifactDiagnostics::Diagnostic> parsedDiagnostics;
370            SlangResult diagnosticParseRes = ArtifactDiagnosticUtil::parseColonDelimitedDiagnostics(
371                allocator,
372                diagnosticsSlice,
373                0,
374                _parseDiagnosticLine,
375                diagnostics);
376
377            SLANG_UNUSED(diagnosticParseRes);
378            SLANG_ASSERT(SLANG_SUCCEEDED(diagnosticParseRes));
379        }
380    }
381
382    // If it failed, make sure we have an error in the diagnostics
383    if (SLANG_FAILED(resultCode))
384    {
385        // In case the parsing failed, we still have an error -> so require there is one in the
386        // diagnostics
387        diagnostics->requireErrorDiagnostic();
388    }
389    else
390    {
391        // Okay, the compile supposedly succeeded, so we
392        // just need to grab the buffer with the output DXIL.
393        SLANG_RETURN_ON_FAIL(dxcResult->GetResult(outBlob.writeRef()));
394    }
395
396    return SLANG_OK;
397}
398
399SlangResult DXCDownstreamCompiler::compile(const CompileOptions& inOptions, IArtifact** outArtifact)
400{
401    if (!isVersionCompatible(inOptions))
402    {
403        // Not possible to compile with this version of the interface.
404        return SLANG_E_NOT_IMPLEMENTED;
405    }
406
407    CompileOptions options = getCompatibleVersion(&inOptions);
408
409    // This compiler can only deal at most, a single source code artifact
410    // Should be okay to link together multiple libraries without any source artifacts (assuming
411    // that means source code)
412    if (options.sourceArtifacts.count > 1)
413    {
414        return SLANG_FAIL;
415    }
416
417    bool hasSource = options.sourceArtifacts.count > 0;
418
419    IArtifact* sourceArtifact = hasSource ? options.sourceArtifacts[0] : nullptr;
420
421    if (hasSource)
422    {
423        if (options.sourceLanguage != SLANG_SOURCE_LANGUAGE_HLSL ||
424            options.targetType != SLANG_DXIL)
425        {
426            SLANG_ASSERT(!"Can only compile HLSL to DXIL");
427            return SLANG_FAIL;
428        }
429    }
430
431    // Find all of the libraries
432    List<IArtifact*> libraries;
433    for (IArtifact* library : options.libraries)
434    {
435        const auto desc = library->getDesc();
436
437        if (desc.kind == ArtifactKind::Library && desc.payload == ArtifactPayload::DXIL)
438        {
439            // Make sure they all have blobs
440            ComPtr<ISlangBlob> libraryBlob;
441            SLANG_RETURN_ON_FAIL(library->loadBlob(ArtifactKeep::Yes, libraryBlob.writeRef()));
442
443            libraries.add(library);
444        }
445    }
446
447    ComPtr<IDxcCompiler> dxcCompiler;
448    SLANG_RETURN_ON_FAIL(m_createInstance(
449        CLSID_DxcCompiler,
450        __uuidof(dxcCompiler),
451        (LPVOID*)dxcCompiler.writeRef()));
452    ComPtr<IDxcLibrary> dxcLibrary;
453    SLANG_RETURN_ON_FAIL(
454        m_createInstance(CLSID_DxcLibrary, __uuidof(dxcLibrary), (LPVOID*)dxcLibrary.writeRef()));
455
456    ComPtr<IDxcBlobEncoding> dxcSourceBlob = nullptr;
457    ComPtr<ISlangBlob> sourceBlob;
458    if (hasSource)
459    {
460        SLANG_RETURN_ON_FAIL(sourceArtifact->loadBlob(ArtifactKeep::Yes, sourceBlob.writeRef()));
461
462        // Create blob from the string
463        SLANG_RETURN_ON_FAIL(dxcLibrary->CreateBlobWithEncodingFromPinned(
464            (LPBYTE)sourceBlob->getBufferPointer(),
465            (UINT32)sourceBlob->getBufferSize(),
466            0,
467            dxcSourceBlob.writeRef()));
468    }
469
470    List<const WCHAR*> args;
471
472    // Add all compiler specific options
473    List<OSString> compilerSpecific;
474    compilerSpecific.setCount(options.compilerSpecificArguments.count);
475
476    for (Index i = 0; i < options.compilerSpecificArguments.count; ++i)
477    {
478        compilerSpecific[i] = asString(options.compilerSpecificArguments[i]).toWString();
479        args.add(compilerSpecific[i]);
480    }
481
482    bool enablePAQs = options.enablePAQ;
483    if (!enablePAQs)
484        args.add(L"-disable-payload-qualifiers");
485    else
486        args.add(L"-enable-payload-qualifiers");
487
488    // TODO: deal with
489    bool treatWarningsAsErrors = false;
490    if (treatWarningsAsErrors)
491    {
492        args.add(L"-WX");
493    }
494
495    switch (options.matrixLayout)
496    {
497    default:
498        break;
499
500    case SLANG_MATRIX_LAYOUT_ROW_MAJOR:
501        args.add(L"-Zpr");
502        break;
503    }
504
505    switch (options.floatingPointMode)
506    {
507    default:
508        break;
509
510    case FloatingPointMode::Precise:
511        args.add(L"-Gis"); // "force IEEE strictness"
512        break;
513    }
514
515    switch (options.denormalModeFp32)
516    {
517    default:
518    case CompileOptions::FloatingPointDenormalMode::Any:
519        break;
520
521    case CompileOptions::FloatingPointDenormalMode::Preserve:
522        args.add(L"-denorm");
523        args.add(L"preserve");
524        break;
525
526    case CompileOptions::FloatingPointDenormalMode::FlushToZero:
527        args.add(L"-denorm");
528        args.add(L"ftz");
529        break;
530    }
531
532    switch (options.optimizationLevel)
533    {
534    default:
535        break;
536
537    case OptimizationLevel::None:
538        args.add(L"-Od");
539        break;
540    case OptimizationLevel::Default:
541        args.add(L"-O1");
542        break;
543    case OptimizationLevel::High:
544        args.add(L"-O2");
545        break;
546    case OptimizationLevel::Maximal:
547        args.add(L"-O3");
548        break;
549    }
550
551    switch (options.debugInfoType)
552    {
553    case DebugInfoType::None:
554        break;
555
556    default:
557        args.add(L"-Zi");
558        break;
559    }
560
561    // Slang strives to produce correct code, and by default
562    // we do not show the user warnings produced by a downstream
563    // compiler. When the downstream compiler *does* produce an
564    // error, then we dump its entire diagnostic log, which can
565    // include many distracting spurious warnings that have nothing
566    // to do with the user's code, and just relate to the idiomatic
567    // way that Slang outputs HLSL.
568    //
569    // It would be nice to use fine-grained flags to disable specific
570    // warnings here, so that we keep ourselves honest (e.g., only
571    // use `-Wno-parentheses` to eliminate that class of false positives),
572    // but alas dxc doesn't support these options even though they
573    // work on mainline Clang. Thus the only option we have available
574    // is the big hammer of turning off *all* warnings coming from dxc.
575    //
576    args.add(L"-no-warnings");
577
578    String profileName = asString(options.profileName);
579    // If we are going to link we have to compile in the lib profile style
580    if (libraries.getCount() && hasSource)
581    {
582        if (!profileName.startsWith("lib"))
583        {
584            const Index index = profileName.indexOf('_');
585            if (index < 0)
586            {
587                profileName = "lib_6_3";
588            }
589            else
590            {
591                StringBuilder buf;
592                buf << "lib" << profileName.getUnownedSlice().tail(index);
593                profileName = buf;
594            }
595        }
596    }
597
598    OSString wideEntryPointName = asString(options.entryPointName).toWString();
599    OSString wideProfileName = profileName.toWString();
600
601    if (options.flags & CompileOptions::Flag::EnableFloat16)
602    {
603        args.add(L"-enable-16bit-types");
604    }
605
606    SearchDirectoryList searchDirectories;
607    for (const auto& includePath : options.includePaths)
608    {
609        searchDirectories.searchDirectories.add(asString(includePath));
610    }
611
612    {
613        // Specify -HV 2021 when using a DXC version that supports the newer language model.
614        const SemanticVersion firstHlsl2021Version(1, 7);
615
616        if (m_desc.version >= firstHlsl2021Version)
617        {
618            args.add(L"-HV");
619            args.add(L"2021");
620        }
621    }
622
623    String sourcePath;
624    ComPtr<IDxcBlob> dxcResultBlob = nullptr;
625    auto diagnostics = ArtifactDiagnostics::create();
626    ComPtr<IDxcOperationResult> dxcOperationResult = nullptr;
627    if (hasSource)
628    {
629        sourcePath = ArtifactUtil::findPath(sourceArtifact);
630        OSString wideSourcePath = sourcePath.toWString();
631
632        DxcIncludeHandler includeHandler(
633            &searchDirectories,
634            options.fileSystemExt,
635            options.sourceManager);
636
637        SLANG_RETURN_ON_FAIL(dxcCompiler->Compile(
638            dxcSourceBlob,
639            wideSourcePath.begin(),
640            wideEntryPointName.begin(),
641            wideProfileName.begin(),
642            args.getBuffer(),
643            UINT32(args.getCount()),
644            nullptr,         // `#define`s
645            0,               // `#define` count
646            &includeHandler, // `#include` handler
647            dxcOperationResult.writeRef()));
648
649        SLANG_RETURN_ON_FAIL(
650            _handleOperationResult(dxcOperationResult, diagnostics, dxcResultBlob));
651    }
652
653    // If we have libraries then we need to link...
654    if (libraries.getCount())
655    {
656        ComPtr<IDxcLinker> linker;
657        SLANG_RETURN_ON_FAIL(
658            m_createInstance(CLSID_DxcLinker, __uuidof(linker), (void**)linker.writeRef()));
659
660        StringSlicePool pool(StringSlicePool::Style::Default);
661
662        List<ComPtr<ISlangBlob>> libraryBlobs;
663        List<OSString> libraryNames;
664
665        for (IArtifact* library : libraries)
666        {
667            ComPtr<ISlangBlob> blob;
668            SLANG_RETURN_ON_FAIL(library->loadBlob(ArtifactKeep::Yes, blob.writeRef()));
669
670            libraryBlobs.add(blob);
671            libraryNames.add(String(_addName(library, pool)).toWString());
672        }
673
674        if (hasSource)
675        {
676            // Add the compiled blob name
677            String name;
678            if (options.modulePath.count)
679            {
680                name = Path::getFileNameWithoutExt(asString(options.modulePath));
681            }
682            else if (sourcePath.getLength())
683            {
684                name = Path::getFileNameWithoutExt(sourcePath);
685            }
686
687            // Add the blob with name
688            {
689                auto blob = (ISlangBlob*)dxcResultBlob.get();
690                libraryBlobs.add(ComPtr<ISlangBlob>(blob));
691                libraryNames.add(String(_addName(name.getUnownedSlice(), pool)).toWString());
692            }
693        }
694
695        const Index librariesCount = libraryNames.getCount();
696        SLANG_ASSERT(libraryBlobs.getCount() == librariesCount);
697        SLANG_ASSERT(libraryNames.getCount() == librariesCount);
698
699        List<const wchar_t*> linkLibraryNames;
700
701        linkLibraryNames.setCount(librariesCount);
702
703        for (Index i = 0; i < librariesCount; ++i)
704        {
705            linkLibraryNames[i] = libraryNames[i].begin();
706
707            // Register the library
708            SLANG_RETURN_ON_FAIL(
709                linker->RegisterLibrary(linkLibraryNames[i], (IDxcBlob*)libraryBlobs[i].get()));
710        }
711
712        // Use the original profile name
713        wideProfileName = asString(options.profileName).toWString();
714
715        ComPtr<IDxcOperationResult> linkDxcResult;
716        SLANG_RETURN_ON_FAIL(linker->Link(
717            wideEntryPointName.begin(),
718            wideProfileName.begin(),
719            linkLibraryNames.getBuffer(),
720            UINT32(librariesCount),
721            nullptr,
722            0,
723            linkDxcResult.writeRef()));
724
725        ComPtr<IDxcBlob> linkedBlob;
726        SLANG_RETURN_ON_FAIL(_handleOperationResult(linkDxcResult, diagnostics, linkedBlob));
727
728        // When we've linked we make that the overall operation result
729        // As presumably it can contain pdb and perhaps other information
730        dxcOperationResult = linkDxcResult;
731
732        // Set the result blob
733        dxcResultBlob = linkedBlob;
734    }
735
736    auto artifact = ArtifactUtil::createArtifactForCompileTarget(options.targetType);
737
738    ArtifactUtil::addAssociated(artifact, diagnostics);
739
740    if (dxcResultBlob)
741    {
742        artifact->addRepresentationUnknown((ISlangBlob*)dxcResultBlob.get());
743    }
744
745    // If asking for PDB extract it.
746    if (options.m_debugInfoFormat == SLANG_DEBUG_INFO_FORMAT_PDB)
747    {
748        ComPtr<IDxcResult> dxcResult;
749        if (SLANG_SUCCEEDED(dxcOperationResult->QueryInterface(dxcResult.writeRef())))
750        {
751            if (dxcResult->HasOutput(DXC_OUT_PDB))
752            {
753                ComPtr<IDxcBlob> pdbBlob;
754                ComPtr<IDxcBlobWide> nameBlob;
755
756                if (SLANG_SUCCEEDED(dxcResult->GetOutput(
757                        DXC_OUT_PDB,
758                        __uuidof(pdbBlob),
759                        (void**)pdbBlob.writeRef(),
760                        nameBlob.writeRef())))
761                {
762                    auto pdbArtifact = ArtifactUtil::createArtifact(ArtifactDesc::make(
763                        ArtifactDesc::Kind::BinaryFormat,
764                        ArtifactDesc::Payload::PdbDebugInfo));
765
766                    if (nameBlob)
767                    {
768                        const auto wideName = (const WCHAR*)nameBlob->GetBufferPointer();
769
770                        const auto name = String::fromWString(wideName);
771                        if (name.getLength())
772                        {
773                            // Set the name on the artifact. This is the name that must be used for
774                            // the PDB to be loadable as a file by other tooling.
775                            pdbArtifact->setName(name.getBuffer());
776                        }
777                    }
778
779                    pdbArtifact->addRepresentationUnknown((ISlangBlob*)pdbBlob.get());
780
781                    // Associate it
782                    artifact->addAssociated(pdbArtifact);
783                }
784            }
785        }
786    }
787
788    *outArtifact = artifact.detach();
789    return SLANG_OK;
790}
791
792bool DXCDownstreamCompiler::canConvert(const ArtifactDesc& from, const ArtifactDesc& to)
793{
794    return ArtifactDescUtil::isDisassembly(from, to) && from.payload == ArtifactPayload::DXIL;
795}
796
797SlangResult DXCDownstreamCompiler::convert(
798    IArtifact* from,
799    const ArtifactDesc& to,
800    IArtifact** outArtifact)
801{
802    // Can only disassemble blobs that are DXIL
803    if (!canConvert(from->getDesc(), to))
804    {
805        return SLANG_FAIL;
806    }
807
808    ComPtr<ISlangBlob> dxilBlob;
809    SLANG_RETURN_ON_FAIL(from->loadBlob(ArtifactKeep::No, dxilBlob.writeRef()));
810
811    ComPtr<IDxcCompiler> dxcCompiler;
812    SLANG_RETURN_ON_FAIL(m_createInstance(
813        CLSID_DxcCompiler,
814        __uuidof(dxcCompiler),
815        (LPVOID*)dxcCompiler.writeRef()));
816    ComPtr<IDxcLibrary> dxcLibrary;
817    SLANG_RETURN_ON_FAIL(
818        m_createInstance(CLSID_DxcLibrary, __uuidof(dxcLibrary), (LPVOID*)dxcLibrary.writeRef()));
819
820    // Create blob from the input data
821    ComPtr<IDxcBlobEncoding> dxcSourceBlob;
822    SLANG_RETURN_ON_FAIL(dxcLibrary->CreateBlobWithEncodingFromPinned(
823        (LPBYTE)dxilBlob->getBufferPointer(),
824        (UINT32)dxilBlob->getBufferSize(),
825        0,
826        dxcSourceBlob.writeRef()));
827
828    ComPtr<IDxcBlobEncoding> dxcResultBlob;
829    SLANG_RETURN_ON_FAIL(dxcCompiler->Disassemble(dxcSourceBlob, dxcResultBlob.writeRef()));
830
831    auto artifact = ArtifactUtil::createArtifact(to);
832
833    // Is compatible with ISlangBlob
834    ISlangBlob* disassemblyBlob = (ISlangBlob*)dxcResultBlob.get();
835    artifact->addRepresentationUnknown(disassemblyBlob);
836
837    *outArtifact = artifact.detach();
838    return SLANG_OK;
839}
840
841SlangResult DXCDownstreamCompiler::getVersionString(slang::IBlob** outVersionString)
842{
843    StringBuilder versionString;
844    // Append the version
845    m_desc.version.append(versionString);
846
847    if (m_commitHash.getLength())
848    {
849        versionString << "#" << m_commitHash;
850    }
851    else
852    {
853        // If we don't have the commitHash, we use the library timestamp, to uniquely identify.
854        versionString << " "
855                      << SharedLibraryUtils::getSharedLibraryTimestamp(
856                             reinterpret_cast<void*>(m_createInstance));
857    }
858
859    *outVersionString = StringBlob::moveCreate(versionString).detach();
860    return SLANG_OK;
861}
862
863/* static */ SlangResult DXCDownstreamCompilerUtil::locateCompilers(
864    const String& path,
865    ISlangSharedLibraryLoader* loader,
866    DownstreamCompilerSet* set)
867{
868    ComPtr<ISlangSharedLibrary> library;
869
870    const char* dependentNames[] = {"dxil", nullptr};
871    SLANG_RETURN_ON_FAIL(DownstreamCompilerUtil::loadSharedLibrary(
872        path,
873        loader,
874        dependentNames,
875        "dxcompiler",
876        library));
877
878    SLANG_ASSERT(library);
879    if (!library)
880    {
881        return SLANG_FAIL;
882    }
883
884    auto compiler = new DXCDownstreamCompiler;
885    ComPtr<IDownstreamCompiler> compilerIntf(compiler);
886    SLANG_RETURN_ON_FAIL(compiler->init(library));
887
888    set->addCompiler(compilerIntf);
889    return SLANG_OK;
890}
891
892#else // SLANG_ENABLE_DXIL_SUPPORT
893
894/* static */ SlangResult DXCDownstreamCompilerUtil::locateCompilers(
895    const String& path,
896    ISlangSharedLibraryLoader* loader,
897    DownstreamCompilerSet* set)
898{
899    SLANG_UNUSED(path);
900    SLANG_UNUSED(loader);
901    SLANG_UNUSED(set);
902    return SLANG_E_NOT_AVAILABLE;
903}
904
905#endif // SLANG_ENABLE_DXIL_SUPPORT
906
907} // namespace Slang