Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/reshadefx/src/effect_symbol_table.cpp
4246 views
1
/*
2
* Copyright (C) 2014 Patrick Mours
3
* SPDX-License-Identifier: BSD-3-Clause
4
*/
5
6
#include "effect_symbol_table.hpp"
7
#include <cassert>
8
#ifndef __APPLE__
9
#include <malloc.h> // alloca
10
#else
11
#include <alloca.h>
12
#endif
13
#include <algorithm> // std::upper_bound, std::sort
14
#include <functional> // std::greater
15
16
enum class intrinsic_id
17
{
18
#define IMPLEMENT_INTRINSIC_SPIRV(name, i, code) name##i,
19
#include "effect_symbol_table_intrinsics.inl"
20
};
21
22
struct intrinsic : reshadefx::function
23
{
24
intrinsic(const char *name, intrinsic_id id, const reshadefx::type &ret_type, std::initializer_list<reshadefx::type> arg_types)
25
{
26
function::return_type = ret_type;
27
function::id = static_cast<uint32_t>(id);
28
function::name = name;
29
function::parameter_list.reserve(arg_types.size());
30
for (const reshadefx::type &arg_type : arg_types)
31
function::parameter_list.push_back({ arg_type });
32
}
33
};
34
35
#define void { reshadefx::type::t_void }
36
#define bool { reshadefx::type::t_bool, 1, 1 }
37
#define bool2 { reshadefx::type::t_bool, 2, 1 }
38
#define bool3 { reshadefx::type::t_bool, 3, 1 }
39
#define bool4 { reshadefx::type::t_bool, 4, 1 }
40
#define int { reshadefx::type::t_int, 1, 1 }
41
#define int2 { reshadefx::type::t_int, 2, 1 }
42
#define int3 { reshadefx::type::t_int, 3, 1 }
43
#define int4 { reshadefx::type::t_int, 4, 1 }
44
#define int2x3 { reshadefx::type::t_int, 2, 3 }
45
#define int2x2 { reshadefx::type::t_int, 2, 2 }
46
#define int2x4 { reshadefx::type::t_int, 2, 4 }
47
#define int3x2 { reshadefx::type::t_int, 3, 2 }
48
#define int3x3 { reshadefx::type::t_int, 3, 3 }
49
#define int3x4 { reshadefx::type::t_int, 3, 4 }
50
#define int4x2 { reshadefx::type::t_int, 4, 2 }
51
#define int4x3 { reshadefx::type::t_int, 4, 3 }
52
#define int4x4 { reshadefx::type::t_int, 4, 4 }
53
#define out_int { reshadefx::type::t_int, 1, 1, reshadefx::type::q_out }
54
#define out_int2 { reshadefx::type::t_int, 2, 1, reshadefx::type::q_out }
55
#define out_int3 { reshadefx::type::t_int, 3, 1, reshadefx::type::q_out }
56
#define out_int4 { reshadefx::type::t_int, 4, 1, reshadefx::type::q_out }
57
#define inout_int { reshadefx::type::t_int, 1, 1, reshadefx::type::q_inout | reshadefx::type::q_groupshared }
58
#define uint { reshadefx::type::t_uint, 1, 1 }
59
#define uint2 { reshadefx::type::t_uint, 2, 1 }
60
#define uint3 { reshadefx::type::t_uint, 3, 1 }
61
#define uint4 { reshadefx::type::t_uint, 4, 1 }
62
#define inout_uint { reshadefx::type::t_uint, 1, 1, reshadefx::type::q_inout | reshadefx::type::q_groupshared }
63
#define float { reshadefx::type::t_float, 1, 1 }
64
#define float2 { reshadefx::type::t_float, 2, 1 }
65
#define float3 { reshadefx::type::t_float, 3, 1 }
66
#define float4 { reshadefx::type::t_float, 4, 1 }
67
#define float2x3 { reshadefx::type::t_float, 2, 3 }
68
#define float2x2 { reshadefx::type::t_float, 2, 2 }
69
#define float2x4 { reshadefx::type::t_float, 2, 4 }
70
#define float3x2 { reshadefx::type::t_float, 3, 2 }
71
#define float3x3 { reshadefx::type::t_float, 3, 3 }
72
#define float3x4 { reshadefx::type::t_float, 3, 4 }
73
#define float4x2 { reshadefx::type::t_float, 4, 2 }
74
#define float4x3 { reshadefx::type::t_float, 4, 3 }
75
#define float4x4 { reshadefx::type::t_float, 4, 4 }
76
#define out_float { reshadefx::type::t_float, 1, 1, reshadefx::type::q_out }
77
#define out_float2 { reshadefx::type::t_float, 2, 1, reshadefx::type::q_out }
78
#define out_float3 { reshadefx::type::t_float, 3, 1, reshadefx::type::q_out }
79
#define out_float4 { reshadefx::type::t_float, 4, 1, reshadefx::type::q_out }
80
#define sampler1d_int { reshadefx::type::t_sampler1d_int, 1, 1 }
81
#define sampler2d_int { reshadefx::type::t_sampler2d_int, 1, 1 }
82
#define sampler3d_int { reshadefx::type::t_sampler3d_int, 1, 1 }
83
#define sampler1d_uint { reshadefx::type::t_sampler1d_uint, 1, 1 }
84
#define sampler2d_uint { reshadefx::type::t_sampler2d_uint, 1, 1 }
85
#define sampler3d_uint { reshadefx::type::t_sampler3d_uint, 1, 1 }
86
#define sampler1d_float { reshadefx::type::t_sampler1d_float, 1, 1 }
87
#define sampler2d_float { reshadefx::type::t_sampler2d_float, 1, 1 }
88
#define sampler3d_float { reshadefx::type::t_sampler3d_float, 1, 1 }
89
#define sampler1d_float4 { reshadefx::type::t_sampler1d_float, 4, 1 }
90
#define sampler2d_float4 { reshadefx::type::t_sampler2d_float, 4, 1 }
91
#define sampler3d_float4 { reshadefx::type::t_sampler3d_float, 4, 1 }
92
#define storage1d_int { reshadefx::type::t_storage1d_int, 1, 1 }
93
#define storage2d_int { reshadefx::type::t_storage2d_int, 1, 1 }
94
#define storage3d_int { reshadefx::type::t_storage3d_int, 1, 1 }
95
#define storage1d_uint { reshadefx::type::t_storage1d_uint, 1, 1 }
96
#define storage2d_uint { reshadefx::type::t_storage2d_uint, 1, 1 }
97
#define storage3d_uint { reshadefx::type::t_storage3d_uint, 1, 1 }
98
#define storage1d_float { reshadefx::type::t_storage1d_float, 1, 1 }
99
#define storage2d_float { reshadefx::type::t_storage2d_float, 1, 1 }
100
#define storage3d_float { reshadefx::type::t_storage3d_float, 1, 1 }
101
#define storage1d_float4 { reshadefx::type::t_storage1d_float, 4, 1 }
102
#define storage2d_float4 { reshadefx::type::t_storage2d_float, 4, 1 }
103
#define storage3d_float4 { reshadefx::type::t_storage3d_float, 4, 1 }
104
#define inout_storage1d_int { reshadefx::type::t_storage1d_int, 1, 1, reshadefx::type::q_inout }
105
#define inout_storage2d_int { reshadefx::type::t_storage2d_int, 1, 1, reshadefx::type::q_inout }
106
#define inout_storage3d_int { reshadefx::type::t_storage3d_int, 1, 1, reshadefx::type::q_inout }
107
#define inout_storage1d_uint { reshadefx::type::t_storage1d_uint, 1, 1, reshadefx::type::q_inout }
108
#define inout_storage2d_uint { reshadefx::type::t_storage2d_uint, 1, 1, reshadefx::type::q_inout }
109
#define inout_storage3d_uint { reshadefx::type::t_storage3d_uint, 1, 1, reshadefx::type::q_inout }
110
111
// Import intrinsic function definitions
112
static const intrinsic s_intrinsics[] =
113
{
114
#define DEFINE_INTRINSIC(name, i, ret_type, ...) intrinsic(#name, intrinsic_id::name##i, ret_type, { __VA_ARGS__ }),
115
#include "effect_symbol_table_intrinsics.inl"
116
};
117
118
#undef void
119
#undef bool
120
#undef bool2
121
#undef bool3
122
#undef bool4
123
#undef int
124
#undef int2
125
#undef int3
126
#undef int4
127
#undef uint
128
#undef uint2
129
#undef uint3
130
#undef uint4
131
#undef float
132
#undef float2
133
#undef float3
134
#undef float4
135
136
unsigned int reshadefx::type::rank(const type &src, const type &dst)
137
{
138
if (src.is_array() != dst.is_array() || (src.array_length != dst.array_length && src.is_bounded_array() && dst.is_bounded_array()))
139
return 0; // Arrays of different sizes are not compatible
140
if (src.is_struct() || dst.is_struct())
141
return src.struct_definition == dst.struct_definition ? 32 : 0; // Structs are only compatible if they are the same type
142
if (!src.is_numeric() || !dst.is_numeric())
143
return src.base == dst.base && src.rows == dst.rows && src.cols == dst.cols ? 32 : 0; // Numeric values are not compatible with other types
144
if (src.is_matrix() && (!dst.is_matrix() || src.rows != dst.rows || src.cols != dst.cols))
145
return 0; // Matrix truncation or dimensions do not match
146
147
// This table is based on the following rules:
148
// - Floating point has a higher rank than integer types
149
// - Integer to floating point promotion has a higher rank than floating point to integer conversion
150
// - Signed to unsigned integer conversion has a higher rank than unsigned to signed integer conversion
151
static const unsigned int ranks[7][7] = {
152
{ 5, 4, 4, 4, 4, 4, 4 }, // bool
153
{ 3, 5, 5, 2, 2, 4, 4 }, // min16int
154
{ 3, 5, 5, 2, 2, 4, 4 }, // int
155
{ 3, 1, 1, 5, 5, 4, 4 }, // min16uint
156
{ 3, 1, 1, 5, 5, 4, 4 }, // uint
157
{ 3, 3, 3, 3, 3, 6, 6 }, // min16float
158
{ 3, 3, 3, 3, 3, 6, 6 } // float
159
};
160
161
assert(src.base > 0 && src.base <= 7); // bool - float
162
assert(dst.base > 0 && dst.base <= 7);
163
164
const unsigned int rank = ranks[src.base - 1][dst.base - 1] << 2;
165
166
if ((src.is_scalar() && dst.is_vector()))
167
return rank >> 1; // Scalar to vector promotion has a lower rank
168
if ((src.is_vector() && dst.is_scalar()) || (src.is_vector() == dst.is_vector() && src.rows > dst.rows && src.cols >= dst.cols))
169
return rank >> 2; // Vector to scalar conversion has an even lower rank
170
if ((src.is_vector() != dst.is_vector()) || src.is_matrix() != dst.is_matrix() || src.components() != dst.components())
171
return 0; // If components weren't converted at this point, the types are not compatible
172
173
return rank * src.components(); // More components causes a higher rank
174
}
175
176
reshadefx::symbol_table::symbol_table()
177
{
178
_current_scope.name = "::";
179
_current_scope.level = 0;
180
_current_scope.namespace_level = 0;
181
}
182
183
void reshadefx::symbol_table::enter_scope()
184
{
185
_current_scope.level++;
186
}
187
void reshadefx::symbol_table::enter_namespace(const std::string &name)
188
{
189
_current_scope.name += name + "::";
190
_current_scope.level++;
191
_current_scope.namespace_level++;
192
}
193
void reshadefx::symbol_table::leave_scope()
194
{
195
assert(_current_scope.level > 0);
196
197
for (auto &symbol : _symbol_stack)
198
{
199
std::vector<scoped_symbol> &scope_list = symbol.second;
200
201
for (auto scope_it = scope_list.begin(); scope_it != scope_list.end();)
202
{
203
if (scope_it->scope.level > scope_it->scope.namespace_level &&
204
scope_it->scope.level >= _current_scope.level)
205
{
206
scope_it = scope_list.erase(scope_it);
207
}
208
else
209
{
210
++scope_it;
211
}
212
}
213
}
214
215
_current_scope.level--;
216
}
217
void reshadefx::symbol_table::leave_namespace()
218
{
219
assert(_current_scope.level > 0);
220
assert(_current_scope.namespace_level > 0);
221
222
_current_scope.name.erase(_current_scope.name.substr(0, _current_scope.name.size() - 2).rfind("::") + 2);
223
_current_scope.level--;
224
_current_scope.namespace_level--;
225
}
226
227
bool reshadefx::symbol_table::insert_symbol(const std::string &name, const symbol &symbol, bool global)
228
{
229
assert(symbol.id != 0 || symbol.op == symbol_type::constant);
230
231
// Make sure the symbol does not exist yet
232
if (symbol.op != symbol_type::function && find_symbol(name, _current_scope, true).id != 0)
233
return false;
234
235
// Insertion routine which keeps the symbol stack sorted by namespace level
236
const auto insert_sorted = [](auto &vec, const auto &item) {
237
return vec.insert(
238
std::upper_bound(vec.begin(), vec.end(), item,
239
[](auto lhs, auto rhs) {
240
return lhs.scope.namespace_level < rhs.scope.namespace_level;
241
}), item);
242
};
243
244
// Global symbols are accessible from every scope
245
if (global)
246
{
247
scope scope = { "", 0, 0 };
248
249
// Walk scope chain from global scope back to current one
250
for (size_t pos = 0; pos != std::string::npos; pos = _current_scope.name.find("::", pos))
251
{
252
// Extract scope name
253
scope.name = _current_scope.name.substr(0, pos += 2);
254
const std::string previous_scope_name = _current_scope.name.substr(pos);
255
256
// Insert symbol into this scope
257
insert_sorted(_symbol_stack[previous_scope_name + name], scoped_symbol { symbol, scope });
258
259
// Continue walking up the scope chain
260
scope.level = ++scope.namespace_level;
261
}
262
}
263
else
264
{
265
// This is a local symbol so it's sufficient to update the symbol stack with just the current scope
266
insert_sorted(_symbol_stack[name], scoped_symbol { symbol, _current_scope });
267
}
268
269
return true;
270
}
271
272
reshadefx::scoped_symbol reshadefx::symbol_table::find_symbol(const std::string &name) const
273
{
274
// Default to start search with current scope and walk back the scope chain
275
return find_symbol(name, _current_scope, false);
276
}
277
reshadefx::scoped_symbol reshadefx::symbol_table::find_symbol(const std::string &name, const scope &scope, bool exclusive) const
278
{
279
const auto stack_it = _symbol_stack.find(name);
280
281
// Check if symbol does exist
282
if (stack_it == _symbol_stack.end() || stack_it->second.empty())
283
return {};
284
285
// Walk up the scope chain starting at the requested scope level and find a matching symbol
286
scoped_symbol result = {};
287
288
for (auto it = stack_it->second.rbegin(), end = stack_it->second.rend(); it != end; ++it)
289
{
290
if (it->scope.level > scope.level ||
291
it->scope.namespace_level > scope.namespace_level || (it->scope.namespace_level == scope.namespace_level && it->scope.name != scope.name))
292
continue;
293
if (exclusive && it->scope.level < scope.level)
294
continue;
295
296
if (it->op == symbol_type::constant || it->op == symbol_type::variable || it->op == symbol_type::structure)
297
return *it; // Variables and structures have the highest priority and are always picked immediately
298
else if (result.id == 0)
299
result = *it; // Function names have a lower priority, so continue searching in case a variable with the same name exists
300
}
301
302
return result;
303
}
304
305
static int compare_functions(const std::vector<reshadefx::expression> &arguments, const reshadefx::function *function1, const reshadefx::function *function2)
306
{
307
const size_t num_arguments = arguments.size();
308
309
// Check if the first function matches the argument types
310
bool function1_viable = true;
311
const auto function1_ranks = static_cast<unsigned int *>(alloca(num_arguments * sizeof(unsigned int)));
312
for (size_t i = 0; i < num_arguments; ++i)
313
{
314
if ((function1_ranks[i] = reshadefx::type::rank(arguments[i].type, function1->parameter_list[i].type)) == 0)
315
{
316
function1_viable = false;
317
break;
318
}
319
}
320
321
// Catch case where the second function does not exist
322
if (function2 == nullptr)
323
return function1_viable ? -1 : 1; // If the first function is not viable, this compare fails
324
325
// Check if the second function matches the argument types
326
bool function2_viable = true;
327
const auto function2_ranks = static_cast<unsigned int *>(alloca(num_arguments * sizeof(unsigned int)));
328
for (size_t i = 0; i < num_arguments; ++i)
329
{
330
if ((function2_ranks[i] = reshadefx::type::rank(arguments[i].type, function2->parameter_list[i].type)) == 0)
331
{
332
function2_viable = false;
333
break;
334
}
335
}
336
337
// If one of the functions is not viable, then the other one automatically wins
338
if (!function1_viable || !function2_viable)
339
return function2_viable - function1_viable;
340
341
// Both functions are possible, so find the one with the higher ranking
342
std::sort(function1_ranks, function1_ranks + num_arguments, std::greater<unsigned int>());
343
std::sort(function2_ranks, function2_ranks + num_arguments, std::greater<unsigned int>());
344
345
for (size_t i = 0; i < num_arguments; ++i)
346
if (function1_ranks[i] > function2_ranks[i])
347
return -1; // Left function wins
348
else if (function2_ranks[i] > function1_ranks[i])
349
return +1; // Right function wins
350
351
return 0; // Both functions are equally viable
352
}
353
354
bool reshadefx::symbol_table::resolve_function_call(const std::string &name, const std::vector<expression> &arguments, const scope &scope, symbol &out_data, bool &is_ambiguous) const
355
{
356
out_data.op = symbol_type::function;
357
358
const function *result = nullptr;
359
unsigned int num_overloads = 0;
360
unsigned int overload_namespace = scope.namespace_level;
361
362
// Look up function name in the symbol stack and loop through the associated symbols
363
const auto stack_it = _symbol_stack.find(name);
364
365
if (stack_it != _symbol_stack.end() && !stack_it->second.empty())
366
{
367
for (auto it = stack_it->second.rbegin(), end = stack_it->second.rend(); it != end; ++it)
368
{
369
if (it->op != symbol_type::function)
370
continue;
371
if (it->scope.level > scope.level ||
372
it->scope.namespace_level > scope.namespace_level || (it->scope.namespace_level == scope.namespace_level && it->scope.name != scope.name))
373
continue;
374
375
const function *const function = it->function;
376
377
if (function == nullptr)
378
continue;
379
380
if (function->parameter_list.empty())
381
{
382
if (arguments.empty())
383
{
384
out_data.id = it->id;
385
out_data.type = function->return_type;
386
out_data.function = result = function;
387
num_overloads = 1;
388
break;
389
}
390
else
391
{
392
continue;
393
}
394
}
395
else if (arguments.size() > function->parameter_list.size() || (arguments.size() < function->parameter_list.size() && !function->parameter_list[arguments.size()].has_default_value))
396
{
397
continue;
398
}
399
400
// A new possibly-matching function was found, compare it against the current result
401
const int comparison = compare_functions(arguments, function, result);
402
403
if (comparison < 0) // The new function is a better match
404
{
405
out_data.id = it->id;
406
out_data.type = function->return_type;
407
out_data.function = result = function;
408
num_overloads = 1;
409
overload_namespace = it->scope.namespace_level;
410
}
411
else if (comparison == 0 && overload_namespace == it->scope.namespace_level) // Both functions are equally viable, so the call is ambiguous
412
{
413
++num_overloads;
414
}
415
}
416
}
417
418
// Try matching against intrinsic functions if no matching user-defined function was found up to this point
419
if (num_overloads == 0)
420
{
421
for (const intrinsic &intrinsic : s_intrinsics)
422
{
423
if (intrinsic.name != name || intrinsic.parameter_list.size() != arguments.size())
424
continue;
425
426
// A new possibly-matching intrinsic function was found, compare it against the current result
427
const int comparison = compare_functions(arguments, &intrinsic, result);
428
429
if (comparison < 0) // The new function is a better match
430
{
431
out_data.op = symbol_type::intrinsic;
432
out_data.id = intrinsic.id;
433
out_data.type = intrinsic.return_type;
434
out_data.function = &intrinsic;
435
result = out_data.function;
436
num_overloads = 1;
437
}
438
else if (comparison == 0 && overload_namespace == 0) // Both functions are equally viable, so the call is ambiguous (intrinsics are always in the global namespace)
439
{
440
++num_overloads;
441
}
442
}
443
}
444
445
is_ambiguous = num_overloads > 1;
446
447
return num_overloads == 1;
448
}
449
450