Path: blob/master/dep/reshadefx/src/effect_symbol_table.cpp
4246 views
/*1* Copyright (C) 2014 Patrick Mours2* SPDX-License-Identifier: BSD-3-Clause3*/45#include "effect_symbol_table.hpp"6#include <cassert>7#ifndef __APPLE__8#include <malloc.h> // alloca9#else10#include <alloca.h>11#endif12#include <algorithm> // std::upper_bound, std::sort13#include <functional> // std::greater1415enum class intrinsic_id16{17#define IMPLEMENT_INTRINSIC_SPIRV(name, i, code) name##i,18#include "effect_symbol_table_intrinsics.inl"19};2021struct intrinsic : reshadefx::function22{23intrinsic(const char *name, intrinsic_id id, const reshadefx::type &ret_type, std::initializer_list<reshadefx::type> arg_types)24{25function::return_type = ret_type;26function::id = static_cast<uint32_t>(id);27function::name = name;28function::parameter_list.reserve(arg_types.size());29for (const reshadefx::type &arg_type : arg_types)30function::parameter_list.push_back({ arg_type });31}32};3334#define void { reshadefx::type::t_void }35#define bool { reshadefx::type::t_bool, 1, 1 }36#define bool2 { reshadefx::type::t_bool, 2, 1 }37#define bool3 { reshadefx::type::t_bool, 3, 1 }38#define bool4 { reshadefx::type::t_bool, 4, 1 }39#define int { reshadefx::type::t_int, 1, 1 }40#define int2 { reshadefx::type::t_int, 2, 1 }41#define int3 { reshadefx::type::t_int, 3, 1 }42#define int4 { reshadefx::type::t_int, 4, 1 }43#define int2x3 { reshadefx::type::t_int, 2, 3 }44#define int2x2 { reshadefx::type::t_int, 2, 2 }45#define int2x4 { reshadefx::type::t_int, 2, 4 }46#define int3x2 { reshadefx::type::t_int, 3, 2 }47#define int3x3 { reshadefx::type::t_int, 3, 3 }48#define int3x4 { reshadefx::type::t_int, 3, 4 }49#define int4x2 { reshadefx::type::t_int, 4, 2 }50#define int4x3 { reshadefx::type::t_int, 4, 3 }51#define int4x4 { reshadefx::type::t_int, 4, 4 }52#define out_int { reshadefx::type::t_int, 1, 1, reshadefx::type::q_out }53#define out_int2 { reshadefx::type::t_int, 2, 1, reshadefx::type::q_out }54#define out_int3 { reshadefx::type::t_int, 3, 1, reshadefx::type::q_out }55#define out_int4 { reshadefx::type::t_int, 4, 1, reshadefx::type::q_out }56#define inout_int { reshadefx::type::t_int, 1, 1, reshadefx::type::q_inout | reshadefx::type::q_groupshared }57#define uint { reshadefx::type::t_uint, 1, 1 }58#define uint2 { reshadefx::type::t_uint, 2, 1 }59#define uint3 { reshadefx::type::t_uint, 3, 1 }60#define uint4 { reshadefx::type::t_uint, 4, 1 }61#define inout_uint { reshadefx::type::t_uint, 1, 1, reshadefx::type::q_inout | reshadefx::type::q_groupshared }62#define float { reshadefx::type::t_float, 1, 1 }63#define float2 { reshadefx::type::t_float, 2, 1 }64#define float3 { reshadefx::type::t_float, 3, 1 }65#define float4 { reshadefx::type::t_float, 4, 1 }66#define float2x3 { reshadefx::type::t_float, 2, 3 }67#define float2x2 { reshadefx::type::t_float, 2, 2 }68#define float2x4 { reshadefx::type::t_float, 2, 4 }69#define float3x2 { reshadefx::type::t_float, 3, 2 }70#define float3x3 { reshadefx::type::t_float, 3, 3 }71#define float3x4 { reshadefx::type::t_float, 3, 4 }72#define float4x2 { reshadefx::type::t_float, 4, 2 }73#define float4x3 { reshadefx::type::t_float, 4, 3 }74#define float4x4 { reshadefx::type::t_float, 4, 4 }75#define out_float { reshadefx::type::t_float, 1, 1, reshadefx::type::q_out }76#define out_float2 { reshadefx::type::t_float, 2, 1, reshadefx::type::q_out }77#define out_float3 { reshadefx::type::t_float, 3, 1, reshadefx::type::q_out }78#define out_float4 { reshadefx::type::t_float, 4, 1, reshadefx::type::q_out }79#define sampler1d_int { reshadefx::type::t_sampler1d_int, 1, 1 }80#define sampler2d_int { reshadefx::type::t_sampler2d_int, 1, 1 }81#define sampler3d_int { reshadefx::type::t_sampler3d_int, 1, 1 }82#define sampler1d_uint { reshadefx::type::t_sampler1d_uint, 1, 1 }83#define sampler2d_uint { reshadefx::type::t_sampler2d_uint, 1, 1 }84#define sampler3d_uint { reshadefx::type::t_sampler3d_uint, 1, 1 }85#define sampler1d_float { reshadefx::type::t_sampler1d_float, 1, 1 }86#define sampler2d_float { reshadefx::type::t_sampler2d_float, 1, 1 }87#define sampler3d_float { reshadefx::type::t_sampler3d_float, 1, 1 }88#define sampler1d_float4 { reshadefx::type::t_sampler1d_float, 4, 1 }89#define sampler2d_float4 { reshadefx::type::t_sampler2d_float, 4, 1 }90#define sampler3d_float4 { reshadefx::type::t_sampler3d_float, 4, 1 }91#define storage1d_int { reshadefx::type::t_storage1d_int, 1, 1 }92#define storage2d_int { reshadefx::type::t_storage2d_int, 1, 1 }93#define storage3d_int { reshadefx::type::t_storage3d_int, 1, 1 }94#define storage1d_uint { reshadefx::type::t_storage1d_uint, 1, 1 }95#define storage2d_uint { reshadefx::type::t_storage2d_uint, 1, 1 }96#define storage3d_uint { reshadefx::type::t_storage3d_uint, 1, 1 }97#define storage1d_float { reshadefx::type::t_storage1d_float, 1, 1 }98#define storage2d_float { reshadefx::type::t_storage2d_float, 1, 1 }99#define storage3d_float { reshadefx::type::t_storage3d_float, 1, 1 }100#define storage1d_float4 { reshadefx::type::t_storage1d_float, 4, 1 }101#define storage2d_float4 { reshadefx::type::t_storage2d_float, 4, 1 }102#define storage3d_float4 { reshadefx::type::t_storage3d_float, 4, 1 }103#define inout_storage1d_int { reshadefx::type::t_storage1d_int, 1, 1, reshadefx::type::q_inout }104#define inout_storage2d_int { reshadefx::type::t_storage2d_int, 1, 1, reshadefx::type::q_inout }105#define inout_storage3d_int { reshadefx::type::t_storage3d_int, 1, 1, reshadefx::type::q_inout }106#define inout_storage1d_uint { reshadefx::type::t_storage1d_uint, 1, 1, reshadefx::type::q_inout }107#define inout_storage2d_uint { reshadefx::type::t_storage2d_uint, 1, 1, reshadefx::type::q_inout }108#define inout_storage3d_uint { reshadefx::type::t_storage3d_uint, 1, 1, reshadefx::type::q_inout }109110// Import intrinsic function definitions111static const intrinsic s_intrinsics[] =112{113#define DEFINE_INTRINSIC(name, i, ret_type, ...) intrinsic(#name, intrinsic_id::name##i, ret_type, { __VA_ARGS__ }),114#include "effect_symbol_table_intrinsics.inl"115};116117#undef void118#undef bool119#undef bool2120#undef bool3121#undef bool4122#undef int123#undef int2124#undef int3125#undef int4126#undef uint127#undef uint2128#undef uint3129#undef uint4130#undef float131#undef float2132#undef float3133#undef float4134135unsigned int reshadefx::type::rank(const type &src, const type &dst)136{137if (src.is_array() != dst.is_array() || (src.array_length != dst.array_length && src.is_bounded_array() && dst.is_bounded_array()))138return 0; // Arrays of different sizes are not compatible139if (src.is_struct() || dst.is_struct())140return src.struct_definition == dst.struct_definition ? 32 : 0; // Structs are only compatible if they are the same type141if (!src.is_numeric() || !dst.is_numeric())142return src.base == dst.base && src.rows == dst.rows && src.cols == dst.cols ? 32 : 0; // Numeric values are not compatible with other types143if (src.is_matrix() && (!dst.is_matrix() || src.rows != dst.rows || src.cols != dst.cols))144return 0; // Matrix truncation or dimensions do not match145146// This table is based on the following rules:147// - Floating point has a higher rank than integer types148// - Integer to floating point promotion has a higher rank than floating point to integer conversion149// - Signed to unsigned integer conversion has a higher rank than unsigned to signed integer conversion150static const unsigned int ranks[7][7] = {151{ 5, 4, 4, 4, 4, 4, 4 }, // bool152{ 3, 5, 5, 2, 2, 4, 4 }, // min16int153{ 3, 5, 5, 2, 2, 4, 4 }, // int154{ 3, 1, 1, 5, 5, 4, 4 }, // min16uint155{ 3, 1, 1, 5, 5, 4, 4 }, // uint156{ 3, 3, 3, 3, 3, 6, 6 }, // min16float157{ 3, 3, 3, 3, 3, 6, 6 } // float158};159160assert(src.base > 0 && src.base <= 7); // bool - float161assert(dst.base > 0 && dst.base <= 7);162163const unsigned int rank = ranks[src.base - 1][dst.base - 1] << 2;164165if ((src.is_scalar() && dst.is_vector()))166return rank >> 1; // Scalar to vector promotion has a lower rank167if ((src.is_vector() && dst.is_scalar()) || (src.is_vector() == dst.is_vector() && src.rows > dst.rows && src.cols >= dst.cols))168return rank >> 2; // Vector to scalar conversion has an even lower rank169if ((src.is_vector() != dst.is_vector()) || src.is_matrix() != dst.is_matrix() || src.components() != dst.components())170return 0; // If components weren't converted at this point, the types are not compatible171172return rank * src.components(); // More components causes a higher rank173}174175reshadefx::symbol_table::symbol_table()176{177_current_scope.name = "::";178_current_scope.level = 0;179_current_scope.namespace_level = 0;180}181182void reshadefx::symbol_table::enter_scope()183{184_current_scope.level++;185}186void reshadefx::symbol_table::enter_namespace(const std::string &name)187{188_current_scope.name += name + "::";189_current_scope.level++;190_current_scope.namespace_level++;191}192void reshadefx::symbol_table::leave_scope()193{194assert(_current_scope.level > 0);195196for (auto &symbol : _symbol_stack)197{198std::vector<scoped_symbol> &scope_list = symbol.second;199200for (auto scope_it = scope_list.begin(); scope_it != scope_list.end();)201{202if (scope_it->scope.level > scope_it->scope.namespace_level &&203scope_it->scope.level >= _current_scope.level)204{205scope_it = scope_list.erase(scope_it);206}207else208{209++scope_it;210}211}212}213214_current_scope.level--;215}216void reshadefx::symbol_table::leave_namespace()217{218assert(_current_scope.level > 0);219assert(_current_scope.namespace_level > 0);220221_current_scope.name.erase(_current_scope.name.substr(0, _current_scope.name.size() - 2).rfind("::") + 2);222_current_scope.level--;223_current_scope.namespace_level--;224}225226bool reshadefx::symbol_table::insert_symbol(const std::string &name, const symbol &symbol, bool global)227{228assert(symbol.id != 0 || symbol.op == symbol_type::constant);229230// Make sure the symbol does not exist yet231if (symbol.op != symbol_type::function && find_symbol(name, _current_scope, true).id != 0)232return false;233234// Insertion routine which keeps the symbol stack sorted by namespace level235const auto insert_sorted = [](auto &vec, const auto &item) {236return vec.insert(237std::upper_bound(vec.begin(), vec.end(), item,238[](auto lhs, auto rhs) {239return lhs.scope.namespace_level < rhs.scope.namespace_level;240}), item);241};242243// Global symbols are accessible from every scope244if (global)245{246scope scope = { "", 0, 0 };247248// Walk scope chain from global scope back to current one249for (size_t pos = 0; pos != std::string::npos; pos = _current_scope.name.find("::", pos))250{251// Extract scope name252scope.name = _current_scope.name.substr(0, pos += 2);253const std::string previous_scope_name = _current_scope.name.substr(pos);254255// Insert symbol into this scope256insert_sorted(_symbol_stack[previous_scope_name + name], scoped_symbol { symbol, scope });257258// Continue walking up the scope chain259scope.level = ++scope.namespace_level;260}261}262else263{264// This is a local symbol so it's sufficient to update the symbol stack with just the current scope265insert_sorted(_symbol_stack[name], scoped_symbol { symbol, _current_scope });266}267268return true;269}270271reshadefx::scoped_symbol reshadefx::symbol_table::find_symbol(const std::string &name) const272{273// Default to start search with current scope and walk back the scope chain274return find_symbol(name, _current_scope, false);275}276reshadefx::scoped_symbol reshadefx::symbol_table::find_symbol(const std::string &name, const scope &scope, bool exclusive) const277{278const auto stack_it = _symbol_stack.find(name);279280// Check if symbol does exist281if (stack_it == _symbol_stack.end() || stack_it->second.empty())282return {};283284// Walk up the scope chain starting at the requested scope level and find a matching symbol285scoped_symbol result = {};286287for (auto it = stack_it->second.rbegin(), end = stack_it->second.rend(); it != end; ++it)288{289if (it->scope.level > scope.level ||290it->scope.namespace_level > scope.namespace_level || (it->scope.namespace_level == scope.namespace_level && it->scope.name != scope.name))291continue;292if (exclusive && it->scope.level < scope.level)293continue;294295if (it->op == symbol_type::constant || it->op == symbol_type::variable || it->op == symbol_type::structure)296return *it; // Variables and structures have the highest priority and are always picked immediately297else if (result.id == 0)298result = *it; // Function names have a lower priority, so continue searching in case a variable with the same name exists299}300301return result;302}303304static int compare_functions(const std::vector<reshadefx::expression> &arguments, const reshadefx::function *function1, const reshadefx::function *function2)305{306const size_t num_arguments = arguments.size();307308// Check if the first function matches the argument types309bool function1_viable = true;310const auto function1_ranks = static_cast<unsigned int *>(alloca(num_arguments * sizeof(unsigned int)));311for (size_t i = 0; i < num_arguments; ++i)312{313if ((function1_ranks[i] = reshadefx::type::rank(arguments[i].type, function1->parameter_list[i].type)) == 0)314{315function1_viable = false;316break;317}318}319320// Catch case where the second function does not exist321if (function2 == nullptr)322return function1_viable ? -1 : 1; // If the first function is not viable, this compare fails323324// Check if the second function matches the argument types325bool function2_viable = true;326const auto function2_ranks = static_cast<unsigned int *>(alloca(num_arguments * sizeof(unsigned int)));327for (size_t i = 0; i < num_arguments; ++i)328{329if ((function2_ranks[i] = reshadefx::type::rank(arguments[i].type, function2->parameter_list[i].type)) == 0)330{331function2_viable = false;332break;333}334}335336// If one of the functions is not viable, then the other one automatically wins337if (!function1_viable || !function2_viable)338return function2_viable - function1_viable;339340// Both functions are possible, so find the one with the higher ranking341std::sort(function1_ranks, function1_ranks + num_arguments, std::greater<unsigned int>());342std::sort(function2_ranks, function2_ranks + num_arguments, std::greater<unsigned int>());343344for (size_t i = 0; i < num_arguments; ++i)345if (function1_ranks[i] > function2_ranks[i])346return -1; // Left function wins347else if (function2_ranks[i] > function1_ranks[i])348return +1; // Right function wins349350return 0; // Both functions are equally viable351}352353bool reshadefx::symbol_table::resolve_function_call(const std::string &name, const std::vector<expression> &arguments, const scope &scope, symbol &out_data, bool &is_ambiguous) const354{355out_data.op = symbol_type::function;356357const function *result = nullptr;358unsigned int num_overloads = 0;359unsigned int overload_namespace = scope.namespace_level;360361// Look up function name in the symbol stack and loop through the associated symbols362const auto stack_it = _symbol_stack.find(name);363364if (stack_it != _symbol_stack.end() && !stack_it->second.empty())365{366for (auto it = stack_it->second.rbegin(), end = stack_it->second.rend(); it != end; ++it)367{368if (it->op != symbol_type::function)369continue;370if (it->scope.level > scope.level ||371it->scope.namespace_level > scope.namespace_level || (it->scope.namespace_level == scope.namespace_level && it->scope.name != scope.name))372continue;373374const function *const function = it->function;375376if (function == nullptr)377continue;378379if (function->parameter_list.empty())380{381if (arguments.empty())382{383out_data.id = it->id;384out_data.type = function->return_type;385out_data.function = result = function;386num_overloads = 1;387break;388}389else390{391continue;392}393}394else if (arguments.size() > function->parameter_list.size() || (arguments.size() < function->parameter_list.size() && !function->parameter_list[arguments.size()].has_default_value))395{396continue;397}398399// A new possibly-matching function was found, compare it against the current result400const int comparison = compare_functions(arguments, function, result);401402if (comparison < 0) // The new function is a better match403{404out_data.id = it->id;405out_data.type = function->return_type;406out_data.function = result = function;407num_overloads = 1;408overload_namespace = it->scope.namespace_level;409}410else if (comparison == 0 && overload_namespace == it->scope.namespace_level) // Both functions are equally viable, so the call is ambiguous411{412++num_overloads;413}414}415}416417// Try matching against intrinsic functions if no matching user-defined function was found up to this point418if (num_overloads == 0)419{420for (const intrinsic &intrinsic : s_intrinsics)421{422if (intrinsic.name != name || intrinsic.parameter_list.size() != arguments.size())423continue;424425// A new possibly-matching intrinsic function was found, compare it against the current result426const int comparison = compare_functions(arguments, &intrinsic, result);427428if (comparison < 0) // The new function is a better match429{430out_data.op = symbol_type::intrinsic;431out_data.id = intrinsic.id;432out_data.type = intrinsic.return_type;433out_data.function = &intrinsic;434result = out_data.function;435num_overloads = 1;436}437else if (comparison == 0 && overload_namespace == 0) // Both functions are equally viable, so the call is ambiguous (intrinsics are always in the global namespace)438{439++num_overloads;440}441}442}443444is_ambiguous = num_overloads > 1;445446return num_overloads == 1;447}448449450