duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

effect_expression.cpp (19101B)


      1 /*
      2  * Copyright (C) 2014 Patrick Mours
      3  * SPDX-License-Identifier: BSD-3-Clause
      4  */
      5 
      6 #include "effect_lexer.hpp"
      7 #include "effect_codegen.hpp"
      8 #include <cmath> // fmod
      9 #include <cassert>
     10 #include <cstring> // memcpy, memset
     11 #include <algorithm> // std::min, std::max
     12 
     13 reshadefx::type reshadefx::type::merge(const type &lhs, const type &rhs)
     14 {
     15 	type result = { std::max(lhs.base, rhs.base) };
     16 
     17 	// Non-numeric types cannot be vectors or matrices
     18 	if (!result.is_numeric())
     19 	{
     20 		result.rows = 0;
     21 		result.cols = 0;
     22 	}
     23 	// If one side of the expression is scalar, it needs to be promoted to the same dimension as the other side
     24 	else if ((lhs.rows == 1 && lhs.cols == 1) || (rhs.rows == 1 && rhs.cols == 1))
     25 	{
     26 		result.rows = std::max(lhs.rows, rhs.rows);
     27 		result.cols = std::max(lhs.cols, rhs.cols);
     28 	}
     29 	else // Otherwise dimensions match or one side is truncated to match the other one
     30 	{
     31 		result.rows = std::min(lhs.rows, rhs.rows);
     32 		result.cols = std::min(lhs.cols, rhs.cols);
     33 	}
     34 
     35 	// Some qualifiers propagate to the result
     36 	result.qualifiers = (lhs.qualifiers & type::q_precise) | (rhs.qualifiers & type::q_precise);
     37 
     38 	// In case this is a structure, assume they are the same
     39 	result.definition = rhs.definition;
     40 	assert(lhs.definition == rhs.definition || lhs.definition == 0);
     41 	assert(lhs.array_length == 0 && rhs.array_length == 0);
     42 
     43 	return result;
     44 }
     45 
     46 std::string reshadefx::type::description() const
     47 {
     48 	std::string result;
     49 	switch (base)
     50 	{
     51 	case reshadefx::type::t_void:
     52 		result = "void";
     53 		break;
     54 	case reshadefx::type::t_bool:
     55 		result = "bool";
     56 		break;
     57 	case reshadefx::type::t_min16int:
     58 		result = "min16int";
     59 		break;
     60 	case reshadefx::type::t_int:
     61 		result = "int";
     62 		break;
     63 	case reshadefx::type::t_min16uint:
     64 		result = "min16uint";
     65 		break;
     66 	case reshadefx::type::t_uint:
     67 		result = "uint";
     68 		break;
     69 	case reshadefx::type::t_min16float:
     70 		result = "min16float";
     71 		break;
     72 	case reshadefx::type::t_float:
     73 		result = "float";
     74 		break;
     75 	case reshadefx::type::t_string:
     76 		result = "string";
     77 		break;
     78 	case reshadefx::type::t_struct:
     79 		result = "struct";
     80 		break;
     81 	case reshadefx::type::t_texture1d:
     82 		result = "texture1D";
     83 		break;
     84 	case reshadefx::type::t_texture2d:
     85 		result = "texture2D";
     86 		break;
     87 	case reshadefx::type::t_texture3d:
     88 		result = "texture3D";
     89 		break;
     90 	case reshadefx::type::t_sampler1d_int:
     91 		result = "sampler1D<int" + std::to_string(rows) + '>';
     92 		break;
     93 	case reshadefx::type::t_sampler2d_int:
     94 		result = "sampler2D<int" + std::to_string(rows) + '>';
     95 		break;
     96 	case reshadefx::type::t_sampler3d_int:
     97 		result = "sampler3D<int" + std::to_string(rows) + '>';
     98 		break;
     99 	case reshadefx::type::t_sampler1d_uint:
    100 		result = "sampler1D<uint" + std::to_string(rows) + '>';
    101 		break;
    102 	case reshadefx::type::t_sampler2d_uint:
    103 		result = "sampler2D<uint" + std::to_string(rows) + '>';
    104 		break;
    105 	case reshadefx::type::t_sampler3d_uint:
    106 		result = "sampler3D<uint" + std::to_string(rows) + '>';
    107 		break;
    108 	case reshadefx::type::t_sampler1d_float:
    109 		result = "sampler1D<float" + std::to_string(rows) + '>';
    110 		break;
    111 	case reshadefx::type::t_sampler2d_float:
    112 		result = "sampler2D<float" + std::to_string(rows) + '>';
    113 		break;
    114 	case reshadefx::type::t_sampler3d_float:
    115 		result = "sampler3D<float" + std::to_string(rows) + '>';
    116 		break;
    117 	case reshadefx::type::t_storage1d_int:
    118 		result = "storage1D<int" + std::to_string(rows) + '>';
    119 		break;
    120 	case reshadefx::type::t_storage2d_int:
    121 		result = "storage2D<int" + std::to_string(rows) + '>';
    122 		break;
    123 	case reshadefx::type::t_storage3d_int:
    124 		result = "storage3D<int" + std::to_string(rows) + '>';
    125 		break;
    126 	case reshadefx::type::t_storage1d_uint:
    127 		result = "storage1D<uint" + std::to_string(rows) + '>';
    128 		break;
    129 	case reshadefx::type::t_storage2d_uint:
    130 		result = "storage2D<uint" + std::to_string(rows) + '>';
    131 		break;
    132 	case reshadefx::type::t_storage3d_uint:
    133 		result = "storage3D<uint" + std::to_string(rows) + '>';
    134 		break;
    135 	case reshadefx::type::t_storage1d_float:
    136 		result = "storage1D<float" + std::to_string(rows) + '>';
    137 		break;
    138 	case reshadefx::type::t_storage2d_float:
    139 		result = "storage2D<float" + std::to_string(rows) + '>';
    140 		break;
    141 	case reshadefx::type::t_storage3d_float:
    142 		result = "storage3D<float" + std::to_string(rows) + '>';
    143 		break;
    144 	case reshadefx::type::t_function:
    145 		result = "function";
    146 		break;
    147 	}
    148 
    149 	if (is_numeric())
    150 	{
    151 		if (rows > 1 || cols > 1)
    152 			result += std::to_string(rows);
    153 		if (cols > 1)
    154 			result += 'x' + std::to_string(cols);
    155 	}
    156 
    157 	if (is_array())
    158 	{
    159 		result += '[';
    160 		if (array_length > 0)
    161 			result += std::to_string(array_length);
    162 		result += ']';
    163 	}
    164 
    165 	return result;
    166 }
    167 
    168 void reshadefx::expression::reset_to_lvalue(const reshadefx::location &loc, uint32_t in_base, const reshadefx::type &in_type)
    169 {
    170 	type = in_type;
    171 	base = in_base;
    172 	location = loc;
    173 	is_lvalue = true;
    174 	is_constant = false;
    175 	chain.clear();
    176 
    177 	// Make sure uniform l-values cannot be assigned to by making them constant
    178 	if (in_type.has(type::q_uniform))
    179 		type.qualifiers |= type::q_const;
    180 
    181 	// Strip away global variable qualifiers
    182 	type.qualifiers &= ~(reshadefx::type::q_extern | reshadefx::type::q_static | reshadefx::type::q_uniform | reshadefx::type::q_groupshared);
    183 }
    184 void reshadefx::expression::reset_to_rvalue(const reshadefx::location &loc, uint32_t in_base, const reshadefx::type &in_type)
    185 {
    186 	type = in_type;
    187 	type.qualifiers |= type::q_const;
    188 	base = in_base;
    189 	location = loc;
    190 	is_lvalue = false;
    191 	is_constant = false;
    192 	chain.clear();
    193 
    194 	// Strip away global variable qualifiers
    195 	type.qualifiers &= ~(reshadefx::type::q_extern | reshadefx::type::q_static | reshadefx::type::q_uniform | reshadefx::type::q_groupshared);
    196 }
    197 
    198 void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, bool data)
    199 {
    200 	type = { type::t_bool, 1, 1, type::q_const };
    201 	base = 0; constant = {}; constant.as_uint[0] = data;
    202 	location = loc;
    203 	is_lvalue = false;
    204 	is_constant = true;
    205 	chain.clear();
    206 }
    207 void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, float data)
    208 {
    209 	type = { type::t_float, 1, 1, type::q_const };
    210 	base = 0; constant = {}; constant.as_float[0] = data;
    211 	location = loc;
    212 	is_lvalue = false;
    213 	is_constant = true;
    214 	chain.clear();
    215 }
    216 void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, int32_t data)
    217 {
    218 	type = { type::t_int,  1, 1, type::q_const };
    219 	base = 0; constant = {}; constant.as_int[0] = data;
    220 	location = loc;
    221 	is_lvalue = false;
    222 	is_constant = true;
    223 	chain.clear();
    224 }
    225 void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, uint32_t data)
    226 {
    227 	type = { type::t_uint, 1, 1, type::q_const };
    228 	base = 0; constant = {}; constant.as_uint[0] = data;
    229 	location = loc;
    230 	is_lvalue = false;
    231 	is_constant = true;
    232 	chain.clear();
    233 }
    234 void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, std::string data)
    235 {
    236 	type = { type::t_string, 0, 0, type::q_const };
    237 	base = 0; constant = {}; constant.string_data = std::move(data);
    238 	location = loc;
    239 	is_lvalue = false;
    240 	is_constant = true;
    241 	chain.clear();
    242 }
    243 void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, reshadefx::constant data, const reshadefx::type &in_type)
    244 {
    245 	type = in_type;
    246 	type.qualifiers |= type::q_const;
    247 	base = 0; constant = std::move(data);
    248 	location = loc;
    249 	is_lvalue = false;
    250 	is_constant = true;
    251 	chain.clear();
    252 }
    253 
    254 void reshadefx::expression::add_cast_operation(const reshadefx::type &cast_type)
    255 {
    256 	// First try to simplify the cast with a swizzle operation (only works with scalars and vectors)
    257 	if (type.cols == 1 && cast_type.cols == 1 && type.rows != cast_type.rows)
    258 	{
    259 		signed char swizzle[] = { 0, 1, 2, 3 };
    260 		// Ignore components in a demotion cast
    261 		for (unsigned int i = cast_type.rows; i < 4; ++i)
    262 			swizzle[i] = -1;
    263 		// Use the last component to fill in a promotion cast
    264 		for (unsigned int i = type.rows; i < cast_type.rows; ++i)
    265 			swizzle[i] = swizzle[type.rows - 1];
    266 
    267 		add_swizzle_access(swizzle, cast_type.rows);
    268 	}
    269 
    270 	if (type == cast_type)
    271 		return; // There is nothing more to do if the expression is already of the target type at this point
    272 
    273 	if (is_constant)
    274 	{
    275 		const auto cast_constant = [](reshadefx::constant &constant, const reshadefx::type &from, const reshadefx::type &to) {
    276 			// Handle scalar to vector promotion first
    277 			if (from.is_scalar() && !to.is_scalar())
    278 				for (unsigned int i = 1; i < to.components(); ++i)
    279 					constant.as_uint[i] = constant.as_uint[0];
    280 
    281 			// Next check whether the type needs casting as well (and don't convert between signed/unsigned, since that is handled by the union)
    282 			if (from.base == to.base || from.is_floating_point() == to.is_floating_point())
    283 				return;
    284 
    285 			if (!to.is_floating_point())
    286 				for (unsigned int i = 0; i < to.components(); ++i)
    287 					constant.as_uint[i] = static_cast<int>(constant.as_float[i]);
    288 			else
    289 				for (unsigned int i = 0; i < to.components(); ++i)
    290 					constant.as_float[i] = static_cast<float>(constant.as_int[i]);
    291 		};
    292 
    293 		for (auto &element : constant.array_data)
    294 			cast_constant(element, type, cast_type);
    295 
    296 		cast_constant(constant, type, cast_type);
    297 	}
    298 	else
    299 	{
    300 		assert(!type.is_array() && !cast_type.is_array());
    301 
    302 		chain.push_back({ operation::op_cast, type, cast_type });
    303 	}
    304 
    305 	type = cast_type;
    306 	type.qualifiers |= type::q_const; // Casting always makes expression not modifiable
    307 }
    308 void reshadefx::expression::add_member_access(unsigned int index, const reshadefx::type &in_type)
    309 {
    310 	assert(type.is_struct());
    311 
    312 	chain.push_back({ operation::op_member, type, in_type, index });
    313 
    314 	// The type is now the type of the member that was accessed
    315 	type = in_type;
    316 	is_constant = false;
    317 }
    318 void reshadefx::expression::add_dynamic_index_access(uint32_t index_expression)
    319 {
    320 	assert(!is_constant); // Cannot have dynamic indexing into constant in SPIR-V
    321 	assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));
    322 
    323 	auto prev_type = type;
    324 
    325 	if (type.is_array())
    326 	{
    327 		type.array_length = 0;
    328 	}
    329 	else if (type.is_matrix())
    330 	{
    331 		type.rows = type.cols;
    332 		type.cols = 1;
    333 	}
    334 	else if (type.is_vector())
    335 	{
    336 		type.rows = 1;
    337 	}
    338 
    339 	chain.push_back({ operation::op_dynamic_index, prev_type, type, index_expression });
    340 }
    341 void reshadefx::expression::add_constant_index_access(unsigned int index)
    342 {
    343 	assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));
    344 
    345 	auto prev_type = type;
    346 
    347 	if (type.is_array())
    348 	{
    349 		assert(type.array_length < 0 || index < static_cast<unsigned int>(type.array_length));
    350 
    351 		type.array_length = 0;
    352 	}
    353 	else if (type.is_matrix())
    354 	{
    355 		assert(index < type.components());
    356 
    357 		type.rows = type.cols;
    358 		type.cols = 1;
    359 	}
    360 	else if (type.is_vector())
    361 	{
    362 		assert(index < type.components());
    363 
    364 		type.rows = 1;
    365 	}
    366 
    367 	if (is_constant)
    368 	{
    369 		if (prev_type.is_array())
    370 		{
    371 			constant = constant.array_data[index];
    372 		}
    373 		else if (prev_type.is_matrix()) // Indexing into a matrix returns a row of it as a vector
    374 		{
    375 			for (unsigned int i = 0; i < prev_type.cols; ++i)
    376 				constant.as_uint[i] = constant.as_uint[index * prev_type.cols + i];
    377 		}
    378 		else // Indexing into a vector returns the element as a scalar
    379 		{
    380 			constant.as_uint[0] = constant.as_uint[index];
    381 		}
    382 	}
    383 	else
    384 	{
    385 		chain.push_back({ operation::op_constant_index, prev_type, type, index });
    386 	}
    387 }
    388 void reshadefx::expression::add_swizzle_access(const signed char swizzle[4], unsigned int length)
    389 {
    390 	assert(type.is_numeric() && !type.is_array());
    391 
    392 	const auto prev_type = type;
    393 
    394 	type.rows = length;
    395 	type.cols = 1;
    396 
    397 	if (is_constant)
    398 	{
    399 		assert(constant.array_data.empty());
    400 
    401 		uint32_t data[16];
    402 		std::memcpy(data, &constant.as_uint[0], sizeof(data));
    403 		for (unsigned int i = 0; i < length; ++i)
    404 			constant.as_uint[i] = data[swizzle[i]];
    405 		std::memset(&constant.as_uint[length], 0, sizeof(uint32_t) * (16 - length)); // Clear the rest of the constant
    406 	}
    407 	else if (length == 1 && prev_type.is_vector()) // Use indexing when possible since the code generation logic is simpler in SPIR-V
    408 	{
    409 		chain.push_back({ operation::op_constant_index, prev_type, type, static_cast<uint32_t>(swizzle[0]) });
    410 	}
    411 	else
    412 	{
    413 		chain.push_back({ operation::op_swizzle, prev_type, type, 0, { swizzle[0], swizzle[1], swizzle[2], swizzle[3] } });
    414 	}
    415 }
    416 
    417 bool reshadefx::expression::evaluate_constant_expression(reshadefx::tokenid op)
    418 {
    419 	if (!is_constant)
    420 		return false;
    421 
    422 	switch (op)
    423 	{
    424 	case tokenid::exclaim:
    425 		for (unsigned int i = 0; i < type.components(); ++i)
    426 			constant.as_uint[i] = !constant.as_uint[i];
    427 		break;
    428 	case tokenid::minus:
    429 		if (type.is_floating_point())
    430 			for (unsigned int i = 0; i < type.components(); ++i)
    431 				constant.as_float[i] = -constant.as_float[i];
    432 		else
    433 			for (unsigned int i = 0; i < type.components(); ++i)
    434 				constant.as_int[i] = -constant.as_int[i];
    435 		break;
    436 	case tokenid::tilde:
    437 		for (unsigned int i = 0; i < type.components(); ++i)
    438 			constant.as_uint[i] = ~constant.as_uint[i];
    439 		break;
    440 	default:
    441 		// Unknown operator token, so nothing to do
    442 		break;
    443 	}
    444 
    445 	return true;
    446 }
    447 bool reshadefx::expression::evaluate_constant_expression(reshadefx::tokenid op, const reshadefx::constant &rhs)
    448 {
    449 	if (!is_constant)
    450 		return false;
    451 
    452 	switch (op)
    453 	{
    454 	case tokenid::percent:
    455 		if (type.is_floating_point()) {
    456 			for (unsigned int i = 0; i < type.components(); ++i)
    457 				// Floating point modulo with zero is defined and results in NaN
    458 				if (rhs.as_float[i] == 0)
    459 					constant.as_float[i] = std::numeric_limits<float>::quiet_NaN();
    460 				else
    461 					constant.as_float[i] = std::fmod(constant.as_float[i], rhs.as_float[i]);
    462 		}
    463 		else if (type.is_signed()) {
    464 			for (unsigned int i = 0; i < type.components(); ++i)
    465 				// Integer modulo with zero on the other hand is not defined, so do not fold this expression in that case
    466 				if (rhs.as_int[i] == 0)
    467 					return false;
    468 				else
    469 					constant.as_int[i] %= rhs.as_int[i];
    470 		}
    471 		else {
    472 			for (unsigned int i = 0; i < type.components(); ++i)
    473 				if (rhs.as_uint[i] == 0)
    474 					return false;
    475 				else
    476 					constant.as_uint[i] %= rhs.as_uint[i];
    477 		}
    478 		break;
    479 	case tokenid::star:
    480 		if (type.is_floating_point())
    481 			for (unsigned int i = 0; i < type.components(); ++i)
    482 				constant.as_float[i] *= rhs.as_float[i];
    483 		else
    484 			for (unsigned int i = 0; i < type.components(); ++i)
    485 				constant.as_uint[i] *= rhs.as_uint[i];
    486 		break;
    487 	case tokenid::plus:
    488 		if (type.is_floating_point())
    489 			for (unsigned int i = 0; i < type.components(); ++i)
    490 				constant.as_float[i] += rhs.as_float[i];
    491 		else
    492 			for (unsigned int i = 0; i < type.components(); ++i)
    493 				constant.as_uint[i] += rhs.as_uint[i];
    494 		break;
    495 	case tokenid::minus:
    496 		if (type.is_floating_point())
    497 			for (unsigned int i = 0; i < type.components(); ++i)
    498 				constant.as_float[i] -= rhs.as_float[i];
    499 		else
    500 			for (unsigned int i = 0; i < type.components(); ++i)
    501 				constant.as_uint[i] -= rhs.as_uint[i];
    502 		break;
    503 	case tokenid::slash:
    504 		if (type.is_floating_point()) {
    505 			for (unsigned int i = 0; i < type.components(); ++i)
    506 				// Floating point division by zero is well defined and results in infinity or NaN
    507 				constant.as_float[i] /= rhs.as_float[i];
    508 		}
    509 		else if (type.is_signed()) {
    510 			for (unsigned int i = 0; i < type.components(); ++i)
    511 				// Integer division by zero on the other hand is not defined, so do not fold this expression in that case
    512 				if (rhs.as_int[i] == 0)
    513 					return false;
    514 				else
    515 					constant.as_int[i] /= rhs.as_int[i];
    516 		}
    517 		else {
    518 			for (unsigned int i = 0; i < type.components(); ++i)
    519 				if (rhs.as_uint[i] == 0)
    520 					return false;
    521 				else
    522 					constant.as_uint[i] /= rhs.as_uint[i];
    523 		}
    524 		break;
    525 	case tokenid::ampersand:
    526 	case tokenid::ampersand_ampersand:
    527 		for (unsigned int i = 0; i < type.components(); ++i)
    528 			constant.as_uint[i] &= rhs.as_uint[i];
    529 		break;
    530 	case tokenid::pipe:
    531 	case tokenid::pipe_pipe:
    532 		for (unsigned int i = 0; i < type.components(); ++i)
    533 			constant.as_uint[i] |= rhs.as_uint[i];
    534 		break;
    535 	case tokenid::caret:
    536 		for (unsigned int i = 0; i < type.components(); ++i)
    537 			constant.as_uint[i] ^= rhs.as_uint[i];
    538 		break;
    539 	case tokenid::less:
    540 		if (type.is_floating_point())
    541 			for (unsigned int i = 0; i < type.components(); ++i)
    542 				constant.as_uint[i] = constant.as_float[i] < rhs.as_float[i];
    543 		else if (type.is_signed())
    544 			for (unsigned int i = 0; i < type.components(); ++i)
    545 				constant.as_uint[i] = constant.as_int[i] < rhs.as_int[i];
    546 		else
    547 			for (unsigned int i = 0; i < type.components(); ++i)
    548 				constant.as_uint[i] = constant.as_uint[i] < rhs.as_uint[i];
    549 		type.base = type::t_bool; // Logic operations change the type to boolean
    550 		break;
    551 	case tokenid::less_equal:
    552 		if (type.is_floating_point())
    553 			for (unsigned int i = 0; i < type.components(); ++i)
    554 				constant.as_uint[i] = constant.as_float[i] <= rhs.as_float[i];
    555 		else if (type.is_signed())
    556 			for (unsigned int i = 0; i < type.components(); ++i)
    557 				constant.as_uint[i] = constant.as_int[i] <= rhs.as_int[i];
    558 		else
    559 			for (unsigned int i = 0; i < type.components(); ++i)
    560 				constant.as_uint[i] = constant.as_uint[i] <= rhs.as_uint[i];
    561 		type.base = type::t_bool;
    562 		break;
    563 	case tokenid::greater:
    564 		if (type.is_floating_point())
    565 			for (unsigned int i = 0; i < type.components(); ++i)
    566 				constant.as_uint[i] = constant.as_float[i] > rhs.as_float[i];
    567 		else if (type.is_signed())
    568 			for (unsigned int i = 0; i < type.components(); ++i)
    569 				constant.as_uint[i] = constant.as_int[i] > rhs.as_int[i];
    570 		else
    571 			for (unsigned int i = 0; i < type.components(); ++i)
    572 				constant.as_uint[i] = constant.as_uint[i] > rhs.as_uint[i];
    573 		type.base = type::t_bool;
    574 		break;
    575 	case tokenid::greater_equal:
    576 		if (type.is_floating_point())
    577 			for (unsigned int i = 0; i < type.components(); ++i)
    578 				constant.as_uint[i] = constant.as_float[i] >= rhs.as_float[i];
    579 		else if (type.is_signed())
    580 			for (unsigned int i = 0; i < type.components(); ++i)
    581 				constant.as_uint[i] = constant.as_int[i] >= rhs.as_int[i];
    582 		else
    583 			for (unsigned int i = 0; i < type.components(); ++i)
    584 				constant.as_uint[i] = constant.as_uint[i] >= rhs.as_uint[i];
    585 		type.base = type::t_bool;
    586 		break;
    587 	case tokenid::equal_equal:
    588 		if (type.is_floating_point())
    589 			for (unsigned int i = 0; i < type.components(); ++i)
    590 				constant.as_uint[i] = constant.as_float[i] == rhs.as_float[i];
    591 		else
    592 			for (unsigned int i = 0; i < type.components(); ++i)
    593 				constant.as_uint[i] = constant.as_uint[i] == rhs.as_uint[i];
    594 		type.base = type::t_bool;
    595 		break;
    596 	case tokenid::exclaim_equal:
    597 		if (type.is_floating_point())
    598 			for (unsigned int i = 0; i < type.components(); ++i)
    599 				constant.as_uint[i] = constant.as_float[i] != rhs.as_float[i];
    600 		else
    601 			for (unsigned int i = 0; i < type.components(); ++i)
    602 				constant.as_uint[i] = constant.as_uint[i] != rhs.as_uint[i];
    603 		type.base = type::t_bool;
    604 		break;
    605 	case tokenid::less_less:
    606 		for (unsigned int i = 0; i < type.components(); ++i)
    607 			constant.as_uint[i] <<= rhs.as_uint[i];
    608 		break;
    609 	case tokenid::greater_greater:
    610 		if (type.is_signed())
    611 			for (unsigned int i = 0; i < type.components(); ++i)
    612 				constant.as_int[i] >>= rhs.as_int[i];
    613 		else
    614 			for (unsigned int i = 0; i < type.components(); ++i)
    615 				constant.as_uint[i] >>= rhs.as_uint[i];
    616 		break;
    617 	default:
    618 		// Unknown operator token, so nothing to do
    619 		break;
    620 	}
    621 
    622 	return true;
    623 }