yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
352576546
master
1#include "slang-compiler.h" 2#include "slang-core-module-textures.h" 3#include "slang-ir-util.h" 4#include "slang-ir.h" 5 6#define STRINGIZE (x ) STRINGIZE2(x) 7#define STRINGIZE2 (x ) #x 8#define LINE_STRING STRINGIZE(__LINE__) 9 10namespace Slang 11{ 12// We are going to generate the core module source code from a more compact 13// description. For example, we need to generate all the `operator` 14// declarations for the basic unary and binary math operations on 15// builtin types. To do this, we will make a big array of all these 16// types, and associate them with data on their categories/capabilities 17// so that we generate only the correct operations. 18// 19enum 20{ 21SINT_MASK = 1 <<0 , 22FLOAT_MASK = 1 <<1 , 23BOOL_RESULT = 1 <<2 , 24BOOL_MASK = 1 <<3 , 25UINT_MASK = 1 <<4 , 26 27INT_MASK = SINT_MASK |UINT_MASK , 28ARITHMETIC_MASK = INT_MASK |FLOAT_MASK , 29LOGICAL_MASK = INT_MASK |BOOL_MASK , 30ANY_MASK = INT_MASK |FLOAT_MASK |BOOL_MASK , 31}; 32 33// We are going to declare initializers that allow for conversion between 34// all of our base types, and we need a way to priotize those conversion 35// by giving them different costs. Rather than maintain a hard-coded table 36// of N^2 costs for N basic types, we are going to try to do things a bit 37// more systematically. 38// 39// Every base type will be given a "kind" and a "rank" for conversion. 40// The kind will classify it as signed/unsigned/float, and the rank will 41// classify it by its logical bit size (with a distinct rank for pointer-sized 42// types that logically sits between 32- and 64-bit types). 43// 44enum BaseTypeConversionKind :uint8_t 45{ 46kBaseTypeConversionKind_Signed , 47kBaseTypeConversionKind_Unsigned , 48kBaseTypeConversionKind_Float , 49kBaseTypeConversionKind_Error , 50}; 51enum BaseTypeConversionRank :uint8_t 52{ 53kBaseTypeConversionRank_Bool , 54kBaseTypeConversionRank_Int8 , 55kBaseTypeConversionRank_Int16 , 56kBaseTypeConversionRank_Int32 , 57kBaseTypeConversionRank_IntPtr , 58kBaseTypeConversionRank_Int64 , 59kBaseTypeConversionRank_Error , 60}; 61 62// Here we declare the table of all our builtin types, so that we can generate all the relevant 63// declarations. 64// 65struct BaseTypeConversionInfo 66{ 67char const * name ; 68BaseType tag ; 69unsigned flags ; 70BaseTypeConversionKind conversionKind ; 71BaseTypeConversionRank conversionRank ; 72}; 73static const BaseTypeConversionInfo kBaseTypes []= { 74// TODO: `void` really shouldn't be in the `BaseType` enumeration, since it behaves so 75// differently across the board 76 {"void" ,BaseType ::Void ,0 ,kBaseTypeConversionKind_Error ,kBaseTypeConversionRank_Error }, 77 78 {"bool" , 79BaseType ::Bool , 80BOOL_MASK , 81kBaseTypeConversionKind_Unsigned , 82kBaseTypeConversionRank_Bool }, 83 84 {"int8_t" , 85BaseType ::Int8 , 86SINT_MASK , 87kBaseTypeConversionKind_Signed , 88kBaseTypeConversionRank_Int8 }, 89 {"int16_t" , 90BaseType ::Int16 , 91SINT_MASK , 92kBaseTypeConversionKind_Signed , 93kBaseTypeConversionRank_Int16 }, 94 {"int" , 95BaseType ::Int , 96SINT_MASK , 97kBaseTypeConversionKind_Signed , 98kBaseTypeConversionRank_Int32 }, 99 {"int64_t" , 100BaseType ::Int64 , 101SINT_MASK , 102kBaseTypeConversionKind_Signed , 103kBaseTypeConversionRank_Int64 }, 104 {"intptr_t" , 105BaseType ::IntPtr , 106SINT_MASK , 107kBaseTypeConversionKind_Signed , 108kBaseTypeConversionRank_IntPtr }, 109 110 111 {"half" , 112BaseType ::Half , 113FLOAT_MASK , 114kBaseTypeConversionKind_Float , 115kBaseTypeConversionRank_Int16 }, 116 {"float" , 117BaseType ::Float , 118FLOAT_MASK , 119kBaseTypeConversionKind_Float , 120kBaseTypeConversionRank_Int32 }, 121 {"double" , 122BaseType ::Double , 123FLOAT_MASK , 124kBaseTypeConversionKind_Float , 125kBaseTypeConversionRank_Int64 }, 126 127 {"uint8_t" , 128BaseType ::UInt8 , 129UINT_MASK , 130kBaseTypeConversionKind_Unsigned , 131kBaseTypeConversionRank_Int8 }, 132 {"uint16_t" , 133BaseType ::UInt16 , 134UINT_MASK , 135kBaseTypeConversionKind_Unsigned , 136kBaseTypeConversionRank_Int16 }, 137 {"uint" , 138BaseType ::UInt , 139UINT_MASK , 140kBaseTypeConversionKind_Unsigned , 141kBaseTypeConversionRank_Int32 }, 142 {"uint64_t" , 143BaseType ::UInt64 , 144UINT_MASK , 145kBaseTypeConversionKind_Unsigned , 146kBaseTypeConversionRank_Int64 }, 147 {"uintptr_t" , 148BaseType ::UIntPtr , 149UINT_MASK , 150kBaseTypeConversionKind_Unsigned , 151kBaseTypeConversionRank_IntPtr }, 152}; 153 154// Given two base types, we need to be able to compute the cost of converting between them. 155ConversionCost getBaseTypeConversionCost ( 156BaseTypeConversionInfo const & toInfo , 157BaseTypeConversionInfo const & fromInfo ) 158{ 159if (toInfo .conversionKind == fromInfo .conversionKind && 160toInfo .conversionRank == fromInfo .conversionRank ) 161 { 162// Thse should represent the exact same type. 163return kConversionCost_None ; 164 } 165 166// Conversions within the same kind are easist to handle 167if (toInfo .conversionKind == fromInfo .conversionKind ) 168 { 169// If we are converting to a "larger" type, then 170// we are doing a lossless promotion, and otherwise 171// we are doing a demotion. 172if (toInfo .conversionRank > fromInfo .conversionRank ) 173return kConversionCost_RankPromotion ; 174else 175return kConversionCost_GeneralConversion ; 176 } 177else if (fromInfo .tag == BaseType ::Bool && toInfo .tag == BaseType ::Int ) 178 { 179return kConversionCost_BoolToInt ; 180 } 181 182// If we are converting from an unsigned integer type to 183// a signed integer type that is guaranteed to be larger, 184// then that is also a lossless promotion. 185// 186// There is one additional wrinkle here, which is that 187// a conversion from a 32-bit unsigned integer to a 188// "pointer-sized" signed integer should be treated 189// as unsafe, because the pointer size might also be 190// 32 bits. 191// 192// The same basic exemption applied when converting 193// *from* a pointer-sized unsigned integer. 194else if ( 195toInfo .conversionKind == kBaseTypeConversionKind_Signed && 196fromInfo .conversionKind == kBaseTypeConversionKind_Unsigned && 197toInfo .conversionRank > fromInfo .conversionRank && 198toInfo .conversionRank != kBaseTypeConversionRank_IntPtr && 199fromInfo .conversionRank != kBaseTypeConversionRank_IntPtr ) 200 { 201return kConversionCost_UnsignedToSignedPromotion ; 202 } 203// Same-size unsigned to signed integer conversion. 204else if ( 205toInfo .conversionKind == kBaseTypeConversionKind_Signed && 206fromInfo .conversionKind == kBaseTypeConversionKind_Unsigned && 207toInfo .conversionRank == fromInfo .conversionRank && 208toInfo .conversionRank != kBaseTypeConversionRank_IntPtr && 209fromInfo .conversionRank != kBaseTypeConversionRank_IntPtr ) 210 { 211return kConversionCost_SameSizeUnsignedToSignedConversion ; 212 } 213 214// Conversion from signed to unsigned is always lossy, 215// but it is preferred over conversions from unsigned 216// to signed, for same-size types. 217else if ( 218toInfo .conversionKind == kBaseTypeConversionKind_Unsigned && 219fromInfo .conversionKind == kBaseTypeConversionKind_Signed && 220toInfo .conversionRank >=fromInfo .conversionRank ) 221 { 222return kConversionCost_SignedToUnsignedConversion ; 223 } 224 225// Conversion from an integer to a floating-point type 226// is never considered a promotion (even when the value 227// would fit in the available mantissa bits). 228// If the destination type is at least 32 bits we consider 229// this a reasonably good conversion, though. 230// 231// Note that this means we do *not* consider implicit 232// conversion to `half` as a good conversion, even for small 233// types. This makes sense because we relaly want to prefer 234// conversion to `float` as the default. 235else if ( 236toInfo .conversionKind == kBaseTypeConversionKind_Float && 237toInfo .conversionRank >=kBaseTypeConversionRank_Int32 && 238fromInfo .conversionRank >=kBaseTypeConversionRank_Int8 ) 239 { 240return kConversionCost_IntegerToFloatConversion ; 241 } 242else if ( 243toInfo .conversionKind == kBaseTypeConversionKind_Float && 244toInfo .conversionRank >=kBaseTypeConversionRank_Int16 && 245fromInfo .conversionRank >=kBaseTypeConversionRank_Int8 ) 246 { 247return kConversionCost_IntegerToHalfConversion ; 248 } 249// All other cases are considered as "general" conversions, 250// where we don't consider any one conversion better than 251// any others. 252else 253 { 254return kConversionCost_GeneralConversion ; 255 } 256} 257 258IROp getBaseTypeConversionOp ( 259BaseTypeConversionInfo const & toInfo , 260BaseTypeConversionInfo const & fromInfo ) 261{ 262if (toInfo .tag == fromInfo .tag ) 263return kIROp_Nop ; 264 265IROp intrinsicOpCode = kIROp_Nop ; 266auto toStyle = getTypeStyle (toInfo .tag ); 267auto fromStyle = getTypeStyle (fromInfo .tag ); 268if (toStyle == kIROp_BoolType ) 269toStyle = kIROp_IntType ; 270if (fromStyle == kIROp_BoolType ) 271fromStyle = kIROp_IntType ; 272if (toStyle == kIROp_IntType && fromStyle == kIROp_IntType ) 273intrinsicOpCode = kIROp_IntCast ; 274if (toStyle == kIROp_IntType && fromStyle == kIROp_FloatType ) 275intrinsicOpCode = kIROp_CastFloatToInt ; 276if (toStyle == kIROp_FloatType && fromStyle == kIROp_IntType ) 277intrinsicOpCode = kIROp_CastIntToFloat ; 278if (toStyle == kIROp_FloatType && fromStyle == kIROp_FloatType ) 279intrinsicOpCode = kIROp_FloatCast ; 280return intrinsicOpCode ; 281} 282 283struct IntrinsicOpInfo 284{ 285IROp opCode ; 286char const * funcName ; 287char const * opName ; 288char const * interface ; 289unsigned flags ; 290}; 291 292[[maybe_unused ]]static const IntrinsicOpInfo intrinsicUnaryOps []= { 293 {kIROp_Neg ,"neg" ,"-" ,"__BuiltinArithmeticType" ,ARITHMETIC_MASK }, 294 {kIROp_Not ,"logicalNot" ,"!" ,nullptr ,BOOL_MASK |BOOL_RESULT }, 295 {kIROp_BitNot ,"not" ,"~" ,"__BuiltinLogicalType" ,INT_MASK }, 296}; 297 298[[maybe_unused ]]static const IntrinsicOpInfo intrinsicBinaryOps []= { 299 {kIROp_Add ,"add" ,"+" ,"__BuiltinArithmeticType" ,ARITHMETIC_MASK }, 300 {kIROp_Sub ,"sub" ,"-" ,"__BuiltinArithmeticType" ,ARITHMETIC_MASK }, 301 {kIROp_Mul ,"mul" ,"*" ,"__BuiltinArithmeticType" ,ARITHMETIC_MASK }, 302 {kIROp_Div ,"div" ,"/" ,"__BuiltinArithmeticType" ,ARITHMETIC_MASK }, 303 {kIROp_IRem ,"irem" ,"%" ,"__BuiltinIntegerType" ,INT_MASK }, 304 {kIROp_FRem ,"frem" ,"%" ,"__BuiltinFloatingPointType" ,FLOAT_MASK }, 305 {kIROp_And ,"logicalAnd" ,"&&" ,nullptr ,BOOL_MASK |BOOL_RESULT }, 306 {kIROp_Or ,"logicalOr" ,"||" ,nullptr ,BOOL_MASK |BOOL_RESULT }, 307 {kIROp_BitAnd ,"and" ,"&" ,"__BuiltinLogicalType" ,LOGICAL_MASK }, 308 {kIROp_BitOr ,"or" ,"|" ,"__BuiltinLogicalType" ,LOGICAL_MASK }, 309 {kIROp_BitXor ,"xor" ,"^" ,"__BuiltinLogicalType" ,LOGICAL_MASK }, 310 {kIROp_Eql ,"eql" ,"==" ,"__BuiltinType" ,ANY_MASK |BOOL_RESULT }, 311 {kIROp_Neq ,"neq" ,"!=" ,"__BuiltinType" ,ANY_MASK |BOOL_RESULT }, 312 {kIROp_Greater ,"greater" ,">" ,"__BuiltinArithmeticType" ,ARITHMETIC_MASK |BOOL_RESULT }, 313 {kIROp_Less ,"less" ,"<" ,"__BuiltinArithmeticType" ,ARITHMETIC_MASK |BOOL_RESULT }, 314 {kIROp_Geq ,"geq" ,">=" ,"__BuiltinArithmeticType" ,ARITHMETIC_MASK |BOOL_RESULT }, 315 {kIROp_Leq ,"leq" ,"<=" ,"__BuiltinArithmeticType" ,ARITHMETIC_MASK |BOOL_RESULT }, 316}; 317 318// Integer types that can be used in atomic operations in CUDA. 319[[maybe_unused ]]static const char * kCudaAtomicIntegerTypes []= 320 {"int" ,"uint" ,"uint64_t" ,"int64_t" }; 321 322// Both the following functions use these macros. 323// NOTE! They require a variable named path to emit the #line correctly if in source file. 324#define SLANG_RAW (TEXT ) sb << TEXT; 325#define SLANG_SPLICE (EXPR ) sb << (EXPR); 326 327#define EMIT_LINE_DIRECTIVE () sb << "#line " << (__LINE__ + 1) << " \"" << path << "\"\n" 328 329ComPtr < ISlangBlob > Session ::getCoreLibraryCode () 330{ 331#if SLANG_EMBED_CORE_MODULE_SOURCE 332if (!coreLibraryCode ) 333 { 334StringBuilder sb ; 335const String path = getCoreModulePath (); 336#include "core.meta.slang.h" 337coreLibraryCode = StringBlob ::moveCreate (sb ); 338 } 339#endif 340return coreLibraryCode ; 341} 342 343ComPtr < ISlangBlob > Session ::getHLSLLibraryCode () 344{ 345#if SLANG_EMBED_CORE_MODULE_SOURCE 346if (!hlslLibraryCode ) 347 { 348const String path = getCoreModulePath (); 349StringBuilder sb ; 350#include "hlsl.meta.slang.h" 351hlslLibraryCode = StringBlob ::moveCreate (sb ); 352 } 353#endif 354return hlslLibraryCode ; 355} 356 357ComPtr < ISlangBlob > Session ::getAutodiffLibraryCode () 358{ 359#if SLANG_EMBED_CORE_MODULE_SOURCE 360if (!autodiffLibraryCode ) 361 { 362const String path = getCoreModulePath (); 363StringBuilder sb ; 364#include "diff.meta.slang.h" 365autodiffLibraryCode = StringBlob ::moveCreate (sb ); 366 } 367#endif 368return autodiffLibraryCode ; 369} 370 371ComPtr < ISlangBlob > Session ::getGLSLLibraryCode () 372{ 373#if SLANG_EMBED_CORE_MODULE_SOURCE 374if (!glslLibraryCode ) 375 { 376const String path = getCoreModulePath (); 377StringBuilder sb ; 378#include "glsl.meta.slang.h" 379glslLibraryCode = StringBlob ::moveCreate (sb ); 380 } 381#endif 382return glslLibraryCode ; 383} 384}// namespace Slang