dep: Add reshadefx
This commit is contained in:
2151
dep/reshadefx/src/effect_codegen_glsl.cpp
Normal file
2151
dep/reshadefx/src/effect_codegen_glsl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1845
dep/reshadefx/src/effect_codegen_hlsl.cpp
Normal file
1845
dep/reshadefx/src/effect_codegen_hlsl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2394
dep/reshadefx/src/effect_codegen_spirv.cpp
Normal file
2394
dep/reshadefx/src/effect_codegen_spirv.cpp
Normal file
File diff suppressed because it is too large
Load Diff
623
dep/reshadefx/src/effect_expression.cpp
Normal file
623
dep/reshadefx/src/effect_expression.cpp
Normal file
@ -0,0 +1,623 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Patrick Mours
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "effect_lexer.hpp"
|
||||
#include "effect_codegen.hpp"
|
||||
#include <cmath> // fmod
|
||||
#include <cassert>
|
||||
#include <cstring> // memcpy, memset
|
||||
#include <algorithm> // std::min, std::max
|
||||
|
||||
reshadefx::type reshadefx::type::merge(const type &lhs, const type &rhs)
|
||||
{
|
||||
type result = { std::max(lhs.base, rhs.base) };
|
||||
|
||||
// Non-numeric types cannot be vectors or matrices
|
||||
if (!result.is_numeric())
|
||||
{
|
||||
result.rows = 0;
|
||||
result.cols = 0;
|
||||
}
|
||||
// If one side of the expression is scalar, it needs to be promoted to the same dimension as the other side
|
||||
else if ((lhs.rows == 1 && lhs.cols == 1) || (rhs.rows == 1 && rhs.cols == 1))
|
||||
{
|
||||
result.rows = std::max(lhs.rows, rhs.rows);
|
||||
result.cols = std::max(lhs.cols, rhs.cols);
|
||||
}
|
||||
else // Otherwise dimensions match or one side is truncated to match the other one
|
||||
{
|
||||
result.rows = std::min(lhs.rows, rhs.rows);
|
||||
result.cols = std::min(lhs.cols, rhs.cols);
|
||||
}
|
||||
|
||||
// Some qualifiers propagate to the result
|
||||
result.qualifiers = (lhs.qualifiers & type::q_precise) | (rhs.qualifiers & type::q_precise);
|
||||
|
||||
// In case this is a structure, assume they are the same
|
||||
result.definition = rhs.definition;
|
||||
assert(lhs.definition == rhs.definition || lhs.definition == 0);
|
||||
assert(lhs.array_length == 0 && rhs.array_length == 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string reshadefx::type::description() const
|
||||
{
|
||||
std::string result;
|
||||
switch (base)
|
||||
{
|
||||
case reshadefx::type::t_void:
|
||||
result = "void";
|
||||
break;
|
||||
case reshadefx::type::t_bool:
|
||||
result = "bool";
|
||||
break;
|
||||
case reshadefx::type::t_min16int:
|
||||
result = "min16int";
|
||||
break;
|
||||
case reshadefx::type::t_int:
|
||||
result = "int";
|
||||
break;
|
||||
case reshadefx::type::t_min16uint:
|
||||
result = "min16uint";
|
||||
break;
|
||||
case reshadefx::type::t_uint:
|
||||
result = "uint";
|
||||
break;
|
||||
case reshadefx::type::t_min16float:
|
||||
result = "min16float";
|
||||
break;
|
||||
case reshadefx::type::t_float:
|
||||
result = "float";
|
||||
break;
|
||||
case reshadefx::type::t_string:
|
||||
result = "string";
|
||||
break;
|
||||
case reshadefx::type::t_struct:
|
||||
result = "struct";
|
||||
break;
|
||||
case reshadefx::type::t_texture1d:
|
||||
result = "texture1D";
|
||||
break;
|
||||
case reshadefx::type::t_texture2d:
|
||||
result = "texture2D";
|
||||
break;
|
||||
case reshadefx::type::t_texture3d:
|
||||
result = "texture3D";
|
||||
break;
|
||||
case reshadefx::type::t_sampler1d_int:
|
||||
result = "sampler1D<int" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_sampler2d_int:
|
||||
result = "sampler2D<int" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_sampler3d_int:
|
||||
result = "sampler3D<int" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_sampler1d_uint:
|
||||
result = "sampler1D<uint" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_sampler2d_uint:
|
||||
result = "sampler2D<uint" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_sampler3d_uint:
|
||||
result = "sampler3D<uint" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_sampler1d_float:
|
||||
result = "sampler1D<float" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_sampler2d_float:
|
||||
result = "sampler2D<float" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_sampler3d_float:
|
||||
result = "sampler3D<float" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_storage1d_int:
|
||||
result = "storage1D<int" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_storage2d_int:
|
||||
result = "storage2D<int" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_storage3d_int:
|
||||
result = "storage3D<int" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_storage1d_uint:
|
||||
result = "storage1D<uint" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_storage2d_uint:
|
||||
result = "storage2D<uint" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_storage3d_uint:
|
||||
result = "storage3D<uint" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_storage1d_float:
|
||||
result = "storage1D<float" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_storage2d_float:
|
||||
result = "storage2D<float" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_storage3d_float:
|
||||
result = "storage3D<float" + std::to_string(rows) + '>';
|
||||
break;
|
||||
case reshadefx::type::t_function:
|
||||
result = "function";
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_numeric())
|
||||
{
|
||||
if (rows > 1 || cols > 1)
|
||||
result += std::to_string(rows);
|
||||
if (cols > 1)
|
||||
result += 'x' + std::to_string(cols);
|
||||
}
|
||||
|
||||
if (is_array())
|
||||
{
|
||||
result += '[';
|
||||
if (array_length > 0)
|
||||
result += std::to_string(array_length);
|
||||
result += ']';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void reshadefx::expression::reset_to_lvalue(const reshadefx::location &loc, uint32_t in_base, const reshadefx::type &in_type)
|
||||
{
|
||||
type = in_type;
|
||||
base = in_base;
|
||||
location = loc;
|
||||
is_lvalue = true;
|
||||
is_constant = false;
|
||||
chain.clear();
|
||||
|
||||
// Make sure uniform l-values cannot be assigned to by making them constant
|
||||
if (in_type.has(type::q_uniform))
|
||||
type.qualifiers |= type::q_const;
|
||||
|
||||
// Strip away global variable qualifiers
|
||||
type.qualifiers &= ~(reshadefx::type::q_extern | reshadefx::type::q_static | reshadefx::type::q_uniform | reshadefx::type::q_groupshared);
|
||||
}
|
||||
void reshadefx::expression::reset_to_rvalue(const reshadefx::location &loc, uint32_t in_base, const reshadefx::type &in_type)
|
||||
{
|
||||
type = in_type;
|
||||
type.qualifiers |= type::q_const;
|
||||
base = in_base;
|
||||
location = loc;
|
||||
is_lvalue = false;
|
||||
is_constant = false;
|
||||
chain.clear();
|
||||
|
||||
// Strip away global variable qualifiers
|
||||
type.qualifiers &= ~(reshadefx::type::q_extern | reshadefx::type::q_static | reshadefx::type::q_uniform | reshadefx::type::q_groupshared);
|
||||
}
|
||||
|
||||
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, bool data)
|
||||
{
|
||||
type = { type::t_bool, 1, 1, type::q_const };
|
||||
base = 0; constant = {}; constant.as_uint[0] = data;
|
||||
location = loc;
|
||||
is_lvalue = false;
|
||||
is_constant = true;
|
||||
chain.clear();
|
||||
}
|
||||
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, float data)
|
||||
{
|
||||
type = { type::t_float, 1, 1, type::q_const };
|
||||
base = 0; constant = {}; constant.as_float[0] = data;
|
||||
location = loc;
|
||||
is_lvalue = false;
|
||||
is_constant = true;
|
||||
chain.clear();
|
||||
}
|
||||
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, int32_t data)
|
||||
{
|
||||
type = { type::t_int, 1, 1, type::q_const };
|
||||
base = 0; constant = {}; constant.as_int[0] = data;
|
||||
location = loc;
|
||||
is_lvalue = false;
|
||||
is_constant = true;
|
||||
chain.clear();
|
||||
}
|
||||
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, uint32_t data)
|
||||
{
|
||||
type = { type::t_uint, 1, 1, type::q_const };
|
||||
base = 0; constant = {}; constant.as_uint[0] = data;
|
||||
location = loc;
|
||||
is_lvalue = false;
|
||||
is_constant = true;
|
||||
chain.clear();
|
||||
}
|
||||
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, std::string data)
|
||||
{
|
||||
type = { type::t_string, 0, 0, type::q_const };
|
||||
base = 0; constant = {}; constant.string_data = std::move(data);
|
||||
location = loc;
|
||||
is_lvalue = false;
|
||||
is_constant = true;
|
||||
chain.clear();
|
||||
}
|
||||
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, reshadefx::constant data, const reshadefx::type &in_type)
|
||||
{
|
||||
type = in_type;
|
||||
type.qualifiers |= type::q_const;
|
||||
base = 0; constant = std::move(data);
|
||||
location = loc;
|
||||
is_lvalue = false;
|
||||
is_constant = true;
|
||||
chain.clear();
|
||||
}
|
||||
|
||||
void reshadefx::expression::add_cast_operation(const reshadefx::type &cast_type)
|
||||
{
|
||||
// First try to simplify the cast with a swizzle operation (only works with scalars and vectors)
|
||||
if (type.cols == 1 && cast_type.cols == 1 && type.rows != cast_type.rows)
|
||||
{
|
||||
signed char swizzle[] = { 0, 1, 2, 3 };
|
||||
// Ignore components in a demotion cast
|
||||
for (unsigned int i = cast_type.rows; i < 4; ++i)
|
||||
swizzle[i] = -1;
|
||||
// Use the last component to fill in a promotion cast
|
||||
for (unsigned int i = type.rows; i < cast_type.rows; ++i)
|
||||
swizzle[i] = swizzle[type.rows - 1];
|
||||
|
||||
add_swizzle_access(swizzle, cast_type.rows);
|
||||
}
|
||||
|
||||
if (type == cast_type)
|
||||
return; // There is nothing more to do if the expression is already of the target type at this point
|
||||
|
||||
if (is_constant)
|
||||
{
|
||||
const auto cast_constant = [](reshadefx::constant &constant, const reshadefx::type &from, const reshadefx::type &to) {
|
||||
// Handle scalar to vector promotion first
|
||||
if (from.is_scalar() && !to.is_scalar())
|
||||
for (unsigned int i = 1; i < to.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_uint[0];
|
||||
|
||||
// Next check whether the type needs casting as well (and don't convert between signed/unsigned, since that is handled by the union)
|
||||
if (from.base == to.base || from.is_floating_point() == to.is_floating_point())
|
||||
return;
|
||||
|
||||
if (!to.is_floating_point())
|
||||
for (unsigned int i = 0; i < to.components(); ++i)
|
||||
constant.as_uint[i] = static_cast<int>(constant.as_float[i]);
|
||||
else
|
||||
for (unsigned int i = 0; i < to.components(); ++i)
|
||||
constant.as_float[i] = static_cast<float>(constant.as_int[i]);
|
||||
};
|
||||
|
||||
for (auto &element : constant.array_data)
|
||||
cast_constant(element, type, cast_type);
|
||||
|
||||
cast_constant(constant, type, cast_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(!type.is_array() && !cast_type.is_array());
|
||||
|
||||
chain.push_back({ operation::op_cast, type, cast_type });
|
||||
}
|
||||
|
||||
type = cast_type;
|
||||
type.qualifiers |= type::q_const; // Casting always makes expression not modifiable
|
||||
}
|
||||
void reshadefx::expression::add_member_access(unsigned int index, const reshadefx::type &in_type)
|
||||
{
|
||||
assert(type.is_struct());
|
||||
|
||||
chain.push_back({ operation::op_member, type, in_type, index });
|
||||
|
||||
// The type is now the type of the member that was accessed
|
||||
type = in_type;
|
||||
is_constant = false;
|
||||
}
|
||||
void reshadefx::expression::add_dynamic_index_access(uint32_t index_expression)
|
||||
{
|
||||
assert(!is_constant); // Cannot have dynamic indexing into constant in SPIR-V
|
||||
assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));
|
||||
|
||||
auto prev_type = type;
|
||||
|
||||
if (type.is_array())
|
||||
{
|
||||
type.array_length = 0;
|
||||
}
|
||||
else if (type.is_matrix())
|
||||
{
|
||||
type.rows = type.cols;
|
||||
type.cols = 1;
|
||||
}
|
||||
else if (type.is_vector())
|
||||
{
|
||||
type.rows = 1;
|
||||
}
|
||||
|
||||
chain.push_back({ operation::op_dynamic_index, prev_type, type, index_expression });
|
||||
}
|
||||
void reshadefx::expression::add_constant_index_access(unsigned int index)
|
||||
{
|
||||
assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));
|
||||
|
||||
auto prev_type = type;
|
||||
|
||||
if (type.is_array())
|
||||
{
|
||||
assert(type.array_length < 0 || index < static_cast<unsigned int>(type.array_length));
|
||||
|
||||
type.array_length = 0;
|
||||
}
|
||||
else if (type.is_matrix())
|
||||
{
|
||||
assert(index < type.components());
|
||||
|
||||
type.rows = type.cols;
|
||||
type.cols = 1;
|
||||
}
|
||||
else if (type.is_vector())
|
||||
{
|
||||
assert(index < type.components());
|
||||
|
||||
type.rows = 1;
|
||||
}
|
||||
|
||||
if (is_constant)
|
||||
{
|
||||
if (prev_type.is_array())
|
||||
{
|
||||
constant = constant.array_data[index];
|
||||
}
|
||||
else if (prev_type.is_matrix()) // Indexing into a matrix returns a row of it as a vector
|
||||
{
|
||||
for (unsigned int i = 0; i < prev_type.cols; ++i)
|
||||
constant.as_uint[i] = constant.as_uint[index * prev_type.cols + i];
|
||||
}
|
||||
else // Indexing into a vector returns the element as a scalar
|
||||
{
|
||||
constant.as_uint[0] = constant.as_uint[index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
chain.push_back({ operation::op_constant_index, prev_type, type, index });
|
||||
}
|
||||
}
|
||||
void reshadefx::expression::add_swizzle_access(const signed char swizzle[4], unsigned int length)
|
||||
{
|
||||
assert(type.is_numeric() && !type.is_array());
|
||||
|
||||
const auto prev_type = type;
|
||||
|
||||
type.rows = length;
|
||||
type.cols = 1;
|
||||
|
||||
if (is_constant)
|
||||
{
|
||||
assert(constant.array_data.empty());
|
||||
|
||||
uint32_t data[16];
|
||||
std::memcpy(data, &constant.as_uint[0], sizeof(data));
|
||||
for (unsigned int i = 0; i < length; ++i)
|
||||
constant.as_uint[i] = data[swizzle[i]];
|
||||
std::memset(&constant.as_uint[length], 0, sizeof(uint32_t) * (16 - length)); // Clear the rest of the constant
|
||||
}
|
||||
else if (length == 1 && prev_type.is_vector()) // Use indexing when possible since the code generation logic is simpler in SPIR-V
|
||||
{
|
||||
chain.push_back({ operation::op_constant_index, prev_type, type, static_cast<uint32_t>(swizzle[0]) });
|
||||
}
|
||||
else
|
||||
{
|
||||
chain.push_back({ operation::op_swizzle, prev_type, type, 0, { swizzle[0], swizzle[1], swizzle[2], swizzle[3] } });
|
||||
}
|
||||
}
|
||||
|
||||
bool reshadefx::expression::evaluate_constant_expression(reshadefx::tokenid op)
|
||||
{
|
||||
if (!is_constant)
|
||||
return false;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case tokenid::exclaim:
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = !constant.as_uint[i];
|
||||
break;
|
||||
case tokenid::minus:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_float[i] = -constant.as_float[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_int[i] = -constant.as_int[i];
|
||||
break;
|
||||
case tokenid::tilde:
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = ~constant.as_uint[i];
|
||||
break;
|
||||
default:
|
||||
// Unknown operator token, so nothing to do
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool reshadefx::expression::evaluate_constant_expression(reshadefx::tokenid op, const reshadefx::constant &rhs)
|
||||
{
|
||||
if (!is_constant)
|
||||
return false;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case tokenid::percent:
|
||||
if (type.is_floating_point()) {
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
// Floating point modulo with zero is defined and results in NaN
|
||||
if (rhs.as_float[i] == 0)
|
||||
constant.as_float[i] = std::numeric_limits<float>::quiet_NaN();
|
||||
else
|
||||
constant.as_float[i] = std::fmod(constant.as_float[i], rhs.as_float[i]);
|
||||
}
|
||||
else if (type.is_signed()) {
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
// Integer modulo with zero on the other hand is not defined, so do not fold this expression in that case
|
||||
if (rhs.as_int[i] == 0)
|
||||
return false;
|
||||
else
|
||||
constant.as_int[i] %= rhs.as_int[i];
|
||||
}
|
||||
else {
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
if (rhs.as_uint[i] == 0)
|
||||
return false;
|
||||
else
|
||||
constant.as_uint[i] %= rhs.as_uint[i];
|
||||
}
|
||||
break;
|
||||
case tokenid::star:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_float[i] *= rhs.as_float[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] *= rhs.as_uint[i];
|
||||
break;
|
||||
case tokenid::plus:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_float[i] += rhs.as_float[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] += rhs.as_uint[i];
|
||||
break;
|
||||
case tokenid::minus:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_float[i] -= rhs.as_float[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] -= rhs.as_uint[i];
|
||||
break;
|
||||
case tokenid::slash:
|
||||
if (type.is_floating_point()) {
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
// Floating point division by zero is well defined and results in infinity or NaN
|
||||
constant.as_float[i] /= rhs.as_float[i];
|
||||
}
|
||||
else if (type.is_signed()) {
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
// Integer division by zero on the other hand is not defined, so do not fold this expression in that case
|
||||
if (rhs.as_int[i] == 0)
|
||||
return false;
|
||||
else
|
||||
constant.as_int[i] /= rhs.as_int[i];
|
||||
}
|
||||
else {
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
if (rhs.as_uint[i] == 0)
|
||||
return false;
|
||||
else
|
||||
constant.as_uint[i] /= rhs.as_uint[i];
|
||||
}
|
||||
break;
|
||||
case tokenid::ampersand:
|
||||
case tokenid::ampersand_ampersand:
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] &= rhs.as_uint[i];
|
||||
break;
|
||||
case tokenid::pipe:
|
||||
case tokenid::pipe_pipe:
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] |= rhs.as_uint[i];
|
||||
break;
|
||||
case tokenid::caret:
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] ^= rhs.as_uint[i];
|
||||
break;
|
||||
case tokenid::less:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_float[i] < rhs.as_float[i];
|
||||
else if (type.is_signed())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_int[i] < rhs.as_int[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_uint[i] < rhs.as_uint[i];
|
||||
type.base = type::t_bool; // Logic operations change the type to boolean
|
||||
break;
|
||||
case tokenid::less_equal:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_float[i] <= rhs.as_float[i];
|
||||
else if (type.is_signed())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_int[i] <= rhs.as_int[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_uint[i] <= rhs.as_uint[i];
|
||||
type.base = type::t_bool;
|
||||
break;
|
||||
case tokenid::greater:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_float[i] > rhs.as_float[i];
|
||||
else if (type.is_signed())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_int[i] > rhs.as_int[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_uint[i] > rhs.as_uint[i];
|
||||
type.base = type::t_bool;
|
||||
break;
|
||||
case tokenid::greater_equal:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_float[i] >= rhs.as_float[i];
|
||||
else if (type.is_signed())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_int[i] >= rhs.as_int[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_uint[i] >= rhs.as_uint[i];
|
||||
type.base = type::t_bool;
|
||||
break;
|
||||
case tokenid::equal_equal:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_float[i] == rhs.as_float[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_uint[i] == rhs.as_uint[i];
|
||||
type.base = type::t_bool;
|
||||
break;
|
||||
case tokenid::exclaim_equal:
|
||||
if (type.is_floating_point())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_float[i] != rhs.as_float[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] = constant.as_uint[i] != rhs.as_uint[i];
|
||||
type.base = type::t_bool;
|
||||
break;
|
||||
case tokenid::less_less:
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] <<= rhs.as_uint[i];
|
||||
break;
|
||||
case tokenid::greater_greater:
|
||||
if (type.is_signed())
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_int[i] >>= rhs.as_int[i];
|
||||
else
|
||||
for (unsigned int i = 0; i < type.components(); ++i)
|
||||
constant.as_uint[i] >>= rhs.as_uint[i];
|
||||
break;
|
||||
default:
|
||||
// Unknown operator token, so nothing to do
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
1166
dep/reshadefx/src/effect_lexer.cpp
Normal file
1166
dep/reshadefx/src/effect_lexer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1538
dep/reshadefx/src/effect_parser_exp.cpp
Normal file
1538
dep/reshadefx/src/effect_parser_exp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2003
dep/reshadefx/src/effect_parser_stmt.cpp
Normal file
2003
dep/reshadefx/src/effect_parser_stmt.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1295
dep/reshadefx/src/effect_preprocessor.cpp
Normal file
1295
dep/reshadefx/src/effect_preprocessor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
477
dep/reshadefx/src/effect_symbol_table.cpp
Normal file
477
dep/reshadefx/src/effect_symbol_table.cpp
Normal file
@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Patrick Mours
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "effect_symbol_table.hpp"
|
||||
#include <cassert>
|
||||
#include <malloc.h> // alloca
|
||||
#include <algorithm> // std::upper_bound, std::sort
|
||||
#include <functional> // std::greater
|
||||
|
||||
enum class intrinsic_id : uint32_t
|
||||
{
|
||||
#define IMPLEMENT_INTRINSIC_SPIRV(name, i, code) name##i,
|
||||
#include "effect_symbol_table_intrinsics.inl"
|
||||
};
|
||||
|
||||
struct intrinsic
|
||||
{
|
||||
intrinsic(const char *name, intrinsic_id id, const reshadefx::type &ret_type, std::initializer_list<reshadefx::type> arg_types) : id(id)
|
||||
{
|
||||
function.name = name;
|
||||
function.return_type = ret_type;
|
||||
function.parameter_list.reserve(arg_types.size());
|
||||
for (const reshadefx::type &arg_type : arg_types)
|
||||
function.parameter_list.push_back({ arg_type });
|
||||
}
|
||||
|
||||
intrinsic_id id;
|
||||
reshadefx::function_info function;
|
||||
};
|
||||
|
||||
#define void { reshadefx::type::t_void }
|
||||
#define bool { reshadefx::type::t_bool, 1, 1 }
|
||||
#define bool2 { reshadefx::type::t_bool, 2, 1 }
|
||||
#define bool3 { reshadefx::type::t_bool, 3, 1 }
|
||||
#define bool4 { reshadefx::type::t_bool, 4, 1 }
|
||||
#define int { reshadefx::type::t_int, 1, 1 }
|
||||
#define int2 { reshadefx::type::t_int, 2, 1 }
|
||||
#define int3 { reshadefx::type::t_int, 3, 1 }
|
||||
#define int4 { reshadefx::type::t_int, 4, 1 }
|
||||
#define int2x3 { reshadefx::type::t_int, 2, 3 }
|
||||
#define int2x2 { reshadefx::type::t_int, 2, 2 }
|
||||
#define int2x4 { reshadefx::type::t_int, 2, 4 }
|
||||
#define int3x2 { reshadefx::type::t_int, 3, 2 }
|
||||
#define int3x3 { reshadefx::type::t_int, 3, 3 }
|
||||
#define int3x4 { reshadefx::type::t_int, 3, 4 }
|
||||
#define int4x2 { reshadefx::type::t_int, 4, 2 }
|
||||
#define int4x3 { reshadefx::type::t_int, 4, 3 }
|
||||
#define int4x4 { reshadefx::type::t_int, 4, 4 }
|
||||
#define out_int { reshadefx::type::t_int, 1, 1, reshadefx::type::q_out }
|
||||
#define out_int2 { reshadefx::type::t_int, 2, 1, reshadefx::type::q_out }
|
||||
#define out_int3 { reshadefx::type::t_int, 3, 1, reshadefx::type::q_out }
|
||||
#define out_int4 { reshadefx::type::t_int, 4, 1, reshadefx::type::q_out }
|
||||
#define inout_int { reshadefx::type::t_int, 1, 1, reshadefx::type::q_inout | reshadefx::type::q_groupshared }
|
||||
#define uint { reshadefx::type::t_uint, 1, 1 }
|
||||
#define uint2 { reshadefx::type::t_uint, 2, 1 }
|
||||
#define uint3 { reshadefx::type::t_uint, 3, 1 }
|
||||
#define uint4 { reshadefx::type::t_uint, 4, 1 }
|
||||
#define inout_uint { reshadefx::type::t_uint, 1, 1, reshadefx::type::q_inout | reshadefx::type::q_groupshared }
|
||||
#define float { reshadefx::type::t_float, 1, 1 }
|
||||
#define float2 { reshadefx::type::t_float, 2, 1 }
|
||||
#define float3 { reshadefx::type::t_float, 3, 1 }
|
||||
#define float4 { reshadefx::type::t_float, 4, 1 }
|
||||
#define float2x3 { reshadefx::type::t_float, 2, 3 }
|
||||
#define float2x2 { reshadefx::type::t_float, 2, 2 }
|
||||
#define float2x4 { reshadefx::type::t_float, 2, 4 }
|
||||
#define float3x2 { reshadefx::type::t_float, 3, 2 }
|
||||
#define float3x3 { reshadefx::type::t_float, 3, 3 }
|
||||
#define float3x4 { reshadefx::type::t_float, 3, 4 }
|
||||
#define float4x2 { reshadefx::type::t_float, 4, 2 }
|
||||
#define float4x3 { reshadefx::type::t_float, 4, 3 }
|
||||
#define float4x4 { reshadefx::type::t_float, 4, 4 }
|
||||
#define out_float { reshadefx::type::t_float, 1, 1, reshadefx::type::q_out }
|
||||
#define out_float2 { reshadefx::type::t_float, 2, 1, reshadefx::type::q_out }
|
||||
#define out_float3 { reshadefx::type::t_float, 3, 1, reshadefx::type::q_out }
|
||||
#define out_float4 { reshadefx::type::t_float, 4, 1, reshadefx::type::q_out }
|
||||
#define sampler1d_int { reshadefx::type::t_sampler1d_int, 1, 1 }
|
||||
#define sampler2d_int { reshadefx::type::t_sampler2d_int, 1, 1 }
|
||||
#define sampler3d_int { reshadefx::type::t_sampler3d_int, 1, 1 }
|
||||
#define sampler1d_uint { reshadefx::type::t_sampler1d_uint, 1, 1 }
|
||||
#define sampler2d_uint { reshadefx::type::t_sampler2d_uint, 1, 1 }
|
||||
#define sampler3d_uint { reshadefx::type::t_sampler3d_uint, 1, 1 }
|
||||
#define sampler1d_float { reshadefx::type::t_sampler1d_float, 1, 1 }
|
||||
#define sampler2d_float { reshadefx::type::t_sampler2d_float, 1, 1 }
|
||||
#define sampler3d_float { reshadefx::type::t_sampler3d_float, 1, 1 }
|
||||
#define sampler1d_float4 { reshadefx::type::t_sampler1d_float, 4, 1 }
|
||||
#define sampler2d_float4 { reshadefx::type::t_sampler2d_float, 4, 1 }
|
||||
#define sampler3d_float4 { reshadefx::type::t_sampler3d_float, 4, 1 }
|
||||
#define storage1d_int { reshadefx::type::t_storage1d_int, 1, 1 }
|
||||
#define storage2d_int { reshadefx::type::t_storage2d_int, 1, 1 }
|
||||
#define storage3d_int { reshadefx::type::t_storage3d_int, 1, 1 }
|
||||
#define storage1d_uint { reshadefx::type::t_storage1d_uint, 1, 1 }
|
||||
#define storage2d_uint { reshadefx::type::t_storage2d_uint, 1, 1 }
|
||||
#define storage3d_uint { reshadefx::type::t_storage3d_uint, 1, 1 }
|
||||
#define storage1d_float { reshadefx::type::t_storage1d_float, 1, 1 }
|
||||
#define storage2d_float { reshadefx::type::t_storage2d_float, 1, 1 }
|
||||
#define storage3d_float { reshadefx::type::t_storage3d_float, 1, 1 }
|
||||
#define storage1d_float4 { reshadefx::type::t_storage1d_float, 4, 1 }
|
||||
#define storage2d_float4 { reshadefx::type::t_storage2d_float, 4, 1 }
|
||||
#define storage3d_float4 { reshadefx::type::t_storage3d_float, 4, 1 }
|
||||
#define inout_storage1d_int { reshadefx::type::t_storage1d_int, 1, 1, reshadefx::type::q_inout }
|
||||
#define inout_storage2d_int { reshadefx::type::t_storage2d_int, 1, 1, reshadefx::type::q_inout }
|
||||
#define inout_storage3d_int { reshadefx::type::t_storage3d_int, 1, 1, reshadefx::type::q_inout }
|
||||
#define inout_storage1d_uint { reshadefx::type::t_storage1d_uint, 1, 1, reshadefx::type::q_inout }
|
||||
#define inout_storage2d_uint { reshadefx::type::t_storage2d_uint, 1, 1, reshadefx::type::q_inout }
|
||||
#define inout_storage3d_uint { reshadefx::type::t_storage3d_uint, 1, 1, reshadefx::type::q_inout }
|
||||
|
||||
// Import intrinsic function definitions
|
||||
static const intrinsic s_intrinsics[] =
|
||||
{
|
||||
#define DEFINE_INTRINSIC(name, i, ret_type, ...) intrinsic(#name, intrinsic_id::name##i, ret_type, { __VA_ARGS__ }),
|
||||
#include "effect_symbol_table_intrinsics.inl"
|
||||
};
|
||||
|
||||
#undef void
|
||||
#undef bool
|
||||
#undef bool2
|
||||
#undef bool3
|
||||
#undef bool4
|
||||
#undef int
|
||||
#undef int2
|
||||
#undef int3
|
||||
#undef int4
|
||||
#undef uint
|
||||
#undef uint2
|
||||
#undef uint3
|
||||
#undef uint4
|
||||
#undef float1
|
||||
#undef float2
|
||||
#undef float3
|
||||
#undef float4
|
||||
#undef float2x2
|
||||
#undef float3x3
|
||||
#undef float4x4
|
||||
#undef out_float
|
||||
#undef out_float2
|
||||
#undef out_float3
|
||||
#undef out_float4
|
||||
#undef sampler1d_int
|
||||
#undef sampler2d_int
|
||||
#undef sampler3d_int
|
||||
#undef sampler1d_uint
|
||||
#undef sampler2d_uint
|
||||
#undef sampler3d_uint
|
||||
#undef sampler1d_float4
|
||||
#undef sampler2d_float4
|
||||
#undef sampler3d_float4
|
||||
#undef storage1d_int
|
||||
#undef storage2d_int
|
||||
#undef storage3d_int
|
||||
#undef storage1d_uint
|
||||
#undef storage2d_uint
|
||||
#undef storage3d_uint
|
||||
#undef storage1d_float4
|
||||
#undef storage2d_float4
|
||||
#undef storage3d_float4
|
||||
#undef inout_storage1d_int
|
||||
#undef inout_storage2d_int
|
||||
#undef inout_storage3d_int
|
||||
#undef inout_storage1d_uint
|
||||
#undef inout_storage2d_uint
|
||||
#undef inout_storage3d_uint
|
||||
|
||||
unsigned int reshadefx::type::rank(const type &src, const type &dst)
|
||||
{
|
||||
if (src.is_array() != dst.is_array() || (src.array_length != dst.array_length && src.array_length > 0 && dst.array_length > 0))
|
||||
return 0; // Arrays of different sizes are not compatible
|
||||
if (src.is_struct() || dst.is_struct())
|
||||
return src.definition == dst.definition ? 32 : 0; // Structs are only compatible if they are the same type
|
||||
if (!src.is_numeric() || !dst.is_numeric())
|
||||
return src.base == dst.base && src.rows == dst.rows && src.cols == dst.cols ? 32 : 0; // Numeric values are not compatible with other types
|
||||
if (src.is_matrix() && (!dst.is_matrix() || src.rows != dst.rows || src.cols != dst.cols))
|
||||
return 0; // Matrix truncation or dimensions do not match
|
||||
|
||||
// This table is based on the following rules:
|
||||
// - Floating point has a higher rank than integer types
|
||||
// - Integer to floating point promotion has a higher rank than floating point to integer conversion
|
||||
// - Signed to unsigned integer conversion has a higher rank than unsigned to signed integer conversion
|
||||
static const int ranks[7][7] = {
|
||||
{ 5, 4, 4, 4, 4, 4, 4 }, // bool
|
||||
{ 3, 5, 5, 2, 2, 4, 4 }, // min16int
|
||||
{ 3, 5, 5, 2, 2, 4, 4 }, // int
|
||||
{ 3, 1, 1, 5, 5, 4, 4 }, // min16uint
|
||||
{ 3, 1, 1, 5, 5, 4, 4 }, // uint
|
||||
{ 3, 3, 3, 3, 3, 6, 6 }, // min16float
|
||||
{ 3, 3, 3, 3, 3, 6, 6 } // float
|
||||
};
|
||||
|
||||
assert(src.base > 0 && src.base <= 7); // bool - float
|
||||
assert(dst.base > 0 && dst.base <= 7);
|
||||
|
||||
const int rank = ranks[src.base - 1][dst.base - 1] << 2;
|
||||
|
||||
if ((src.is_scalar() && dst.is_vector()))
|
||||
return rank >> 1; // Scalar to vector promotion has a lower rank
|
||||
if ((src.is_vector() && dst.is_scalar()) || (src.is_vector() == dst.is_vector() && src.rows > dst.rows && src.cols >= dst.cols))
|
||||
return rank >> 2; // Vector to scalar conversion has an even lower rank
|
||||
if ((src.is_vector() != dst.is_vector()) || src.is_matrix() != dst.is_matrix() || src.components() != dst.components())
|
||||
return 0; // If components weren't converted at this point, the types are not compatible
|
||||
|
||||
return rank * src.components(); // More components causes a higher rank
|
||||
}
|
||||
|
||||
reshadefx::symbol_table::symbol_table()
|
||||
{
|
||||
_current_scope.name = "::";
|
||||
_current_scope.level = 0;
|
||||
_current_scope.namespace_level = 0;
|
||||
}
|
||||
|
||||
void reshadefx::symbol_table::enter_scope()
|
||||
{
|
||||
_current_scope.level++;
|
||||
}
|
||||
void reshadefx::symbol_table::enter_namespace(const std::string &name)
|
||||
{
|
||||
_current_scope.name += name + "::";
|
||||
_current_scope.level++;
|
||||
_current_scope.namespace_level++;
|
||||
}
|
||||
void reshadefx::symbol_table::leave_scope()
|
||||
{
|
||||
assert(_current_scope.level > 0);
|
||||
|
||||
for (auto &symbol : _symbol_stack)
|
||||
{
|
||||
std::vector<scoped_symbol> &scope_list = symbol.second;
|
||||
|
||||
for (auto scope_it = scope_list.begin(); scope_it != scope_list.end();)
|
||||
{
|
||||
if (scope_it->scope.level > scope_it->scope.namespace_level &&
|
||||
scope_it->scope.level >= _current_scope.level)
|
||||
{
|
||||
scope_it = scope_list.erase(scope_it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++scope_it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_current_scope.level--;
|
||||
}
|
||||
void reshadefx::symbol_table::leave_namespace()
|
||||
{
|
||||
assert(_current_scope.level > 0);
|
||||
assert(_current_scope.namespace_level > 0);
|
||||
|
||||
_current_scope.name.erase(_current_scope.name.substr(0, _current_scope.name.size() - 2).rfind("::") + 2);
|
||||
_current_scope.level--;
|
||||
_current_scope.namespace_level--;
|
||||
}
|
||||
|
||||
bool reshadefx::symbol_table::insert_symbol(const std::string &name, const symbol &symbol, bool global)
|
||||
{
|
||||
assert(symbol.id != 0 || symbol.op == symbol_type::constant);
|
||||
|
||||
// Make sure the symbol does not exist yet
|
||||
if (symbol.op != symbol_type::function && find_symbol(name, _current_scope, true).id != 0)
|
||||
return false;
|
||||
|
||||
// Insertion routine which keeps the symbol stack sorted by namespace level
|
||||
const auto insert_sorted = [](auto &vec, const auto &item) {
|
||||
return vec.insert(
|
||||
std::upper_bound(vec.begin(), vec.end(), item,
|
||||
[](auto lhs, auto rhs) {
|
||||
return lhs.scope.namespace_level < rhs.scope.namespace_level;
|
||||
}), item);
|
||||
};
|
||||
|
||||
// Global symbols are accessible from every scope
|
||||
if (global)
|
||||
{
|
||||
scope scope = { "", 0, 0 };
|
||||
|
||||
// Walk scope chain from global scope back to current one
|
||||
for (size_t pos = 0; pos != std::string::npos; pos = _current_scope.name.find("::", pos))
|
||||
{
|
||||
// Extract scope name
|
||||
scope.name = _current_scope.name.substr(0, pos += 2);
|
||||
const auto previous_scope_name = _current_scope.name.substr(pos);
|
||||
|
||||
// Insert symbol into this scope
|
||||
insert_sorted(_symbol_stack[previous_scope_name + name], scoped_symbol { symbol, scope });
|
||||
|
||||
// Continue walking up the scope chain
|
||||
scope.level = ++scope.namespace_level;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a local symbol so it's sufficient to update the symbol stack with just the current scope
|
||||
insert_sorted(_symbol_stack[name], scoped_symbol { symbol, _current_scope });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
reshadefx::scoped_symbol reshadefx::symbol_table::find_symbol(const std::string &name) const
|
||||
{
|
||||
// Default to start search with current scope and walk back the scope chain
|
||||
return find_symbol(name, _current_scope, false);
|
||||
}
|
||||
reshadefx::scoped_symbol reshadefx::symbol_table::find_symbol(const std::string &name, const scope &scope, bool exclusive) const
|
||||
{
|
||||
const auto stack_it = _symbol_stack.find(name);
|
||||
|
||||
// Check if symbol does exist
|
||||
if (stack_it == _symbol_stack.end() || stack_it->second.empty())
|
||||
return {};
|
||||
|
||||
// Walk up the scope chain starting at the requested scope level and find a matching symbol
|
||||
scoped_symbol result = {};
|
||||
|
||||
for (auto it = stack_it->second.rbegin(), end = stack_it->second.rend(); it != end; ++it)
|
||||
{
|
||||
if (it->scope.level > scope.level ||
|
||||
it->scope.namespace_level > scope.namespace_level || (it->scope.namespace_level == scope.namespace_level && it->scope.name != scope.name))
|
||||
continue;
|
||||
if (exclusive && it->scope.level < scope.level)
|
||||
continue;
|
||||
|
||||
if (it->op == symbol_type::constant || it->op == symbol_type::variable || it->op == symbol_type::structure)
|
||||
return *it; // Variables and structures have the highest priority and are always picked immediately
|
||||
else if (result.id == 0)
|
||||
result = *it; // Function names have a lower priority, so continue searching in case a variable with the same name exists
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int compare_functions(const std::vector<reshadefx::expression> &arguments, const reshadefx::function_info *function1, const reshadefx::function_info *function2)
|
||||
{
|
||||
const size_t num_arguments = arguments.size();
|
||||
|
||||
// Check if the first function matches the argument types
|
||||
bool function1_viable = true;
|
||||
const auto function1_ranks = static_cast<unsigned int *>(alloca(num_arguments * sizeof(unsigned int)));
|
||||
for (size_t i = 0; i < num_arguments; ++i)
|
||||
{
|
||||
if ((function1_ranks[i] = reshadefx::type::rank(arguments[i].type, function1->parameter_list[i].type)) == 0)
|
||||
{
|
||||
function1_viable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Catch case where the second function does not exist
|
||||
if (function2 == nullptr)
|
||||
return function1_viable ? -1 : 1; // If the first function is not viable, this compare fails
|
||||
|
||||
// Check if the second function matches the argument types
|
||||
bool function2_viable = true;
|
||||
const auto function2_ranks = static_cast<unsigned int *>(alloca(num_arguments * sizeof(unsigned int)));
|
||||
for (size_t i = 0; i < num_arguments; ++i)
|
||||
{
|
||||
if ((function2_ranks[i] = reshadefx::type::rank(arguments[i].type, function2->parameter_list[i].type)) == 0)
|
||||
{
|
||||
function2_viable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If one of the functions is not viable, then the other one automatically wins
|
||||
if (!function1_viable || !function2_viable)
|
||||
return function2_viable - function1_viable;
|
||||
|
||||
// Both functions are possible, so find the one with the higher ranking
|
||||
std::sort(function1_ranks, function1_ranks + num_arguments, std::greater<unsigned int>());
|
||||
std::sort(function2_ranks, function2_ranks + num_arguments, std::greater<unsigned int>());
|
||||
|
||||
for (size_t i = 0; i < num_arguments; ++i)
|
||||
if (function1_ranks[i] > function2_ranks[i])
|
||||
return -1; // Left function wins
|
||||
else if (function2_ranks[i] > function1_ranks[i])
|
||||
return +1; // Right function wins
|
||||
|
||||
return 0; // Both functions are equally viable
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
out_data.op = symbol_type::function;
|
||||
|
||||
const function_info *result = nullptr;
|
||||
unsigned int num_overloads = 0;
|
||||
unsigned int overload_namespace = scope.namespace_level;
|
||||
|
||||
// Look up function name in the symbol stack and loop through the associated symbols
|
||||
const auto stack_it = _symbol_stack.find(name);
|
||||
|
||||
if (stack_it != _symbol_stack.end() && !stack_it->second.empty())
|
||||
{
|
||||
for (auto it = stack_it->second.rbegin(), end = stack_it->second.rend(); it != end; ++it)
|
||||
{
|
||||
if (it->op != symbol_type::function)
|
||||
continue;
|
||||
if (it->scope.level > scope.level ||
|
||||
it->scope.namespace_level > scope.namespace_level || (it->scope.namespace_level == scope.namespace_level && it->scope.name != scope.name))
|
||||
continue;
|
||||
|
||||
const function_info *const function = it->function;
|
||||
|
||||
if (function == nullptr)
|
||||
continue;
|
||||
|
||||
if (function->parameter_list.empty())
|
||||
{
|
||||
if (arguments.empty())
|
||||
{
|
||||
out_data.id = it->id;
|
||||
out_data.type = function->return_type;
|
||||
out_data.function = result = function;
|
||||
num_overloads = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (arguments.size() != function->parameter_list.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// A new possibly-matching function was found, compare it against the current result
|
||||
const int comparison = compare_functions(arguments, function, result);
|
||||
|
||||
if (comparison < 0) // The new function is a better match
|
||||
{
|
||||
out_data.id = it->id;
|
||||
out_data.type = function->return_type;
|
||||
out_data.function = result = function;
|
||||
num_overloads = 1;
|
||||
overload_namespace = it->scope.namespace_level;
|
||||
}
|
||||
else if (comparison == 0 && overload_namespace == it->scope.namespace_level) // Both functions are equally viable, so the call is ambiguous
|
||||
{
|
||||
++num_overloads;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try matching against intrinsic functions if no matching user-defined function was found up to this point
|
||||
if (num_overloads == 0)
|
||||
{
|
||||
for (const intrinsic &intrinsic : s_intrinsics)
|
||||
{
|
||||
if (intrinsic.function.name != name || intrinsic.function.parameter_list.size() != arguments.size())
|
||||
continue;
|
||||
|
||||
// A new possibly-matching intrinsic function was found, compare it against the current result
|
||||
const int comparison = compare_functions(arguments, &intrinsic.function, result);
|
||||
|
||||
if (comparison < 0) // The new function is a better match
|
||||
{
|
||||
out_data.op = symbol_type::intrinsic;
|
||||
out_data.id = static_cast<uint32_t>(intrinsic.id);
|
||||
out_data.type = intrinsic.function.return_type;
|
||||
out_data.function = &intrinsic.function;
|
||||
result = out_data.function;
|
||||
num_overloads = 1;
|
||||
}
|
||||
else if (comparison == 0 && overload_namespace == 0) // Both functions are equally viable, so the call is ambiguous (intrinsics are always in the global namespace)
|
||||
{
|
||||
++num_overloads;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_ambiguous = num_overloads > 1;
|
||||
|
||||
return num_overloads == 1;
|
||||
}
|
||||
4196
dep/reshadefx/src/effect_symbol_table_intrinsics.inl
Normal file
4196
dep/reshadefx/src/effect_symbol_table_intrinsics.inl
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user