yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Theresa FoleyA new approach to AST node deduplication (#8072)352576546

master
12.8 KiB384 linesraw
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{
21    SINT_MASK = 1 << 0,
22    FLOAT_MASK = 1 << 1,
23    BOOL_RESULT = 1 << 2,
24    BOOL_MASK = 1 << 3,
25    UINT_MASK = 1 << 4,
26
27    INT_MASK = SINT_MASK | UINT_MASK,
28    ARITHMETIC_MASK = INT_MASK | FLOAT_MASK,
29    LOGICAL_MASK = INT_MASK | BOOL_MASK,
30    ANY_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{
46    kBaseTypeConversionKind_Signed,
47    kBaseTypeConversionKind_Unsigned,
48    kBaseTypeConversionKind_Float,
49    kBaseTypeConversionKind_Error,
50};
51enum BaseTypeConversionRank : uint8_t
52{
53    kBaseTypeConversionRank_Bool,
54    kBaseTypeConversionRank_Int8,
55    kBaseTypeConversionRank_Int16,
56    kBaseTypeConversionRank_Int32,
57    kBaseTypeConversionRank_IntPtr,
58    kBaseTypeConversionRank_Int64,
59    kBaseTypeConversionRank_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{
67    char const* name;
68    BaseType tag;
69    unsigned flags;
70    BaseTypeConversionKind conversionKind;
71    BaseTypeConversionRank 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",
79     BaseType::Bool,
80     BOOL_MASK,
81     kBaseTypeConversionKind_Unsigned,
82     kBaseTypeConversionRank_Bool},
83
84    {"int8_t",
85     BaseType::Int8,
86     SINT_MASK,
87     kBaseTypeConversionKind_Signed,
88     kBaseTypeConversionRank_Int8},
89    {"int16_t",
90     BaseType::Int16,
91     SINT_MASK,
92     kBaseTypeConversionKind_Signed,
93     kBaseTypeConversionRank_Int16},
94    {"int",
95     BaseType::Int,
96     SINT_MASK,
97     kBaseTypeConversionKind_Signed,
98     kBaseTypeConversionRank_Int32},
99    {"int64_t",
100     BaseType::Int64,
101     SINT_MASK,
102     kBaseTypeConversionKind_Signed,
103     kBaseTypeConversionRank_Int64},
104    {"intptr_t",
105     BaseType::IntPtr,
106     SINT_MASK,
107     kBaseTypeConversionKind_Signed,
108     kBaseTypeConversionRank_IntPtr},
109
110
111    {"half",
112     BaseType::Half,
113     FLOAT_MASK,
114     kBaseTypeConversionKind_Float,
115     kBaseTypeConversionRank_Int16},
116    {"float",
117     BaseType::Float,
118     FLOAT_MASK,
119     kBaseTypeConversionKind_Float,
120     kBaseTypeConversionRank_Int32},
121    {"double",
122     BaseType::Double,
123     FLOAT_MASK,
124     kBaseTypeConversionKind_Float,
125     kBaseTypeConversionRank_Int64},
126
127    {"uint8_t",
128     BaseType::UInt8,
129     UINT_MASK,
130     kBaseTypeConversionKind_Unsigned,
131     kBaseTypeConversionRank_Int8},
132    {"uint16_t",
133     BaseType::UInt16,
134     UINT_MASK,
135     kBaseTypeConversionKind_Unsigned,
136     kBaseTypeConversionRank_Int16},
137    {"uint",
138     BaseType::UInt,
139     UINT_MASK,
140     kBaseTypeConversionKind_Unsigned,
141     kBaseTypeConversionRank_Int32},
142    {"uint64_t",
143     BaseType::UInt64,
144     UINT_MASK,
145     kBaseTypeConversionKind_Unsigned,
146     kBaseTypeConversionRank_Int64},
147    {"uintptr_t",
148     BaseType::UIntPtr,
149     UINT_MASK,
150     kBaseTypeConversionKind_Unsigned,
151     kBaseTypeConversionRank_IntPtr},
152};
153
154// Given two base types, we need to be able to compute the cost of converting between them.
155ConversionCost getBaseTypeConversionCost(
156    BaseTypeConversionInfo const& toInfo,
157    BaseTypeConversionInfo const& fromInfo)
158{
159    if (toInfo.conversionKind == fromInfo.conversionKind &&
160        toInfo.conversionRank == fromInfo.conversionRank)
161    {
162        // Thse should represent the exact same type.
163        return kConversionCost_None;
164    }
165
166    // Conversions within the same kind are easist to handle
167    if (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.
172        if (toInfo.conversionRank > fromInfo.conversionRank)
173            return kConversionCost_RankPromotion;
174        else
175            return kConversionCost_GeneralConversion;
176    }
177    else if (fromInfo.tag == BaseType::Bool && toInfo.tag == BaseType::Int)
178    {
179        return 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.
194    else if (
195        toInfo.conversionKind == kBaseTypeConversionKind_Signed &&
196        fromInfo.conversionKind == kBaseTypeConversionKind_Unsigned &&
197        toInfo.conversionRank > fromInfo.conversionRank &&
198        toInfo.conversionRank != kBaseTypeConversionRank_IntPtr &&
199        fromInfo.conversionRank != kBaseTypeConversionRank_IntPtr)
200    {
201        return kConversionCost_UnsignedToSignedPromotion;
202    }
203    // Same-size unsigned to signed integer conversion.
204    else if (
205        toInfo.conversionKind == kBaseTypeConversionKind_Signed &&
206        fromInfo.conversionKind == kBaseTypeConversionKind_Unsigned &&
207        toInfo.conversionRank == fromInfo.conversionRank &&
208        toInfo.conversionRank != kBaseTypeConversionRank_IntPtr &&
209        fromInfo.conversionRank != kBaseTypeConversionRank_IntPtr)
210    {
211        return 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.
217    else if (
218        toInfo.conversionKind == kBaseTypeConversionKind_Unsigned &&
219        fromInfo.conversionKind == kBaseTypeConversionKind_Signed &&
220        toInfo.conversionRank >= fromInfo.conversionRank)
221    {
222        return 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.
235    else if (
236        toInfo.conversionKind == kBaseTypeConversionKind_Float &&
237        toInfo.conversionRank >= kBaseTypeConversionRank_Int32 &&
238        fromInfo.conversionRank >= kBaseTypeConversionRank_Int8)
239    {
240        return kConversionCost_IntegerToFloatConversion;
241    }
242    else if (
243        toInfo.conversionKind == kBaseTypeConversionKind_Float &&
244        toInfo.conversionRank >= kBaseTypeConversionRank_Int16 &&
245        fromInfo.conversionRank >= kBaseTypeConversionRank_Int8)
246    {
247        return 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.
252    else
253    {
254        return kConversionCost_GeneralConversion;
255    }
256}
257
258IROp getBaseTypeConversionOp(
259    BaseTypeConversionInfo const& toInfo,
260    BaseTypeConversionInfo const& fromInfo)
261{
262    if (toInfo.tag == fromInfo.tag)
263        return kIROp_Nop;
264
265    IROp intrinsicOpCode = kIROp_Nop;
266    auto toStyle = getTypeStyle(toInfo.tag);
267    auto fromStyle = getTypeStyle(fromInfo.tag);
268    if (toStyle == kIROp_BoolType)
269        toStyle = kIROp_IntType;
270    if (fromStyle == kIROp_BoolType)
271        fromStyle = kIROp_IntType;
272    if (toStyle == kIROp_IntType && fromStyle == kIROp_IntType)
273        intrinsicOpCode = kIROp_IntCast;
274    if (toStyle == kIROp_IntType && fromStyle == kIROp_FloatType)
275        intrinsicOpCode = kIROp_CastFloatToInt;
276    if (toStyle == kIROp_FloatType && fromStyle == kIROp_IntType)
277        intrinsicOpCode = kIROp_CastIntToFloat;
278    if (toStyle == kIROp_FloatType && fromStyle == kIROp_FloatType)
279        intrinsicOpCode = kIROp_FloatCast;
280    return intrinsicOpCode;
281}
282
283struct IntrinsicOpInfo
284{
285    IROp opCode;
286    char const* funcName;
287    char const* opName;
288    char const* interface;
289    unsigned 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
332    if (!coreLibraryCode)
333    {
334        StringBuilder sb;
335        const String path = getCoreModulePath();
336#include "core.meta.slang.h"
337        coreLibraryCode = StringBlob::moveCreate(sb);
338    }
339#endif
340    return coreLibraryCode;
341}
342
343ComPtr<ISlangBlob> Session::getHLSLLibraryCode()
344{
345#if SLANG_EMBED_CORE_MODULE_SOURCE
346    if (!hlslLibraryCode)
347    {
348        const String path = getCoreModulePath();
349        StringBuilder sb;
350#include "hlsl.meta.slang.h"
351        hlslLibraryCode = StringBlob::moveCreate(sb);
352    }
353#endif
354    return hlslLibraryCode;
355}
356
357ComPtr<ISlangBlob> Session::getAutodiffLibraryCode()
358{
359#if SLANG_EMBED_CORE_MODULE_SOURCE
360    if (!autodiffLibraryCode)
361    {
362        const String path = getCoreModulePath();
363        StringBuilder sb;
364#include "diff.meta.slang.h"
365        autodiffLibraryCode = StringBlob::moveCreate(sb);
366    }
367#endif
368    return autodiffLibraryCode;
369}
370
371ComPtr<ISlangBlob> Session::getGLSLLibraryCode()
372{
373#if SLANG_EMBED_CORE_MODULE_SOURCE
374    if (!glslLibraryCode)
375    {
376        const String path = getCoreModulePath();
377        StringBuilder sb;
378#include "glsl.meta.slang.h"
379        glslLibraryCode = StringBlob::moveCreate(sb);
380    }
381#endif
382    return glslLibraryCode;
383}
384} // namespace Slang