yum-mirror/slang

Making it easier to work with shaders

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

Ellie HermaszewskaMove switch statement bodies to their own lines (#5493)b118451e3

master
14.8 KiB460 linesraw
1#include "../../source/compiler-core/slang-diagnostic-sink.h"
2#include "../../source/compiler-core/slang-lexer.h"
3#include "../../source/compiler-core/slang-perfect-hash.h"
4#include "../../source/compiler-core/slang-spirv-core-grammar.h"
5#include "../../source/core/slang-dictionary.h"
6#include "../../source/core/slang-io.h"
7#include "../../source/core/slang-writer.h"
8
9#include <cstdio>
10
11using namespace Slang;
12
13//
14// Go from a dictionary to a C++ embedding of a perfect hash
15//
16template<typename S, typename T, typename F>
17String dictToPerfectHash(
18    const Dictionary<S, T>& dict,
19    const UnownedStringSlice& type,
20    const UnownedStringSlice& funcName,
21    F valueToString)
22{
23    HashParams hashParams;
24    List<String> names;
25    for (const auto& [name, val] : dict)
26        names.add(name);
27    auto r = minimalPerfectHash(names, hashParams);
28    SLANG_ASSERT(r == HashFindResult::Success);
29    List<String> values;
30    values.reserve(hashParams.destTable.getCount());
31    for (const auto& v : hashParams.destTable)
32    {
33        values.add(valueToString(dict.getValue(v.getUnownedSlice())));
34    }
35    return perfectHashToEmbeddableCpp(hashParams, type, funcName, values);
36}
37
38//
39// Go from a dictionary to a C++ embedding of switch table
40//
41template<typename K, typename V, typename F1, typename F2>
42void dictToSwitch(
43    const Dictionary<K, V>& dict,
44    const char* funName,
45    const char* keyType,
46    const char* valueType,
47    const char* unpackKey,
48    const F1 keyToString,
49    const F2 valueToAssignmentString,
50    WriterHelper& w)
51{
52    const auto line = [&](const auto& l)
53    {
54        w.put(l);
55        w.put("\n");
56    };
57
58    w.print("static bool %s(const %s& k, %s& v)\n", funName, keyType, valueType);
59    line("{");
60    w.print("    switch(%s)\n", unpackKey);
61    line("    {");
62    for (const auto& [k, v] : dict)
63    {
64        const auto kStr = keyToString(k);
65        const auto vStr = valueToAssignmentString(v);
66        w.print(
67            "        case %s:\n"
68            "        {\n"
69            "            %s;\n"
70            "            return true;\n"
71            "        }\n",
72            kStr.getBuffer(),
73            vStr.getBuffer());
74    }
75    line("        default: return false;");
76    line("    }");
77    line("}");
78    line("");
79}
80
81//
82// Go from a dictionary to a C++ embedding of switch table, specific to the
83// two-level table of a QualifiedEnumValue
84//
85template<typename V, typename F>
86void qualifiedEnumValueNameSwitch(
87    const Dictionary<Slang::SPIRVCoreGrammarInfo::QualifiedEnumValue, V>& dict,
88    const char* funName,
89    const char* keyType,
90    const char* valueType,
91    const char* unpackKey1,
92    const F valueToAssignmentString,
93    WriterHelper& w)
94{
95    const auto line = [&](const auto& l)
96    {
97        w.put(l);
98        w.put("\n");
99    };
100
101    using K1 = Slang::SPIRVCoreGrammarInfo::OperandKind;
102    using K2 = SpvWord;
103    Dictionary<K1, Dictionary<K2, V>> stepDict;
104    for (const auto& [k, v] : dict)
105    {
106        const auto& [k1, k2] = k;
107        stepDict[k1][k2] = v;
108    }
109
110    w.print("static bool %s(const %s& k, %s& v)\n", funName, keyType, valueType);
111    line("{");
112    line("    const auto& [k1, k2] = k;");
113    w.print("    switch(%s)\n", unpackKey1);
114    line("    {");
115    for (const auto& [k1, inner] : stepDict)
116    {
117        const auto k1Str = String(k1.index);
118        w.print("        case %s:\n", k1Str.getBuffer());
119
120        line("        switch(k2)");
121        line("        {");
122        for (const auto& [k2, v] : inner)
123        {
124            const auto k2Str = String(k2);
125            const auto vStr = valueToAssignmentString(v);
126            w.print("            case %s: %s; return true;\n", k2Str.getBuffer(), vStr.getBuffer());
127        }
128        line("            default: return false;");
129        line("        }");
130    }
131    line("        default: return false;");
132    line("    }");
133    line("}");
134    line("");
135}
136
137static const char* opClassToString(Slang::SPIRVCoreGrammarInfo::OpInfo::Class c)
138{
139    switch (c)
140    {
141#define GO(n)                             \
142    case SPIRVCoreGrammarInfo::OpInfo::n: \
143        return #n;
144        GO(Miscellaneous)
145        GO(Debug)
146        GO(Annotation)
147        GO(Extension)
148        GO(ModeSetting)
149        GO(TypeDeclaration)
150        GO(ConstantCreation)
151        GO(Memory)
152        GO(Function)
153        GO(Image)
154        GO(Conversion)
155        GO(Composite)
156        GO(Arithmetic)
157        GO(Bit)
158        GO(Relational_and_Logical)
159        GO(Derivative)
160        GO(ControlFlow)
161        GO(Atomic)
162        GO(Primitive)
163        GO(Barrier)
164        GO(Group)
165        GO(DeviceSideEnqueue)
166        GO(Pipe)
167        GO(NonUniform)
168        GO(Reserved)
169    default:
170        GO(Other)
171#undef GO
172    }
173}
174
175//
176// Write a C++ embedding of the SPIRVCoreGrammarInfo struct
177//
178void writeInfo(const char* const outCppPath, const SPIRVCoreGrammarInfo& info)
179{
180    StringBuilder sb;
181    StringWriter writer(&sb, WriterFlags(0));
182    WriterHelper w(&writer);
183    const auto line = [&](const auto& l)
184    {
185        w.put(l);
186        w.put("\n");
187    };
188
189    //
190    // Intro
191    //
192    line("// Source embedding for SPIR-V core grammar");
193    line("//");
194    line("// This file was carefully generated by a machine,");
195    line("// don't even think about modifying it yourself!");
196    line("//");
197    line("");
198    line("#include \"core/slang-smart-pointer.h\"");
199    line("#include \"compiler-core/slang-spirv-core-grammar.h\"");
200    line("namespace Slang");
201    line("{");
202    line("using OperandKind = SPIRVCoreGrammarInfo::OperandKind;");
203    line("using QualifiedEnumName = SPIRVCoreGrammarInfo::QualifiedEnumName;");
204    line("using QualifiedEnumValue = SPIRVCoreGrammarInfo::QualifiedEnumValue;");
205
206    //
207    // Each block writes the lookup function for a member table
208    // Read the memberAssignments addition to see which one
209    //
210    List<String> memberAssignments;
211
212
213    {
214        memberAssignments.add("info->opcodes.embedded = &lookupSpvOp;");
215        w.put("static ");
216        w.put(dictToPerfectHash(
217                  info.opcodes.dict,
218                  UnownedStringSlice("SpvOp"),
219                  UnownedStringSlice("lookupSpvOp"),
220                  [](const auto n)
221                  {
222                      const auto radix = 10;
223                      return "static_cast<SpvOp>(" + String(n, radix) + ")";
224                  })
225                  .getBuffer());
226    }
227
228    {
229        memberAssignments.add("info->capabilities.embedded = &lookupSpvCapability;");
230        w.put("static ");
231        w.put(dictToPerfectHash(
232                  info.capabilities.dict,
233                  UnownedStringSlice("SpvCapability"),
234                  UnownedStringSlice("lookupSpvCapability"),
235                  [](const auto n)
236                  {
237                      const auto radix = 10;
238                      return "static_cast<SpvCapability>(" + String(n, radix) + ")";
239                  })
240                  .getBuffer());
241    }
242
243    {
244        memberAssignments.add("info->allEnumsWithTypePrefix.embedded = &lookupEnumWithTypePrefix;");
245        w.put("static ");
246        w.put(dictToPerfectHash(
247                  info.allEnumsWithTypePrefix.dict,
248                  UnownedStringSlice("SpvWord"),
249                  UnownedStringSlice("lookupEnumWithTypePrefix"),
250                  [](const auto n)
251                  {
252                      const auto radix = 10;
253                      return "SpvWord{" + String(n, radix) + "}";
254                  })
255                  .getBuffer());
256    }
257
258    {
259        memberAssignments.add("info->opInfos.embedded = &getOpInfo;");
260        dictToSwitch(
261            info.opInfos.dict,
262            "getOpInfo",
263            "SpvOp",
264            "SPIRVCoreGrammarInfo::OpInfo",
265            "k",
266            [&](SpvOp o) { return "Spv" + String(info.opNames.dict.getValue(o)); },
267            [](const Slang::SPIRVCoreGrammarInfo::OpInfo& i)
268            {
269                const char* classStr = opClassToString(i.class_);
270                String ret;
271                if (i.numOperandTypes)
272                {
273                    ret.append("const static OperandKind operandTypes[] = {");
274                    String operandTypes;
275                    for (Index o = 0; o < i.numOperandTypes; ++o)
276                    {
277                        if (o != 0)
278                            ret.append(", ");
279                        ret.append("{" + String(i.operandTypes[o].index) + "}");
280                    }
281                    ret.append("};\n            ");
282                }
283                ret.append(
284                    String("v = {SPIRVCoreGrammarInfo::OpInfo::") + classStr + ", " +
285                    String(i.resultTypeIndex) + ", " + String(i.resultIdIndex) + ", " +
286                    String(i.minOperandCount) + ", " +
287                    (i.maxOperandCount == 0xffff ? String("0xffff") : String(i.maxOperandCount)) +
288                    ", " + String(i.numOperandTypes) + ", " +
289                    (i.numOperandTypes ? "operandTypes" : "nullptr") + "}");
290                return ret;
291            },
292            w);
293    }
294
295    {
296        memberAssignments.add("info->opNames.embedded = &getOpName;");
297        dictToSwitch(
298            info.opNames.dict,
299            "getOpName",
300            "SpvOp",
301            "UnownedStringSlice",
302            "k",
303            [&](SpvOp o) { return "Spv" + String(info.opNames.dict.getValue(o)); },
304            [](const UnownedStringSlice& i)
305            { return "v = UnownedStringSlice{\"" + String(i) + "\"}"; },
306            w);
307    }
308
309    {
310        memberAssignments.add("info->operandKinds.embedded = &lookupOperandKind;");
311        w.put("static ");
312        w.put(dictToPerfectHash(
313                  info.operandKinds.dict,
314                  UnownedStringSlice("OperandKind"),
315                  UnownedStringSlice("lookupOperandKind"),
316                  [](const auto n)
317                  {
318                      const auto radix = 10;
319                      return "OperandKind{" + String(n.index, radix) + "}";
320                  })
321                  .getBuffer());
322    }
323
324    {
325        memberAssignments.add("info->allEnums.embedded = &lookupQualifiedEnum;");
326
327        // First construct a helper function which will lookup an enum name
328        // with a hex prefix representing the kind. This allows us to just
329        // reuse the existing string-based perfect hasher
330        Dictionary<String, SpvWord> enumDict;
331        Index maxNameLength = 0;
332        for (const auto& [q, v] : info.allEnums.dict)
333        {
334            const auto i = q.kind.index;
335            String k;
336            k.appendChar(char((i >> 4) + 'a'));
337            k.appendChar(char((i & 0xf) + 'a'));
338            k.append(q.name);
339            enumDict.add(k, v);
340            maxNameLength = std::max(maxNameLength, k.getLength());
341        }
342        w.put(dictToPerfectHash(
343                  enumDict,
344                  UnownedStringSlice("SpvWord"),
345                  UnownedStringSlice("lookupEnumWithHexPrefix"),
346                  [&](const auto n) { return "SpvWord{" + String(n) + "}"; })
347                  .getBuffer());
348
349        // Utilise this helper
350        line("static bool lookupQualifiedEnum(const QualifiedEnumName& k, SpvWord& v)");
351        line("{");
352        line("    static_assert(sizeof(k.kind.index) == 1);");
353        w.print("    if(k.name.getLength() > %d)\n", (int)maxNameLength);
354        line("        return false;");
355        w.print("    char name[%d];\n", (int)maxNameLength + 2);
356        line("    name[0] = char((k.kind.index >> 4) + 'a');");
357        line("    name[1] = char((k.kind.index & 0xf) + 'a');");
358        line("    memcpy(name+2, k.name.begin(), k.name.getLength());");
359        line("    return lookupEnumWithHexPrefix(UnownedStringSlice(name, k.name.getLength() + 2), "
360             "v);");
361        line("}");
362        line("");
363    }
364
365    {
366        memberAssignments.add("info->allEnumNames.embedded = &getQualifiedEnumName;");
367        qualifiedEnumValueNameSwitch(
368            info.allEnumNames.dict,
369            "getQualifiedEnumName",
370            "QualifiedEnumValue",
371            "UnownedStringSlice",
372            "k1.index",
373            [](const UnownedStringSlice& i)
374            { return "v = UnownedStringSlice{\"" + String(i) + "\"}"; },
375            w);
376    }
377
378    {
379        memberAssignments.add("info->operandKindNames.embedded = &getOperandKindName;");
380        dictToSwitch(
381            info.operandKindNames.dict,
382            "getOperandKindName",
383            "OperandKind",
384            "UnownedStringSlice",
385            "k.index",
386            [&](Slang::SPIRVCoreGrammarInfo::OperandKind o) { return String(o.index); },
387            [](const UnownedStringSlice& i)
388            { return "v = UnownedStringSlice{\"" + String(i) + "\"}"; },
389            w);
390    }
391
392    {
393        memberAssignments.add(
394            "info->operandKindUnderneathIds.embedded = &getOperandKindUnderneathId;");
395        dictToSwitch(
396            info.operandKindUnderneathIds.dict,
397            "getOperandKindUnderneathId",
398            "OperandKind",
399            "OperandKind",
400            "k.index",
401            [](Slang::SPIRVCoreGrammarInfo::OperandKind o) { return String(o.index); },
402            [](Slang::SPIRVCoreGrammarInfo::OperandKind i)
403            { return "v = OperandKind{" + String(i.index) + "}"; },
404            w);
405    }
406
407    //
408    // Now write out the function which holds onto the static embedded info table
409    //
410    line("RefPtr<SPIRVCoreGrammarInfo>& SPIRVCoreGrammarInfo::getEmbeddedVersion()");
411    line("{");
412    line("    static RefPtr<SPIRVCoreGrammarInfo> embedded = [](){");
413    line("        RefPtr<SPIRVCoreGrammarInfo> info = new SPIRVCoreGrammarInfo();");
414    for (const auto& a : memberAssignments)
415        line(("        " + a).getBuffer());
416
417    //
418    line("        return info;");
419    line("    }();");
420    line("    return embedded;");
421    line("}");
422    line("}");
423
424    File::writeAllTextIfChanged(outCppPath, sb.getUnownedSlice());
425}
426
427int main(int argc, const char* const* argv)
428{
429    using namespace Slang;
430
431    if (argc != 3)
432    {
433        fprintf(
434            stderr,
435            "Usage: %s spirv.core.grammar.json output.cpp\n",
436            argc >= 1 ? argv[0] : "slang-spirv-embed-generator");
437        return 1;
438    }
439
440    const char* const inPath = argv[1];
441    const char* const outCppPath = argv[2];
442
443    RefPtr<FileWriter> writer(new FileWriter(stderr, WriterFlag::AutoFlush));
444    SourceManager sourceManager;
445    sourceManager.initialize(nullptr, nullptr);
446    DiagnosticSink sink(&sourceManager, Lexer::sourceLocationLexer);
447    sink.writer = writer;
448
449    String contents;
450    SLANG_RETURN_ON_FAIL(File::readAllText(inPath, contents));
451    PathInfo pathInfo = PathInfo::makeFromString(inPath);
452    SourceFile* sourceFile = sourceManager.createSourceFileWithString(pathInfo, contents);
453    SourceView* sourceView = sourceManager.createSourceView(sourceFile, nullptr, SourceLoc());
454
455    RefPtr<SPIRVCoreGrammarInfo> info = SPIRVCoreGrammarInfo::loadFromJSON(*sourceView, sink);
456
457    writeInfo(outCppPath, *info);
458
459    return 0;
460}