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