summaryrefslogtreecommitdiffstats
path: root/source/compiler-core
diff options
context:
space:
mode:
Diffstat (limited to 'source/compiler-core')
-rw-r--r--source/compiler-core/slang-json-value.h5
-rw-r--r--source/compiler-core/slang-misc-diagnostic-defs.h2
-rw-r--r--source/compiler-core/slang-perfect-hash.cpp214
-rw-r--r--source/compiler-core/slang-perfect-hash.h30
-rw-r--r--source/compiler-core/slang-spirv-core-grammar.cpp340
-rw-r--r--source/compiler-core/slang-spirv-core-grammar.h145
6 files changed, 736 insertions, 0 deletions
diff --git a/source/compiler-core/slang-json-value.h b/source/compiler-core/slang-json-value.h
index 25e74cb36..0abe7c9a6 100644
--- a/source/compiler-core/slang-json-value.h
+++ b/source/compiler-core/slang-json-value.h
@@ -311,6 +311,11 @@ public:
/// Returns true if all the keys are unique
static bool areKeysUnique(const JSONKeyValue* keyValues, Index keyValueCount);
+ /// Access the internal set of strings, removing anything from this
+ /// will invalidate the container, so only do it immediately prior to
+ /// destruction.
+ StringSlicePool& getStringSlicePool() {return m_slicePool;};
+
protected:
struct Range
{
diff --git a/source/compiler-core/slang-misc-diagnostic-defs.h b/source/compiler-core/slang-misc-diagnostic-defs.h
index aed83eb30..a2ccc4657 100644
--- a/source/compiler-core/slang-misc-diagnostic-defs.h
+++ b/source/compiler-core/slang-misc-diagnostic-defs.h
@@ -33,4 +33,6 @@ DIAGNOSTIC(100005, Error, invalidArgumentForOption, "invalid argument format for
DIAGNOSTIC(99999, Note, noteLocationOfInternalError, "an internal error threw an exception while working on code near this location")
+DIAGNOSTIC(29104, Error, spirvCoreGrammarJSONParseFailure, "unexpected JSON in spirv core grammar file: $0")
+
#undef DIAGNOSTIC
diff --git a/source/compiler-core/slang-perfect-hash.cpp b/source/compiler-core/slang-perfect-hash.cpp
new file mode 100644
index 000000000..bfe8d4876
--- /dev/null
+++ b/source/compiler-core/slang-perfect-hash.cpp
@@ -0,0 +1,214 @@
+#include "slang-perfect-hash.h"
+
+#include "../core/slang-string-util.h"
+#include "../core/slang-writer.h"
+
+namespace Slang
+{
+
+// Implemented according to "Hash, displace, and compress"
+// https://cmph.sourceforge.net/papers/esa09.pdf
+HashFindResult minimalPerfectHash(const List<String>& ss, HashParams& hashParams)
+{
+ // Check for uniqueness
+ for (Index i = 0; i < ss.getCount(); ++i)
+ {
+ for (Index j = i + 1; j < ss.getCount(); ++j)
+ {
+ if (ss[i] == ss[j])
+ {
+ return HashFindResult::NonUniqueKeys;
+ }
+ }
+ }
+
+ SLANG_ASSERT(UIndex(ss.getCount()) < std::numeric_limits<UInt32>::max());
+ const UInt32 nBuckets = UInt32(ss.getCount());
+ List<List<String>> initialBuckets;
+ initialBuckets.setCount(nBuckets);
+
+ const auto hash = [&](const String& s, const HashCode32 salt = 0) -> UInt32
+ {
+ //
+ // The current getStableHashCode is susceptible to patterns of
+ // collisions causing the search to fail for the SPIR-V opnames; it
+ // performs poorly on short strings, taking over 300000 iterations to
+ // diverge on "Ceil" and "FMix" (and place them in already unoccupied
+ // slots)!
+ //
+ // Use FNV Hash here which seem perform much better on these short inputs
+ // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
+ //
+ // If you change this, don't forget to also sync the version below in
+ // the printing code.
+ UInt32 h = salt;
+ for (const char c : s)
+ h = (h * 0x01000193) ^ c;
+ return h % nBuckets;
+ };
+
+ // Assign the inputs into their buckets according to the hash without salt.
+ // Sort the buckets according to size, so that later we can make these have
+ // unique destinations starting with the largest ones first as they are at
+ // most risk of collision.
+ for (const auto& s : ss)
+ {
+ initialBuckets[hash(s)].add(s);
+ }
+ initialBuckets.stableSort([](const List<String>& a, const List<String>& b) { return a.getCount() > b.getCount(); });
+
+ // These are our outputs, the salts are calculated such that for all input
+ // word, x, hash(x, salt[hash(x, 0)]) is unique
+ //
+ // We keep the final table as we need to detect when we've been given a
+ // word not in our language.
+ hashParams.saltTable.setCount(nBuckets);
+ for (auto& s : hashParams.saltTable)
+ s = 0;
+ hashParams.destTable.setCount(nBuckets);
+ for (auto& s : hashParams.destTable)
+ s.reduceLength(0);
+
+ // This mask will, in each salt tryout, be used to prevent collisions
+ // within a single bucket.
+ List<bool> bucketDestinations = List<bool>::makeRepeated(false, nBuckets);
+
+ for (const auto& b : initialBuckets)
+ {
+ // Break if we've reached the empty buckets
+ if (!b.getCount())
+ {
+ break;
+ }
+
+ // Try out all the salts until we get one which has no internal
+ // collisions for this bucket and also no collisions with the buckets
+ // we've processed so far.
+ UInt32 salt = 1;
+ while (true)
+ {
+ bool collision = false;
+ for (auto& d : bucketDestinations)
+ {
+ d = false;
+ }
+
+ for (const auto& s : b)
+ {
+ const auto i = hash(s, salt);
+ if (hashParams.destTable[i].getLength() || bucketDestinations[i])
+ {
+ collision = true;
+ break;
+ }
+ bucketDestinations[i] = true;
+ }
+ if (!collision)
+ {
+ break;
+ }
+ salt++;
+
+ // If we fail to find a solution after some massive amount of tries
+ // it's almost certainly because of some property of the hash
+ // function and language causing an irresolvable collision.
+ if (salt > 10000 * nBuckets)
+ {
+ return HashFindResult::UnavoidableHashCollision;
+ }
+ }
+ for (const auto& s : b)
+ {
+ hashParams.saltTable[hash(s)] = salt;
+ hashParams.destTable[hash(s, salt)] = s;
+ }
+ }
+ return HashFindResult::Success;
+}
+
+String perfectHashToEmbeddableCpp(
+ const HashParams& hashParams,
+ const UnownedStringSlice& valueType,
+ const UnownedStringSlice& funcName,
+ const List<String>& values)
+{
+ SLANG_ASSERT(hashParams.saltTable.getCount() == hashParams.destTable.getCount());
+ SLANG_ASSERT(hashParams.saltTable.getCount() == values.getCount());
+
+ StringBuilder sb;
+ StringWriter writer(&sb, WriterFlags(0));
+ WriterHelper w(&writer);
+ const auto line = [&](const char* l){
+ w.put(l);
+ w.put("\n");
+ };
+
+ w.print("bool %s(const UnownedStringSlice& str, %s& value)\n", String(funcName).getBuffer(), String(valueType).getBuffer());
+ line("{");
+
+ w.print(" static const unsigned tableSalt[%ld] = {\n", hashParams.saltTable.getCount());
+ w.print(" ");
+ for (Index i = 0; i < hashParams.saltTable.getCount(); ++i)
+ {
+ const auto salt = hashParams.saltTable[i];
+ if (i != hashParams.saltTable.getCount() - 1)
+ {
+ w.print(" %d,", salt);
+ if (i % 16 == 15)
+ {
+ w.print("\n ");
+ }
+ }
+ else
+ {
+ w.print(" %d", salt);
+ }
+ }
+ line("\n };");
+ line("");
+
+ w.print(" using KV = std::pair<const char*, %s>;\n", String(valueType).getBuffer());
+ line("");
+
+ w.print(" static const KV words[%ld] =\n", hashParams.destTable.getCount());
+ line(" {");
+ for (Index i = 0; i < hashParams.destTable.getCount(); ++i)
+ {
+ const auto& s = hashParams.destTable[i];
+ const auto& v = values[i];
+ w.print(
+ " {\"%s\", %s},\n",
+ s.getBuffer(),
+ v.getBuffer()
+ );
+ }
+ line(" };");
+ line("");
+
+ // Make sure to update the hash function in the search function above if
+ // you change this.
+ line(" static const auto hash = [](const UnownedStringSlice& str, UInt32 salt){");
+ line(" UInt32 h = salt;");
+ line(" for (const char c : str)");
+ line(" h = (h * 0x01000193) ^ c;");
+ w.print(" return h %% %ld;\n", hashParams.saltTable.getCount());
+ line(" };");
+ line("");
+
+ line(" const auto i = hash(str, tableSalt[hash(str, 0)]);");
+ line(" if(str == words[i].first)");
+ line(" {");
+ line(" value = words[i].second;");
+ line(" return true;");
+ line(" }");
+ line(" else");
+ line(" {");
+ line(" return false;");
+ line(" }");
+ line("}");
+ line("");
+
+ return sb;
+}
+
+}
diff --git a/source/compiler-core/slang-perfect-hash.h b/source/compiler-core/slang-perfect-hash.h
new file mode 100644
index 000000000..1488c7a62
--- /dev/null
+++ b/source/compiler-core/slang-perfect-hash.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "../core/slang-string.h"
+#include "../core/slang-list.h"
+
+namespace Slang
+{
+
+struct HashParams
+{
+ List<UInt32> saltTable;
+ List<String> destTable;
+};
+
+enum class HashFindResult {
+ Success,
+ NonUniqueKeys,
+ UnavoidableHashCollision,
+};
+
+// Calculate a minimal perfect hash of a list of input strings
+HashFindResult minimalPerfectHash(const List<String>& ss, HashParams& hashParams);
+
+String perfectHashToEmbeddableCpp(
+ const HashParams& hashParams,
+ const UnownedStringSlice& valueType,
+ const UnownedStringSlice& funcName,
+ const List<String>& values);
+
+}
diff --git a/source/compiler-core/slang-spirv-core-grammar.cpp b/source/compiler-core/slang-spirv-core-grammar.cpp
new file mode 100644
index 000000000..8140d002a
--- /dev/null
+++ b/source/compiler-core/slang-spirv-core-grammar.cpp
@@ -0,0 +1,340 @@
+#include "slang-spirv-core-grammar.h"
+
+#include "../core/slang-rtti-util.h"
+#include "../core/slang-string-util.h"
+#include "slang-json-native.h"
+#include "slang-core-diagnostics.h"
+#include <limits>
+
+namespace Slang
+{
+using SpvWord = uint32_t;
+
+//
+// Structs which mirror the structure of spirv.core.grammar.json
+//
+// Commented members are those which currently don't use
+struct InstructionPrintingClass
+{
+ UnownedStringSlice tag;
+ UnownedStringSlice heading;
+};
+SLANG_MAKE_STRUCT_RTTI_INFO(
+ InstructionPrintingClass,
+ SLANG_RTTI_FIELD(tag),
+ SLANG_OPTIONAL_RTTI_FIELD(heading)
+);
+
+struct Operand
+{
+ UnownedStringSlice kind;
+ UnownedStringSlice quantifier;
+ // UnownedStringSlice name;
+};
+SLANG_MAKE_STRUCT_RTTI_INFO(
+ Operand,
+ SLANG_RTTI_FIELD(kind),
+ SLANG_OPTIONAL_RTTI_FIELD(quantifier)
+ //SLANG_RTTI_FIELD(name),
+);
+
+struct Instruction
+{
+ UnownedStringSlice opname;
+ UnownedStringSlice class_;
+ SpvWord opcode;
+ List<UnownedStringSlice> capabilities;
+ List<Operand> operands;
+};
+SLANG_MAKE_STRUCT_RTTI_INFO(
+ Instruction,
+ SLANG_RTTI_FIELD(opname),
+ SLANG_RTTI_FIELD_IMPL(class_, "class", 0),
+ SLANG_RTTI_FIELD(opcode),
+ SLANG_OPTIONAL_RTTI_FIELD(capabilities),
+ SLANG_OPTIONAL_RTTI_FIELD(operands)
+);
+
+struct Enumerant
+{
+ UnownedStringSlice enumerant;
+ JSONValue value;
+ List<UnownedStringSlice> capabilities;
+ // List<Operand> parameters;
+ // UnownedStringSlice version;
+ // UnownedStringSlice lastVersion;
+ // List<UnownedStringSlice> extensions;
+};
+SLANG_MAKE_STRUCT_RTTI_INFO(
+ Enumerant,
+ SLANG_RTTI_FIELD(enumerant),
+ SLANG_RTTI_FIELD(value),
+ SLANG_OPTIONAL_RTTI_FIELD(capabilities),
+ // SLANG_OPTIONAL_RTTI_FIELD(parameters),
+ // SLANG_OPTIONAL_RTTI_FIELD(version),
+ // SLANG_OPTIONAL_RTTI_FIELD(lastVersion),
+ // SLANG_OPTIONAL_RTTI_FIELD(extensions)
+);
+
+struct OperandKind
+{
+ UnownedStringSlice category;
+ UnownedStringSlice kind;
+ List<Enumerant> enumerants;
+};
+SLANG_MAKE_STRUCT_RTTI_INFO(
+ OperandKind,
+ SLANG_RTTI_FIELD(category),
+ SLANG_RTTI_FIELD(kind),
+ SLANG_OPTIONAL_RTTI_FIELD(enumerants)
+);
+
+struct SPIRVSpec
+{
+ // List<UnownedStringSlice> copyright;
+ // UnownedStringSlice magic_number;
+ // UInt32 major_version;
+ // UInt32 minor_version;
+ // UInt32 revision;
+ List<InstructionPrintingClass> instruction_printing_class;
+ List<Instruction> instructions;
+ List<OperandKind> operand_kinds;
+};
+SLANG_MAKE_STRUCT_RTTI_INFO(
+ SPIRVSpec,
+ // SLANG_RTTI_FIELD(copyright),
+ // SLANG_RTTI_FIELD(magic_number),
+ // SLANG_RTTI_FIELD(major_version)
+ // SLANG_RTTI_FIELD(minor_version)
+ // SLANG_RTTI_FIELD(revision)
+ SLANG_RTTI_FIELD(instruction_printing_class),
+ SLANG_RTTI_FIELD(instructions),
+ SLANG_RTTI_FIELD(operand_kinds)
+);
+
+static Dictionary<UnownedStringSlice, SpvWord> operandKindToDict(
+ JSONContainer& container,
+ DiagnosticSink& sink,
+ const OperandKind& k)
+{
+ Dictionary<UnownedStringSlice, SpvWord> dict;
+ dict.reserve(k.enumerants.getCount());
+ for(const auto& e : k.enumerants)
+ {
+ SpvWord valueInt = 0;
+ switch(e.value.getKind())
+ {
+ case JSONValue::Kind::Integer:
+ {
+ // TODO: Range check here?
+ valueInt = SpvWord(container.asInteger(e.value));
+ break;
+ }
+ case JSONValue::Kind::String:
+ {
+ Int i = 0;
+ const auto str = container.getString(e.value);
+ if(SLANG_FAILED(StringUtil::parseInt(str, i)))
+ sink.diagnose(
+ e.value.loc,
+ MiscDiagnostics::spirvCoreGrammarJSONParseFailure,
+ "Expected an integer value"
+ );
+ // TODO: Range check here?
+ valueInt = SpvWord(i);
+ break;
+ }
+ default:
+ sink.diagnose(
+ e.value.loc,
+ MiscDiagnostics::spirvCoreGrammarJSONParseFailure,
+ "Expected an integer value (or a string with an integer inside)"
+ );
+ }
+ dict.add(e.enumerant, valueInt);
+ }
+ return dict;
+}
+
+//
+//
+//
+RefPtr<SPIRVCoreGrammarInfo> SPIRVCoreGrammarInfo::loadFromJSON(SourceView& source, DiagnosticSink& sink)
+{
+ //
+ // Load the JSON
+ //
+ SLANG_ASSERT(source.getSourceManager() == sink.getSourceManager());
+ JSONLexer lexer;
+ lexer.init(&source, &sink);
+ JSONParser parser;
+ JSONContainer container(sink.getSourceManager());
+ JSONBuilder builder(&container);
+ RttiTypeFuncsMap typeMap;
+ typeMap = JSONNativeUtil::getTypeFuncsMap();
+ SLANG_RETURN_NULL_ON_FAIL(parser.parse(&lexer, &source, &builder, &sink));
+ JSONToNativeConverter converter(&container, &typeMap, &sink);
+ SPIRVSpec spec;
+ if(SLANG_FAILED(converter.convert(builder.getRootValue(), &spec)))
+ {
+ // TODO: not having a source loc here is not great...
+ sink.diagnoseWithoutSourceView(
+ SourceLoc{},
+ MiscDiagnostics::spirvCoreGrammarJSONParseFailure,
+ "Failed to match SPIR-V grammar JSON to the expected schema"
+ );
+ return nullptr;
+ }
+
+ //
+ // Convert to the internal representation
+ //
+ RefPtr<SPIRVCoreGrammarInfo> res{new SPIRVCoreGrammarInfo};
+
+ res->operandKinds.dict.reserve(spec.operand_kinds.getCount());
+ uint32_t operandKindIndex = 0;
+ for(const auto& c : spec.operand_kinds)
+ {
+ if(operandKindIndex > std::numeric_limits<decltype(OperandKind::index)>::max())
+ {
+ sink.diagnoseWithoutSourceView(
+ SourceLoc{},
+ MiscDiagnostics::spirvCoreGrammarJSONParseFailure,
+ "Too many enum categories, expected fewer than 256"
+ );
+ }
+ res->operandKinds.dict.add(c.kind, {static_cast<decltype(OperandKind::index)>(operandKindIndex)});
+ operandKindIndex++;
+ }
+
+ // It's important we reserve the memory now, as we require the iterators to
+ // be stable, as references to them are maintained by the OpInfo structs.
+ Index totalNumOperands = 0;
+ for(const auto& i : spec.instructions)
+ totalNumOperands += i.operands.getCapacity();
+ res->operandTypesStorage.reserve(totalNumOperands);
+
+ res->opcodes.dict.reserve(spec.instructions.getCount());
+ for(const auto& i : spec.instructions)
+ {
+ res->opcodes.dict.add(i.opname, SpvOp(i.opcode));
+
+ const auto class_ =
+ i.class_ == "Type-Declaration" ? OpInfo::TypeDeclaration
+ : i.class_ == "Constant-Creation" ? OpInfo::ConstantCreation
+ : i.class_ == "Debug" ? OpInfo::Debug
+ : OpInfo::Other;
+
+ const auto resultTypeIndex
+ = i.operands.findFirstIndex([](const auto& o){return o.kind == "IdResultType";});
+ const auto resultIdIndex
+ = i.operands.findFirstIndex([](const auto& o){return o.kind == "IdResult";});
+ SLANG_ASSERT(resultTypeIndex >= -1 || resultTypeIndex <= 0);
+ SLANG_ASSERT(resultIdIndex >= -1 || resultTypeIndex <= 1);
+
+ uint16_t minOperandCount = 0;
+ uint16_t maxOperandCount = 0;
+ uint16_t numOperandTypes = 0;
+ const OperandKind* operandTypes = res->operandTypesStorage.end();
+ for(const auto& o : i.operands)
+ {
+ if(maxOperandCount == 0xffff)
+ {
+ // We are about to overflow maxWordCount, either someone has
+ // put 2^16 operands in the json, or we have a "*" quantified
+ // operand not in the last position and should implement
+ // support for that
+ sink.diagnoseWithoutSourceView(
+ SourceLoc{},
+ MiscDiagnostics::spirvCoreGrammarJSONParseFailure,
+ "\"*\"-qualified operand wasn't the last operand"
+ );
+ }
+
+ const auto catIndex = res->operandKinds.lookup(o.kind);
+ if(!catIndex)
+ {
+ sink.diagnoseWithoutSourceView(
+ SourceLoc{},
+ MiscDiagnostics::spirvCoreGrammarJSONParseFailure,
+ "Operand references a kind which doesn't exist"
+ );
+ continue;
+ }
+
+ numOperandTypes++;
+ res->operandTypesStorage.add(*catIndex);
+
+ if(o.quantifier == "")
+ {
+ // This catches the case where an "?" or "*" qualified operand
+ // appears before any unqualified operands
+ if(minOperandCount != maxOperandCount)
+ sink.diagnoseWithoutSourceView(
+ SourceLoc{},
+ MiscDiagnostics::spirvCoreGrammarJSONParseFailure,
+ "\"*\" or \"?\" operand appeared before an unqualified operand"
+ );
+ minOperandCount++;
+ maxOperandCount++;
+ }
+ else if(o.quantifier == "?")
+ {
+ maxOperandCount++;
+ }
+ else if(o.quantifier == "*")
+ {
+ maxOperandCount = 0xffff;
+ }
+ }
+
+ // There are duplicate opcodes in the json (for renamed instructions,
+ // or the same instruction with different capabilities), for now just
+ // keep the first one.
+ res->opInfos.dict.addIfNotExists(SpvOp(i.opcode), {
+ class_,
+ static_cast<int8_t>(resultTypeIndex),
+ static_cast<int8_t>(resultIdIndex),
+ minOperandCount,
+ maxOperandCount,
+ numOperandTypes,
+ operandTypes
+ });
+ res->opNames.dict.addIfNotExists(SpvOp(i.opcode), i.opname);
+ }
+
+ for(const auto& k : spec.operand_kinds)
+ {
+ const auto kindIndex = res->operandKinds.dict.getValue(k.kind);
+ const auto d = operandKindToDict(container, sink, k);
+ for(const auto& [n, v] : d)
+ {
+ // Add the string to this slice pool as we'll be taking ownership
+ // of it shortly but don't want to invalidate it in the meantime.
+ const auto s = container.getStringSlicePool().addAndGetSlice(String(k.kind) + n);
+ res->allEnumsWithTypePrefix.dict.add(s, v);
+ res->allEnums.dict.add({kindIndex, n}, v);
+ res->allEnumNames.dict.addIfNotExists({kindIndex, v}, n);
+ }
+
+ res->operandKindNames.dict.add(kindIndex, k.kind);
+
+ if(k.kind == "Capability")
+ for(const auto& [n, v] : d)
+ res->capabilities.dict.add(n, SpvCapability(v));
+
+ // If this starts with Id, and the suffix is also an operand kind,
+ // assume that this is an Id wrapper
+ if(k.kind.startsWith("Id"))
+ {
+ const UnownedStringSlice underneathIdKind{k.kind.begin()+2, k.kind.end()};
+ OperandKind targetIndex;
+ if(res->operandKinds.dict.tryGetValue(underneathIdKind, targetIndex))
+ res->operandKindUnderneathIds.dict.add(kindIndex, targetIndex);
+ }
+ }
+ // Steal the strings from the JSON container before it dies
+ res->strings.swapWith(container.getStringSlicePool());
+ return res;
+}
+}
diff --git a/source/compiler-core/slang-spirv-core-grammar.h b/source/compiler-core/slang-spirv-core-grammar.h
new file mode 100644
index 000000000..7fe8f6cd7
--- /dev/null
+++ b/source/compiler-core/slang-spirv-core-grammar.h
@@ -0,0 +1,145 @@
+#pragma once
+
+#include "../core/slang-smart-pointer.h"
+#include "../core/slang-string.h"
+#include "../core/slang-string-slice-pool.h"
+#include "../core/slang-dictionary.h"
+#include "../../external/spirv-headers/include/spirv/unified1/spirv.h"
+#include <optional>
+
+namespace Slang
+{
+ using SpvWord = uint32_t;
+ class DiagnosticSink;
+ class SourceView;
+
+ struct SPIRVCoreGrammarInfo : public RefObject
+ {
+ static RefPtr<SPIRVCoreGrammarInfo> loadFromJSON(SourceView& source, DiagnosticSink& sink);
+ static RefPtr<SPIRVCoreGrammarInfo> getEmbeddedVersion();
+
+ template<typename K, typename T>
+ struct Lookup
+ {
+ std::optional<T> lookup(const K& name) const
+ {
+ T ret;
+ if(embedded ? embedded(name, ret) : dict.tryGetValue(name, ret))
+ return ret;
+ else
+ return std::nullopt;
+ }
+
+ bool (*embedded)(const K&, T&) = nullptr;
+ Dictionary<K, T> dict;
+ };
+
+ struct OperandKind
+ {
+ uint8_t index;
+ SLANG_COMPONENTWISE_HASHABLE_1;
+ SLANG_COMPONENTWISE_EQUALITY_1(OperandKind);
+ };
+
+ struct QualifiedEnumName
+ {
+ OperandKind kind;
+ UnownedStringSlice name;
+ SLANG_COMPONENTWISE_HASHABLE_2;
+ SLANG_COMPONENTWISE_EQUALITY_2(QualifiedEnumName);
+ };
+
+ struct QualifiedEnumValue
+ {
+ OperandKind kind;
+ SpvWord value;
+ SLANG_COMPONENTWISE_HASHABLE_2;
+ SLANG_COMPONENTWISE_EQUALITY_2(QualifiedEnumValue);
+ };
+
+ struct OpInfo
+ {
+ enum Class
+ {
+ // Unrecognized instructions go in here
+ Other,
+
+ // Adding to this? Don't forget to update the embedding generator
+ Miscellaneous,
+ Debug,
+ Annotation,
+ Extension,
+ ModeSetting,
+ TypeDeclaration,
+ ConstantCreation,
+ Memory,
+ Function,
+ Image,
+ Conversion,
+ Composite,
+ Arithmetic,
+ Bit,
+ Relational_and_Logical,
+ Derivative,
+ ControlFlow,
+ Atomic,
+ Primitive,
+ Barrier,
+ Group,
+ DeviceSideEnqueue,
+ Pipe,
+ NonUniform,
+ Reserved,
+ };
+ constexpr static int8_t kNoResultTypeId = -1;
+ constexpr static int8_t kNoResultId = -1;
+
+ Class class_;
+ // -1 or 0
+ int8_t resultTypeIndex = kNoResultTypeId;
+ // -1 or 0 or 1
+ int8_t resultIdIndex = kNoResultId;
+ // The range of valid operand counts for this instruction,
+ // including any result type and id. Multi-word operands count as a
+ // single operand.
+ uint16_t minOperandCount;
+ uint16_t maxOperandCount;
+ // when looking up an operand type, clamp to this number-1 to
+ // account for variable length operands at the end
+ uint16_t numOperandTypes;
+ const OperandKind* operandTypes;
+ };
+
+ //
+ // Our tables:
+ //
+
+ // Instruction name to opcode
+ Lookup<UnownedStringSlice, SpvOp> opcodes;
+ // Capability name to value
+ Lookup<UnownedStringSlice, SpvCapability> capabilities;
+ // String-qualified enum name (one with the type prefix) to value
+ Lookup<UnownedStringSlice, SpvWord> allEnumsWithTypePrefix;
+ // kind * enum name to value
+ Lookup<QualifiedEnumName, SpvWord> allEnums;
+ // kine * enum value to unqualified name
+ Lookup<QualifiedEnumValue, UnownedStringSlice> allEnumNames;
+ // Any other information on instructions
+ Lookup<SpvOp, OpInfo> opInfos;
+ // Opcode to instruction name
+ Lookup<SpvOp, UnownedStringSlice> opNames;
+ // Operand kind string to numeric id
+ Lookup<UnownedStringSlice, OperandKind> operandKinds;
+ // Operand kind id to string
+ Lookup<OperandKind, UnownedStringSlice> operandKindNames;
+ // Operand kind to the "un-id" version of itself, for example IdMemorySemantics to MemorySemantics
+ Lookup<OperandKind, OperandKind> operandKindUnderneathIds;
+
+ private:
+
+ // If this is loaded from JSON, we keep the strings around instead of
+ // copying them as dictionary keys
+ StringSlicePool strings = StringSlicePool(StringSlicePool::Style::Empty);
+ List<OperandKind> operandTypesStorage;
+ };
+}