diff options
Diffstat (limited to 'source/compiler-core/slang-spirv-core-grammar.cpp')
| -rw-r--r-- | source/compiler-core/slang-spirv-core-grammar.cpp | 340 |
1 files changed, 340 insertions, 0 deletions
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; +} +} |
