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 }