yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
b173149f4
master
1#include "slang-spirv-core-grammar.h" 2 3#include "../core/slang-rtti-util.h" 4#include "../core/slang-string-util.h" 5#include "slang-core-diagnostics.h" 6#include "slang-json-native.h" 7 8#include <limits> 9 10namespace Slang 11{ 12using SpvWord = uint32_t ; 13 14// 15// Structs which mirror the structure of spirv.core.grammar.json 16// 17// Commented members are those which currently don't use 18struct InstructionPrintingClass 19{ 20UnownedStringSlice tag ; 21UnownedStringSlice heading ; 22}; 23SLANG_MAKE_STRUCT_RTTI_INFO ( 24InstructionPrintingClass , 25SLANG_RTTI_FIELD (tag ), 26SLANG_OPTIONAL_RTTI_FIELD (heading )); 27 28struct Operand 29{ 30UnownedStringSlice kind ; 31UnownedStringSlice quantifier ; 32// UnownedStringSlice name; 33}; 34SLANG_MAKE_STRUCT_RTTI_INFO ( 35Operand , 36SLANG_RTTI_FIELD (kind ), 37SLANG_OPTIONAL_RTTI_FIELD (quantifier ) 38// SLANG_RTTI_FIELD(name), 39); 40 41struct Instruction 42{ 43UnownedStringSlice opname ; 44UnownedStringSlice class_ ; 45SpvWord opcode ; 46List < UnownedStringSlice > capabilities ; 47List < UnownedStringSlice > aliases ; 48List < Operand > operands ; 49}; 50SLANG_MAKE_STRUCT_RTTI_INFO ( 51Instruction , 52SLANG_RTTI_FIELD (opname ), 53SLANG_RTTI_FIELD_IMPL (class_ ,"class" ,0 ), 54SLANG_RTTI_FIELD (opcode ), 55SLANG_OPTIONAL_RTTI_FIELD (capabilities ), 56SLANG_OPTIONAL_RTTI_FIELD (aliases ), 57SLANG_OPTIONAL_RTTI_FIELD (operands )); 58 59struct Enumerant 60{ 61UnownedStringSlice enumerant ; 62JSONValue value ; 63List < UnownedStringSlice > capabilities ; 64List < UnownedStringSlice > aliases ; 65// List<Operand> parameters; 66// UnownedStringSlice version; 67// UnownedStringSlice lastVersion; 68// List<UnownedStringSlice> extensions; 69}; 70SLANG_MAKE_STRUCT_RTTI_INFO ( 71Enumerant , 72SLANG_RTTI_FIELD (enumerant ), 73SLANG_RTTI_FIELD (value ), 74SLANG_OPTIONAL_RTTI_FIELD (capabilities ), 75SLANG_OPTIONAL_RTTI_FIELD (aliases ), 76// SLANG_OPTIONAL_RTTI_FIELD(parameters), 77// SLANG_OPTIONAL_RTTI_FIELD(version), 78// SLANG_OPTIONAL_RTTI_FIELD(lastVersion), 79// SLANG_OPTIONAL_RTTI_FIELD(extensions) 80); 81 82struct OperandKind 83{ 84UnownedStringSlice category ; 85UnownedStringSlice kind ; 86List < Enumerant > enumerants ; 87}; 88SLANG_MAKE_STRUCT_RTTI_INFO ( 89OperandKind, 90SLANG_RTTI_FIELD (category), 91SLANG_RTTI_FIELD (kind), 92SLANG_OPTIONAL_RTTI_FIELD (enumerants)); 93 94struct SPIRVSpec 95{ 96// List<UnownedStringSlice> copyright; 97// UnownedStringSlice magic_number; 98// UInt32 major_version; 99// UInt32 minor_version; 100// UInt32 revision; 101List < InstructionPrintingClass > instruction_printing_class ; 102List < Instruction > instructions ; 103List < OperandKind > operand_kinds ; 104}; 105SLANG_MAKE_STRUCT_RTTI_INFO ( 106SPIRVSpec, 107// SLANG_RTTI_FIELD(copyright), 108// SLANG_RTTI_FIELD(magic_number), 109// SLANG_RTTI_FIELD(major_version) 110// SLANG_RTTI_FIELD(minor_version) 111// SLANG_RTTI_FIELD(revision) 112SLANG_RTTI_FIELD (instruction_printing_class), 113SLANG_RTTI_FIELD (instructions), 114SLANG_RTTI_FIELD (operand_kinds)); 115 116static Dictionary < UnownedStringSlice , SpvWord > operandKindToDict ( 117JSONContainer & container, 118DiagnosticSink & sink, 119const OperandKind & k) 120{ 121Dictionary < UnownedStringSlice , SpvWord > dict; 122dict. reserve (k. enumerants . getCount ()); 123for ( const auto & e : k. enumerants ) 124{ 125SpvWord valueInt = 0 ; 126switch (e. value . getKind ()) 127{ 128case JSONValue :: Kind ::Integer: 129{ 130// TODO: Range check here? 131valueInt = SpvWord (container. asInteger (e. value )); 132break ; 133} 134case JSONValue :: Kind ::String: 135{ 136Int i = 0 ; 137const auto str = container. getString (e. value ); 138if ( SLANG_FAILED ( StringUtil :: parseInt (str, i))) 139sink. diagnose ( 140e. value . loc , 141MiscDiagnostics ::spirvCoreGrammarJSONParseFailure, 142"Expected an integer value" ); 143// TODO: Range check here? 144valueInt = SpvWord (i); 145break ; 146} 147default : 148sink. diagnose ( 149e. value . loc , 150MiscDiagnostics ::spirvCoreGrammarJSONParseFailure, 151"Expected an integer value (or a string with an integer inside)" ); 152} 153dict. add (e. enumerant , valueInt); 154 155for ( auto alias : e. aliases ) 156{ 157dict. add (alias, valueInt); 158} 159} 160return dict; 161} 162 163// 164// 165// 166RefPtr < SPIRVCoreGrammarInfo > SPIRVCoreGrammarInfo :: loadFromJSON ( 167SourceView & source, 168DiagnosticSink & sink) 169{ 170// 171// Load the JSON 172// 173SLANG_ASSERT (source. getSourceManager () == sink. getSourceManager ()); 174JSONLexer lexer; 175lexer. init ( & source, & sink); 176JSONParser parser; 177JSONContainer container(sink. getSourceManager ()); 178JSONBuilder builder( & container); 179RttiTypeFuncsMap typeMap; 180typeMap = JSONNativeUtil :: getTypeFuncsMap (); 181SLANG_RETURN_NULL_ON_FAIL (parser. parse ( & lexer, & source, & builder, & sink)); 182JSONToNativeConverter converter( & container, & typeMap, & sink); 183SPIRVSpec spec; 184if ( SLANG_FAILED (converter. convert (builder. getRootValue (), & spec))) 185{ 186// TODO: not having a source loc here is not great... 187sink. diagnoseWithoutSourceView ( 188SourceLoc {}, 189MiscDiagnostics ::spirvCoreGrammarJSONParseFailure, 190"Failed to match SPIR-V grammar JSON to the expected schema" ); 191return nullptr ; 192} 193 194// 195// Convert to the internal representation 196// 197RefPtr < SPIRVCoreGrammarInfo > res{ new SPIRVCoreGrammarInfo }; 198 199res -> operandKinds . dict . reserve (spec. operand_kinds . getCount ()); 200uint32_t operandKindIndex = 0 ; 201for ( const auto & c : spec. operand_kinds ) 202{ 203if (operandKindIndex > std:: numeric_limits < decltype( OperandKind ::index) > ::max()) 204{ 205sink. diagnoseWithoutSourceView ( 206SourceLoc {}, 207MiscDiagnostics ::spirvCoreGrammarJSONParseFailure, 208"Too many enum categories, expected fewer than 256" ); 209} 210res -> operandKinds . dict . add ( 211c. kind , 212{ static_cast < decltype( OperandKind ::index) > (operandKindIndex)}); 213operandKindIndex ++ ; 214} 215 216// It's important we reserve the memory now, as we require the iterators to 217// be stable, as references to them are maintained by the OpInfo structs. 218Index totalNumOperands = 0 ; 219for ( const auto & i : spec. instructions ) 220totalNumOperands += i. operands . getCapacity (); 221res -> operandTypesStorage . reserve (totalNumOperands); 222 223res -> opcodes . dict . reserve (spec. instructions . getCount ()); 224for ( const auto & i : spec. instructions ) 225{ 226res -> opcodes . dict . add (i. opname , SpvOp (i. opcode )); 227 228for ( auto alias : i. aliases ) 229{ 230res -> opcodes . dict . add (alias, SpvOp (i. opcode )); 231} 232 233const auto class_ = i. class_ == "Type-Declaration" ? OpInfo ::TypeDeclaration 234: i. class_ == "Constant-Creation" ? OpInfo ::ConstantCreation 235: i. class_ == "Debug" ? OpInfo ::Debug 236: OpInfo ::Other; 237 238const auto resultTypeIndex = 239i. operands . findFirstIndex ([]( const auto & o) { return o. kind == "IdResultType" ; }); 240const auto resultIdIndex = 241i. operands . findFirstIndex ([]( const auto & o) { return o. kind == "IdResult" ; }); 242SLANG_ASSERT (resultTypeIndex >= -1 || resultTypeIndex <= 0 ); 243SLANG_ASSERT (resultIdIndex >= -1 || resultTypeIndex <= 1 ); 244 245uint16_t minOperandCount = 0 ; 246uint16_t maxOperandCount = 0 ; 247uint16_t numOperandTypes = 0 ; 248const OperandKind * operandTypes = res -> operandTypesStorage . end (); 249for ( const auto & o : i. operands ) 250{ 251if (maxOperandCount == 0xffff ) 252{ 253// We are about to overflow maxWordCount, either someone has 254// put 2^16 operands in the json, or we have a "*" quantified 255// operand not in the last position and should implement 256// support for that 257sink. diagnoseWithoutSourceView ( 258SourceLoc {}, 259MiscDiagnostics ::spirvCoreGrammarJSONParseFailure, 260"\"*\"-qualified operand wasn't the last operand" ); 261} 262 263const auto catIndex = res -> operandKinds . lookup (o. kind ); 264if (!catIndex) 265{ 266sink. diagnoseWithoutSourceView ( 267SourceLoc {}, 268MiscDiagnostics ::spirvCoreGrammarJSONParseFailure, 269"Operand references a kind which doesn't exist" ); 270continue ; 271} 272 273numOperandTypes ++ ; 274res -> operandTypesStorage . add ( * catIndex); 275 276// The number of "ImageOperands" is dependent on the bitmask 277// operand, for our purposes treat them as unbounded 278if (o. quantifier == "*" || o. kind == "ImageOperands" ) 279{ 280maxOperandCount = 0xffff ; 281} 282else if (o. quantifier == "?" ) 283{ 284maxOperandCount ++ ; 285} 286else if (o. quantifier == "" ) 287{ 288// This catches the case where an "?" or "*" qualified operand 289// appears before any unqualified operands 290if (minOperandCount != maxOperandCount) 291sink. diagnoseWithoutSourceView ( 292SourceLoc {}, 293MiscDiagnostics ::spirvCoreGrammarJSONParseFailure, 294"\"*\" or \"?\" operand appeared before an unqualified operand" ); 295minOperandCount ++ ; 296maxOperandCount ++ ; 297} 298else 299{ 300sink. diagnose ( 301SourceLoc {}, 302MiscDiagnostics ::spirvCoreGrammarJSONParseFailure, 303"quantifier wasn't empty, * or ?" ); 304} 305} 306 307// There are duplicate opcodes in the json (for renamed instructions, 308// or the same instruction with different capabilities), for now just 309// keep the first one. 310res -> opInfos . dict . addIfNotExists ( 311SpvOp (i. opcode ), 312{class_, 313static_cast < int8_t > (resultTypeIndex), 314static_cast < int8_t > (resultIdIndex), 315minOperandCount, 316maxOperandCount, 317numOperandTypes, 318operandTypes}); 319res -> opNames . dict . addIfNotExists ( SpvOp (i. opcode ), i. opname ); 320} 321 322for ( const auto & k : spec. operand_kinds ) 323{ 324const auto kindIndex = res -> operandKinds . dict . getValue (k. kind ); 325const auto d = operandKindToDict (container, sink, k); 326for ( const auto & [n, v] : d) 327{ 328// Add the string to this slice pool as we'll be taking ownership 329// of it shortly but don't want to invalidate it in the meantime. 330const auto s = container. getStringSlicePool (). addAndGetSlice ( String (k. kind ) + n); 331res -> allEnumsWithTypePrefix . dict . add (s, v); 332res -> allEnums . dict . add ({kindIndex, n}, v); 333res -> allEnumNames . dict . addIfNotExists ({kindIndex, v}, n); 334} 335 336res -> operandKindNames . dict . add (kindIndex, k. kind ); 337 338if (k. kind == "Capability" ) 339for ( const auto & [n, v] : d) 340res -> capabilities . dict . add (n, SpvCapability (v)); 341 342// If this starts with Id, and the suffix is also an operand kind, 343// assume that this is an Id wrapper 344if (k. kind . startsWith ( "Id" )) 345{ 346const UnownedStringSlice underneathIdKind{k. kind . begin () + 2 , k. kind . end ()}; 347OperandKind targetIndex; 348if (res -> operandKinds . dict . tryGetValue (underneathIdKind, targetIndex)) 349res -> operandKindUnderneathIds . dict . add (kindIndex, targetIndex); 350} 351} 352// Steal the strings from the JSON container before it dies 353res -> strings . swapWith (container. getStringSlicePool ()); 354return res; 355} 356} // namespace Slang