blob: 3de16afe05d3441f8bfe9a2c95c20941c74fdacb [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"
15#include "src/sksl/ir/SkSLBoolLiteral.h"
16#include "src/sksl/ir/SkSLConstructor.h"
John Stilesb0ce42d2021-04-09 10:39:42 -040017#include "src/sksl/ir/SkSLConstructorCompound.h"
John Stiles2938eea2021-04-01 18:58:25 -040018#include "src/sksl/ir/SkSLConstructorSplat.h"
John Stilesdc8ec312021-01-11 11:05:21 -050019#include "src/sksl/ir/SkSLExpression.h"
20#include "src/sksl/ir/SkSLFloatLiteral.h"
21#include "src/sksl/ir/SkSLIntLiteral.h"
John Stiles8f440b42021-03-05 16:48:56 -050022#include "src/sksl/ir/SkSLPrefixExpression.h"
John Stilesdc8ec312021-01-11 11:05:21 -050023#include "src/sksl/ir/SkSLType.h"
24#include "src/sksl/ir/SkSLVariable.h"
25#include "src/sksl/ir/SkSLVariableReference.h"
26
27namespace SkSL {
28
John Stiles8a9da732021-01-20 14:32:33 -050029static std::unique_ptr<Expression> eliminate_no_op_boolean(const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050030 Operator op,
John Stiles8a9da732021-01-20 14:32:33 -050031 const Expression& right) {
32 SkASSERT(right.is<BoolLiteral>());
33 bool rightVal = right.as<BoolLiteral>().value();
34
35 // Detect no-op Boolean expressions and optimize them away.
John Stiles45990502021-02-16 10:55:27 -050036 if ((op.kind() == Token::Kind::TK_LOGICALAND && rightVal) || // (expr && true) -> (expr)
37 (op.kind() == Token::Kind::TK_LOGICALOR && !rightVal) || // (expr || false) -> (expr)
38 (op.kind() == Token::Kind::TK_LOGICALXOR && !rightVal) || // (expr ^^ false) -> (expr)
39 (op.kind() == Token::Kind::TK_EQEQ && rightVal) || // (expr == true) -> (expr)
40 (op.kind() == Token::Kind::TK_NEQ && !rightVal)) { // (expr != false) -> (expr)
John Stiles8a9da732021-01-20 14:32:33 -050041
42 return left.clone();
43 }
44
45 return nullptr;
46}
47
John Stilesdc8ec312021-01-11 11:05:21 -050048static std::unique_ptr<Expression> short_circuit_boolean(const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050049 Operator op,
John Stilesdc8ec312021-01-11 11:05:21 -050050 const Expression& right) {
51 SkASSERT(left.is<BoolLiteral>());
52 bool leftVal = left.as<BoolLiteral>().value();
53
John Stiles8a9da732021-01-20 14:32:33 -050054 // When the literal is on the left, we can sometimes eliminate the other expression entirely.
John Stiles45990502021-02-16 10:55:27 -050055 if ((op.kind() == Token::Kind::TK_LOGICALAND && !leftVal) || // (false && expr) -> (false)
56 (op.kind() == Token::Kind::TK_LOGICALOR && leftVal)) { // (true || expr) -> (true)
John Stiles8a9da732021-01-20 14:32:33 -050057
58 return left.clone();
John Stilesdc8ec312021-01-11 11:05:21 -050059 }
60
John Stiles8a9da732021-01-20 14:32:33 -050061 // We can't eliminate the right-side expression via short-circuit, but we might still be able to
62 // simplify away a no-op expression.
63 return eliminate_no_op_boolean(right, op, left);
John Stilesdc8ec312021-01-11 11:05:21 -050064}
65
John Stiles878d8fb2021-08-24 11:33:27 -040066static std::unique_ptr<Expression> simplify_vector_equality(const Context& context,
67 const Expression& left,
68 Operator op,
69 const Expression& right) {
70 if (op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ) {
71 bool equality = (op.kind() == Token::Kind::TK_EQEQ);
72
73 switch (left.compareConstant(right)) {
74 case Expression::ComparisonResult::kNotEqual:
75 equality = !equality;
76 [[fallthrough]];
77
78 case Expression::ComparisonResult::kEqual:
79 return BoolLiteral::Make(context, left.fOffset, equality);
80
81 case Expression::ComparisonResult::kUnknown:
82 break;
83 }
84 }
85 return nullptr;
86}
87
Brian Osman21d2b6a2021-02-01 13:48:50 -050088// 'T' is the actual stored type of the literal data (SKSL_FLOAT or SKSL_INT).
89// 'U' is an unsigned version of that, used to perform addition, subtraction, and multiplication,
90// to avoid signed-integer overflow errors. This mimics the use of URESULT vs. RESULT when doing
91// scalar folding in Simplify, later in this file.
92template <typename T, typename U = T>
John Stilesdc8ec312021-01-11 11:05:21 -050093static std::unique_ptr<Expression> simplify_vector(const Context& context,
John Stilesdc8ec312021-01-11 11:05:21 -050094 const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050095 Operator op,
John Stilesdc8ec312021-01-11 11:05:21 -050096 const Expression& right) {
John Stiles508eba72021-01-11 13:07:47 -050097 SkASSERT(left.type().isVector());
John Stilesdc8ec312021-01-11 11:05:21 -050098 SkASSERT(left.type() == right.type());
99 const Type& type = left.type();
100
John Stiles878d8fb2021-08-24 11:33:27 -0400101 // Handle equality operations: == !=
102 if (std::unique_ptr<Expression> result = simplify_vector_equality(context, left, op, right)) {
103 return result;
John Stilesdc8ec312021-01-11 11:05:21 -0500104 }
105
106 // Handle floating-point arithmetic: + - * /
John Stiles54f00492021-02-19 11:46:10 -0500107 const auto vectorComponentwiseFold = [&](auto foldFn) -> std::unique_ptr<Expression> {
John Stilesdc8ec312021-01-11 11:05:21 -0500108 const Type& componentType = type.componentType();
109 ExpressionArray args;
110 args.reserve_back(type.columns());
111 for (int i = 0; i < type.columns(); i++) {
John Stiles21a50ec2021-04-06 14:49:36 -0400112 U value = foldFn(left.getConstantSubexpression(i)->as<Literal<T>>().value(),
113 right.getConstantSubexpression(i)->as<Literal<T>>().value());
John Stiles9ce80f72021-03-11 22:35:19 -0500114 args.push_back(Literal<T>::Make(left.fOffset, value, &componentType));
John Stilesdc8ec312021-01-11 11:05:21 -0500115 }
John Stilesb0ce42d2021-04-09 10:39:42 -0400116 return ConstructorCompound::Make(context, left.fOffset, type, std::move(args));
John Stilesdc8ec312021-01-11 11:05:21 -0500117 };
118
John Stiles45990502021-02-16 10:55:27 -0500119 switch (op.kind()) {
Brian Osman21d2b6a2021-02-01 13:48:50 -0500120 case Token::Kind::TK_PLUS: return vectorComponentwiseFold([](U a, U b) { return a + b; });
121 case Token::Kind::TK_MINUS: return vectorComponentwiseFold([](U a, U b) { return a - b; });
122 case Token::Kind::TK_STAR: return vectorComponentwiseFold([](U a, U b) { return a * b; });
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500123 case Token::Kind::TK_SLASH: return vectorComponentwiseFold([](T a, T b) { return a / b; });
John Stilesdc8ec312021-01-11 11:05:21 -0500124 default:
125 return nullptr;
126 }
127}
128
John Stiles8f440b42021-03-05 16:48:56 -0500129static std::unique_ptr<Expression> cast_expression(const Context& context,
130 const Expression& expr,
131 const Type& type) {
132 ExpressionArray ctorArgs;
133 ctorArgs.push_back(expr.clone());
134 std::unique_ptr<Expression> ctor = Constructor::Convert(context, expr.fOffset, type,
135 std::move(ctorArgs));
136 SkASSERT(ctor);
137 return ctor;
138}
139
John Stiles2938eea2021-04-01 18:58:25 -0400140static ConstructorSplat splat_scalar(const Expression& scalar, const Type& type) {
John Stiles508eba72021-01-11 13:07:47 -0500141 SkASSERT(type.isVector());
142 SkASSERT(type.componentType() == scalar.type());
143
John Stiles2938eea2021-04-01 18:58:25 -0400144 // Use a constructor to splat the scalar expression across a vector.
145 return ConstructorSplat{scalar.fOffset, type, scalar.clone()};
John Stiles508eba72021-01-11 13:07:47 -0500146}
147
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500148bool ConstantFolder::GetConstantInt(const Expression& value, SKSL_INT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500149 const Expression* expr = GetConstantValueForVariable(value);
150 if (!expr->is<IntLiteral>()) {
151 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500152 }
John Stilese80e1692021-03-02 17:02:25 -0500153 *out = expr->as<IntLiteral>().value();
154 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500155}
156
157bool ConstantFolder::GetConstantFloat(const Expression& value, SKSL_FLOAT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500158 const Expression* expr = GetConstantValueForVariable(value);
159 if (!expr->is<FloatLiteral>()) {
160 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500161 }
John Stilese80e1692021-03-02 17:02:25 -0500162 *out = expr->as<FloatLiteral>().value();
163 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500164}
165
John Stiles8f440b42021-03-05 16:48:56 -0500166static bool is_constant_scalar_value(const Expression& inExpr, float match) {
167 const Expression* expr = ConstantFolder::GetConstantValueForVariable(inExpr);
168 return (expr->is<IntLiteral>() && expr->as<IntLiteral>().value() == match) ||
169 (expr->is<FloatLiteral>() && expr->as<FloatLiteral>().value() == match);
170}
171
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500172static bool contains_constant_zero(const Expression& expr) {
John Stiles55dc5c82021-08-19 18:15:49 -0400173 int numSlots = expr.type().slotCount();
174 for (int index = 0; index < numSlots; ++index) {
175 const Expression* subexpr = expr.getConstantSubexpression(index);
176 if (subexpr && is_constant_scalar_value(*subexpr, 0.0f)) {
177 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500178 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500179 }
John Stiles55dc5c82021-08-19 18:15:49 -0400180 return false;
John Stiles8f440b42021-03-05 16:48:56 -0500181}
182
183static bool is_constant_value(const Expression& expr, float value) {
John Stiles55dc5c82021-08-19 18:15:49 -0400184 int numSlots = expr.type().slotCount();
185 for (int index = 0; index < numSlots; ++index) {
186 const Expression* subexpr = expr.getConstantSubexpression(index);
187 if (!subexpr || !is_constant_scalar_value(*subexpr, value)) {
188 return false;
John Stiles8f440b42021-03-05 16:48:56 -0500189 }
John Stiles8f440b42021-03-05 16:48:56 -0500190 }
John Stiles55dc5c82021-08-19 18:15:49 -0400191 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500192}
193
John Stiles45990502021-02-16 10:55:27 -0500194bool ConstantFolder::ErrorOnDivideByZero(const Context& context, int offset, Operator op,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500195 const Expression& right) {
John Stiles45990502021-02-16 10:55:27 -0500196 switch (op.kind()) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500197 case Token::Kind::TK_SLASH:
198 case Token::Kind::TK_SLASHEQ:
199 case Token::Kind::TK_PERCENT:
200 case Token::Kind::TK_PERCENTEQ:
201 if (contains_constant_zero(right)) {
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400202 context.fErrors->error(offset, "division by zero");
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500203 return true;
204 }
205 return false;
206 default:
207 return false;
208 }
209}
210
John Stilese80e1692021-03-02 17:02:25 -0500211const Expression* ConstantFolder::GetConstantValueForVariable(const Expression& inExpr) {
212 for (const Expression* expr = &inExpr;;) {
213 if (!expr->is<VariableReference>()) {
214 break;
215 }
216 const VariableReference& varRef = expr->as<VariableReference>();
217 if (varRef.refKind() != VariableRefKind::kRead) {
218 break;
219 }
220 const Variable& var = *varRef.variable();
221 if (!(var.modifiers().fFlags & Modifiers::kConst_Flag)) {
222 break;
223 }
224 expr = var.initialValue();
John Stilesca65f8f2021-03-29 17:14:39 -0400225 if (!expr) {
John Stiles3f373222021-08-23 13:52:17 -0400226 // Function parameters can be const but won't have an initial value.
John Stilesca65f8f2021-03-29 17:14:39 -0400227 break;
228 }
John Stilese80e1692021-03-02 17:02:25 -0500229 if (expr->isCompileTimeConstant()) {
230 return expr;
231 }
John Stilese80e1692021-03-02 17:02:25 -0500232 }
233 // We didn't find a compile-time constant at the end. Return the expression as-is.
234 return &inExpr;
235}
236
John Stilesffba5242021-05-07 10:50:22 -0400237std::unique_ptr<Expression> ConstantFolder::MakeConstantValueForVariable(
238 std::unique_ptr<Expression> expr) {
239 const Expression* constantExpr = GetConstantValueForVariable(*expr);
240 if (constantExpr != expr.get()) {
241 expr = constantExpr->clone();
242 }
243 return expr;
244}
245
John Stiles8f440b42021-03-05 16:48:56 -0500246static std::unique_ptr<Expression> simplify_no_op_arithmetic(const Context& context,
247 const Expression& left,
248 Operator op,
249 const Expression& right,
250 const Type& resultType) {
251 switch (op.kind()) {
252 case Token::Kind::TK_PLUS:
253 if (is_constant_value(right, 0.0)) { // x + 0
254 return cast_expression(context, left, resultType);
255 }
256 if (is_constant_value(left, 0.0)) { // 0 + x
257 return cast_expression(context, right, resultType);
258 }
259 break;
260
261 case Token::Kind::TK_STAR:
262 if (is_constant_value(right, 1.0)) { // x * 1
263 return cast_expression(context, left, resultType);
264 }
265 if (is_constant_value(left, 1.0)) { // 1 * x
266 return cast_expression(context, right, resultType);
267 }
268 if (is_constant_value(right, 0.0) && !left.hasSideEffects()) { // x * 0
269 return cast_expression(context, right, resultType);
270 }
271 if (is_constant_value(left, 0.0) && !right.hasSideEffects()) { // 0 * x
272 return cast_expression(context, left, resultType);
273 }
274 break;
275
276 case Token::Kind::TK_MINUS:
277 if (is_constant_value(right, 0.0)) { // x - 0
278 return cast_expression(context, left, resultType);
279 }
280 if (is_constant_value(left, 0.0)) { // 0 - x (to `-x`)
281 return PrefixExpression::Make(context, Token::Kind::TK_MINUS,
282 cast_expression(context, right, resultType));
283 }
284 break;
285
286 case Token::Kind::TK_SLASH:
287 if (is_constant_value(right, 1.0)) { // x / 1
288 return cast_expression(context, left, resultType);
289 }
John Stiles8f440b42021-03-05 16:48:56 -0500290 break;
291
292 case Token::Kind::TK_PLUSEQ:
293 case Token::Kind::TK_MINUSEQ:
294 if (is_constant_value(right, 0.0)) { // x += 0, x -= 0
295 std::unique_ptr<Expression> result = cast_expression(context, left, resultType);
296 Analysis::UpdateRefKind(result.get(), VariableRefKind::kRead);
297 return result;
298 }
299 break;
300
301 case Token::Kind::TK_STAREQ:
302 case Token::Kind::TK_SLASHEQ:
303 if (is_constant_value(right, 1.0)) { // x *= 1, x /= 1
304 std::unique_ptr<Expression> result = cast_expression(context, left, resultType);
305 Analysis::UpdateRefKind(result.get(), VariableRefKind::kRead);
306 return result;
307 }
308 break;
309
310 default:
311 break;
312 }
313
314 return nullptr;
315}
316
John Stilesafaddc92021-05-26 22:21:42 -0400317template <typename T>
318static std::unique_ptr<Expression> fold_float_expression(int offset,
319 T result,
320 const Type* resultType) {
321 // If constant-folding this expression would generate a NaN/infinite result, leave it as-is.
John Stiles35981292021-05-27 12:26:37 -0400322 if constexpr (!std::is_same<T, bool>::value) {
John Stilesafaddc92021-05-26 22:21:42 -0400323 if (!std::isfinite(result)) {
324 return nullptr;
325 }
326 }
327
328 return Literal<T>::Make(offset, result, resultType);
329}
330
John Stiles35981292021-05-27 12:26:37 -0400331template <typename T>
332static std::unique_ptr<Expression> fold_int_expression(int offset,
333 T result,
334 const Type* resultType) {
335 // If constant-folding this expression would overflow the result type, leave it as-is.
336 if constexpr (!std::is_same<T, bool>::value) {
337 if (result < resultType->minimumValue() || result > resultType->maximumValue()) {
338 return nullptr;
339 }
340 }
341
342 return Literal<T>::Make(offset, result, resultType);
343}
344
John Stilesdc8ec312021-01-11 11:05:21 -0500345std::unique_ptr<Expression> ConstantFolder::Simplify(const Context& context,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500346 int offset,
John Stilese80e1692021-03-02 17:02:25 -0500347 const Expression& leftExpr,
John Stiles45990502021-02-16 10:55:27 -0500348 Operator op,
John Stiles8f440b42021-03-05 16:48:56 -0500349 const Expression& rightExpr,
350 const Type& resultType) {
John Stilesbe6cbf32021-03-09 20:15:17 -0500351 // When optimization is enabled, replace constant variables with trivial initial-values.
352 const Expression* left;
353 const Expression* right;
354 if (context.fConfig->fSettings.fOptimize) {
355 left = GetConstantValueForVariable(leftExpr);
356 right = GetConstantValueForVariable(rightExpr);
357 } else {
358 left = &leftExpr;
359 right = &rightExpr;
360 }
John Stilese80e1692021-03-02 17:02:25 -0500361
John Stilesabc3b782021-02-05 10:07:19 -0500362 // If this is the comma operator, the left side is evaluated but not otherwise used in any way.
363 // So if the left side has no side effects, it can just be eliminated entirely.
John Stilese80e1692021-03-02 17:02:25 -0500364 if (op.kind() == Token::Kind::TK_COMMA && !left->hasSideEffects()) {
365 return right->clone();
John Stilesabc3b782021-02-05 10:07:19 -0500366 }
367
John Stiles95d0bad2021-03-01 17:02:28 -0500368 // If this is the assignment operator, and both sides are the same trivial expression, this is
369 // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`).
370 // This can happen when other parts of the assignment are optimized away.
John Stiles5676c572021-03-08 17:10:52 -0500371 if (op.kind() == Token::Kind::TK_EQ && Analysis::IsSameExpressionTree(*left, *right)) {
John Stilese80e1692021-03-02 17:02:25 -0500372 return right->clone();
John Stiles95d0bad2021-03-01 17:02:28 -0500373 }
374
John Stiles8a9da732021-01-20 14:32:33 -0500375 // Simplify the expression when both sides are constant Boolean literals.
John Stilese80e1692021-03-02 17:02:25 -0500376 if (left->is<BoolLiteral>() && right->is<BoolLiteral>()) {
377 bool leftVal = left->as<BoolLiteral>().value();
378 bool rightVal = right->as<BoolLiteral>().value();
John Stilesdc8ec312021-01-11 11:05:21 -0500379 bool result;
John Stiles45990502021-02-16 10:55:27 -0500380 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500381 case Token::Kind::TK_LOGICALAND: result = leftVal && rightVal; break;
382 case Token::Kind::TK_LOGICALOR: result = leftVal || rightVal; break;
383 case Token::Kind::TK_LOGICALXOR: result = leftVal ^ rightVal; break;
John Stiles26fdcbb2021-01-19 19:00:31 -0500384 case Token::Kind::TK_EQEQ: result = leftVal == rightVal; break;
385 case Token::Kind::TK_NEQ: result = leftVal != rightVal; break;
John Stilesdc8ec312021-01-11 11:05:21 -0500386 default: return nullptr;
387 }
John Stiles9ce80f72021-03-11 22:35:19 -0500388 return BoolLiteral::Make(context, offset, result);
John Stilesdc8ec312021-01-11 11:05:21 -0500389 }
390
John Stiles8a9da732021-01-20 14:32:33 -0500391 // If the left side is a Boolean literal, apply short-circuit optimizations.
John Stilese80e1692021-03-02 17:02:25 -0500392 if (left->is<BoolLiteral>()) {
393 return short_circuit_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500394 }
395
396 // If the right side is a Boolean literal...
John Stilese80e1692021-03-02 17:02:25 -0500397 if (right->is<BoolLiteral>()) {
John Stiles8a9da732021-01-20 14:32:33 -0500398 // ... and the left side has no side effects...
John Stilese80e1692021-03-02 17:02:25 -0500399 if (!left->hasSideEffects()) {
John Stiles8a9da732021-01-20 14:32:33 -0500400 // We can reverse the expressions and short-circuit optimizations are still valid.
John Stilese80e1692021-03-02 17:02:25 -0500401 return short_circuit_boolean(*right, op, *left);
John Stiles8a9da732021-01-20 14:32:33 -0500402 }
403
404 // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions.
John Stilese80e1692021-03-02 17:02:25 -0500405 return eliminate_no_op_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500406 }
407
John Stiles5676c572021-03-08 17:10:52 -0500408 if (op.kind() == Token::Kind::TK_EQEQ && Analysis::IsSameExpressionTree(*left, *right)) {
409 // With == comparison, if both sides are the same trivial expression, this is self-
410 // comparison and is always true. (We are not concerned with NaN.)
John Stiles9ce80f72021-03-11 22:35:19 -0500411 return BoolLiteral::Make(context, leftExpr.fOffset, /*value=*/true);
John Stiles5676c572021-03-08 17:10:52 -0500412 }
413
414 if (op.kind() == Token::Kind::TK_NEQ && Analysis::IsSameExpressionTree(*left, *right)) {
415 // With != comparison, if both sides are the same trivial expression, this is self-
416 // comparison and is always false. (We are not concerned with NaN.)
John Stiles9ce80f72021-03-11 22:35:19 -0500417 return BoolLiteral::Make(context, leftExpr.fOffset, /*value=*/false);
John Stiles5676c572021-03-08 17:10:52 -0500418 }
419
John Stilese80e1692021-03-02 17:02:25 -0500420 if (ErrorOnDivideByZero(context, offset, op, *right)) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500421 return nullptr;
422 }
423
John Stiles8f440b42021-03-05 16:48:56 -0500424 // Optimize away no-op arithmetic like `x * 1`, `x *= 1`, `x + 0`, `x * 0`, `0 / x`, etc.
425 const Type& leftType = left->type();
426 const Type& rightType = right->type();
427 if ((leftType.isScalar() || leftType.isVector()) &&
428 (rightType.isScalar() || rightType.isVector())) {
429 std::unique_ptr<Expression> expr = simplify_no_op_arithmetic(context, *left, op, *right,
430 resultType);
431 if (expr) {
432 return expr;
433 }
434 }
435
436 // Other than the cases above, constant folding requires both sides to be constant.
John Stilese80e1692021-03-02 17:02:25 -0500437 if (!left->isCompileTimeConstant() || !right->isCompileTimeConstant()) {
John Stiles8a9da732021-01-20 14:32:33 -0500438 return nullptr;
439 }
440
John Stilesdc8ec312021-01-11 11:05:21 -0500441 // Note that we expressly do not worry about precision and overflow here -- we use the maximum
442 // precision to calculate the results and hope the result makes sense.
John Stiles9ce80f72021-03-11 22:35:19 -0500443 // TODO(skia:10932): detect and handle integer overflow properly.
Brian Osman21d2b6a2021-02-01 13:48:50 -0500444 using SKSL_UINT = uint64_t;
John Stilese80e1692021-03-02 17:02:25 -0500445 if (left->is<IntLiteral>() && right->is<IntLiteral>()) {
446 SKSL_INT leftVal = left->as<IntLiteral>().value();
447 SKSL_INT rightVal = right->as<IntLiteral>().value();
John Stiles35981292021-05-27 12:26:37 -0400448
449 #define RESULT(Op) fold_int_expression(offset, \
450 (SKSL_INT)(leftVal) Op (SKSL_INT)(rightVal), &resultType)
451 #define URESULT(Op) fold_int_expression(offset, \
452 (SKSL_INT)((SKSL_UINT)(leftVal) Op (SKSL_UINT)(rightVal)), &resultType)
John Stiles45990502021-02-16 10:55:27 -0500453 switch (op.kind()) {
John Stiles35981292021-05-27 12:26:37 -0400454 case Token::Kind::TK_PLUS: return URESULT(+);
455 case Token::Kind::TK_MINUS: return URESULT(-);
456 case Token::Kind::TK_STAR: return URESULT(*);
John Stilesdc8ec312021-01-11 11:05:21 -0500457 case Token::Kind::TK_SLASH:
458 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400459 context.fErrors->error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500460 return nullptr;
461 }
John Stiles35981292021-05-27 12:26:37 -0400462 return RESULT(/);
John Stilesdc8ec312021-01-11 11:05:21 -0500463 case Token::Kind::TK_PERCENT:
464 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400465 context.fErrors->error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500466 return nullptr;
467 }
John Stiles35981292021-05-27 12:26:37 -0400468 return RESULT(%);
469 case Token::Kind::TK_BITWISEAND: return RESULT(&);
470 case Token::Kind::TK_BITWISEOR: return RESULT(|);
471 case Token::Kind::TK_BITWISEXOR: return RESULT(^);
472 case Token::Kind::TK_EQEQ: return RESULT(==);
473 case Token::Kind::TK_NEQ: return RESULT(!=);
474 case Token::Kind::TK_GT: return RESULT(>);
475 case Token::Kind::TK_GTEQ: return RESULT(>=);
476 case Token::Kind::TK_LT: return RESULT(<);
477 case Token::Kind::TK_LTEQ: return RESULT(<=);
John Stilesdc8ec312021-01-11 11:05:21 -0500478 case Token::Kind::TK_SHL:
479 if (rightVal >= 0 && rightVal <= 31) {
Brian Osmana7eb6812021-02-01 11:43:05 -0500480 // Left-shifting a negative (or really, any signed) value is undefined behavior
481 // in C++, but not GLSL. Do the shift on unsigned values, to avoid UBSAN.
John Stiles35981292021-05-27 12:26:37 -0400482 return URESULT(<<);
John Stilesdc8ec312021-01-11 11:05:21 -0500483 }
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400484 context.fErrors->error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500485 return nullptr;
486 case Token::Kind::TK_SHR:
487 if (rightVal >= 0 && rightVal <= 31) {
John Stiles35981292021-05-27 12:26:37 -0400488 return RESULT(>>);
John Stilesdc8ec312021-01-11 11:05:21 -0500489 }
Ethan Nicholas39f6da42021-08-23 13:10:07 -0400490 context.fErrors->error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500491 return nullptr;
492
493 default:
494 return nullptr;
495 }
John Stilesafaddc92021-05-26 22:21:42 -0400496 #undef RESULT
497 #undef URESULT
John Stilesdc8ec312021-01-11 11:05:21 -0500498 }
499
500 // Perform constant folding on pairs of floating-point literals.
John Stilese80e1692021-03-02 17:02:25 -0500501 if (left->is<FloatLiteral>() && right->is<FloatLiteral>()) {
502 SKSL_FLOAT leftVal = left->as<FloatLiteral>().value();
503 SKSL_FLOAT rightVal = right->as<FloatLiteral>().value();
John Stilesafaddc92021-05-26 22:21:42 -0400504
505 #define RESULT(Op) fold_float_expression(offset, leftVal Op rightVal, &resultType)
John Stiles45990502021-02-16 10:55:27 -0500506 switch (op.kind()) {
John Stilesafaddc92021-05-26 22:21:42 -0400507 case Token::Kind::TK_PLUS: return RESULT(+);
508 case Token::Kind::TK_MINUS: return RESULT(-);
509 case Token::Kind::TK_STAR: return RESULT(*);
510 case Token::Kind::TK_SLASH: return RESULT(/);
511 case Token::Kind::TK_EQEQ: return RESULT(==);
512 case Token::Kind::TK_NEQ: return RESULT(!=);
513 case Token::Kind::TK_GT: return RESULT(>);
514 case Token::Kind::TK_GTEQ: return RESULT(>=);
515 case Token::Kind::TK_LT: return RESULT(<);
516 case Token::Kind::TK_LTEQ: return RESULT(<=);
517 default: return nullptr;
John Stilesdc8ec312021-01-11 11:05:21 -0500518 }
John Stilesafaddc92021-05-26 22:21:42 -0400519 #undef RESULT
John Stilesdc8ec312021-01-11 11:05:21 -0500520 }
521
522 // Perform constant folding on pairs of vectors.
John Stilesdc8ec312021-01-11 11:05:21 -0500523 if (leftType.isVector() && leftType == rightType) {
524 if (leftType.componentType().isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500525 return simplify_vector<SKSL_FLOAT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500526 }
527 if (leftType.componentType().isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500528 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500529 }
John Stiles878d8fb2021-08-24 11:33:27 -0400530 if (leftType.componentType().isBoolean()) {
531 return simplify_vector_equality(context, *left, op, *right);
532 }
John Stilesdc8ec312021-01-11 11:05:21 -0500533 return nullptr;
534 }
535
John Stiles508eba72021-01-11 13:07:47 -0500536 // Perform constant folding on vectors against scalars, e.g.: half4(2) + 2
537 if (leftType.isVector() && leftType.componentType() == rightType) {
538 if (rightType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500539 return simplify_vector<SKSL_FLOAT>(context, *left, op,
540 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500541 }
542 if (rightType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500543 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op,
544 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500545 }
John Stiles878d8fb2021-08-24 11:33:27 -0400546 if (rightType.isBoolean()) {
547 return simplify_vector_equality(context, *left, op,
548 splat_scalar(*right, left->type()));
549 }
John Stiles508eba72021-01-11 13:07:47 -0500550 return nullptr;
551 }
552
553 // Perform constant folding on scalars against vectors, e.g.: 2 + half4(2)
554 if (rightType.isVector() && rightType.componentType() == leftType) {
555 if (leftType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500556 return simplify_vector<SKSL_FLOAT>(context, splat_scalar(*left, right->type()), op,
557 *right);
John Stiles508eba72021-01-11 13:07:47 -0500558 }
559 if (leftType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500560 return simplify_vector<SKSL_INT, SKSL_UINT>(context, splat_scalar(*left, right->type()),
561 op, *right);
John Stiles508eba72021-01-11 13:07:47 -0500562 }
John Stiles878d8fb2021-08-24 11:33:27 -0400563 if (leftType.isBoolean()) {
564 return simplify_vector_equality(context, splat_scalar(*left, right->type()),
565 op, *right);
566 }
John Stiles508eba72021-01-11 13:07:47 -0500567 return nullptr;
568 }
569
John Stiles22a54542021-03-31 11:33:48 -0400570 // Perform constant folding on pairs of matrices or arrays.
571 if ((leftType.isMatrix() && rightType.isMatrix()) ||
572 (leftType.isArray() && rightType.isArray())) {
John Stilesdc8ec312021-01-11 11:05:21 -0500573 bool equality;
John Stiles45990502021-02-16 10:55:27 -0500574 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500575 case Token::Kind::TK_EQEQ:
576 equality = true;
577 break;
578 case Token::Kind::TK_NEQ:
579 equality = false;
580 break;
581 default:
582 return nullptr;
583 }
584
John Stilese80e1692021-03-02 17:02:25 -0500585 switch (left->compareConstant(*right)) {
John Stilesdc8ec312021-01-11 11:05:21 -0500586 case Expression::ComparisonResult::kNotEqual:
587 equality = !equality;
588 [[fallthrough]];
589
590 case Expression::ComparisonResult::kEqual:
John Stiles9ce80f72021-03-11 22:35:19 -0500591 return BoolLiteral::Make(context, offset, equality);
John Stilesdc8ec312021-01-11 11:05:21 -0500592
593 case Expression::ComparisonResult::kUnknown:
594 return nullptr;
595 }
596 }
597
598 // We aren't able to constant-fold.
John Stilesdc8ec312021-01-11 11:05:21 -0500599 return nullptr;
600}
601
602} // namespace SkSL