624 lines
19 KiB
C++
624 lines
19 KiB
C++
/*
|
|
* 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;
|
|
}
|