Path: blob/master/dep/reshadefx/src/effect_expression.cpp
4246 views
/*1* Copyright (C) 2014 Patrick Mours2* SPDX-License-Identifier: BSD-3-Clause3*/45#include "effect_expression.hpp"6#include <cmath> // std::fmod7#include <cassert>8#include <cstring> // std::memcpy, std::memset9#include <algorithm> // std::max, std::min1011reshadefx::type reshadefx::type::merge(const type &lhs, const type &rhs)12{13type result;14result.base = std::max(lhs.base, rhs.base);1516// Non-numeric types cannot be vectors or matrices17if (!result.is_numeric())18{19result.rows = 0;20result.cols = 0;21}22// If one side of the expression is scalar, it needs to be promoted to the same dimension as the other side23else if ((lhs.rows == 1 && lhs.cols == 1) || (rhs.rows == 1 && rhs.cols == 1))24{25result.rows = std::max(lhs.rows, rhs.rows);26result.cols = std::max(lhs.cols, rhs.cols);27}28else // Otherwise dimensions match or one side is truncated to match the other one29{30result.rows = std::min(lhs.rows, rhs.rows);31result.cols = std::min(lhs.cols, rhs.cols);32}3334// Some qualifiers propagate to the result35result.qualifiers = (lhs.qualifiers & type::q_precise) | (rhs.qualifiers & type::q_precise);3637// Cannot merge array types, assume no arrays38result.array_length = 0;39assert(lhs.array_length == 0 && rhs.array_length == 0);4041// In case this is a structure, assume they are the same42result.struct_definition = rhs.struct_definition;43assert(lhs.struct_definition == rhs.struct_definition || lhs.struct_definition == 0);4445return result;46}4748std::string reshadefx::type::description() const49{50std::string result;51switch (base)52{53case t_void:54result = "void";55break;56case t_bool:57result = "bool";58break;59case t_min16int:60result = "min16int";61break;62case t_int:63result = "int";64break;65case t_min16uint:66result = "min16uint";67break;68case t_uint:69result = "uint";70break;71case t_min16float:72result = "min16float";73break;74case t_float:75result = "float";76break;77case t_string:78result = "string";79break;80case t_struct:81result = "struct";82break;83case t_texture1d:84result = "texture1D";85break;86case t_texture2d:87result = "texture2D";88break;89case t_texture3d:90result = "texture3D";91break;92case t_sampler1d_int:93result = "sampler1D<int" + std::to_string(rows) + '>';94break;95case t_sampler2d_int:96result = "sampler2D<int" + std::to_string(rows) + '>';97break;98case t_sampler3d_int:99result = "sampler3D<int" + std::to_string(rows) + '>';100break;101case t_sampler1d_uint:102result = "sampler1D<uint" + std::to_string(rows) + '>';103break;104case t_sampler2d_uint:105result = "sampler2D<uint" + std::to_string(rows) + '>';106break;107case t_sampler3d_uint:108result = "sampler3D<uint" + std::to_string(rows) + '>';109break;110case t_sampler1d_float:111result = "sampler1D<float" + std::to_string(rows) + '>';112break;113case t_sampler2d_float:114result = "sampler2D<float" + std::to_string(rows) + '>';115break;116case t_sampler3d_float:117result = "sampler3D<float" + std::to_string(rows) + '>';118break;119case t_storage1d_int:120result = "storage1D<int" + std::to_string(rows) + '>';121break;122case t_storage2d_int:123result = "storage2D<int" + std::to_string(rows) + '>';124break;125case t_storage3d_int:126result = "storage3D<int" + std::to_string(rows) + '>';127break;128case t_storage1d_uint:129result = "storage1D<uint" + std::to_string(rows) + '>';130break;131case t_storage2d_uint:132result = "storage2D<uint" + std::to_string(rows) + '>';133break;134case t_storage3d_uint:135result = "storage3D<uint" + std::to_string(rows) + '>';136break;137case t_storage1d_float:138result = "storage1D<float" + std::to_string(rows) + '>';139break;140case t_storage2d_float:141result = "storage2D<float" + std::to_string(rows) + '>';142break;143case t_storage3d_float:144result = "storage3D<float" + std::to_string(rows) + '>';145break;146case t_function:147assert(false);148break;149}150151if (is_numeric())152{153if (rows > 1 || cols > 1)154result += std::to_string(rows);155if (cols > 1)156result += 'x' + std::to_string(cols);157}158159if (is_array())160{161result += '[';162if (is_bounded_array())163result += std::to_string(array_length);164result += ']';165}166167return result;168}169170void reshadefx::expression::reset_to_lvalue(const reshadefx::location &loc, uint32_t in_base, const reshadefx::type &in_type)171{172type = in_type;173base = in_base;174location = loc;175is_lvalue = true;176is_constant = false;177chain.clear();178179// Make sure uniform l-values cannot be assigned to by making them constant180if (in_type.has(type::q_uniform))181type.qualifiers |= type::q_const;182183// Strip away global variable qualifiers184type.qualifiers &= ~(type::q_extern | type::q_static | type::q_uniform | type::q_groupshared);185}186void reshadefx::expression::reset_to_rvalue(const reshadefx::location &loc, uint32_t in_base, const reshadefx::type &in_type)187{188type = in_type;189type.qualifiers |= type::q_const;190base = in_base;191location = loc;192is_lvalue = false;193is_constant = false;194chain.clear();195196// Strip away global variable qualifiers197type.qualifiers &= ~(type::q_extern | type::q_static | type::q_uniform | type::q_groupshared);198}199200void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, bool data)201{202type = { type::t_bool, 1, 1, type::q_const };203base = 0; constant = {}; constant.as_uint[0] = data;204location = loc;205is_lvalue = false;206is_constant = true;207chain.clear();208}209void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, float data)210{211type = { type::t_float, 1, 1, type::q_const };212base = 0; constant = {}; constant.as_float[0] = data;213location = loc;214is_lvalue = false;215is_constant = true;216chain.clear();217}218void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, int32_t data)219{220type = { type::t_int, 1, 1, type::q_const };221base = 0; constant = {}; constant.as_int[0] = data;222location = loc;223is_lvalue = false;224is_constant = true;225chain.clear();226}227void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, uint32_t data)228{229type = { type::t_uint, 1, 1, type::q_const };230base = 0; constant = {}; constant.as_uint[0] = data;231location = loc;232is_lvalue = false;233is_constant = true;234chain.clear();235}236void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, std::string data)237{238type = { type::t_string, 0, 0, type::q_const };239base = 0; constant = {}; constant.string_data = std::move(data);240location = loc;241is_lvalue = false;242is_constant = true;243chain.clear();244}245void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, reshadefx::constant data, const reshadefx::type &in_type)246{247type = in_type;248type.qualifiers |= type::q_const;249base = 0; constant = std::move(data);250location = loc;251is_lvalue = false;252is_constant = true;253chain.clear();254}255256void reshadefx::expression::add_cast_operation(const reshadefx::type &cast_type)257{258// First try to simplify the cast with a swizzle operation (only works with scalars and vectors)259if (type.cols == 1 && cast_type.cols == 1 && type.rows != cast_type.rows)260{261signed char swizzle[] = { 0, 1, 2, 3 };262// Ignore components in a demotion cast263for (unsigned int i = cast_type.rows; i < 4; ++i)264swizzle[i] = -1;265// Use the last component to fill in a promotion cast266for (unsigned int i = type.rows; i < cast_type.rows; ++i)267swizzle[i] = swizzle[type.rows - 1];268269add_swizzle_access(swizzle, cast_type.rows);270}271272if (type == cast_type)273return; // There is nothing more to do if the expression is already of the target type at this point274275if (is_constant)276{277const auto cast_constant = [](reshadefx::constant &constant, const reshadefx::type &from, const reshadefx::type &to) {278// Handle scalar to vector promotion first279if (from.is_scalar() && !to.is_scalar())280for (unsigned int i = 1; i < to.components(); ++i)281constant.as_uint[i] = constant.as_uint[0];282283// Next check whether the type needs casting as well (and don't convert between signed/unsigned, since that is handled by the union)284if (from.base == to.base || from.is_floating_point() == to.is_floating_point())285return;286287if (!to.is_floating_point())288for (unsigned int i = 0; i < to.components(); ++i)289constant.as_uint[i] = static_cast<int>(constant.as_float[i]);290else291for (unsigned int i = 0; i < to.components(); ++i)292constant.as_float[i] = static_cast<float>(constant.as_int[i]);293};294295for (struct constant &element : constant.array_data)296cast_constant(element, type, cast_type);297298cast_constant(constant, type, cast_type);299}300else301{302assert(!type.is_array() && !cast_type.is_array());303304chain.push_back({ operation::op_cast, type, cast_type });305}306307type = cast_type;308type.qualifiers |= type::q_const; // Casting always makes expression not modifiable309}310void reshadefx::expression::add_member_access(unsigned int index, const reshadefx::type &in_type)311{312assert(type.is_struct());313314chain.push_back({ operation::op_member, type, in_type, index });315316// The type is now the type of the member that was accessed317type = in_type;318is_constant = false;319}320void reshadefx::expression::add_dynamic_index_access(uint32_t index_expression)321{322assert(!is_constant); // Cannot have dynamic indexing into constant in SPIR-V323assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));324325struct type prev_type = type;326327if (type.is_array())328{329type.array_length = 0;330}331else if (type.is_matrix())332{333type.rows = type.cols;334type.cols = 1;335}336else if (type.is_vector())337{338type.rows = 1;339}340341chain.push_back({ operation::op_dynamic_index, prev_type, type, index_expression });342}343void reshadefx::expression::add_constant_index_access(unsigned int index)344{345assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));346347struct type prev_type = type;348349if (type.is_array())350{351assert(index < type.array_length);352353type.array_length = 0;354}355else if (type.is_matrix())356{357assert(index < type.components());358359type.rows = type.cols;360type.cols = 1;361}362else if (type.is_vector())363{364assert(index < type.components());365366type.rows = 1;367}368369if (is_constant)370{371if (prev_type.is_array())372{373constant = constant.array_data[index];374}375else if (prev_type.is_matrix()) // Indexing into a matrix returns a row of it as a vector376{377for (unsigned int i = 0; i < prev_type.cols; ++i)378constant.as_uint[i] = constant.as_uint[index * prev_type.cols + i];379}380else // Indexing into a vector returns the element as a scalar381{382constant.as_uint[0] = constant.as_uint[index];383}384}385else386{387chain.push_back({ operation::op_constant_index, prev_type, type, index });388}389}390void reshadefx::expression::add_swizzle_access(const signed char swizzle[4], unsigned int length)391{392assert(type.is_numeric() && !type.is_array());393394const struct type prev_type = type;395396type.rows = length;397type.cols = 1;398399if (is_constant)400{401assert(constant.array_data.empty());402403uint32_t data[16];404std::memcpy(data, &constant.as_uint[0], sizeof(data));405for (unsigned int i = 0; i < length; ++i)406constant.as_uint[i] = data[swizzle[i]];407std::memset(&constant.as_uint[length], 0, sizeof(uint32_t) * (16 - length)); // Clear the rest of the constant408}409else if (length == 1 && prev_type.is_vector()) // Use indexing when possible since the code generation logic is simpler in SPIR-V410{411chain.push_back({ operation::op_constant_index, prev_type, type, static_cast<uint32_t>(swizzle[0]) });412}413else414{415chain.push_back({ operation::op_swizzle, prev_type, type, 0, { swizzle[0], swizzle[1], swizzle[2], swizzle[3] } });416}417}418419bool reshadefx::expression::evaluate_constant_expression(reshadefx::tokenid op)420{421if (!is_constant)422return false;423424switch (op)425{426case tokenid::exclaim:427for (unsigned int i = 0; i < type.components(); ++i)428constant.as_uint[i] = !constant.as_uint[i];429break;430case tokenid::minus:431if (type.is_floating_point())432for (unsigned int i = 0; i < type.components(); ++i)433constant.as_float[i] = -constant.as_float[i];434else435for (unsigned int i = 0; i < type.components(); ++i)436constant.as_int[i] = -constant.as_int[i];437break;438case tokenid::tilde:439for (unsigned int i = 0; i < type.components(); ++i)440constant.as_uint[i] = ~constant.as_uint[i];441break;442default:443// Unknown operator token, so nothing to do444break;445}446447return true;448}449bool reshadefx::expression::evaluate_constant_expression(reshadefx::tokenid op, const reshadefx::constant &rhs)450{451if (!is_constant)452return false;453454switch (op)455{456case tokenid::percent:457if (type.is_floating_point()) {458for (unsigned int i = 0; i < type.components(); ++i)459// Floating point modulo with zero is defined and results in NaN460if (rhs.as_float[i] == 0)461constant.as_float[i] = std::numeric_limits<float>::quiet_NaN();462else463constant.as_float[i] = std::fmod(constant.as_float[i], rhs.as_float[i]);464}465else if (type.is_signed()) {466for (unsigned int i = 0; i < type.components(); ++i)467// Integer modulo with zero on the other hand is not defined, so do not fold this expression in that case468if (rhs.as_int[i] == 0)469return false;470else471constant.as_int[i] %= rhs.as_int[i];472}473else {474for (unsigned int i = 0; i < type.components(); ++i)475if (rhs.as_uint[i] == 0)476return false;477else478constant.as_uint[i] %= rhs.as_uint[i];479}480break;481case tokenid::star:482if (type.is_floating_point())483for (unsigned int i = 0; i < type.components(); ++i)484constant.as_float[i] *= rhs.as_float[i];485else486for (unsigned int i = 0; i < type.components(); ++i)487constant.as_uint[i] *= rhs.as_uint[i];488break;489case tokenid::plus:490if (type.is_floating_point())491for (unsigned int i = 0; i < type.components(); ++i)492constant.as_float[i] += rhs.as_float[i];493else494for (unsigned int i = 0; i < type.components(); ++i)495constant.as_uint[i] += rhs.as_uint[i];496break;497case tokenid::minus:498if (type.is_floating_point())499for (unsigned int i = 0; i < type.components(); ++i)500constant.as_float[i] -= rhs.as_float[i];501else502for (unsigned int i = 0; i < type.components(); ++i)503constant.as_uint[i] -= rhs.as_uint[i];504break;505case tokenid::slash:506if (type.is_floating_point()) {507for (unsigned int i = 0; i < type.components(); ++i)508// Floating point division by zero is well defined and results in infinity or NaN509constant.as_float[i] /= rhs.as_float[i];510}511else if (type.is_signed()) {512for (unsigned int i = 0; i < type.components(); ++i)513// Integer division by zero on the other hand is not defined, so do not fold this expression in that case514if (rhs.as_int[i] == 0)515return false;516else517constant.as_int[i] /= rhs.as_int[i];518}519else {520for (unsigned int i = 0; i < type.components(); ++i)521if (rhs.as_uint[i] == 0)522return false;523else524constant.as_uint[i] /= rhs.as_uint[i];525}526break;527case tokenid::ampersand:528case tokenid::ampersand_ampersand:529for (unsigned int i = 0; i < type.components(); ++i)530constant.as_uint[i] &= rhs.as_uint[i];531break;532case tokenid::pipe:533case tokenid::pipe_pipe:534for (unsigned int i = 0; i < type.components(); ++i)535constant.as_uint[i] |= rhs.as_uint[i];536break;537case tokenid::caret:538for (unsigned int i = 0; i < type.components(); ++i)539constant.as_uint[i] ^= rhs.as_uint[i];540break;541case tokenid::less:542if (type.is_floating_point())543for (unsigned int i = 0; i < type.components(); ++i)544constant.as_uint[i] = constant.as_float[i] < rhs.as_float[i];545else if (type.is_signed())546for (unsigned int i = 0; i < type.components(); ++i)547constant.as_uint[i] = constant.as_int[i] < rhs.as_int[i];548else549for (unsigned int i = 0; i < type.components(); ++i)550constant.as_uint[i] = constant.as_uint[i] < rhs.as_uint[i];551type.base = type::t_bool; // Logic operations change the type to boolean552break;553case tokenid::less_equal:554if (type.is_floating_point())555for (unsigned int i = 0; i < type.components(); ++i)556constant.as_uint[i] = constant.as_float[i] <= rhs.as_float[i];557else if (type.is_signed())558for (unsigned int i = 0; i < type.components(); ++i)559constant.as_uint[i] = constant.as_int[i] <= rhs.as_int[i];560else561for (unsigned int i = 0; i < type.components(); ++i)562constant.as_uint[i] = constant.as_uint[i] <= rhs.as_uint[i];563type.base = type::t_bool;564break;565case tokenid::greater:566if (type.is_floating_point())567for (unsigned int i = 0; i < type.components(); ++i)568constant.as_uint[i] = constant.as_float[i] > rhs.as_float[i];569else if (type.is_signed())570for (unsigned int i = 0; i < type.components(); ++i)571constant.as_uint[i] = constant.as_int[i] > rhs.as_int[i];572else573for (unsigned int i = 0; i < type.components(); ++i)574constant.as_uint[i] = constant.as_uint[i] > rhs.as_uint[i];575type.base = type::t_bool;576break;577case tokenid::greater_equal:578if (type.is_floating_point())579for (unsigned int i = 0; i < type.components(); ++i)580constant.as_uint[i] = constant.as_float[i] >= rhs.as_float[i];581else if (type.is_signed())582for (unsigned int i = 0; i < type.components(); ++i)583constant.as_uint[i] = constant.as_int[i] >= rhs.as_int[i];584else585for (unsigned int i = 0; i < type.components(); ++i)586constant.as_uint[i] = constant.as_uint[i] >= rhs.as_uint[i];587type.base = type::t_bool;588break;589case tokenid::equal_equal:590if (type.is_floating_point())591for (unsigned int i = 0; i < type.components(); ++i)592constant.as_uint[i] = constant.as_float[i] == rhs.as_float[i];593else594for (unsigned int i = 0; i < type.components(); ++i)595constant.as_uint[i] = constant.as_uint[i] == rhs.as_uint[i];596type.base = type::t_bool;597break;598case tokenid::exclaim_equal:599if (type.is_floating_point())600for (unsigned int i = 0; i < type.components(); ++i)601constant.as_uint[i] = constant.as_float[i] != rhs.as_float[i];602else603for (unsigned int i = 0; i < type.components(); ++i)604constant.as_uint[i] = constant.as_uint[i] != rhs.as_uint[i];605type.base = type::t_bool;606break;607case tokenid::less_less:608for (unsigned int i = 0; i < type.components(); ++i)609constant.as_uint[i] <<= rhs.as_uint[i];610break;611case tokenid::greater_greater:612if (type.is_signed())613for (unsigned int i = 0; i < type.components(); ++i)614constant.as_int[i] >>= rhs.as_int[i];615else616for (unsigned int i = 0; i < type.components(); ++i)617constant.as_uint[i] >>= rhs.as_uint[i];618break;619default:620// Unknown operator token, so nothing to do621break;622}623624return true;625}626627628