blob: 862a0bc216838bd82319dc7dc920b58f9336dc87 [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
12#include "src/sksl/SkSLContext.h"
13#include "src/sksl/SkSLErrorReporter.h"
14#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
Brian Osman21d2b6a2021-02-01 13:48:50 -050066// 'T' is the actual stored type of the literal data (SKSL_FLOAT or SKSL_INT).
67// 'U' is an unsigned version of that, used to perform addition, subtraction, and multiplication,
68// to avoid signed-integer overflow errors. This mimics the use of URESULT vs. RESULT when doing
69// scalar folding in Simplify, later in this file.
70template <typename T, typename U = T>
John Stilesdc8ec312021-01-11 11:05:21 -050071static std::unique_ptr<Expression> simplify_vector(const Context& context,
John Stilesdc8ec312021-01-11 11:05:21 -050072 const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050073 Operator op,
John Stilesdc8ec312021-01-11 11:05:21 -050074 const Expression& right) {
John Stiles508eba72021-01-11 13:07:47 -050075 SkASSERT(left.type().isVector());
John Stilesdc8ec312021-01-11 11:05:21 -050076 SkASSERT(left.type() == right.type());
77 const Type& type = left.type();
78
79 // Handle boolean operations: == !=
John Stiles45990502021-02-16 10:55:27 -050080 if (op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ) {
81 bool equality = (op.kind() == Token::Kind::TK_EQEQ);
John Stilesdc8ec312021-01-11 11:05:21 -050082
83 switch (left.compareConstant(right)) {
84 case Expression::ComparisonResult::kNotEqual:
85 equality = !equality;
86 [[fallthrough]];
87
88 case Expression::ComparisonResult::kEqual:
John Stiles9ce80f72021-03-11 22:35:19 -050089 return BoolLiteral::Make(context, left.fOffset, equality);
John Stilesdc8ec312021-01-11 11:05:21 -050090
91 case Expression::ComparisonResult::kUnknown:
92 return nullptr;
93 }
94 }
95
96 // Handle floating-point arithmetic: + - * /
John Stiles54f00492021-02-19 11:46:10 -050097 const auto vectorComponentwiseFold = [&](auto foldFn) -> std::unique_ptr<Expression> {
John Stilesdc8ec312021-01-11 11:05:21 -050098 const Type& componentType = type.componentType();
99 ExpressionArray args;
100 args.reserve_back(type.columns());
101 for (int i = 0; i < type.columns(); i++) {
John Stiles21a50ec2021-04-06 14:49:36 -0400102 U value = foldFn(left.getConstantSubexpression(i)->as<Literal<T>>().value(),
103 right.getConstantSubexpression(i)->as<Literal<T>>().value());
John Stiles9ce80f72021-03-11 22:35:19 -0500104 args.push_back(Literal<T>::Make(left.fOffset, value, &componentType));
John Stilesdc8ec312021-01-11 11:05:21 -0500105 }
John Stilesb0ce42d2021-04-09 10:39:42 -0400106 return ConstructorCompound::Make(context, left.fOffset, type, std::move(args));
John Stilesdc8ec312021-01-11 11:05:21 -0500107 };
108
John Stiles45990502021-02-16 10:55:27 -0500109 switch (op.kind()) {
Brian Osman21d2b6a2021-02-01 13:48:50 -0500110 case Token::Kind::TK_PLUS: return vectorComponentwiseFold([](U a, U b) { return a + b; });
111 case Token::Kind::TK_MINUS: return vectorComponentwiseFold([](U a, U b) { return a - b; });
112 case Token::Kind::TK_STAR: return vectorComponentwiseFold([](U a, U b) { return a * b; });
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500113 case Token::Kind::TK_SLASH: return vectorComponentwiseFold([](T a, T b) { return a / b; });
John Stilesdc8ec312021-01-11 11:05:21 -0500114 default:
115 return nullptr;
116 }
117}
118
John Stiles8f440b42021-03-05 16:48:56 -0500119static std::unique_ptr<Expression> cast_expression(const Context& context,
120 const Expression& expr,
121 const Type& type) {
122 ExpressionArray ctorArgs;
123 ctorArgs.push_back(expr.clone());
124 std::unique_ptr<Expression> ctor = Constructor::Convert(context, expr.fOffset, type,
125 std::move(ctorArgs));
126 SkASSERT(ctor);
127 return ctor;
128}
129
John Stiles2938eea2021-04-01 18:58:25 -0400130static ConstructorSplat splat_scalar(const Expression& scalar, const Type& type) {
John Stiles508eba72021-01-11 13:07:47 -0500131 SkASSERT(type.isVector());
132 SkASSERT(type.componentType() == scalar.type());
133
John Stiles2938eea2021-04-01 18:58:25 -0400134 // Use a constructor to splat the scalar expression across a vector.
135 return ConstructorSplat{scalar.fOffset, type, scalar.clone()};
John Stiles508eba72021-01-11 13:07:47 -0500136}
137
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500138bool ConstantFolder::GetConstantInt(const Expression& value, SKSL_INT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500139 const Expression* expr = GetConstantValueForVariable(value);
140 if (!expr->is<IntLiteral>()) {
141 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500142 }
John Stilese80e1692021-03-02 17:02:25 -0500143 *out = expr->as<IntLiteral>().value();
144 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500145}
146
147bool ConstantFolder::GetConstantFloat(const Expression& value, SKSL_FLOAT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500148 const Expression* expr = GetConstantValueForVariable(value);
149 if (!expr->is<FloatLiteral>()) {
150 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500151 }
John Stilese80e1692021-03-02 17:02:25 -0500152 *out = expr->as<FloatLiteral>().value();
153 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500154}
155
John Stiles8f440b42021-03-05 16:48:56 -0500156static bool is_constant_scalar_value(const Expression& inExpr, float match) {
157 const Expression* expr = ConstantFolder::GetConstantValueForVariable(inExpr);
158 return (expr->is<IntLiteral>() && expr->as<IntLiteral>().value() == match) ||
159 (expr->is<FloatLiteral>() && expr->as<FloatLiteral>().value() == match);
160}
161
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500162static bool contains_constant_zero(const Expression& expr) {
John Stiles2938eea2021-04-01 18:58:25 -0400163 if (expr.isAnyConstructor()) {
164 for (const auto& arg : expr.asAnyConstructor().argumentSpan()) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500165 if (contains_constant_zero(*arg)) {
166 return true;
167 }
168 }
169 return false;
170 }
John Stiles8f440b42021-03-05 16:48:56 -0500171 return is_constant_scalar_value(expr, 0.0);
172}
173
174static bool is_constant_value(const Expression& expr, float value) {
175 // This check only supports scalars and vectors (and in particular, not matrices).
176 SkASSERT(expr.type().isScalar() || expr.type().isVector());
177
John Stiles2938eea2021-04-01 18:58:25 -0400178 if (expr.isAnyConstructor()) {
179 for (const auto& arg : expr.asAnyConstructor().argumentSpan()) {
John Stiles8f440b42021-03-05 16:48:56 -0500180 if (!is_constant_value(*arg, value)) {
181 return false;
182 }
183 }
184 return true;
185 }
186 return is_constant_scalar_value(expr, value);
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500187}
188
John Stiles45990502021-02-16 10:55:27 -0500189bool ConstantFolder::ErrorOnDivideByZero(const Context& context, int offset, Operator op,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500190 const Expression& right) {
John Stiles45990502021-02-16 10:55:27 -0500191 switch (op.kind()) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500192 case Token::Kind::TK_SLASH:
193 case Token::Kind::TK_SLASHEQ:
194 case Token::Kind::TK_PERCENT:
195 case Token::Kind::TK_PERCENTEQ:
196 if (contains_constant_zero(right)) {
197 context.fErrors.error(offset, "division by zero");
198 return true;
199 }
200 return false;
201 default:
202 return false;
203 }
204}
205
John Stilese80e1692021-03-02 17:02:25 -0500206const Expression* ConstantFolder::GetConstantValueForVariable(const Expression& inExpr) {
207 for (const Expression* expr = &inExpr;;) {
208 if (!expr->is<VariableReference>()) {
209 break;
210 }
211 const VariableReference& varRef = expr->as<VariableReference>();
212 if (varRef.refKind() != VariableRefKind::kRead) {
213 break;
214 }
215 const Variable& var = *varRef.variable();
216 if (!(var.modifiers().fFlags & Modifiers::kConst_Flag)) {
217 break;
218 }
219 expr = var.initialValue();
John Stilesca65f8f2021-03-29 17:14:39 -0400220 if (!expr) {
221 SkDEBUGFAILF("found a const variable without an initial value (%s)",
222 var.description().c_str());
223 break;
224 }
John Stilese80e1692021-03-02 17:02:25 -0500225 if (expr->isCompileTimeConstant()) {
226 return expr;
227 }
228 if (!expr->is<VariableReference>()) {
229 break;
230 }
231 }
232 // We didn't find a compile-time constant at the end. Return the expression as-is.
233 return &inExpr;
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 }
280 if (is_constant_value(left, 0.0) &&
281 !is_constant_value(right, 0.0) &&
282 !right.hasSideEffects()) { // 0 / x (where x is not 0)
283 return cast_expression(context, left, resultType);
284 }
285 break;
286
287 case Token::Kind::TK_PLUSEQ:
288 case Token::Kind::TK_MINUSEQ:
289 if (is_constant_value(right, 0.0)) { // x += 0, x -= 0
290 std::unique_ptr<Expression> result = cast_expression(context, left, resultType);
291 Analysis::UpdateRefKind(result.get(), VariableRefKind::kRead);
292 return result;
293 }
294 break;
295
296 case Token::Kind::TK_STAREQ:
297 case Token::Kind::TK_SLASHEQ:
298 if (is_constant_value(right, 1.0)) { // x *= 1, x /= 1
299 std::unique_ptr<Expression> result = cast_expression(context, left, resultType);
300 Analysis::UpdateRefKind(result.get(), VariableRefKind::kRead);
301 return result;
302 }
303 break;
304
305 default:
306 break;
307 }
308
309 return nullptr;
310}
311
John Stilesdc8ec312021-01-11 11:05:21 -0500312std::unique_ptr<Expression> ConstantFolder::Simplify(const Context& context,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500313 int offset,
John Stilese80e1692021-03-02 17:02:25 -0500314 const Expression& leftExpr,
John Stiles45990502021-02-16 10:55:27 -0500315 Operator op,
John Stiles8f440b42021-03-05 16:48:56 -0500316 const Expression& rightExpr,
317 const Type& resultType) {
John Stilesbe6cbf32021-03-09 20:15:17 -0500318 // When optimization is enabled, replace constant variables with trivial initial-values.
319 const Expression* left;
320 const Expression* right;
321 if (context.fConfig->fSettings.fOptimize) {
322 left = GetConstantValueForVariable(leftExpr);
323 right = GetConstantValueForVariable(rightExpr);
324 } else {
325 left = &leftExpr;
326 right = &rightExpr;
327 }
John Stilese80e1692021-03-02 17:02:25 -0500328
John Stilesabc3b782021-02-05 10:07:19 -0500329 // If this is the comma operator, the left side is evaluated but not otherwise used in any way.
330 // So if the left side has no side effects, it can just be eliminated entirely.
John Stilese80e1692021-03-02 17:02:25 -0500331 if (op.kind() == Token::Kind::TK_COMMA && !left->hasSideEffects()) {
332 return right->clone();
John Stilesabc3b782021-02-05 10:07:19 -0500333 }
334
John Stiles95d0bad2021-03-01 17:02:28 -0500335 // If this is the assignment operator, and both sides are the same trivial expression, this is
336 // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`).
337 // This can happen when other parts of the assignment are optimized away.
John Stiles5676c572021-03-08 17:10:52 -0500338 if (op.kind() == Token::Kind::TK_EQ && Analysis::IsSameExpressionTree(*left, *right)) {
John Stilese80e1692021-03-02 17:02:25 -0500339 return right->clone();
John Stiles95d0bad2021-03-01 17:02:28 -0500340 }
341
John Stiles8a9da732021-01-20 14:32:33 -0500342 // Simplify the expression when both sides are constant Boolean literals.
John Stilese80e1692021-03-02 17:02:25 -0500343 if (left->is<BoolLiteral>() && right->is<BoolLiteral>()) {
344 bool leftVal = left->as<BoolLiteral>().value();
345 bool rightVal = right->as<BoolLiteral>().value();
John Stilesdc8ec312021-01-11 11:05:21 -0500346 bool result;
John Stiles45990502021-02-16 10:55:27 -0500347 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500348 case Token::Kind::TK_LOGICALAND: result = leftVal && rightVal; break;
349 case Token::Kind::TK_LOGICALOR: result = leftVal || rightVal; break;
350 case Token::Kind::TK_LOGICALXOR: result = leftVal ^ rightVal; break;
John Stiles26fdcbb2021-01-19 19:00:31 -0500351 case Token::Kind::TK_EQEQ: result = leftVal == rightVal; break;
352 case Token::Kind::TK_NEQ: result = leftVal != rightVal; break;
John Stilesdc8ec312021-01-11 11:05:21 -0500353 default: return nullptr;
354 }
John Stiles9ce80f72021-03-11 22:35:19 -0500355 return BoolLiteral::Make(context, offset, result);
John Stilesdc8ec312021-01-11 11:05:21 -0500356 }
357
John Stiles8a9da732021-01-20 14:32:33 -0500358 // If the left side is a Boolean literal, apply short-circuit optimizations.
John Stilese80e1692021-03-02 17:02:25 -0500359 if (left->is<BoolLiteral>()) {
360 return short_circuit_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500361 }
362
363 // If the right side is a Boolean literal...
John Stilese80e1692021-03-02 17:02:25 -0500364 if (right->is<BoolLiteral>()) {
John Stiles8a9da732021-01-20 14:32:33 -0500365 // ... and the left side has no side effects...
John Stilese80e1692021-03-02 17:02:25 -0500366 if (!left->hasSideEffects()) {
John Stiles8a9da732021-01-20 14:32:33 -0500367 // We can reverse the expressions and short-circuit optimizations are still valid.
John Stilese80e1692021-03-02 17:02:25 -0500368 return short_circuit_boolean(*right, op, *left);
John Stiles8a9da732021-01-20 14:32:33 -0500369 }
370
371 // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions.
John Stilese80e1692021-03-02 17:02:25 -0500372 return eliminate_no_op_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500373 }
374
John Stiles5676c572021-03-08 17:10:52 -0500375 if (op.kind() == Token::Kind::TK_EQEQ && Analysis::IsSameExpressionTree(*left, *right)) {
376 // With == comparison, if both sides are the same trivial expression, this is self-
377 // comparison and is always true. (We are not concerned with NaN.)
John Stiles9ce80f72021-03-11 22:35:19 -0500378 return BoolLiteral::Make(context, leftExpr.fOffset, /*value=*/true);
John Stiles5676c572021-03-08 17:10:52 -0500379 }
380
381 if (op.kind() == Token::Kind::TK_NEQ && Analysis::IsSameExpressionTree(*left, *right)) {
382 // With != comparison, if both sides are the same trivial expression, this is self-
383 // comparison and is always false. (We are not concerned with NaN.)
John Stiles9ce80f72021-03-11 22:35:19 -0500384 return BoolLiteral::Make(context, leftExpr.fOffset, /*value=*/false);
John Stiles5676c572021-03-08 17:10:52 -0500385 }
386
John Stilese80e1692021-03-02 17:02:25 -0500387 if (ErrorOnDivideByZero(context, offset, op, *right)) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500388 return nullptr;
389 }
390
John Stiles8f440b42021-03-05 16:48:56 -0500391 // Optimize away no-op arithmetic like `x * 1`, `x *= 1`, `x + 0`, `x * 0`, `0 / x`, etc.
392 const Type& leftType = left->type();
393 const Type& rightType = right->type();
394 if ((leftType.isScalar() || leftType.isVector()) &&
395 (rightType.isScalar() || rightType.isVector())) {
396 std::unique_ptr<Expression> expr = simplify_no_op_arithmetic(context, *left, op, *right,
397 resultType);
398 if (expr) {
399 return expr;
400 }
401 }
402
403 // Other than the cases above, constant folding requires both sides to be constant.
John Stilese80e1692021-03-02 17:02:25 -0500404 if (!left->isCompileTimeConstant() || !right->isCompileTimeConstant()) {
John Stiles8a9da732021-01-20 14:32:33 -0500405 return nullptr;
406 }
407
John Stilesdc8ec312021-01-11 11:05:21 -0500408 // Note that we expressly do not worry about precision and overflow here -- we use the maximum
409 // precision to calculate the results and hope the result makes sense.
John Stiles9ce80f72021-03-11 22:35:19 -0500410 // TODO(skia:10932): detect and handle integer overflow properly.
Brian Osman21d2b6a2021-02-01 13:48:50 -0500411 using SKSL_UINT = uint64_t;
John Stiles9ce80f72021-03-11 22:35:19 -0500412 #define RESULT(t, op) t ## Literal::Make(context, offset, leftVal op rightVal)
413 #define URESULT(t, op) t ## Literal::Make(context, offset, (SKSL_UINT) leftVal op \
414 (SKSL_UINT) rightVal)
John Stilese80e1692021-03-02 17:02:25 -0500415 if (left->is<IntLiteral>() && right->is<IntLiteral>()) {
416 SKSL_INT leftVal = left->as<IntLiteral>().value();
417 SKSL_INT rightVal = right->as<IntLiteral>().value();
John Stiles45990502021-02-16 10:55:27 -0500418 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500419 case Token::Kind::TK_PLUS: return URESULT(Int, +);
420 case Token::Kind::TK_MINUS: return URESULT(Int, -);
421 case Token::Kind::TK_STAR: return URESULT(Int, *);
422 case Token::Kind::TK_SLASH:
423 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500424 context.fErrors.error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500425 return nullptr;
426 }
427 return RESULT(Int, /);
428 case Token::Kind::TK_PERCENT:
429 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500430 context.fErrors.error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500431 return nullptr;
432 }
433 return RESULT(Int, %);
434 case Token::Kind::TK_BITWISEAND: return RESULT(Int, &);
435 case Token::Kind::TK_BITWISEOR: return RESULT(Int, |);
436 case Token::Kind::TK_BITWISEXOR: return RESULT(Int, ^);
437 case Token::Kind::TK_EQEQ: return RESULT(Bool, ==);
438 case Token::Kind::TK_NEQ: return RESULT(Bool, !=);
439 case Token::Kind::TK_GT: return RESULT(Bool, >);
440 case Token::Kind::TK_GTEQ: return RESULT(Bool, >=);
441 case Token::Kind::TK_LT: return RESULT(Bool, <);
442 case Token::Kind::TK_LTEQ: return RESULT(Bool, <=);
443 case Token::Kind::TK_SHL:
444 if (rightVal >= 0 && rightVal <= 31) {
Brian Osmana7eb6812021-02-01 11:43:05 -0500445 // Left-shifting a negative (or really, any signed) value is undefined behavior
446 // in C++, but not GLSL. Do the shift on unsigned values, to avoid UBSAN.
447 return URESULT(Int, <<);
John Stilesdc8ec312021-01-11 11:05:21 -0500448 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500449 context.fErrors.error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500450 return nullptr;
451 case Token::Kind::TK_SHR:
452 if (rightVal >= 0 && rightVal <= 31) {
453 return RESULT(Int, >>);
454 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500455 context.fErrors.error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500456 return nullptr;
457
458 default:
459 return nullptr;
460 }
461 }
462
463 // Perform constant folding on pairs of floating-point literals.
John Stilese80e1692021-03-02 17:02:25 -0500464 if (left->is<FloatLiteral>() && right->is<FloatLiteral>()) {
465 SKSL_FLOAT leftVal = left->as<FloatLiteral>().value();
466 SKSL_FLOAT rightVal = right->as<FloatLiteral>().value();
John Stiles45990502021-02-16 10:55:27 -0500467 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500468 case Token::Kind::TK_PLUS: return RESULT(Float, +);
469 case Token::Kind::TK_MINUS: return RESULT(Float, -);
470 case Token::Kind::TK_STAR: return RESULT(Float, *);
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500471 case Token::Kind::TK_SLASH: return RESULT(Float, /);
John Stilesdc8ec312021-01-11 11:05:21 -0500472 case Token::Kind::TK_EQEQ: return RESULT(Bool, ==);
473 case Token::Kind::TK_NEQ: return RESULT(Bool, !=);
474 case Token::Kind::TK_GT: return RESULT(Bool, >);
475 case Token::Kind::TK_GTEQ: return RESULT(Bool, >=);
476 case Token::Kind::TK_LT: return RESULT(Bool, <);
477 case Token::Kind::TK_LTEQ: return RESULT(Bool, <=);
478 default: return nullptr;
479 }
480 }
481
482 // Perform constant folding on pairs of vectors.
John Stilesdc8ec312021-01-11 11:05:21 -0500483 if (leftType.isVector() && leftType == rightType) {
484 if (leftType.componentType().isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500485 return simplify_vector<SKSL_FLOAT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500486 }
487 if (leftType.componentType().isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500488 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500489 }
490 return nullptr;
491 }
492
John Stiles508eba72021-01-11 13:07:47 -0500493 // Perform constant folding on vectors against scalars, e.g.: half4(2) + 2
494 if (leftType.isVector() && leftType.componentType() == rightType) {
495 if (rightType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500496 return simplify_vector<SKSL_FLOAT>(context, *left, op,
497 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500498 }
499 if (rightType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500500 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op,
501 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500502 }
503 return nullptr;
504 }
505
506 // Perform constant folding on scalars against vectors, e.g.: 2 + half4(2)
507 if (rightType.isVector() && rightType.componentType() == leftType) {
508 if (leftType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500509 return simplify_vector<SKSL_FLOAT>(context, splat_scalar(*left, right->type()), op,
510 *right);
John Stiles508eba72021-01-11 13:07:47 -0500511 }
512 if (leftType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500513 return simplify_vector<SKSL_INT, SKSL_UINT>(context, splat_scalar(*left, right->type()),
514 op, *right);
John Stiles508eba72021-01-11 13:07:47 -0500515 }
516 return nullptr;
517 }
518
John Stiles22a54542021-03-31 11:33:48 -0400519 // Perform constant folding on pairs of matrices or arrays.
520 if ((leftType.isMatrix() && rightType.isMatrix()) ||
521 (leftType.isArray() && rightType.isArray())) {
John Stilesdc8ec312021-01-11 11:05:21 -0500522 bool equality;
John Stiles45990502021-02-16 10:55:27 -0500523 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500524 case Token::Kind::TK_EQEQ:
525 equality = true;
526 break;
527 case Token::Kind::TK_NEQ:
528 equality = false;
529 break;
530 default:
531 return nullptr;
532 }
533
John Stilese80e1692021-03-02 17:02:25 -0500534 switch (left->compareConstant(*right)) {
John Stilesdc8ec312021-01-11 11:05:21 -0500535 case Expression::ComparisonResult::kNotEqual:
536 equality = !equality;
537 [[fallthrough]];
538
539 case Expression::ComparisonResult::kEqual:
John Stiles9ce80f72021-03-11 22:35:19 -0500540 return BoolLiteral::Make(context, offset, equality);
John Stilesdc8ec312021-01-11 11:05:21 -0500541
542 case Expression::ComparisonResult::kUnknown:
543 return nullptr;
544 }
545 }
546
547 // We aren't able to constant-fold.
548 #undef RESULT
549 #undef URESULT
550 return nullptr;
551}
552
553} // namespace SkSL