1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2020 Google LLC 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/sksl/SkSLConstantFolder.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include <limits> 11cb93a386Sopenharmony_ci 12cb93a386Sopenharmony_ci#include "include/sksl/SkSLErrorReporter.h" 13cb93a386Sopenharmony_ci#include "src/sksl/SkSLAnalysis.h" 14cb93a386Sopenharmony_ci#include "src/sksl/SkSLContext.h" 15cb93a386Sopenharmony_ci#include "src/sksl/SkSLProgramSettings.h" 16cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLBinaryExpression.h" 17cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructor.h" 18cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorCompound.h" 19cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorSplat.h" 20cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLExpression.h" 21cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLLiteral.h" 22cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLPrefixExpression.h" 23cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLType.h" 24cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLVariable.h" 25cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLVariableReference.h" 26cb93a386Sopenharmony_ci 27cb93a386Sopenharmony_cinamespace SkSL { 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> eliminate_no_op_boolean(const Expression& left, 30cb93a386Sopenharmony_ci Operator op, 31cb93a386Sopenharmony_ci const Expression& right) { 32cb93a386Sopenharmony_ci bool rightVal = right.as<Literal>().boolValue(); 33cb93a386Sopenharmony_ci 34cb93a386Sopenharmony_ci // Detect no-op Boolean expressions and optimize them away. 35cb93a386Sopenharmony_ci if ((op.kind() == Token::Kind::TK_LOGICALAND && rightVal) || // (expr && true) -> (expr) 36cb93a386Sopenharmony_ci (op.kind() == Token::Kind::TK_LOGICALOR && !rightVal) || // (expr || false) -> (expr) 37cb93a386Sopenharmony_ci (op.kind() == Token::Kind::TK_LOGICALXOR && !rightVal) || // (expr ^^ false) -> (expr) 38cb93a386Sopenharmony_ci (op.kind() == Token::Kind::TK_EQEQ && rightVal) || // (expr == true) -> (expr) 39cb93a386Sopenharmony_ci (op.kind() == Token::Kind::TK_NEQ && !rightVal)) { // (expr != false) -> (expr) 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci return left.clone(); 42cb93a386Sopenharmony_ci } 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_ci return nullptr; 45cb93a386Sopenharmony_ci} 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> short_circuit_boolean(const Expression& left, 48cb93a386Sopenharmony_ci Operator op, 49cb93a386Sopenharmony_ci const Expression& right) { 50cb93a386Sopenharmony_ci bool leftVal = left.as<Literal>().boolValue(); 51cb93a386Sopenharmony_ci 52cb93a386Sopenharmony_ci // When the literal is on the left, we can sometimes eliminate the other expression entirely. 53cb93a386Sopenharmony_ci if ((op.kind() == Token::Kind::TK_LOGICALAND && !leftVal) || // (false && expr) -> (false) 54cb93a386Sopenharmony_ci (op.kind() == Token::Kind::TK_LOGICALOR && leftVal)) { // (true || expr) -> (true) 55cb93a386Sopenharmony_ci 56cb93a386Sopenharmony_ci return left.clone(); 57cb93a386Sopenharmony_ci } 58cb93a386Sopenharmony_ci 59cb93a386Sopenharmony_ci // We can't eliminate the right-side expression via short-circuit, but we might still be able to 60cb93a386Sopenharmony_ci // simplify away a no-op expression. 61cb93a386Sopenharmony_ci return eliminate_no_op_boolean(right, op, left); 62cb93a386Sopenharmony_ci} 63cb93a386Sopenharmony_ci 64cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> simplify_vector_equality(const Context& context, 65cb93a386Sopenharmony_ci const Expression& left, 66cb93a386Sopenharmony_ci Operator op, 67cb93a386Sopenharmony_ci const Expression& right) { 68cb93a386Sopenharmony_ci if (op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ) { 69cb93a386Sopenharmony_ci bool equality = (op.kind() == Token::Kind::TK_EQEQ); 70cb93a386Sopenharmony_ci 71cb93a386Sopenharmony_ci switch (left.compareConstant(right)) { 72cb93a386Sopenharmony_ci case Expression::ComparisonResult::kNotEqual: 73cb93a386Sopenharmony_ci equality = !equality; 74cb93a386Sopenharmony_ci [[fallthrough]]; 75cb93a386Sopenharmony_ci 76cb93a386Sopenharmony_ci case Expression::ComparisonResult::kEqual: 77cb93a386Sopenharmony_ci return Literal::MakeBool(context, left.fLine, equality); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci case Expression::ComparisonResult::kUnknown: 80cb93a386Sopenharmony_ci break; 81cb93a386Sopenharmony_ci } 82cb93a386Sopenharmony_ci } 83cb93a386Sopenharmony_ci return nullptr; 84cb93a386Sopenharmony_ci} 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> simplify_vector(const Context& context, 87cb93a386Sopenharmony_ci const Expression& left, 88cb93a386Sopenharmony_ci Operator op, 89cb93a386Sopenharmony_ci const Expression& right) { 90cb93a386Sopenharmony_ci SkASSERT(left.type().isVector()); 91cb93a386Sopenharmony_ci SkASSERT(left.type() == right.type()); 92cb93a386Sopenharmony_ci const Type& type = left.type(); 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci // Handle equality operations: == != 95cb93a386Sopenharmony_ci if (std::unique_ptr<Expression> result = simplify_vector_equality(context, left, op, right)) { 96cb93a386Sopenharmony_ci return result; 97cb93a386Sopenharmony_ci } 98cb93a386Sopenharmony_ci 99cb93a386Sopenharmony_ci // Handle floating-point arithmetic: + - * / 100cb93a386Sopenharmony_ci using FoldFn = double (*)(double, double); 101cb93a386Sopenharmony_ci FoldFn foldFn; 102cb93a386Sopenharmony_ci switch (op.kind()) { 103cb93a386Sopenharmony_ci case Token::Kind::TK_PLUS: foldFn = +[](double a, double b) { return a + b; }; break; 104cb93a386Sopenharmony_ci case Token::Kind::TK_MINUS: foldFn = +[](double a, double b) { return a - b; }; break; 105cb93a386Sopenharmony_ci case Token::Kind::TK_STAR: foldFn = +[](double a, double b) { return a * b; }; break; 106cb93a386Sopenharmony_ci case Token::Kind::TK_SLASH: foldFn = +[](double a, double b) { return a / b; }; break; 107cb93a386Sopenharmony_ci default: 108cb93a386Sopenharmony_ci return nullptr; 109cb93a386Sopenharmony_ci } 110cb93a386Sopenharmony_ci 111cb93a386Sopenharmony_ci const Type& componentType = type.componentType(); 112cb93a386Sopenharmony_ci double minimumValue = -INFINITY, maximumValue = INFINITY; 113cb93a386Sopenharmony_ci if (componentType.isInteger()) { 114cb93a386Sopenharmony_ci minimumValue = componentType.minimumValue(); 115cb93a386Sopenharmony_ci maximumValue = componentType.maximumValue(); 116cb93a386Sopenharmony_ci } 117cb93a386Sopenharmony_ci 118cb93a386Sopenharmony_ci ExpressionArray args; 119cb93a386Sopenharmony_ci args.reserve_back(type.columns()); 120cb93a386Sopenharmony_ci for (int i = 0; i < type.columns(); i++) { 121cb93a386Sopenharmony_ci double value = foldFn(*left.getConstantValue(i), *right.getConstantValue(i)); 122cb93a386Sopenharmony_ci if (value < minimumValue || value > maximumValue) { 123cb93a386Sopenharmony_ci return nullptr; 124cb93a386Sopenharmony_ci } 125cb93a386Sopenharmony_ci 126cb93a386Sopenharmony_ci args.push_back(Literal::Make(left.fLine, value, &componentType)); 127cb93a386Sopenharmony_ci } 128cb93a386Sopenharmony_ci return ConstructorCompound::Make(context, left.fLine, type, std::move(args)); 129cb93a386Sopenharmony_ci} 130cb93a386Sopenharmony_ci 131cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> cast_expression(const Context& context, 132cb93a386Sopenharmony_ci const Expression& expr, 133cb93a386Sopenharmony_ci const Type& type) { 134cb93a386Sopenharmony_ci ExpressionArray ctorArgs; 135cb93a386Sopenharmony_ci ctorArgs.push_back(expr.clone()); 136cb93a386Sopenharmony_ci return Constructor::Convert(context, expr.fLine, type, std::move(ctorArgs)); 137cb93a386Sopenharmony_ci} 138cb93a386Sopenharmony_ci 139cb93a386Sopenharmony_cibool ConstantFolder::GetConstantInt(const Expression& value, SKSL_INT* out) { 140cb93a386Sopenharmony_ci const Expression* expr = GetConstantValueForVariable(value); 141cb93a386Sopenharmony_ci if (!expr->isIntLiteral()) { 142cb93a386Sopenharmony_ci return false; 143cb93a386Sopenharmony_ci } 144cb93a386Sopenharmony_ci *out = expr->as<Literal>().intValue(); 145cb93a386Sopenharmony_ci return true; 146cb93a386Sopenharmony_ci} 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_cibool ConstantFolder::GetConstantValue(const Expression& value, double* out) { 149cb93a386Sopenharmony_ci const Expression* expr = GetConstantValueForVariable(value); 150cb93a386Sopenharmony_ci if (!expr->is<Literal>()) { 151cb93a386Sopenharmony_ci return false; 152cb93a386Sopenharmony_ci } 153cb93a386Sopenharmony_ci *out = expr->as<Literal>().value(); 154cb93a386Sopenharmony_ci return true; 155cb93a386Sopenharmony_ci} 156cb93a386Sopenharmony_ci 157cb93a386Sopenharmony_cistatic bool contains_constant_zero(const Expression& expr) { 158cb93a386Sopenharmony_ci int numSlots = expr.type().slotCount(); 159cb93a386Sopenharmony_ci for (int index = 0; index < numSlots; ++index) { 160cb93a386Sopenharmony_ci skstd::optional<double> slotVal = expr.getConstantValue(index); 161cb93a386Sopenharmony_ci if (slotVal.has_value() && *slotVal == 0.0) { 162cb93a386Sopenharmony_ci return true; 163cb93a386Sopenharmony_ci } 164cb93a386Sopenharmony_ci } 165cb93a386Sopenharmony_ci return false; 166cb93a386Sopenharmony_ci} 167cb93a386Sopenharmony_ci 168cb93a386Sopenharmony_cistatic bool is_constant_value(const Expression& expr, double value) { 169cb93a386Sopenharmony_ci int numSlots = expr.type().slotCount(); 170cb93a386Sopenharmony_ci for (int index = 0; index < numSlots; ++index) { 171cb93a386Sopenharmony_ci skstd::optional<double> slotVal = expr.getConstantValue(index); 172cb93a386Sopenharmony_ci if (!slotVal.has_value() || *slotVal != value) { 173cb93a386Sopenharmony_ci return false; 174cb93a386Sopenharmony_ci } 175cb93a386Sopenharmony_ci } 176cb93a386Sopenharmony_ci return true; 177cb93a386Sopenharmony_ci} 178cb93a386Sopenharmony_ci 179cb93a386Sopenharmony_cistatic bool error_on_divide_by_zero(const Context& context, int line, Operator op, 180cb93a386Sopenharmony_ci const Expression& right) { 181cb93a386Sopenharmony_ci switch (op.kind()) { 182cb93a386Sopenharmony_ci case Token::Kind::TK_SLASH: 183cb93a386Sopenharmony_ci case Token::Kind::TK_SLASHEQ: 184cb93a386Sopenharmony_ci case Token::Kind::TK_PERCENT: 185cb93a386Sopenharmony_ci case Token::Kind::TK_PERCENTEQ: 186cb93a386Sopenharmony_ci if (contains_constant_zero(right)) { 187cb93a386Sopenharmony_ci context.fErrors->error(line, "division by zero"); 188cb93a386Sopenharmony_ci return true; 189cb93a386Sopenharmony_ci } 190cb93a386Sopenharmony_ci return false; 191cb93a386Sopenharmony_ci default: 192cb93a386Sopenharmony_ci return false; 193cb93a386Sopenharmony_ci } 194cb93a386Sopenharmony_ci} 195cb93a386Sopenharmony_ci 196cb93a386Sopenharmony_ciconst Expression* ConstantFolder::GetConstantValueForVariable(const Expression& inExpr) { 197cb93a386Sopenharmony_ci for (const Expression* expr = &inExpr;;) { 198cb93a386Sopenharmony_ci if (!expr->is<VariableReference>()) { 199cb93a386Sopenharmony_ci break; 200cb93a386Sopenharmony_ci } 201cb93a386Sopenharmony_ci const VariableReference& varRef = expr->as<VariableReference>(); 202cb93a386Sopenharmony_ci if (varRef.refKind() != VariableRefKind::kRead) { 203cb93a386Sopenharmony_ci break; 204cb93a386Sopenharmony_ci } 205cb93a386Sopenharmony_ci const Variable& var = *varRef.variable(); 206cb93a386Sopenharmony_ci if (!(var.modifiers().fFlags & Modifiers::kConst_Flag)) { 207cb93a386Sopenharmony_ci break; 208cb93a386Sopenharmony_ci } 209cb93a386Sopenharmony_ci expr = var.initialValue(); 210cb93a386Sopenharmony_ci if (!expr) { 211cb93a386Sopenharmony_ci // Function parameters can be const but won't have an initial value. 212cb93a386Sopenharmony_ci break; 213cb93a386Sopenharmony_ci } 214cb93a386Sopenharmony_ci if (expr->isCompileTimeConstant()) { 215cb93a386Sopenharmony_ci return expr; 216cb93a386Sopenharmony_ci } 217cb93a386Sopenharmony_ci } 218cb93a386Sopenharmony_ci // We didn't find a compile-time constant at the end. Return the expression as-is. 219cb93a386Sopenharmony_ci return &inExpr; 220cb93a386Sopenharmony_ci} 221cb93a386Sopenharmony_ci 222cb93a386Sopenharmony_cistd::unique_ptr<Expression> ConstantFolder::MakeConstantValueForVariable( 223cb93a386Sopenharmony_ci std::unique_ptr<Expression> expr) { 224cb93a386Sopenharmony_ci const Expression* constantExpr = GetConstantValueForVariable(*expr); 225cb93a386Sopenharmony_ci if (constantExpr != expr.get()) { 226cb93a386Sopenharmony_ci expr = constantExpr->clone(); 227cb93a386Sopenharmony_ci } 228cb93a386Sopenharmony_ci return expr; 229cb93a386Sopenharmony_ci} 230cb93a386Sopenharmony_ci 231cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> simplify_no_op_arithmetic(const Context& context, 232cb93a386Sopenharmony_ci const Expression& left, 233cb93a386Sopenharmony_ci Operator op, 234cb93a386Sopenharmony_ci const Expression& right, 235cb93a386Sopenharmony_ci const Type& resultType) { 236cb93a386Sopenharmony_ci switch (op.kind()) { 237cb93a386Sopenharmony_ci case Token::Kind::TK_PLUS: 238cb93a386Sopenharmony_ci if (is_constant_value(right, 0.0)) { // x + 0 239cb93a386Sopenharmony_ci return cast_expression(context, left, resultType); 240cb93a386Sopenharmony_ci } 241cb93a386Sopenharmony_ci if (is_constant_value(left, 0.0)) { // 0 + x 242cb93a386Sopenharmony_ci return cast_expression(context, right, resultType); 243cb93a386Sopenharmony_ci } 244cb93a386Sopenharmony_ci break; 245cb93a386Sopenharmony_ci 246cb93a386Sopenharmony_ci case Token::Kind::TK_STAR: 247cb93a386Sopenharmony_ci if (is_constant_value(right, 1.0)) { // x * 1 248cb93a386Sopenharmony_ci return cast_expression(context, left, resultType); 249cb93a386Sopenharmony_ci } 250cb93a386Sopenharmony_ci if (is_constant_value(left, 1.0)) { // 1 * x 251cb93a386Sopenharmony_ci return cast_expression(context, right, resultType); 252cb93a386Sopenharmony_ci } 253cb93a386Sopenharmony_ci if (is_constant_value(right, 0.0) && !left.hasSideEffects()) { // x * 0 254cb93a386Sopenharmony_ci return cast_expression(context, right, resultType); 255cb93a386Sopenharmony_ci } 256cb93a386Sopenharmony_ci if (is_constant_value(left, 0.0) && !right.hasSideEffects()) { // 0 * x 257cb93a386Sopenharmony_ci return cast_expression(context, left, resultType); 258cb93a386Sopenharmony_ci } 259cb93a386Sopenharmony_ci break; 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_ci case Token::Kind::TK_MINUS: 262cb93a386Sopenharmony_ci if (is_constant_value(right, 0.0)) { // x - 0 263cb93a386Sopenharmony_ci return cast_expression(context, left, resultType); 264cb93a386Sopenharmony_ci } 265cb93a386Sopenharmony_ci if (is_constant_value(left, 0.0)) { // 0 - x (to `-x`) 266cb93a386Sopenharmony_ci if (std::unique_ptr<Expression> val = cast_expression(context, right, resultType)) { 267cb93a386Sopenharmony_ci return PrefixExpression::Make(context, Token::Kind::TK_MINUS, std::move(val)); 268cb93a386Sopenharmony_ci } 269cb93a386Sopenharmony_ci } 270cb93a386Sopenharmony_ci break; 271cb93a386Sopenharmony_ci 272cb93a386Sopenharmony_ci case Token::Kind::TK_SLASH: 273cb93a386Sopenharmony_ci if (is_constant_value(right, 1.0)) { // x / 1 274cb93a386Sopenharmony_ci return cast_expression(context, left, resultType); 275cb93a386Sopenharmony_ci } 276cb93a386Sopenharmony_ci break; 277cb93a386Sopenharmony_ci 278cb93a386Sopenharmony_ci case Token::Kind::TK_PLUSEQ: 279cb93a386Sopenharmony_ci case Token::Kind::TK_MINUSEQ: 280cb93a386Sopenharmony_ci if (is_constant_value(right, 0.0)) { // x += 0, x -= 0 281cb93a386Sopenharmony_ci if (std::unique_ptr<Expression> var = cast_expression(context, left, resultType)) { 282cb93a386Sopenharmony_ci Analysis::UpdateVariableRefKind(var.get(), VariableRefKind::kRead); 283cb93a386Sopenharmony_ci return var; 284cb93a386Sopenharmony_ci } 285cb93a386Sopenharmony_ci } 286cb93a386Sopenharmony_ci break; 287cb93a386Sopenharmony_ci 288cb93a386Sopenharmony_ci case Token::Kind::TK_STAREQ: 289cb93a386Sopenharmony_ci case Token::Kind::TK_SLASHEQ: 290cb93a386Sopenharmony_ci if (is_constant_value(right, 1.0)) { // x *= 1, x /= 1 291cb93a386Sopenharmony_ci if (std::unique_ptr<Expression> var = cast_expression(context, left, resultType)) { 292cb93a386Sopenharmony_ci Analysis::UpdateVariableRefKind(var.get(), VariableRefKind::kRead); 293cb93a386Sopenharmony_ci return var; 294cb93a386Sopenharmony_ci } 295cb93a386Sopenharmony_ci } 296cb93a386Sopenharmony_ci break; 297cb93a386Sopenharmony_ci 298cb93a386Sopenharmony_ci default: 299cb93a386Sopenharmony_ci break; 300cb93a386Sopenharmony_ci } 301cb93a386Sopenharmony_ci 302cb93a386Sopenharmony_ci return nullptr; 303cb93a386Sopenharmony_ci} 304cb93a386Sopenharmony_ci 305cb93a386Sopenharmony_citemplate <typename T> 306cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> fold_float_expression(int line, 307cb93a386Sopenharmony_ci T result, 308cb93a386Sopenharmony_ci const Type* resultType) { 309cb93a386Sopenharmony_ci // If constant-folding this expression would generate a NaN/infinite result, leave it as-is. 310cb93a386Sopenharmony_ci if constexpr (!std::is_same<T, bool>::value) { 311cb93a386Sopenharmony_ci if (!std::isfinite(result)) { 312cb93a386Sopenharmony_ci return nullptr; 313cb93a386Sopenharmony_ci } 314cb93a386Sopenharmony_ci } 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci return Literal::Make(line, result, resultType); 317cb93a386Sopenharmony_ci} 318cb93a386Sopenharmony_ci 319cb93a386Sopenharmony_citemplate <typename T> 320cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> fold_int_expression(int line, 321cb93a386Sopenharmony_ci T result, 322cb93a386Sopenharmony_ci const Type* resultType) { 323cb93a386Sopenharmony_ci // If constant-folding this expression would overflow the result type, leave it as-is. 324cb93a386Sopenharmony_ci if constexpr (!std::is_same<T, bool>::value) { 325cb93a386Sopenharmony_ci if (result < resultType->minimumValue() || result > resultType->maximumValue()) { 326cb93a386Sopenharmony_ci return nullptr; 327cb93a386Sopenharmony_ci } 328cb93a386Sopenharmony_ci } 329cb93a386Sopenharmony_ci 330cb93a386Sopenharmony_ci return Literal::Make(line, result, resultType); 331cb93a386Sopenharmony_ci} 332cb93a386Sopenharmony_ci 333cb93a386Sopenharmony_cistd::unique_ptr<Expression> ConstantFolder::Simplify(const Context& context, 334cb93a386Sopenharmony_ci int line, 335cb93a386Sopenharmony_ci const Expression& leftExpr, 336cb93a386Sopenharmony_ci Operator op, 337cb93a386Sopenharmony_ci const Expression& rightExpr, 338cb93a386Sopenharmony_ci const Type& resultType) { 339cb93a386Sopenharmony_ci // Replace constant variables with their literal values. 340cb93a386Sopenharmony_ci const Expression* left = GetConstantValueForVariable(leftExpr); 341cb93a386Sopenharmony_ci const Expression* right = GetConstantValueForVariable(rightExpr); 342cb93a386Sopenharmony_ci 343cb93a386Sopenharmony_ci // If this is the comma operator, the left side is evaluated but not otherwise used in any way. 344cb93a386Sopenharmony_ci // So if the left side has no side effects, it can just be eliminated entirely. 345cb93a386Sopenharmony_ci if (op.kind() == Token::Kind::TK_COMMA && !left->hasSideEffects()) { 346cb93a386Sopenharmony_ci return right->clone(); 347cb93a386Sopenharmony_ci } 348cb93a386Sopenharmony_ci 349cb93a386Sopenharmony_ci // If this is the assignment operator, and both sides are the same trivial expression, this is 350cb93a386Sopenharmony_ci // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`). 351cb93a386Sopenharmony_ci // This can happen when other parts of the assignment are optimized away. 352cb93a386Sopenharmony_ci if (op.kind() == Token::Kind::TK_EQ && Analysis::IsSameExpressionTree(*left, *right)) { 353cb93a386Sopenharmony_ci return right->clone(); 354cb93a386Sopenharmony_ci } 355cb93a386Sopenharmony_ci 356cb93a386Sopenharmony_ci // Simplify the expression when both sides are constant Boolean literals. 357cb93a386Sopenharmony_ci if (left->isBoolLiteral() && right->isBoolLiteral()) { 358cb93a386Sopenharmony_ci bool leftVal = left->as<Literal>().boolValue(); 359cb93a386Sopenharmony_ci bool rightVal = right->as<Literal>().boolValue(); 360cb93a386Sopenharmony_ci bool result; 361cb93a386Sopenharmony_ci switch (op.kind()) { 362cb93a386Sopenharmony_ci case Token::Kind::TK_LOGICALAND: result = leftVal && rightVal; break; 363cb93a386Sopenharmony_ci case Token::Kind::TK_LOGICALOR: result = leftVal || rightVal; break; 364cb93a386Sopenharmony_ci case Token::Kind::TK_LOGICALXOR: result = leftVal ^ rightVal; break; 365cb93a386Sopenharmony_ci case Token::Kind::TK_EQEQ: result = leftVal == rightVal; break; 366cb93a386Sopenharmony_ci case Token::Kind::TK_NEQ: result = leftVal != rightVal; break; 367cb93a386Sopenharmony_ci default: return nullptr; 368cb93a386Sopenharmony_ci } 369cb93a386Sopenharmony_ci return Literal::MakeBool(context, line, result); 370cb93a386Sopenharmony_ci } 371cb93a386Sopenharmony_ci 372cb93a386Sopenharmony_ci // If the left side is a Boolean literal, apply short-circuit optimizations. 373cb93a386Sopenharmony_ci if (left->isBoolLiteral()) { 374cb93a386Sopenharmony_ci return short_circuit_boolean(*left, op, *right); 375cb93a386Sopenharmony_ci } 376cb93a386Sopenharmony_ci 377cb93a386Sopenharmony_ci // If the right side is a Boolean literal... 378cb93a386Sopenharmony_ci if (right->isBoolLiteral()) { 379cb93a386Sopenharmony_ci // ... and the left side has no side effects... 380cb93a386Sopenharmony_ci if (!left->hasSideEffects()) { 381cb93a386Sopenharmony_ci // We can reverse the expressions and short-circuit optimizations are still valid. 382cb93a386Sopenharmony_ci return short_circuit_boolean(*right, op, *left); 383cb93a386Sopenharmony_ci } 384cb93a386Sopenharmony_ci 385cb93a386Sopenharmony_ci // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions. 386cb93a386Sopenharmony_ci return eliminate_no_op_boolean(*left, op, *right); 387cb93a386Sopenharmony_ci } 388cb93a386Sopenharmony_ci 389cb93a386Sopenharmony_ci if (op.kind() == Token::Kind::TK_EQEQ && Analysis::IsSameExpressionTree(*left, *right)) { 390cb93a386Sopenharmony_ci // With == comparison, if both sides are the same trivial expression, this is self- 391cb93a386Sopenharmony_ci // comparison and is always true. (We are not concerned with NaN.) 392cb93a386Sopenharmony_ci return Literal::MakeBool(context, leftExpr.fLine, /*value=*/true); 393cb93a386Sopenharmony_ci } 394cb93a386Sopenharmony_ci 395cb93a386Sopenharmony_ci if (op.kind() == Token::Kind::TK_NEQ && Analysis::IsSameExpressionTree(*left, *right)) { 396cb93a386Sopenharmony_ci // With != comparison, if both sides are the same trivial expression, this is self- 397cb93a386Sopenharmony_ci // comparison and is always false. (We are not concerned with NaN.) 398cb93a386Sopenharmony_ci return Literal::MakeBool(context, leftExpr.fLine, /*value=*/false); 399cb93a386Sopenharmony_ci } 400cb93a386Sopenharmony_ci 401cb93a386Sopenharmony_ci if (error_on_divide_by_zero(context, line, op, *right)) { 402cb93a386Sopenharmony_ci return nullptr; 403cb93a386Sopenharmony_ci } 404cb93a386Sopenharmony_ci 405cb93a386Sopenharmony_ci // Optimize away no-op arithmetic like `x * 1`, `x *= 1`, `x + 0`, `x * 0`, `0 / x`, etc. 406cb93a386Sopenharmony_ci const Type& leftType = left->type(); 407cb93a386Sopenharmony_ci const Type& rightType = right->type(); 408cb93a386Sopenharmony_ci if ((leftType.isScalar() || leftType.isVector()) && 409cb93a386Sopenharmony_ci (rightType.isScalar() || rightType.isVector())) { 410cb93a386Sopenharmony_ci std::unique_ptr<Expression> expr = simplify_no_op_arithmetic(context, *left, op, *right, 411cb93a386Sopenharmony_ci resultType); 412cb93a386Sopenharmony_ci if (expr) { 413cb93a386Sopenharmony_ci return expr; 414cb93a386Sopenharmony_ci } 415cb93a386Sopenharmony_ci } 416cb93a386Sopenharmony_ci 417cb93a386Sopenharmony_ci // Other than the cases above, constant folding requires both sides to be constant. 418cb93a386Sopenharmony_ci if (!left->isCompileTimeConstant() || !right->isCompileTimeConstant()) { 419cb93a386Sopenharmony_ci return nullptr; 420cb93a386Sopenharmony_ci } 421cb93a386Sopenharmony_ci 422cb93a386Sopenharmony_ci // Note that we expressly do not worry about precision and overflow here -- we use the maximum 423cb93a386Sopenharmony_ci // precision to calculate the results and hope the result makes sense. 424cb93a386Sopenharmony_ci // TODO(skia:10932): detect and handle integer overflow properly. 425cb93a386Sopenharmony_ci using SKSL_UINT = uint64_t; 426cb93a386Sopenharmony_ci if (left->isIntLiteral() && right->isIntLiteral()) { 427cb93a386Sopenharmony_ci SKSL_INT leftVal = left->as<Literal>().intValue(); 428cb93a386Sopenharmony_ci SKSL_INT rightVal = right->as<Literal>().intValue(); 429cb93a386Sopenharmony_ci 430cb93a386Sopenharmony_ci #define RESULT(Op) fold_int_expression(line, \ 431cb93a386Sopenharmony_ci (SKSL_INT)(leftVal) Op (SKSL_INT)(rightVal), &resultType) 432cb93a386Sopenharmony_ci #define URESULT(Op) fold_int_expression(line, \ 433cb93a386Sopenharmony_ci (SKSL_INT)((SKSL_UINT)(leftVal) Op (SKSL_UINT)(rightVal)), &resultType) 434cb93a386Sopenharmony_ci switch (op.kind()) { 435cb93a386Sopenharmony_ci case Token::Kind::TK_PLUS: return URESULT(+); 436cb93a386Sopenharmony_ci case Token::Kind::TK_MINUS: return URESULT(-); 437cb93a386Sopenharmony_ci case Token::Kind::TK_STAR: return URESULT(*); 438cb93a386Sopenharmony_ci case Token::Kind::TK_SLASH: 439cb93a386Sopenharmony_ci if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) { 440cb93a386Sopenharmony_ci context.fErrors->error(line, "arithmetic overflow"); 441cb93a386Sopenharmony_ci return nullptr; 442cb93a386Sopenharmony_ci } 443cb93a386Sopenharmony_ci return RESULT(/); 444cb93a386Sopenharmony_ci case Token::Kind::TK_PERCENT: 445cb93a386Sopenharmony_ci if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) { 446cb93a386Sopenharmony_ci context.fErrors->error(line, "arithmetic overflow"); 447cb93a386Sopenharmony_ci return nullptr; 448cb93a386Sopenharmony_ci } 449cb93a386Sopenharmony_ci return RESULT(%); 450cb93a386Sopenharmony_ci case Token::Kind::TK_BITWISEAND: return RESULT(&); 451cb93a386Sopenharmony_ci case Token::Kind::TK_BITWISEOR: return RESULT(|); 452cb93a386Sopenharmony_ci case Token::Kind::TK_BITWISEXOR: return RESULT(^); 453cb93a386Sopenharmony_ci case Token::Kind::TK_EQEQ: return RESULT(==); 454cb93a386Sopenharmony_ci case Token::Kind::TK_NEQ: return RESULT(!=); 455cb93a386Sopenharmony_ci case Token::Kind::TK_GT: return RESULT(>); 456cb93a386Sopenharmony_ci case Token::Kind::TK_GTEQ: return RESULT(>=); 457cb93a386Sopenharmony_ci case Token::Kind::TK_LT: return RESULT(<); 458cb93a386Sopenharmony_ci case Token::Kind::TK_LTEQ: return RESULT(<=); 459cb93a386Sopenharmony_ci case Token::Kind::TK_SHL: 460cb93a386Sopenharmony_ci if (rightVal >= 0 && rightVal <= 31) { 461cb93a386Sopenharmony_ci // Left-shifting a negative (or really, any signed) value is undefined behavior 462cb93a386Sopenharmony_ci // in C++, but not GLSL. Do the shift on unsigned values, to avoid UBSAN. 463cb93a386Sopenharmony_ci return URESULT(<<); 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci context.fErrors->error(line, "shift value out of range"); 466cb93a386Sopenharmony_ci return nullptr; 467cb93a386Sopenharmony_ci case Token::Kind::TK_SHR: 468cb93a386Sopenharmony_ci if (rightVal >= 0 && rightVal <= 31) { 469cb93a386Sopenharmony_ci return RESULT(>>); 470cb93a386Sopenharmony_ci } 471cb93a386Sopenharmony_ci context.fErrors->error(line, "shift value out of range"); 472cb93a386Sopenharmony_ci return nullptr; 473cb93a386Sopenharmony_ci 474cb93a386Sopenharmony_ci default: 475cb93a386Sopenharmony_ci return nullptr; 476cb93a386Sopenharmony_ci } 477cb93a386Sopenharmony_ci #undef RESULT 478cb93a386Sopenharmony_ci #undef URESULT 479cb93a386Sopenharmony_ci } 480cb93a386Sopenharmony_ci 481cb93a386Sopenharmony_ci // Perform constant folding on pairs of floating-point literals. 482cb93a386Sopenharmony_ci if (left->isFloatLiteral() && right->isFloatLiteral()) { 483cb93a386Sopenharmony_ci SKSL_FLOAT leftVal = left->as<Literal>().floatValue(); 484cb93a386Sopenharmony_ci SKSL_FLOAT rightVal = right->as<Literal>().floatValue(); 485cb93a386Sopenharmony_ci 486cb93a386Sopenharmony_ci #define RESULT(Op) fold_float_expression(line, leftVal Op rightVal, &resultType) 487cb93a386Sopenharmony_ci switch (op.kind()) { 488cb93a386Sopenharmony_ci case Token::Kind::TK_PLUS: return RESULT(+); 489cb93a386Sopenharmony_ci case Token::Kind::TK_MINUS: return RESULT(-); 490cb93a386Sopenharmony_ci case Token::Kind::TK_STAR: return RESULT(*); 491cb93a386Sopenharmony_ci case Token::Kind::TK_SLASH: return RESULT(/); 492cb93a386Sopenharmony_ci case Token::Kind::TK_EQEQ: return RESULT(==); 493cb93a386Sopenharmony_ci case Token::Kind::TK_NEQ: return RESULT(!=); 494cb93a386Sopenharmony_ci case Token::Kind::TK_GT: return RESULT(>); 495cb93a386Sopenharmony_ci case Token::Kind::TK_GTEQ: return RESULT(>=); 496cb93a386Sopenharmony_ci case Token::Kind::TK_LT: return RESULT(<); 497cb93a386Sopenharmony_ci case Token::Kind::TK_LTEQ: return RESULT(<=); 498cb93a386Sopenharmony_ci default: return nullptr; 499cb93a386Sopenharmony_ci } 500cb93a386Sopenharmony_ci #undef RESULT 501cb93a386Sopenharmony_ci } 502cb93a386Sopenharmony_ci 503cb93a386Sopenharmony_ci // Perform constant folding on pairs of vectors. 504cb93a386Sopenharmony_ci if (leftType.isVector() && leftType == rightType) { 505cb93a386Sopenharmony_ci if (leftType.componentType().isFloat()) { 506cb93a386Sopenharmony_ci return simplify_vector(context, *left, op, *right); 507cb93a386Sopenharmony_ci } 508cb93a386Sopenharmony_ci if (leftType.componentType().isInteger()) { 509cb93a386Sopenharmony_ci return simplify_vector(context, *left, op, *right); 510cb93a386Sopenharmony_ci } 511cb93a386Sopenharmony_ci if (leftType.componentType().isBoolean()) { 512cb93a386Sopenharmony_ci return simplify_vector_equality(context, *left, op, *right); 513cb93a386Sopenharmony_ci } 514cb93a386Sopenharmony_ci return nullptr; 515cb93a386Sopenharmony_ci } 516cb93a386Sopenharmony_ci 517cb93a386Sopenharmony_ci // Perform constant folding on vectors against scalars, e.g.: half4(2) + 2 518cb93a386Sopenharmony_ci if (leftType.isVector() && leftType.componentType() == rightType) { 519cb93a386Sopenharmony_ci if (rightType.isFloat()) { 520cb93a386Sopenharmony_ci return simplify_vector(context, *left, op, ConstructorSplat(*right, left->type())); 521cb93a386Sopenharmony_ci } 522cb93a386Sopenharmony_ci if (rightType.isInteger()) { 523cb93a386Sopenharmony_ci return simplify_vector(context, *left, op, ConstructorSplat(*right, left->type())); 524cb93a386Sopenharmony_ci } 525cb93a386Sopenharmony_ci if (rightType.isBoolean()) { 526cb93a386Sopenharmony_ci return simplify_vector_equality(context, *left, op, 527cb93a386Sopenharmony_ci ConstructorSplat(*right, left->type())); 528cb93a386Sopenharmony_ci } 529cb93a386Sopenharmony_ci return nullptr; 530cb93a386Sopenharmony_ci } 531cb93a386Sopenharmony_ci 532cb93a386Sopenharmony_ci // Perform constant folding on scalars against vectors, e.g.: 2 + half4(2) 533cb93a386Sopenharmony_ci if (rightType.isVector() && rightType.componentType() == leftType) { 534cb93a386Sopenharmony_ci if (leftType.isFloat()) { 535cb93a386Sopenharmony_ci return simplify_vector(context, ConstructorSplat(*left, right->type()), op, *right); 536cb93a386Sopenharmony_ci } 537cb93a386Sopenharmony_ci if (leftType.isInteger()) { 538cb93a386Sopenharmony_ci return simplify_vector(context, ConstructorSplat(*left, right->type()), op, *right); 539cb93a386Sopenharmony_ci } 540cb93a386Sopenharmony_ci if (leftType.isBoolean()) { 541cb93a386Sopenharmony_ci return simplify_vector_equality(context, ConstructorSplat(*left, right->type()), 542cb93a386Sopenharmony_ci op, *right); 543cb93a386Sopenharmony_ci } 544cb93a386Sopenharmony_ci return nullptr; 545cb93a386Sopenharmony_ci } 546cb93a386Sopenharmony_ci 547cb93a386Sopenharmony_ci // Perform constant folding on pairs of matrices or arrays. 548cb93a386Sopenharmony_ci if ((leftType.isMatrix() && rightType.isMatrix()) || 549cb93a386Sopenharmony_ci (leftType.isArray() && rightType.isArray())) { 550cb93a386Sopenharmony_ci bool equality; 551cb93a386Sopenharmony_ci switch (op.kind()) { 552cb93a386Sopenharmony_ci case Token::Kind::TK_EQEQ: 553cb93a386Sopenharmony_ci equality = true; 554cb93a386Sopenharmony_ci break; 555cb93a386Sopenharmony_ci case Token::Kind::TK_NEQ: 556cb93a386Sopenharmony_ci equality = false; 557cb93a386Sopenharmony_ci break; 558cb93a386Sopenharmony_ci default: 559cb93a386Sopenharmony_ci return nullptr; 560cb93a386Sopenharmony_ci } 561cb93a386Sopenharmony_ci 562cb93a386Sopenharmony_ci switch (left->compareConstant(*right)) { 563cb93a386Sopenharmony_ci case Expression::ComparisonResult::kNotEqual: 564cb93a386Sopenharmony_ci equality = !equality; 565cb93a386Sopenharmony_ci [[fallthrough]]; 566cb93a386Sopenharmony_ci 567cb93a386Sopenharmony_ci case Expression::ComparisonResult::kEqual: 568cb93a386Sopenharmony_ci return Literal::MakeBool(context, line, equality); 569cb93a386Sopenharmony_ci 570cb93a386Sopenharmony_ci case Expression::ComparisonResult::kUnknown: 571cb93a386Sopenharmony_ci return nullptr; 572cb93a386Sopenharmony_ci } 573cb93a386Sopenharmony_ci } 574cb93a386Sopenharmony_ci 575cb93a386Sopenharmony_ci // We aren't able to constant-fold. 576cb93a386Sopenharmony_ci return nullptr; 577cb93a386Sopenharmony_ci} 578cb93a386Sopenharmony_ci 579cb93a386Sopenharmony_ci} // namespace SkSL 580