blob: c540cd17ab5753bc0a6e2afe3e494d3de92ca308 [file] [log] [blame]
John Stilesdc8ec312021-01-11 11:05:21 -05001/*
2 * Copyright 2020 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "src/sksl/SkSLConstantFolder.h"
9
10#include <limits>
11
Ethan Nicholas4a5e22a2021-08-13 17:29:51 -040012#include "include/sksl/SkSLErrorReporter.h"
John Stilesdc8ec312021-01-11 11:05:21 -050013#include "src/sksl/SkSLContext.h"
John Stilesdc8ec312021-01-11 11:05:21 -050014#include "src/sksl/ir/SkSLBinaryExpression.h"
John Stilesdc8ec312021-01-11 11:05:21 -050015#include "src/sksl/ir/SkSLConstructor.h"
John Stilesb0ce42d2021-04-09 10:39:42 -040016#include "src/sksl/ir/SkSLConstructorCompound.h"
John Stiles2938eea2021-04-01 18:58:25 -040017#include "src/sksl/ir/SkSLConstructorSplat.h"
John Stilesdc8ec312021-01-11 11:05:21 -050018#include "src/sksl/ir/SkSLExpression.h"
John Stiles7591d4b2021-09-13 13:32:06 -040019#include "src/sksl/ir/SkSLLiteral.h"
John Stiles8f440b42021-03-05 16:48:56 -050020#include "src/sksl/ir/SkSLPrefixExpression.h"
John Stilesdc8ec312021-01-11 11:05:21 -050021#include "src/sksl/ir/SkSLType.h"
22#include "src/sksl/ir/SkSLVariable.h"
23#include "src/sksl/ir/SkSLVariableReference.h"
24
25namespace SkSL {
26
John Stiles8a9da732021-01-20 14:32:33 -050027static std::unique_ptr<Expression> eliminate_no_op_boolean(const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050028 Operator op,
John Stiles8a9da732021-01-20 14:32:33 -050029 const Expression& right) {
John Stiles7591d4b2021-09-13 13:32:06 -040030 bool rightVal = right.as<Literal>().boolValue();
John Stiles8a9da732021-01-20 14:32:33 -050031
32 // Detect no-op Boolean expressions and optimize them away.
John Stiles45990502021-02-16 10:55:27 -050033 if ((op.kind() == Token::Kind::TK_LOGICALAND && rightVal) || // (expr && true) -> (expr)
34 (op.kind() == Token::Kind::TK_LOGICALOR && !rightVal) || // (expr || false) -> (expr)
35 (op.kind() == Token::Kind::TK_LOGICALXOR && !rightVal) || // (expr ^^ false) -> (expr)
36 (op.kind() == Token::Kind::TK_EQEQ && rightVal) || // (expr == true) -> (expr)
37 (op.kind() == Token::Kind::TK_NEQ && !rightVal)) { // (expr != false) -> (expr)
John Stiles8a9da732021-01-20 14:32:33 -050038
39 return left.clone();
40 }
41
42 return nullptr;
43}
44
John Stilesdc8ec312021-01-11 11:05:21 -050045static std::unique_ptr<Expression> short_circuit_boolean(const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050046 Operator op,
John Stilesdc8ec312021-01-11 11:05:21 -050047 const Expression& right) {
John Stiles7591d4b2021-09-13 13:32:06 -040048 bool leftVal = left.as<Literal>().boolValue();
John Stilesdc8ec312021-01-11 11:05:21 -050049
John Stiles8a9da732021-01-20 14:32:33 -050050 // When the literal is on the left, we can sometimes eliminate the other expression entirely.
John Stiles45990502021-02-16 10:55:27 -050051 if ((op.kind() == Token::Kind::TK_LOGICALAND && !leftVal) || // (false && expr) -> (false)
52 (op.kind() == Token::Kind::TK_LOGICALOR && leftVal)) { // (true || expr) -> (true)
John Stiles8a9da732021-01-20 14:32:33 -050053
54 return left.clone();
John Stilesdc8ec312021-01-11 11:05:21 -050055 }
56
John Stiles8a9da732021-01-20 14:32:33 -050057 // We can't eliminate the right-side expression via short-circuit, but we might still be able to
58 // simplify away a no-op expression.
59 return eliminate_no_op_boolean(right, op, left);
John Stilesdc8ec312021-01-11 11:05:21 -050060}
61
John Stiles878d8fb2021-08-24 11:33:27 -040062static std::unique_ptr<Expression> simplify_vector_equality(const Context& context,
63 const Expression& left,
64 Operator op,
65 const Expression& right) {
66 if (op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ) {
67 bool equality = (op.kind() == Token::Kind::TK_EQEQ);
68
69 switch (left.compareConstant(right)) {
70 case Expression::ComparisonResult::kNotEqual:
71 equality = !equality;
72 [[fallthrough]];
73
74 case Expression::ComparisonResult::kEqual:
John Stiles7591d4b2021-09-13 13:32:06 -040075 return Literal::MakeBool(context, left.fOffset, equality);
John Stiles878d8fb2021-08-24 11:33:27 -040076
77 case Expression::ComparisonResult::kUnknown:
78 break;
79 }
80 }
81 return nullptr;
82}
83
John Stilesdc8ec312021-01-11 11:05:21 -050084static std::unique_ptr<Expression> simplify_vector(const Context& context,
John Stilesdc8ec312021-01-11 11:05:21 -050085 const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050086 Operator op,
John Stilesdc8ec312021-01-11 11:05:21 -050087 const Expression& right) {
John Stiles508eba72021-01-11 13:07:47 -050088 SkASSERT(left.type().isVector());
John Stilesdc8ec312021-01-11 11:05:21 -050089 SkASSERT(left.type() == right.type());
90 const Type& type = left.type();
91
John Stiles878d8fb2021-08-24 11:33:27 -040092 // Handle equality operations: == !=
93 if (std::unique_ptr<Expression> result = simplify_vector_equality(context, left, op, right)) {
94 return result;
John Stilesdc8ec312021-01-11 11:05:21 -050095 }
96
97 // Handle floating-point arithmetic: + - * /
John Stiles7591d4b2021-09-13 13:32:06 -040098 using FoldFn = double (*)(double, double);
99 FoldFn foldFn;
John Stiles45990502021-02-16 10:55:27 -0500100 switch (op.kind()) {
John Stiles7591d4b2021-09-13 13:32:06 -0400101 case Token::Kind::TK_PLUS: foldFn = +[](double a, double b) { return a + b; }; break;
102 case Token::Kind::TK_MINUS: foldFn = +[](double a, double b) { return a - b; }; break;
103 case Token::Kind::TK_STAR: foldFn = +[](double a, double b) { return a * b; }; break;
104 case Token::Kind::TK_SLASH: foldFn = +[](double a, double b) { return a / b; }; break;
John Stilesdc8ec312021-01-11 11:05:21 -0500105 default:
106 return nullptr;
107 }
John Stiles7591d4b2021-09-13 13:32:06 -0400108
109 const Type& componentType = type.componentType();
110 ExpressionArray args;
111 args.reserve_back(type.columns());
112 for (int i = 0; i < type.columns(); i++) {
113 double value = foldFn(left.getConstantSubexpression(i)->as<Literal>().value(),
114 right.getConstantSubexpression(i)->as<Literal>().value());
115 args.push_back(Literal::Make(left.fOffset, value, &componentType));
116 }
117 return ConstructorCompound::Make(context, left.fOffset, type, std::move(args));
John Stilesdc8ec312021-01-11 11:05:21 -0500118}
119
John Stiles8f440b42021-03-05 16:48:56 -0500120static std::unique_ptr<Expression> cast_expression(const Context& context,
121 const Expression& expr,
122 const Type& type) {
123 ExpressionArray ctorArgs;
124 ctorArgs.push_back(expr.clone());
125 std::unique_ptr<Expression> ctor = Constructor::Convert(context, expr.fOffset, type,
126 std::move(ctorArgs));
127 SkASSERT(ctor);
128 return ctor;
129}
130
John Stiles2938eea2021-04-01 18:58:25 -0400131static ConstructorSplat splat_scalar(const Expression& scalar, const Type& type) {
John Stiles508eba72021-01-11 13:07:47 -0500132 SkASSERT(type.isVector());
133 SkASSERT(type.componentType() == scalar.type());
134
John Stiles2938eea2021-04-01 18:58:25 -0400135 // Use a constructor to splat the scalar expression across a vector.
136 return ConstructorSplat{scalar.fOffset, type, scalar.clone()};
John Stiles508eba72021-01-11 13:07:47 -0500137}
138
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500139bool ConstantFolder::GetConstantInt(const Expression& value, SKSL_INT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500140 const Expression* expr = GetConstantValueForVariable(value);
John Stiles7591d4b2021-09-13 13:32:06 -0400141 if (!expr->isIntLiteral()) {
John Stilese80e1692021-03-02 17:02:25 -0500142 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500143 }
John Stiles7591d4b2021-09-13 13:32:06 -0400144 *out = expr->as<Literal>().intValue();
John Stilese80e1692021-03-02 17:02:25 -0500145 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500146}
147
Brian Osman448b2d52021-09-23 11:36:15 -0400148bool ConstantFolder::GetConstantValue(const Expression& value, double* out) {
149 const Expression* expr = GetConstantValueForVariable(value);
150 if (!expr->is<Literal>()) {
151 return false;
152 }
153 *out = expr->as<Literal>().value();
154 return true;
155}
156
John Stiles7591d4b2021-09-13 13:32:06 -0400157static bool is_constant_scalar_value(const Expression& inExpr, double match) {
John Stiles8f440b42021-03-05 16:48:56 -0500158 const Expression* expr = ConstantFolder::GetConstantValueForVariable(inExpr);
John Stiles7591d4b2021-09-13 13:32:06 -0400159 return (expr->is<Literal>() && expr->as<Literal>().value() == match);
John Stiles8f440b42021-03-05 16:48:56 -0500160}
161
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500162static bool contains_constant_zero(const Expression& expr) {
John Stiles55dc5c82021-08-19 18:15:49 -0400163 int numSlots = expr.type().slotCount();
164 for (int index = 0; index < numSlots; ++index) {
165 const Expression* subexpr = expr.getConstantSubexpression(index);
John Stiles7591d4b2021-09-13 13:32:06 -0400166 if (subexpr && is_constant_scalar_value(*subexpr, 0.0)) {
John Stiles55dc5c82021-08-19 18:15:49 -0400167 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500168 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500169 }
John Stiles55dc5c82021-08-19 18:15:49 -0400170 return false;
John Stiles8f440b42021-03-05 16:48:56 -0500171}
172
John Stiles7591d4b2021-09-13 13:32:06 -0400173static bool is_constant_value(const Expression& expr, double value) {
John Stiles55dc5c82021-08-19 18:15:49 -0400174 int numSlots = expr.type().slotCount();
175 for (int index = 0; index < numSlots; ++index) {
176 const Expression* subexpr = expr.getConstantSubexpression(index);
177 if (!subexpr || !is_constant_scalar_value(*subexpr, value)) {
178 return false;
John Stiles8f440b42021-03-05 16:48:56 -0500179 }
John Stiles8f440b42021-03-05 16:48:56 -0500180 }
John Stiles55dc5c82021-08-19 18:15:49 -0400181 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500182}
183
John Stiles45990502021-02-16 10:55:27 -0500184bool ConstantFolder::ErrorOnDivideByZero(const Context& context, int offset, Operator op,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500185 const Expression& right) {
John Stiles45990502021-02-16 10:55:27 -0500186 switch (op.kind()) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500187 case Token::Kind::TK_SLASH:
188 case Token::Kind::TK_SLASHEQ:
189 case Token::Kind::TK_PERCENT:
190 case Token::Kind::TK_PERCENTEQ:
191 if (contains_constant_zero(right)) {
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400192 context.fErrors->error(offset, "division by zero");
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500193 return true;
194 }
195 return false;
196 default:
197 return false;
198 }
199}
200
John Stilese80e1692021-03-02 17:02:25 -0500201const Expression* ConstantFolder::GetConstantValueForVariable(const Expression& inExpr) {
202 for (const Expression* expr = &inExpr;;) {
203 if (!expr->is<VariableReference>()) {
204 break;
205 }
206 const VariableReference& varRef = expr->as<VariableReference>();
207 if (varRef.refKind() != VariableRefKind::kRead) {
208 break;
209 }
210 const Variable& var = *varRef.variable();
211 if (!(var.modifiers().fFlags & Modifiers::kConst_Flag)) {
212 break;
213 }
214 expr = var.initialValue();
John Stilesca65f8f2021-03-29 17:14:39 -0400215 if (!expr) {
John Stiles3f373222021-08-23 13:52:17 -0400216 // Function parameters can be const but won't have an initial value.
John Stilesca65f8f2021-03-29 17:14:39 -0400217 break;
218 }
John Stilese80e1692021-03-02 17:02:25 -0500219 if (expr->isCompileTimeConstant()) {
220 return expr;
221 }
John Stilese80e1692021-03-02 17:02:25 -0500222 }
223 // We didn't find a compile-time constant at the end. Return the expression as-is.
224 return &inExpr;
225}
226
John Stilesffba5242021-05-07 10:50:22 -0400227std::unique_ptr<Expression> ConstantFolder::MakeConstantValueForVariable(
228 std::unique_ptr<Expression> expr) {
229 const Expression* constantExpr = GetConstantValueForVariable(*expr);
230 if (constantExpr != expr.get()) {
231 expr = constantExpr->clone();
232 }
233 return expr;
234}
235
John Stiles8f440b42021-03-05 16:48:56 -0500236static std::unique_ptr<Expression> simplify_no_op_arithmetic(const Context& context,
237 const Expression& left,
238 Operator op,
239 const Expression& right,
240 const Type& resultType) {
241 switch (op.kind()) {
242 case Token::Kind::TK_PLUS:
243 if (is_constant_value(right, 0.0)) { // x + 0
244 return cast_expression(context, left, resultType);
245 }
246 if (is_constant_value(left, 0.0)) { // 0 + x
247 return cast_expression(context, right, resultType);
248 }
249 break;
250
251 case Token::Kind::TK_STAR:
252 if (is_constant_value(right, 1.0)) { // x * 1
253 return cast_expression(context, left, resultType);
254 }
255 if (is_constant_value(left, 1.0)) { // 1 * x
256 return cast_expression(context, right, resultType);
257 }
258 if (is_constant_value(right, 0.0) && !left.hasSideEffects()) { // x * 0
259 return cast_expression(context, right, resultType);
260 }
261 if (is_constant_value(left, 0.0) && !right.hasSideEffects()) { // 0 * x
262 return cast_expression(context, left, resultType);
263 }
264 break;
265
266 case Token::Kind::TK_MINUS:
267 if (is_constant_value(right, 0.0)) { // x - 0
268 return cast_expression(context, left, resultType);
269 }
270 if (is_constant_value(left, 0.0)) { // 0 - x (to `-x`)
271 return PrefixExpression::Make(context, Token::Kind::TK_MINUS,
272 cast_expression(context, right, resultType));
273 }
274 break;
275
276 case Token::Kind::TK_SLASH:
277 if (is_constant_value(right, 1.0)) { // x / 1
278 return cast_expression(context, left, resultType);
279 }
John Stiles8f440b42021-03-05 16:48:56 -0500280 break;
281
282 case Token::Kind::TK_PLUSEQ:
283 case Token::Kind::TK_MINUSEQ:
284 if (is_constant_value(right, 0.0)) { // x += 0, x -= 0
285 std::unique_ptr<Expression> result = cast_expression(context, left, resultType);
John Stilesbb8cf582021-08-26 23:34:59 -0400286 Analysis::UpdateVariableRefKind(result.get(), VariableRefKind::kRead);
John Stiles8f440b42021-03-05 16:48:56 -0500287 return result;
288 }
289 break;
290
291 case Token::Kind::TK_STAREQ:
292 case Token::Kind::TK_SLASHEQ:
293 if (is_constant_value(right, 1.0)) { // x *= 1, x /= 1
294 std::unique_ptr<Expression> result = cast_expression(context, left, resultType);
John Stilesbb8cf582021-08-26 23:34:59 -0400295 Analysis::UpdateVariableRefKind(result.get(), VariableRefKind::kRead);
John Stiles8f440b42021-03-05 16:48:56 -0500296 return result;
297 }
298 break;
299
300 default:
301 break;
302 }
303
304 return nullptr;
305}
306
John Stilesafaddc92021-05-26 22:21:42 -0400307template <typename T>
308static std::unique_ptr<Expression> fold_float_expression(int offset,
309 T result,
310 const Type* resultType) {
311 // If constant-folding this expression would generate a NaN/infinite result, leave it as-is.
John Stiles35981292021-05-27 12:26:37 -0400312 if constexpr (!std::is_same<T, bool>::value) {
John Stilesafaddc92021-05-26 22:21:42 -0400313 if (!std::isfinite(result)) {
314 return nullptr;
315 }
316 }
317
John Stiles7591d4b2021-09-13 13:32:06 -0400318 return Literal::Make(offset, result, resultType);
John Stilesafaddc92021-05-26 22:21:42 -0400319}
320
John Stiles35981292021-05-27 12:26:37 -0400321template <typename T>
322static std::unique_ptr<Expression> fold_int_expression(int offset,
323 T result,
324 const Type* resultType) {
325 // If constant-folding this expression would overflow the result type, leave it as-is.
326 if constexpr (!std::is_same<T, bool>::value) {
327 if (result < resultType->minimumValue() || result > resultType->maximumValue()) {
328 return nullptr;
329 }
330 }
331
John Stiles7591d4b2021-09-13 13:32:06 -0400332 return Literal::Make(offset, result, resultType);
John Stiles35981292021-05-27 12:26:37 -0400333}
334
John Stilesdc8ec312021-01-11 11:05:21 -0500335std::unique_ptr<Expression> ConstantFolder::Simplify(const Context& context,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500336 int offset,
John Stilese80e1692021-03-02 17:02:25 -0500337 const Expression& leftExpr,
John Stiles45990502021-02-16 10:55:27 -0500338 Operator op,
John Stiles8f440b42021-03-05 16:48:56 -0500339 const Expression& rightExpr,
340 const Type& resultType) {
John Stilesbe6cbf32021-03-09 20:15:17 -0500341 // When optimization is enabled, replace constant variables with trivial initial-values.
342 const Expression* left;
343 const Expression* right;
344 if (context.fConfig->fSettings.fOptimize) {
345 left = GetConstantValueForVariable(leftExpr);
346 right = GetConstantValueForVariable(rightExpr);
347 } else {
348 left = &leftExpr;
349 right = &rightExpr;
350 }
John Stilese80e1692021-03-02 17:02:25 -0500351
John Stilesabc3b782021-02-05 10:07:19 -0500352 // If this is the comma operator, the left side is evaluated but not otherwise used in any way.
353 // So if the left side has no side effects, it can just be eliminated entirely.
John Stilese80e1692021-03-02 17:02:25 -0500354 if (op.kind() == Token::Kind::TK_COMMA && !left->hasSideEffects()) {
355 return right->clone();
John Stilesabc3b782021-02-05 10:07:19 -0500356 }
357
John Stiles95d0bad2021-03-01 17:02:28 -0500358 // If this is the assignment operator, and both sides are the same trivial expression, this is
359 // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`).
360 // This can happen when other parts of the assignment are optimized away.
John Stiles5676c572021-03-08 17:10:52 -0500361 if (op.kind() == Token::Kind::TK_EQ && Analysis::IsSameExpressionTree(*left, *right)) {
John Stilese80e1692021-03-02 17:02:25 -0500362 return right->clone();
John Stiles95d0bad2021-03-01 17:02:28 -0500363 }
364
John Stiles8a9da732021-01-20 14:32:33 -0500365 // Simplify the expression when both sides are constant Boolean literals.
John Stiles7591d4b2021-09-13 13:32:06 -0400366 if (left->isBoolLiteral() && right->isBoolLiteral()) {
367 bool leftVal = left->as<Literal>().boolValue();
368 bool rightVal = right->as<Literal>().boolValue();
John Stilesdc8ec312021-01-11 11:05:21 -0500369 bool result;
John Stiles45990502021-02-16 10:55:27 -0500370 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500371 case Token::Kind::TK_LOGICALAND: result = leftVal && rightVal; break;
372 case Token::Kind::TK_LOGICALOR: result = leftVal || rightVal; break;
373 case Token::Kind::TK_LOGICALXOR: result = leftVal ^ rightVal; break;
John Stiles26fdcbb2021-01-19 19:00:31 -0500374 case Token::Kind::TK_EQEQ: result = leftVal == rightVal; break;
375 case Token::Kind::TK_NEQ: result = leftVal != rightVal; break;
John Stilesdc8ec312021-01-11 11:05:21 -0500376 default: return nullptr;
377 }
John Stiles7591d4b2021-09-13 13:32:06 -0400378 return Literal::MakeBool(context, offset, result);
John Stilesdc8ec312021-01-11 11:05:21 -0500379 }
380
John Stiles8a9da732021-01-20 14:32:33 -0500381 // If the left side is a Boolean literal, apply short-circuit optimizations.
John Stiles7591d4b2021-09-13 13:32:06 -0400382 if (left->isBoolLiteral()) {
John Stilese80e1692021-03-02 17:02:25 -0500383 return short_circuit_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500384 }
385
386 // If the right side is a Boolean literal...
John Stiles7591d4b2021-09-13 13:32:06 -0400387 if (right->isBoolLiteral()) {
John Stiles8a9da732021-01-20 14:32:33 -0500388 // ... and the left side has no side effects...
John Stilese80e1692021-03-02 17:02:25 -0500389 if (!left->hasSideEffects()) {
John Stiles8a9da732021-01-20 14:32:33 -0500390 // We can reverse the expressions and short-circuit optimizations are still valid.
John Stilese80e1692021-03-02 17:02:25 -0500391 return short_circuit_boolean(*right, op, *left);
John Stiles8a9da732021-01-20 14:32:33 -0500392 }
393
394 // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions.
John Stilese80e1692021-03-02 17:02:25 -0500395 return eliminate_no_op_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500396 }
397
John Stiles5676c572021-03-08 17:10:52 -0500398 if (op.kind() == Token::Kind::TK_EQEQ && Analysis::IsSameExpressionTree(*left, *right)) {
399 // With == comparison, if both sides are the same trivial expression, this is self-
400 // comparison and is always true. (We are not concerned with NaN.)
John Stiles7591d4b2021-09-13 13:32:06 -0400401 return Literal::MakeBool(context, leftExpr.fOffset, /*value=*/true);
John Stiles5676c572021-03-08 17:10:52 -0500402 }
403
404 if (op.kind() == Token::Kind::TK_NEQ && Analysis::IsSameExpressionTree(*left, *right)) {
405 // With != comparison, if both sides are the same trivial expression, this is self-
406 // comparison and is always false. (We are not concerned with NaN.)
John Stiles7591d4b2021-09-13 13:32:06 -0400407 return Literal::MakeBool(context, leftExpr.fOffset, /*value=*/false);
John Stiles5676c572021-03-08 17:10:52 -0500408 }
409
John Stilese80e1692021-03-02 17:02:25 -0500410 if (ErrorOnDivideByZero(context, offset, op, *right)) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500411 return nullptr;
412 }
413
John Stiles8f440b42021-03-05 16:48:56 -0500414 // Optimize away no-op arithmetic like `x * 1`, `x *= 1`, `x + 0`, `x * 0`, `0 / x`, etc.
415 const Type& leftType = left->type();
416 const Type& rightType = right->type();
417 if ((leftType.isScalar() || leftType.isVector()) &&
418 (rightType.isScalar() || rightType.isVector())) {
419 std::unique_ptr<Expression> expr = simplify_no_op_arithmetic(context, *left, op, *right,
420 resultType);
421 if (expr) {
422 return expr;
423 }
424 }
425
426 // Other than the cases above, constant folding requires both sides to be constant.
John Stilese80e1692021-03-02 17:02:25 -0500427 if (!left->isCompileTimeConstant() || !right->isCompileTimeConstant()) {
John Stiles8a9da732021-01-20 14:32:33 -0500428 return nullptr;
429 }
430
John Stilesdc8ec312021-01-11 11:05:21 -0500431 // Note that we expressly do not worry about precision and overflow here -- we use the maximum
432 // precision to calculate the results and hope the result makes sense.
John Stiles9ce80f72021-03-11 22:35:19 -0500433 // TODO(skia:10932): detect and handle integer overflow properly.
Brian Osman21d2b6a2021-02-01 13:48:50 -0500434 using SKSL_UINT = uint64_t;
John Stiles7591d4b2021-09-13 13:32:06 -0400435 if (left->isIntLiteral() && right->isIntLiteral()) {
436 SKSL_INT leftVal = left->as<Literal>().intValue();
437 SKSL_INT rightVal = right->as<Literal>().intValue();
John Stiles35981292021-05-27 12:26:37 -0400438
439 #define RESULT(Op) fold_int_expression(offset, \
440 (SKSL_INT)(leftVal) Op (SKSL_INT)(rightVal), &resultType)
441 #define URESULT(Op) fold_int_expression(offset, \
442 (SKSL_INT)((SKSL_UINT)(leftVal) Op (SKSL_UINT)(rightVal)), &resultType)
John Stiles45990502021-02-16 10:55:27 -0500443 switch (op.kind()) {
John Stiles35981292021-05-27 12:26:37 -0400444 case Token::Kind::TK_PLUS: return URESULT(+);
445 case Token::Kind::TK_MINUS: return URESULT(-);
446 case Token::Kind::TK_STAR: return URESULT(*);
John Stilesdc8ec312021-01-11 11:05:21 -0500447 case Token::Kind::TK_SLASH:
448 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400449 context.fErrors->error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500450 return nullptr;
451 }
John Stiles35981292021-05-27 12:26:37 -0400452 return RESULT(/);
John Stilesdc8ec312021-01-11 11:05:21 -0500453 case Token::Kind::TK_PERCENT:
454 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400455 context.fErrors->error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500456 return nullptr;
457 }
John Stiles35981292021-05-27 12:26:37 -0400458 return RESULT(%);
459 case Token::Kind::TK_BITWISEAND: return RESULT(&);
460 case Token::Kind::TK_BITWISEOR: return RESULT(|);
461 case Token::Kind::TK_BITWISEXOR: return RESULT(^);
462 case Token::Kind::TK_EQEQ: return RESULT(==);
463 case Token::Kind::TK_NEQ: return RESULT(!=);
464 case Token::Kind::TK_GT: return RESULT(>);
465 case Token::Kind::TK_GTEQ: return RESULT(>=);
466 case Token::Kind::TK_LT: return RESULT(<);
467 case Token::Kind::TK_LTEQ: return RESULT(<=);
John Stilesdc8ec312021-01-11 11:05:21 -0500468 case Token::Kind::TK_SHL:
469 if (rightVal >= 0 && rightVal <= 31) {
Brian Osmana7eb6812021-02-01 11:43:05 -0500470 // Left-shifting a negative (or really, any signed) value is undefined behavior
471 // in C++, but not GLSL. Do the shift on unsigned values, to avoid UBSAN.
John Stiles35981292021-05-27 12:26:37 -0400472 return URESULT(<<);
John Stilesdc8ec312021-01-11 11:05:21 -0500473 }
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400474 context.fErrors->error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500475 return nullptr;
476 case Token::Kind::TK_SHR:
477 if (rightVal >= 0 && rightVal <= 31) {
John Stiles35981292021-05-27 12:26:37 -0400478 return RESULT(>>);
John Stilesdc8ec312021-01-11 11:05:21 -0500479 }
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400480 context.fErrors->error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500481 return nullptr;
482
483 default:
484 return nullptr;
485 }
John Stilesafaddc92021-05-26 22:21:42 -0400486 #undef RESULT
487 #undef URESULT
John Stilesdc8ec312021-01-11 11:05:21 -0500488 }
489
490 // Perform constant folding on pairs of floating-point literals.
John Stiles7591d4b2021-09-13 13:32:06 -0400491 if (left->isFloatLiteral() && right->isFloatLiteral()) {
492 SKSL_FLOAT leftVal = left->as<Literal>().floatValue();
493 SKSL_FLOAT rightVal = right->as<Literal>().floatValue();
John Stilesafaddc92021-05-26 22:21:42 -0400494
495 #define RESULT(Op) fold_float_expression(offset, leftVal Op rightVal, &resultType)
John Stiles45990502021-02-16 10:55:27 -0500496 switch (op.kind()) {
John Stilesafaddc92021-05-26 22:21:42 -0400497 case Token::Kind::TK_PLUS: return RESULT(+);
498 case Token::Kind::TK_MINUS: return RESULT(-);
499 case Token::Kind::TK_STAR: return RESULT(*);
500 case Token::Kind::TK_SLASH: return RESULT(/);
501 case Token::Kind::TK_EQEQ: return RESULT(==);
502 case Token::Kind::TK_NEQ: return RESULT(!=);
503 case Token::Kind::TK_GT: return RESULT(>);
504 case Token::Kind::TK_GTEQ: return RESULT(>=);
505 case Token::Kind::TK_LT: return RESULT(<);
506 case Token::Kind::TK_LTEQ: return RESULT(<=);
507 default: return nullptr;
John Stilesdc8ec312021-01-11 11:05:21 -0500508 }
John Stilesafaddc92021-05-26 22:21:42 -0400509 #undef RESULT
John Stilesdc8ec312021-01-11 11:05:21 -0500510 }
511
512 // Perform constant folding on pairs of vectors.
John Stilesdc8ec312021-01-11 11:05:21 -0500513 if (leftType.isVector() && leftType == rightType) {
514 if (leftType.componentType().isFloat()) {
John Stiles7591d4b2021-09-13 13:32:06 -0400515 return simplify_vector(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500516 }
517 if (leftType.componentType().isInteger()) {
John Stiles7591d4b2021-09-13 13:32:06 -0400518 return simplify_vector(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500519 }
John Stiles878d8fb2021-08-24 11:33:27 -0400520 if (leftType.componentType().isBoolean()) {
521 return simplify_vector_equality(context, *left, op, *right);
522 }
John Stilesdc8ec312021-01-11 11:05:21 -0500523 return nullptr;
524 }
525
John Stiles508eba72021-01-11 13:07:47 -0500526 // Perform constant folding on vectors against scalars, e.g.: half4(2) + 2
527 if (leftType.isVector() && leftType.componentType() == rightType) {
528 if (rightType.isFloat()) {
John Stiles7591d4b2021-09-13 13:32:06 -0400529 return simplify_vector(context, *left, op, splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500530 }
531 if (rightType.isInteger()) {
John Stiles7591d4b2021-09-13 13:32:06 -0400532 return simplify_vector(context, *left, op, splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500533 }
John Stiles878d8fb2021-08-24 11:33:27 -0400534 if (rightType.isBoolean()) {
535 return simplify_vector_equality(context, *left, op,
536 splat_scalar(*right, left->type()));
537 }
John Stiles508eba72021-01-11 13:07:47 -0500538 return nullptr;
539 }
540
541 // Perform constant folding on scalars against vectors, e.g.: 2 + half4(2)
542 if (rightType.isVector() && rightType.componentType() == leftType) {
543 if (leftType.isFloat()) {
John Stiles7591d4b2021-09-13 13:32:06 -0400544 return simplify_vector(context, splat_scalar(*left, right->type()), op, *right);
John Stiles508eba72021-01-11 13:07:47 -0500545 }
546 if (leftType.isInteger()) {
John Stiles7591d4b2021-09-13 13:32:06 -0400547 return simplify_vector(context, splat_scalar(*left, right->type()), op, *right);
John Stiles508eba72021-01-11 13:07:47 -0500548 }
John Stiles878d8fb2021-08-24 11:33:27 -0400549 if (leftType.isBoolean()) {
550 return simplify_vector_equality(context, splat_scalar(*left, right->type()),
551 op, *right);
552 }
John Stiles508eba72021-01-11 13:07:47 -0500553 return nullptr;
554 }
555
John Stiles22a54542021-03-31 11:33:48 -0400556 // Perform constant folding on pairs of matrices or arrays.
557 if ((leftType.isMatrix() && rightType.isMatrix()) ||
558 (leftType.isArray() && rightType.isArray())) {
John Stilesdc8ec312021-01-11 11:05:21 -0500559 bool equality;
John Stiles45990502021-02-16 10:55:27 -0500560 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500561 case Token::Kind::TK_EQEQ:
562 equality = true;
563 break;
564 case Token::Kind::TK_NEQ:
565 equality = false;
566 break;
567 default:
568 return nullptr;
569 }
570
John Stilese80e1692021-03-02 17:02:25 -0500571 switch (left->compareConstant(*right)) {
John Stilesdc8ec312021-01-11 11:05:21 -0500572 case Expression::ComparisonResult::kNotEqual:
573 equality = !equality;
574 [[fallthrough]];
575
576 case Expression::ComparisonResult::kEqual:
John Stiles7591d4b2021-09-13 13:32:06 -0400577 return Literal::MakeBool(context, offset, equality);
John Stilesdc8ec312021-01-11 11:05:21 -0500578
579 case Expression::ComparisonResult::kUnknown:
580 return nullptr;
581 }
582 }
583
584 // We aren't able to constant-fold.
John Stilesdc8ec312021-01-11 11:05:21 -0500585 return nullptr;
586}
587
588} // namespace SkSL