dep: Add reshadefx
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user