yum-mirror/slang

Making it easier to work with shaders

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

Yong HeAdd Slang Byte Code generation and interpreter. (#6896)c39c29bf4

master
32.3 KiB1061 linesraw
1// slang-artifact-desc-util.cpp
2#include "slang-artifact-desc-util.h"
3
4#include "../core/slang-io.h"
5#include "../core/slang-type-text-util.h"
6#include "slang-artifact-impl.h"
7#include "slang-artifact-representation.h"
8
9namespace Slang
10{
11
12namespace
13{ // anonymous
14
15struct HierarchicalEnumEntry
16{
17    Index value;
18    Index parent;
19    const char* name;
20};
21
22static bool _isHierarchicalEnumOk(ConstArrayView<HierarchicalEnumEntry> entries, Count countOf)
23{
24    // All values should be set
25    if (entries.getCount() != countOf)
26    {
27        return false;
28    }
29
30    List<uint8_t> isUsed;
31    isUsed.setCount(countOf);
32    ::memset(isUsed.getBuffer(), 0, countOf);
33
34    for (const auto& entry : entries)
35    {
36        const auto value = entry.value;
37        // Must be in range
38        if (value < 0 || value >= countOf)
39        {
40            return false;
41        }
42
43        if (isUsed[value] != 0)
44        {
45            return false;
46        }
47        // Mark as used
48        isUsed[value]++;
49    }
50
51    // There can't be any gaps
52    for (auto v : isUsed)
53    {
54        if (v == 0)
55        {
56            return false;
57        }
58    }
59
60    // Okay, looks reasonable..
61    return true;
62}
63
64template<typename T>
65struct HierarchicalEnumTable
66{
67    HierarchicalEnumTable(ConstArrayView<HierarchicalEnumEntry> entries)
68    {
69        // Remove warnings around this not being used.
70        {
71            const auto unused = _isHierarchicalEnumOk;
72            SLANG_UNUSED(unused);
73        }
74
75        SLANG_COMPILE_TIME_ASSERT(Index(T::Invalid) < Index(T::Base));
76        SLANG_ASSERT(entries.getCount() == Count(T::CountOf));
77
78        SLANG_ASSERT(_isHierarchicalEnumOk(entries, Count(T::CountOf)));
79
80        ::memset(&m_parents, 0, sizeof(m_parents));
81
82        for (const auto& entry : entries)
83        {
84            const auto value = entry.value;
85            m_parents[value] = T(entry.parent);
86            m_names[value] = UnownedStringSlice(entry.name);
87        }
88
89        // TODO(JS): NOTE! If we wanted to use parent to indicate if a value was *invalid*
90        // we would want the Parent of Base to be Base.
91        //
92        // Base parent should be invalid
93        SLANG_ASSERT(getParent(T::Base) == T::Invalid);
94        // Invalids parent should be invalid
95        SLANG_ASSERT(getParent(T::Invalid) == T::Invalid);
96    }
97
98    T getParent(T kind) const { return (kind >= T::CountOf) ? T::Invalid : m_parents[Index(kind)]; }
99    UnownedStringSlice getName(T kind) const
100    {
101        return (kind >= T::CountOf) ? UnownedStringSlice() : m_names[Index(kind)];
102    }
103
104    bool isDerivedFrom(T type, T base) const
105    {
106        if (Index(type) >= Index(T::CountOf))
107        {
108            return false;
109        }
110
111        do
112        {
113            if (type == base)
114            {
115                return true;
116            }
117            type = m_parents[Index(type)];
118        } while (Index(type) >= Index(T::Base));
119
120        return false;
121    }
122
123protected:
124    T m_parents[Count(T::CountOf)];
125    UnownedStringSlice m_names[Count(T::CountOf)];
126};
127
128} // namespace
129
130// Macro utils to create "enum hierarchy" tables
131
132#define SLANG_HIERARCHICAL_ENUM_GET_VALUES(ENUM_TYPE, ENUM_TYPE_MACRO, ENUM_ENTRY_MACRO)   \
133    static ConstArrayView<HierarchicalEnumEntry> _getEntries##ENUM_TYPE()                  \
134    {                                                                                      \
135        static const HierarchicalEnumEntry values[] = {ENUM_TYPE_MACRO(ENUM_ENTRY_MACRO)}; \
136        return makeConstArrayView(values);                                                 \
137    }
138
139#define SLANG_HIERARCHICAL_ENUM(ENUM_TYPE, ENUM_TYPE_MACRO, ENUM_VALUE_MACRO)                   \
140    SLANG_HIERARCHICAL_ENUM_GET_VALUES(ENUM_TYPE, ENUM_TYPE_MACRO, ENUM_VALUE_MACRO)            \
141                                                                                                \
142    static const HierarchicalEnumTable<ENUM_TYPE> g_table##ENUM_TYPE(_getEntries##ENUM_TYPE()); \
143                                                                                                \
144    ENUM_TYPE getParent(ENUM_TYPE kind)                                                         \
145    {                                                                                           \
146        return g_table##ENUM_TYPE.getParent(kind);                                              \
147    }                                                                                           \
148    UnownedStringSlice getName(ENUM_TYPE kind)                                                  \
149    {                                                                                           \
150        return g_table##ENUM_TYPE.getName(kind);                                                \
151    }                                                                                           \
152    bool isDerivedFrom(ENUM_TYPE kind, ENUM_TYPE base)                                          \
153    {                                                                                           \
154        return g_table##ENUM_TYPE.isDerivedFrom(kind, base);                                    \
155    }
156
157/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ArtifactKind !!!!!!!!!!!!!!!!!!!!!!! */
158
159// clang-format off
160#define SLANG_ARTIFACT_KIND(x) \
161    x(Invalid, Invalid) \
162    x(Base, Invalid) \
163        x(None, Base) \
164        x(Unknown, Base) \
165        x(BinaryFormat, Base) \
166            x(Container, BinaryFormat) \
167                x(Zip, Container) \
168                x(RiffContainer, Container) \
169                x(RiffLz4Container, Container) \
170                x(RiffDeflateContainer, Container) \
171            x(CompileBinary, BinaryFormat) \
172                x(ObjectCode, CompileBinary) \
173                x(Library, CompileBinary) \
174                x(Executable, CompileBinary) \
175                x(SharedLibrary, CompileBinary) \
176                x(HostCallable, CompileBinary) \
177        x(Text, Base) \
178            x(HumanText, Text) \
179            x(Source, Text) \
180            x(Assembly, Text) \
181            x(Json, Text) \
182        x(Instance, Base)
183
184#define SLANG_ARTIFACT_KIND_ENTRY(TYPE, PARENT) { Index(ArtifactKind::TYPE), Index(ArtifactKind::PARENT), #TYPE },
185
186SLANG_HIERARCHICAL_ENUM(ArtifactKind, SLANG_ARTIFACT_KIND, SLANG_ARTIFACT_KIND_ENTRY)
187
188/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ArtifactPayload !!!!!!!!!!!!!!!!!!!!!!! */
189
190#define SLANG_ARTIFACT_PAYLOAD(x) \
191    x(Invalid, Invalid) \
192    x(Base, Invalid) \
193        x(None, Base) \
194        x(Unknown, Base) \
195        x(Source, Base) \
196            x(C, Source) \
197            x(Cpp, Source) \
198            x(HLSL, Source) \
199            x(GLSL, Source) \
200            x(CUDA, Source) \
201            x(Metal, Source) \
202            x(Slang, Source) \
203            x(WGSL, Source) \
204        x(KernelLike, Base) \
205            x(DXIL, KernelLike) \
206            x(DXBC, KernelLike) \
207            x(SPIRV, KernelLike) \
208            x(PTX, KernelLike) \
209            x(CuBin, KernelLike) \
210            x(MetalAIR, KernelLike) \
211            x(WGSL_SPIRV, KernelLike) \
212        x(CPULike, Base) \
213            x(UnknownCPU, CPULike) \
214            x(X86, CPULike) \
215            x(X86_64, CPULike) \
216            x(Aarch, CPULike) \
217            x(Aarch64, CPULike) \
218            x(HostCPU, CPULike) \
219            x(UniversalCPU, CPULike) \
220        x(GeneralIR, Base) \
221            x(SlangIR, GeneralIR) \
222            x(LLVMIR, GeneralIR) \
223        x(AST, Base) \
224            x(SlangAST, AST) \
225        x(CompileResults, Base) \
226        x(Metadata, Base) \
227            x(DebugInfo, Metadata) \
228                x(PdbDebugInfo, DebugInfo) \
229            x(Diagnostics, Metadata) \
230            x(PostEmitMetadata, Metadata) \
231        x(Miscellaneous, Base) \
232            x(Log, Miscellaneous) \
233            x(Lock, Miscellaneous) \
234        x(SourceMap, Base)
235
236#define SLANG_ARTIFACT_PAYLOAD_ENTRY(TYPE, PARENT) { Index(ArtifactPayload::TYPE), Index(ArtifactPayload::PARENT), #TYPE },
237
238SLANG_HIERARCHICAL_ENUM(ArtifactPayload, SLANG_ARTIFACT_PAYLOAD, SLANG_ARTIFACT_PAYLOAD_ENTRY)
239
240/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ArtifactStyle !!!!!!!!!!!!!!!!!!!!!!! */
241
242#define SLANG_ARTIFACT_STYLE(x) \
243    x(Invalid, Invalid) \
244    x(Base, Invalid) \
245        x(None, Base) \
246        x(Unknown, Base) \
247        x(CodeLike, Base) \
248            x(Kernel, CodeLike) \
249            x(Host, CodeLike) \
250        x(Obfuscated, Base)
251// clang-format on
252
253#define SLANG_ARTIFACT_STYLE_ENTRY(TYPE, PARENT) \
254    {Index(ArtifactStyle::TYPE), Index(ArtifactStyle::PARENT), #TYPE},
255
256SLANG_HIERARCHICAL_ENUM(ArtifactStyle, SLANG_ARTIFACT_STYLE, SLANG_ARTIFACT_STYLE_ENTRY)
257
258/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ArtifactDescUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
259
260/* static */ ArtifactDesc ArtifactDescUtil::makeDescForCompileTarget(SlangCompileTarget target)
261{
262    switch (target)
263    {
264    case SLANG_TARGET_UNKNOWN:
265        return Desc::make(Kind::Unknown, Payload::None, Style::Unknown, 0);
266    case SLANG_TARGET_NONE:
267        return Desc::make(Kind::None, Payload::None, Style::Unknown, 0);
268    case SLANG_GLSL:
269        {
270            // For the moment we Desc::make all just map to GLSL, but we could use flags
271            // or some other mechanism to distinguish the types
272            return Desc::make(Kind::Source, Payload::GLSL, Style::Kernel, 0);
273        }
274    case SLANG_HLSL:
275        return Desc::make(Kind::Source, Payload::HLSL, Style::Kernel, 0);
276    case SLANG_SPIRV:
277        return Desc::make(Kind::ObjectCode, Payload::SPIRV, Style::Kernel, 0);
278    case SLANG_SPIRV_ASM:
279        return Desc::make(Kind::Assembly, Payload::SPIRV, Style::Kernel, 0);
280    case SLANG_DXBC:
281        return Desc::make(Kind::ObjectCode, Payload::DXBC, Style::Kernel, 0);
282    case SLANG_DXBC_ASM:
283        return Desc::make(Kind::Assembly, Payload::DXBC, Style::Kernel, 0);
284    case SLANG_DXIL:
285        return Desc::make(Kind::ObjectCode, Payload::DXIL, Style::Kernel, 0);
286    case SLANG_DXIL_ASM:
287        return Desc::make(Kind::Assembly, Payload::DXIL, Style::Kernel, 0);
288    case SLANG_C_SOURCE:
289        return Desc::make(Kind::Source, Payload::C, Style::Kernel, 0);
290    case SLANG_CPP_SOURCE:
291        return Desc::make(Kind::Source, Payload::Cpp, Style::Kernel, 0);
292    case SLANG_HOST_CPP_SOURCE:
293        return Desc::make(Kind::Source, Payload::Cpp, Style::Host, 0);
294    case SLANG_CPP_PYTORCH_BINDING:
295        return Desc::make(Kind::Source, Payload::Cpp, Style::Host, 0);
296    case SLANG_HOST_EXECUTABLE:
297        return Desc::make(Kind::Executable, Payload::HostCPU, Style::Host, 0);
298    case SLANG_HOST_SHARED_LIBRARY:
299        return Desc::make(Kind::SharedLibrary, Payload::HostCPU, Style::Host, 0);
300    case SLANG_SHADER_SHARED_LIBRARY:
301        return Desc::make(Kind::SharedLibrary, Payload::HostCPU, Style::Kernel, 0);
302    case SLANG_SHADER_HOST_CALLABLE:
303        return Desc::make(Kind::HostCallable, Payload::HostCPU, Style::Kernel, 0);
304    case SLANG_CUDA_SOURCE:
305        return Desc::make(Kind::Source, Payload::CUDA, Style::Kernel, 0);
306        // TODO(JS):
307        // Not entirely clear how best to represent PTX here. We could mark as 'Assembly'.
308        // Saying it is 'Executable' implies it is Binary (which PTX isn't). Executable also
309        // implies 'complete for executation', irrespective of it being text.
310    case SLANG_PTX:
311        return Desc::make(Kind::ObjectCode, Payload::PTX, Style::Kernel, 0);
312    case SLANG_OBJECT_CODE:
313        return Desc::make(Kind::ObjectCode, Payload::HostCPU, Style::Kernel, 0);
314    case SLANG_HOST_HOST_CALLABLE:
315        return Desc::make(Kind::HostCallable, Payload::HostCPU, Style::Host, 0);
316    case SLANG_METAL:
317        return Desc::make(Kind::Source, Payload::Metal, Style::Kernel, 0);
318    case SLANG_METAL_LIB:
319        return Desc::make(Kind::ObjectCode, Payload::MetalAIR, Style::Kernel, 0);
320    case SLANG_METAL_LIB_ASM:
321        return Desc::make(Kind::Assembly, Payload::MetalAIR, Style::Kernel, 0);
322    case SLANG_WGSL:
323        return Desc::make(Kind::Source, Payload::WGSL, Style::Kernel, 0);
324    case SLANG_WGSL_SPIRV_ASM:
325        return Desc::make(Kind::Assembly, Payload::WGSL_SPIRV, Style::Kernel, 0);
326    case SLANG_WGSL_SPIRV:
327        return Desc::make(Kind::ObjectCode, Payload::WGSL_SPIRV, Style::Kernel, 0);
328
329    case SLANG_HOST_VM:
330        return Desc::make(Kind::ObjectCode, Payload::UniversalCPU, Style::Host, 0);
331    default:
332        break;
333    }
334
335    SLANG_UNEXPECTED("Unhandled type");
336}
337
338
339/* static */ ArtifactPayload ArtifactDescUtil::getPayloadForSourceLanaguage(
340    SlangSourceLanguage language)
341{
342    switch (language)
343    {
344    default:
345    case SLANG_SOURCE_LANGUAGE_UNKNOWN:
346        return Payload::Unknown;
347    case SLANG_SOURCE_LANGUAGE_SLANG:
348        return Payload::Slang;
349    case SLANG_SOURCE_LANGUAGE_HLSL:
350        return Payload::HLSL;
351    case SLANG_SOURCE_LANGUAGE_GLSL:
352        return Payload::GLSL;
353    case SLANG_SOURCE_LANGUAGE_C:
354        return Payload::C;
355    case SLANG_SOURCE_LANGUAGE_CPP:
356        return Payload::Cpp;
357    case SLANG_SOURCE_LANGUAGE_CUDA:
358        return Payload::CUDA;
359    }
360}
361
362/* static */ ArtifactDesc ArtifactDescUtil::makeDescForSourceLanguage(SlangSourceLanguage language)
363{
364    return Desc::make(Kind::Source, getPayloadForSourceLanaguage(language), Style::Unknown, 0);
365}
366
367/* static */ SlangCompileTarget ArtifactDescUtil::getCompileTargetFromDesc(const ArtifactDesc& desc)
368{
369    switch (desc.kind)
370    {
371    case ArtifactKind::None:
372        return SLANG_TARGET_NONE;
373    case ArtifactKind::Source:
374        {
375            switch (desc.payload)
376            {
377            case Payload::HLSL:
378                return SLANG_HLSL;
379            case Payload::GLSL:
380                return SLANG_GLSL;
381            case Payload::C:
382                return SLANG_C_SOURCE;
383            case Payload::Cpp:
384                return (desc.style == Style::Host) ? SLANG_HOST_CPP_SOURCE : SLANG_CPP_SOURCE;
385            case Payload::CUDA:
386                return SLANG_CUDA_SOURCE;
387            case Payload::Metal:
388                return SLANG_METAL;
389            case Payload::WGSL:
390                return SLANG_WGSL;
391            default:
392                break;
393            }
394            break;
395        }
396    case ArtifactKind::Assembly:
397        {
398            switch (desc.payload)
399            {
400            case Payload::SPIRV:
401                return SLANG_SPIRV_ASM;
402            case Payload::DXIL:
403                return SLANG_DXIL_ASM;
404            case Payload::DXBC:
405                return SLANG_DXBC_ASM;
406            case Payload::PTX:
407                return SLANG_PTX;
408            case Payload::MetalAIR:
409                return SLANG_METAL_LIB_ASM;
410            case Payload::WGSL_SPIRV:
411                return SLANG_WGSL_SPIRV_ASM;
412            default:
413                break;
414            }
415        }
416    default:
417        break;
418    }
419
420    if (isDerivedFrom(desc.kind, ArtifactKind::CompileBinary))
421    {
422        if (isDerivedFrom(desc.payload, ArtifactPayload::CPULike))
423        {
424            switch (desc.kind)
425            {
426            case Kind::Executable:
427                return SLANG_HOST_EXECUTABLE;
428            case Kind::SharedLibrary:
429                return desc.style == ArtifactStyle::Host ? SLANG_HOST_SHARED_LIBRARY
430                                                         : SLANG_SHADER_SHARED_LIBRARY;
431            case Kind::HostCallable:
432                return desc.style == ArtifactStyle::Host ? SLANG_HOST_HOST_CALLABLE
433                                                         : SLANG_SHADER_HOST_CALLABLE;
434            case Kind::ObjectCode:
435                return SLANG_OBJECT_CODE;
436            default:
437                break;
438            }
439        }
440        else
441        {
442            switch (desc.payload)
443            {
444            case Payload::SPIRV:
445                return SLANG_SPIRV;
446            case Payload::DXIL:
447                return SLANG_DXIL;
448            case Payload::DXBC:
449                return SLANG_DXBC;
450            case Payload::PTX:
451                return SLANG_PTX;
452            case Payload::MetalAIR:
453                return SLANG_METAL_LIB_ASM;
454            case Payload::WGSL_SPIRV:
455                return SLANG_WGSL_SPIRV;
456            default:
457                break;
458            }
459        }
460    }
461
462    return SLANG_TARGET_UNKNOWN;
463}
464
465
466namespace
467{ // anonymous
468struct KindExtension
469{
470    ArtifactKind kind;
471    UnownedStringSlice ext;
472};
473} // namespace
474
475#define SLANG_KIND_EXTENSION(kind, ext) {ArtifactKind::kind, toSlice(ext)},
476
477static const KindExtension g_cpuKindExts[] = {
478#if SLANG_WINDOWS_FAMILY
479    SLANG_KIND_EXTENSION(Library, "lib") SLANG_KIND_EXTENSION(ObjectCode, "obj")
480        SLANG_KIND_EXTENSION(Executable, "exe") SLANG_KIND_EXTENSION(SharedLibrary, "dll")
481#else
482    SLANG_KIND_EXTENSION(Library, "a") SLANG_KIND_EXTENSION(ObjectCode, "o")
483        SLANG_KIND_EXTENSION(Executable, "")
484
485#if __CYGWIN__
486            SLANG_KIND_EXTENSION(SharedLibrary, "dll")
487#elif SLANG_APPLE_FAMILY
488            SLANG_KIND_EXTENSION(SharedLibrary, "dylib")
489#else
490            SLANG_KIND_EXTENSION(SharedLibrary, "so")
491#endif
492
493#endif
494};
495
496/* static */ bool ArtifactDescUtil::isCpuBinary(const ArtifactDesc& desc)
497{
498    return isDerivedFrom(desc.kind, ArtifactKind::CompileBinary) &&
499           isDerivedFrom(desc.payload, ArtifactPayload::CPULike);
500}
501
502/* static */ bool ArtifactDescUtil::isText(const ArtifactDesc& desc)
503{
504    // If it's derived from text...
505    if (isDerivedFrom(desc.kind, ArtifactKind::Text))
506    {
507        return true;
508    }
509
510    // Special case PTX...
511    if (isDerivedFrom(desc.kind, ArtifactKind::CompileBinary))
512    {
513        return desc.payload == ArtifactPayload::PTX;
514    }
515
516    // Not text
517    return false;
518}
519
520/* static */ bool ArtifactDescUtil::isGpuUsable(const ArtifactDesc& desc)
521{
522    if (isDerivedFrom(desc.kind, ArtifactKind::CompileBinary))
523    {
524        return isDerivedFrom(desc.payload, ArtifactPayload::KernelLike);
525    }
526
527    // PTX is a kind of special case, it's an 'assembly' (low level text represention) that can be
528    // passed to CUDA runtime
529    return desc.kind == ArtifactKind::Assembly && desc.payload == ArtifactPayload::PTX;
530}
531
532/* static */ bool ArtifactDescUtil::isKindBinaryLinkable(Kind kind)
533{
534    switch (kind)
535    {
536    case Kind::Library:
537    case Kind::ObjectCode:
538        {
539            return true;
540        }
541    default:
542        break;
543    }
544    return false;
545}
546
547/* static */ bool ArtifactDescUtil::isLinkable(const ArtifactDesc& desc)
548{
549    // If is a container with compile results *assume* that result is linkable
550    if (isDerivedFrom(desc.kind, ArtifactKind::Container) &&
551        isDerivedFrom(desc.payload, ArtifactPayload::CompileResults))
552    {
553        return true;
554    }
555
556    // if it's a compile binary or a container
557    if (isDerivedFrom(desc.kind, ArtifactKind::CompileBinary))
558    {
559        if (isDerivedFrom(desc.payload, ArtifactPayload::KernelLike))
560        {
561            // It seems as if DXBC is potentially linkable from
562            // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-keywords#export
563
564            // We can't *actually* link PTX or SPIR-V currently but it is in principal possible
565            // so let's say we accept for now
566
567            return true;
568        }
569        else if (isDerivedFrom(desc.payload, ArtifactPayload::CPULike))
570        {
571            // If kind is exe or shared library, linking will arguably not work
572            if (desc.kind == ArtifactKind::SharedLibrary || desc.kind == ArtifactKind::Executable)
573            {
574                return false;
575            }
576
577            return true;
578        }
579        else if (isDerivedFrom(desc.payload, ArtifactPayload::GeneralIR))
580        {
581            // We'll *assume* IR is linkable
582            return true;
583        }
584    }
585    return false;
586}
587
588/* static */ bool ArtifactDescUtil::isCpuLikeTarget(const ArtifactDesc& desc)
589{
590    if (isDerivedFrom(desc.kind, ArtifactKind::CompileBinary))
591    {
592        return isDerivedFrom(desc.payload, ArtifactPayload::CPULike);
593    }
594    else if (isDerivedFrom(desc.kind, ArtifactKind::Source))
595    {
596        // We'll assume C/C++ are targetting CPU, although that is perhaps somewhat arguable.
597        return desc.payload == Payload::C || desc.payload == Payload::Cpp;
598    }
599
600    return false;
601}
602
603/* static */ ArtifactDesc ArtifactDescUtil::getDescFromExtension(const UnownedStringSlice& slice)
604{
605    if (slice == "slang-module" || slice == "slang-lib")
606    {
607        return ArtifactDesc::make(ArtifactKind::Library, ArtifactPayload::SlangIR);
608    }
609
610    // Metal
611    // https://developer.apple.com/documentation/metal/shader_libraries/building_a_library_with_metal_s_command-line_tools
612    if (slice == toSlice("air"))
613    {
614        return ArtifactDesc::make(ArtifactKind::ObjectCode, ArtifactPayload::MetalAIR);
615    }
616    else if (slice == toSlice("metallib") || slice == toSlice("metalar"))
617    {
618        return ArtifactDesc::make(ArtifactKind::Library, ArtifactPayload::MetalAIR);
619    }
620
621    if (slice == toSlice("zip"))
622    {
623        return ArtifactDesc::make(ArtifactKind::Zip, ArtifactPayload::Unknown);
624    }
625
626    if (slice.startsWith(toSlice("riff")))
627    {
628        auto tail = slice.tail(4);
629        if (tail.getLength() == 0)
630        {
631            return ArtifactDesc::make(ArtifactKind::RiffContainer, ArtifactPayload::Unknown);
632        }
633        else if (tail == "-lz4")
634        {
635            return ArtifactDesc::make(ArtifactKind::RiffLz4Container, ArtifactPayload::Unknown);
636        }
637        else if (tail == "-deflate")
638        {
639            return ArtifactDesc::make(ArtifactKind::RiffDeflateContainer, ArtifactPayload::Unknown);
640        }
641    }
642
643    if (slice == toSlice("asm"))
644    {
645        // We'll assume asm means current CPU assembler..
646        return ArtifactDesc::make(ArtifactKind::Assembly, ArtifactPayload::HostCPU);
647    }
648
649    // TODO(JS): Unfortunately map extension is also used from output for linkage from
650    // Visual Studio. It's used here for source map.
651    if (slice == toSlice("map"))
652    {
653        return ArtifactDesc::make(ArtifactKind::Json, ArtifactPayload::SourceMap);
654    }
655
656    if (slice == toSlice("pdb"))
657    {
658        // Program database
659        return ArtifactDesc::make(ArtifactKind::Assembly, ArtifactPayload::PdbDebugInfo);
660    }
661
662    for (const auto& kindExt : g_cpuKindExts)
663    {
664        if (slice == kindExt.ext)
665        {
666            // We'll assume it's for the host CPU for now..
667            return ArtifactDesc::make(kindExt.kind, Payload::HostCPU);
668        }
669    }
670
671    const auto target = TypeTextUtil::findCompileTargetFromExtension(slice);
672
673    return makeDescForCompileTarget(target);
674}
675
676/* static */ ArtifactDesc ArtifactDescUtil::getDescFromPath(const UnownedStringSlice& slice)
677{
678    auto extension = Path::getPathExt(slice);
679    return getDescFromExtension(extension);
680}
681
682/* static*/ SlangResult ArtifactDescUtil::appendCpuExtensionForKind(Kind kind, StringBuilder& out)
683{
684    for (const auto& kindExt : g_cpuKindExts)
685    {
686        if (kind == kindExt.kind)
687        {
688            out << kindExt.ext;
689            return SLANG_OK;
690        }
691    }
692    return SLANG_E_NOT_FOUND;
693}
694
695static UnownedStringSlice _getPayloadExtension(ArtifactPayload payload)
696{
697    typedef ArtifactPayload Payload;
698    switch (payload)
699    {
700    /* Source types */
701    case Payload::HLSL:
702        return toSlice("hlsl");
703    case Payload::GLSL:
704        return toSlice("glsl");
705
706    case Payload::Cpp:
707        return toSlice("cpp");
708    case Payload::C:
709        return toSlice("c");
710
711    case Payload::Metal:
712        return toSlice("metal");
713
714    case Payload::CUDA:
715        return toSlice("cu");
716
717    case Payload::Slang:
718        return toSlice("slang");
719
720    /* Binary types */
721    case Payload::DXIL:
722        return toSlice("dxil");
723    case Payload::DXBC:
724        return toSlice("dxbc");
725    case Payload::SPIRV:
726        return toSlice("spv");
727
728    case Payload::PTX:
729        return toSlice("ptx");
730
731    case Payload::LLVMIR:
732        return toSlice("llvm-ir");
733
734    case Payload::SlangIR:
735        return toSlice("slang-ir");
736
737    case Payload::MetalAIR:
738        return toSlice("metallib");
739
740    case Payload::PdbDebugInfo:
741        return toSlice("pdb");
742    case Payload::SourceMap:
743        return toSlice("map");
744
745    default:
746        break;
747    }
748    return UnownedStringSlice();
749}
750
751SlangResult ArtifactDescUtil::appendDefaultExtension(const ArtifactDesc& desc, StringBuilder& out)
752{
753    switch (desc.kind)
754    {
755    case ArtifactKind::Library:
756        {
757            // Special cases
758            if (desc.payload == Payload::SlangIR)
759            {
760                out << toSlice("slang-module");
761                return SLANG_OK;
762            }
763            else if (desc.payload == Payload::MetalAIR)
764            {
765                // https://developer.apple.com/documentation/metal/shader_libraries/building_a_library_with_metal_s_command-line_tools
766                out << toSlice("metallib");
767                return SLANG_OK;
768            }
769
770            break;
771        }
772    case ArtifactKind::Zip:
773        {
774            out << toSlice("zip");
775            return SLANG_OK;
776        }
777    case ArtifactKind::RiffContainer:
778        {
779            out << toSlice("riff");
780            return SLANG_OK;
781        }
782    case ArtifactKind::RiffLz4Container:
783        {
784            out << toSlice("riff-lz4");
785            return SLANG_OK;
786        }
787    case ArtifactKind::RiffDeflateContainer:
788        {
789            out << toSlice("riff-deflate");
790            return SLANG_OK;
791        }
792    case ArtifactKind::Assembly:
793        {
794            // Special case PTX, because it is assembly
795            if (desc.payload == Payload::PTX)
796            {
797                out << _getPayloadExtension(desc.payload);
798                return SLANG_OK;
799            }
800
801            // We'll just use asm for all CPU assembly type
802            if (isDerivedFrom(desc.payload, ArtifactPayload::CPULike))
803            {
804                out << toSlice("asm");
805                return SLANG_OK;
806            }
807
808            // Use the payload extension "-asm"
809            out << _getPayloadExtension(desc.payload);
810            out << toSlice("-asm");
811            return SLANG_OK;
812        }
813    case ArtifactKind::Source:
814        {
815            auto ext = _getPayloadExtension(desc.payload);
816            if (ext.begin() != nullptr)
817            {
818                out << ext;
819                return SLANG_OK;
820            }
821            // Don't know the extension for that
822            return SLANG_E_NOT_FOUND;
823        }
824    case ArtifactKind::Json:
825        {
826            auto ext = _getPayloadExtension(desc.payload);
827            if (ext.begin() != nullptr)
828            {
829                // TODO(JS):
830                // Do we need to alter the extension or the name if it's an
831                // obfuscated map?
832                // if (isDerivedFrom(desc.style, ArtifactStyle::Obfuscated))
833                //{
834                //}
835
836                out << ext;
837                return SLANG_OK;
838            }
839
840            // Not really what kind of json, so just use 'generic' json extension
841            out << "json";
842            return SLANG_OK;
843        }
844    case ArtifactKind::CompileBinary:
845        {
846            if (isDerivedFrom(desc.payload, ArtifactPayload::SlangIR) ||
847                isDerivedFrom(desc.payload, ArtifactPayload::SlangAST))
848            {
849                out << "slang-module";
850                return SLANG_OK;
851            }
852            break;
853        }
854    default:
855        break;
856    }
857
858    if (ArtifactDescUtil::isGpuUsable(desc))
859    {
860        auto ext = _getPayloadExtension(desc.payload);
861        if (ext.getLength())
862        {
863            out << ext;
864            return SLANG_OK;
865        }
866    }
867
868    if (ArtifactDescUtil::isCpuLikeTarget(desc) &&
869        !isDerivedFrom(desc.payload, ArtifactPayload::Source))
870    {
871        return appendCpuExtensionForKind(desc.kind, out);
872    }
873
874    return SLANG_E_NOT_FOUND;
875}
876
877/* static */ String ArtifactDescUtil::getBaseNameFromPath(
878    const ArtifactDesc& desc,
879    const UnownedStringSlice& path)
880{
881    const String name = Path::getFileName(path);
882    return getBaseNameFromName(desc, name.getUnownedSlice());
883}
884
885/* static */ String ArtifactDescUtil::getBaseNameFromName(
886    const ArtifactDesc& desc,
887    const UnownedStringSlice& inName)
888{
889    String name(inName);
890
891    const bool isSharedLibraryPrefixPlatform = SLANG_LINUX_FAMILY || SLANG_APPLE_FAMILY;
892    if (isSharedLibraryPrefixPlatform)
893    {
894        // Strip lib prefix
895        if (isCpuBinary(desc) &&
896            (desc.kind == ArtifactKind::Library || desc.kind == ArtifactKind::SharedLibrary))
897        {
898            // If it starts with lib strip it
899            if (name.startsWith("lib"))
900            {
901                const String stripLib = name.getUnownedSlice().tail(3);
902                name = stripLib;
903            }
904        }
905    }
906
907    // Strip any extension
908    {
909        StringBuilder descExt;
910        if (SLANG_SUCCEEDED(appendDefaultExtension(desc, descExt)) && descExt.getLength())
911        {
912            // TODO(JS):
913            // It has an extension. We could check if they are the same
914            // but if they are not that might be fine, because of case insensitivity
915            // or perhaps there are multiple valid extensions. So for now we just strip
916            // and don't bother confirming with something like..
917            // if (Path::getPathExt(name) == descExt))
918
919            name = Path::getFileNameWithoutExt(name);
920        }
921    }
922
923    return name;
924}
925
926/* static */ String ArtifactDescUtil::getBaseName(
927    const ArtifactDesc& desc,
928    IPathArtifactRepresentation* pathRep)
929{
930    UnownedStringSlice path(pathRep->getPath());
931    return getBaseNameFromPath(desc, path);
932}
933
934/* static */ SlangResult ArtifactDescUtil::hasDefinedNameForDesc(const ArtifactDesc& desc)
935{
936    StringBuilder buf;
937    return SLANG_SUCCEEDED(appendDefaultExtension(desc, buf));
938}
939
940/* static */ SlangResult ArtifactDescUtil::calcNameForDesc(
941    const ArtifactDesc& desc,
942    const UnownedStringSlice& inBaseName,
943    StringBuilder& outName)
944{
945    UnownedStringSlice baseName(inBaseName);
946
947    // If there is no basename, set one
948    if (baseName.getLength() == 0)
949    {
950        baseName = toSlice("unknown");
951    }
952
953    // Prefix
954    if (isCpuBinary(desc) &&
955        (desc.kind == ArtifactKind::SharedLibrary || desc.kind == ArtifactKind::Library))
956    {
957        const bool isSharedLibraryPrefixPlatform = SLANG_LINUX_FAMILY || SLANG_APPLE_FAMILY;
958        if (isSharedLibraryPrefixPlatform)
959        {
960            outName << "lib";
961        }
962    }
963
964    // Output the basename
965    outName << baseName;
966
967    // If there is an extension append it
968    StringBuilder ext;
969    if (SLANG_SUCCEEDED(appendDefaultExtension(desc, ext)))
970    {
971        if (ext.getLength())
972        {
973            outName.appendChar('.');
974            outName.append(ext);
975        }
976    }
977    else
978    {
979        // If we can't determine the type we can output with .unknown
980        outName.append(toSlice(".unknown"));
981    }
982
983    return SLANG_OK;
984}
985
986/* static */ SlangResult ArtifactDescUtil::calcPathForDesc(
987    const ArtifactDesc& desc,
988    const UnownedStringSlice& basePath,
989    StringBuilder& outPath)
990{
991    outPath.clear();
992
993    // Append the directory
994    Index pos = Path::findLastSeparatorIndex(basePath);
995    if (pos >= 0)
996    {
997        // Keep the stem including the delimiter
998        outPath.append(basePath.head(pos + 1));
999
1000        StringBuilder buf;
1001        const auto baseName = basePath.tail(pos + 1);
1002
1003        SLANG_RETURN_ON_FAIL(calcNameForDesc(desc, baseName, buf));
1004        outPath.append(buf);
1005
1006        return SLANG_OK;
1007    }
1008    else
1009    {
1010        return calcNameForDesc(desc, basePath, outPath);
1011    }
1012}
1013
1014/* static */ bool ArtifactDescUtil::isDisassembly(const ArtifactDesc& from, const ArtifactDesc& to)
1015{
1016    // From must be a binary like type
1017    if (!isDerivedFrom(from.kind, ArtifactKind::CompileBinary))
1018    {
1019        return false;
1020    }
1021
1022
1023    // Target must be assembly, and the payload be the same type
1024    if (!(to.kind == ArtifactKind::Assembly && to.payload == from.payload))
1025    {
1026        return false;
1027    }
1028
1029    const auto payload = from.payload;
1030
1031    // Check the payload seems like something plausible to 'disassemble'
1032    if (!(isDerivedFrom(payload, ArtifactPayload::KernelLike) ||
1033          isDerivedFrom(payload, ArtifactPayload::CPULike) ||
1034          isDerivedFrom(payload, ArtifactPayload::GeneralIR)))
1035    {
1036        return false;
1037    }
1038
1039    // If the flags or style are different, then it's something more than just disassembly
1040    if (!(from.style == to.style && from.flags == to.flags))
1041    {
1042        return false;
1043    }
1044
1045    return true;
1046}
1047
1048/* static */ void ArtifactDescUtil::appendText(const ArtifactDesc& desc, StringBuilder& out)
1049{
1050    out << getName(desc.kind) << "/" << getName(desc.payload) << "/" << getName(desc.style);
1051    // TODO(JS): Output flags? None currently used
1052}
1053
1054/* static */ String ArtifactDescUtil::getText(const ArtifactDesc& desc)
1055{
1056    StringBuilder buf;
1057    appendText(desc, buf);
1058    return buf;
1059}
1060
1061} // namespace Slang