blob: 1f0ec47b047e8ed4cc127ba2eb4c60332e011f8f [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"
17#include "src/sksl/ir/SkSLExpression.h"
18#include "src/sksl/ir/SkSLFloatLiteral.h"
19#include "src/sksl/ir/SkSLIntLiteral.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) {
30 SkASSERT(right.is<BoolLiteral>());
31 bool rightVal = right.as<BoolLiteral>().value();
32
33 // Detect no-op Boolean expressions and optimize them away.
John Stiles45990502021-02-16 10:55:27 -050034 if ((op.kind() == Token::Kind::TK_LOGICALAND && rightVal) || // (expr && true) -> (expr)
35 (op.kind() == Token::Kind::TK_LOGICALOR && !rightVal) || // (expr || false) -> (expr)
36 (op.kind() == Token::Kind::TK_LOGICALXOR && !rightVal) || // (expr ^^ false) -> (expr)
37 (op.kind() == Token::Kind::TK_EQEQ && rightVal) || // (expr == true) -> (expr)
38 (op.kind() == Token::Kind::TK_NEQ && !rightVal)) { // (expr != false) -> (expr)
John Stiles8a9da732021-01-20 14:32:33 -050039
40 return left.clone();
41 }
42
43 return nullptr;
44}
45
John Stilesdc8ec312021-01-11 11:05:21 -050046static std::unique_ptr<Expression> short_circuit_boolean(const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050047 Operator op,
John Stilesdc8ec312021-01-11 11:05:21 -050048 const Expression& right) {
49 SkASSERT(left.is<BoolLiteral>());
50 bool leftVal = left.as<BoolLiteral>().value();
51
John Stiles8a9da732021-01-20 14:32:33 -050052 // When the literal is on the left, we can sometimes eliminate the other expression entirely.
John Stiles45990502021-02-16 10:55:27 -050053 if ((op.kind() == Token::Kind::TK_LOGICALAND && !leftVal) || // (false && expr) -> (false)
54 (op.kind() == Token::Kind::TK_LOGICALOR && leftVal)) { // (true || expr) -> (true)
John Stiles8a9da732021-01-20 14:32:33 -050055
56 return left.clone();
John Stilesdc8ec312021-01-11 11:05:21 -050057 }
58
John Stiles8a9da732021-01-20 14:32:33 -050059 // We can't eliminate the right-side expression via short-circuit, but we might still be able to
60 // simplify away a no-op expression.
61 return eliminate_no_op_boolean(right, op, left);
John Stilesdc8ec312021-01-11 11:05:21 -050062}
63
Brian Osman21d2b6a2021-02-01 13:48:50 -050064// 'T' is the actual stored type of the literal data (SKSL_FLOAT or SKSL_INT).
65// 'U' is an unsigned version of that, used to perform addition, subtraction, and multiplication,
66// to avoid signed-integer overflow errors. This mimics the use of URESULT vs. RESULT when doing
67// scalar folding in Simplify, later in this file.
68template <typename T, typename U = T>
John Stilesdc8ec312021-01-11 11:05:21 -050069static std::unique_ptr<Expression> simplify_vector(const Context& context,
John Stilesdc8ec312021-01-11 11:05:21 -050070 const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050071 Operator op,
John Stilesdc8ec312021-01-11 11:05:21 -050072 const Expression& right) {
John Stiles508eba72021-01-11 13:07:47 -050073 SkASSERT(left.type().isVector());
John Stilesdc8ec312021-01-11 11:05:21 -050074 SkASSERT(left.type() == right.type());
75 const Type& type = left.type();
76
77 // Handle boolean operations: == !=
John Stiles45990502021-02-16 10:55:27 -050078 if (op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ) {
79 bool equality = (op.kind() == Token::Kind::TK_EQEQ);
John Stilesdc8ec312021-01-11 11:05:21 -050080
81 switch (left.compareConstant(right)) {
82 case Expression::ComparisonResult::kNotEqual:
83 equality = !equality;
84 [[fallthrough]];
85
86 case Expression::ComparisonResult::kEqual:
87 return std::make_unique<BoolLiteral>(context, left.fOffset, equality);
88
89 case Expression::ComparisonResult::kUnknown:
90 return nullptr;
91 }
92 }
93
94 // Handle floating-point arithmetic: + - * /
John Stiles54f00492021-02-19 11:46:10 -050095 const auto vectorComponentwiseFold = [&](auto foldFn) -> std::unique_ptr<Expression> {
John Stilesdc8ec312021-01-11 11:05:21 -050096 const Type& componentType = type.componentType();
97 ExpressionArray args;
98 args.reserve_back(type.columns());
99 for (int i = 0; i < type.columns(); i++) {
Brian Osman21d2b6a2021-02-01 13:48:50 -0500100 U value = foldFn(left.getVecComponent<T>(i), right.getVecComponent<T>(i));
John Stilesdc8ec312021-01-11 11:05:21 -0500101 args.push_back(std::make_unique<Literal<T>>(left.fOffset, value, &componentType));
102 }
John Stiles23521a82021-03-02 17:02:51 -0500103 auto foldedCtor = Constructor::Convert(context, left.fOffset, type, std::move(args));
104 SkASSERT(foldedCtor);
105 return foldedCtor;
John Stilesdc8ec312021-01-11 11:05:21 -0500106 };
107
John Stiles45990502021-02-16 10:55:27 -0500108 switch (op.kind()) {
Brian Osman21d2b6a2021-02-01 13:48:50 -0500109 case Token::Kind::TK_PLUS: return vectorComponentwiseFold([](U a, U b) { return a + b; });
110 case Token::Kind::TK_MINUS: return vectorComponentwiseFold([](U a, U b) { return a - b; });
111 case Token::Kind::TK_STAR: return vectorComponentwiseFold([](U a, U b) { return a * b; });
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500112 case Token::Kind::TK_SLASH: return vectorComponentwiseFold([](T a, T b) { return a / b; });
John Stilesdc8ec312021-01-11 11:05:21 -0500113 default:
114 return nullptr;
115 }
116}
117
John Stiles8f440b42021-03-05 16:48:56 -0500118static std::unique_ptr<Expression> cast_expression(const Context& context,
119 const Expression& expr,
120 const Type& type) {
121 ExpressionArray ctorArgs;
122 ctorArgs.push_back(expr.clone());
123 std::unique_ptr<Expression> ctor = Constructor::Convert(context, expr.fOffset, type,
124 std::move(ctorArgs));
125 SkASSERT(ctor);
126 return ctor;
127}
128
John Stiles508eba72021-01-11 13:07:47 -0500129static Constructor splat_scalar(const Expression& scalar, const Type& type) {
130 SkASSERT(type.isVector());
131 SkASSERT(type.componentType() == scalar.type());
132
133 // Use a Constructor to splat the scalar expression across a vector.
134 ExpressionArray arg;
135 arg.push_back(scalar.clone());
John Stiles54f00492021-02-19 11:46:10 -0500136 return Constructor{scalar.fOffset, type, std::move(arg)};
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);
141 if (!expr->is<IntLiteral>()) {
142 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500143 }
John Stilese80e1692021-03-02 17:02:25 -0500144 *out = expr->as<IntLiteral>().value();
145 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500146}
147
148bool ConstantFolder::GetConstantFloat(const Expression& value, SKSL_FLOAT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500149 const Expression* expr = GetConstantValueForVariable(value);
150 if (!expr->is<FloatLiteral>()) {
151 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500152 }
John Stilese80e1692021-03-02 17:02:25 -0500153 *out = expr->as<FloatLiteral>().value();
154 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500155}
156
John Stiles8f440b42021-03-05 16:48:56 -0500157static bool is_constant_scalar_value(const Expression& inExpr, float match) {
158 const Expression* expr = ConstantFolder::GetConstantValueForVariable(inExpr);
159 return (expr->is<IntLiteral>() && expr->as<IntLiteral>().value() == match) ||
160 (expr->is<FloatLiteral>() && expr->as<FloatLiteral>().value() == match);
161}
162
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500163static bool contains_constant_zero(const Expression& expr) {
164 if (expr.is<Constructor>()) {
165 for (const auto& arg : expr.as<Constructor>().arguments()) {
166 if (contains_constant_zero(*arg)) {
167 return true;
168 }
169 }
170 return false;
171 }
John Stiles8f440b42021-03-05 16:48:56 -0500172 return is_constant_scalar_value(expr, 0.0);
173}
174
175static bool is_constant_value(const Expression& expr, float value) {
176 // This check only supports scalars and vectors (and in particular, not matrices).
177 SkASSERT(expr.type().isScalar() || expr.type().isVector());
178
179 if (expr.is<Constructor>()) {
180 for (const auto& arg : expr.as<Constructor>().arguments()) {
181 if (!is_constant_value(*arg, value)) {
182 return false;
183 }
184 }
185 return true;
186 }
187 return is_constant_scalar_value(expr, value);
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500188}
189
John Stiles45990502021-02-16 10:55:27 -0500190bool ConstantFolder::ErrorOnDivideByZero(const Context& context, int offset, Operator op,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500191 const Expression& right) {
John Stiles45990502021-02-16 10:55:27 -0500192 switch (op.kind()) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500193 case Token::Kind::TK_SLASH:
194 case Token::Kind::TK_SLASHEQ:
195 case Token::Kind::TK_PERCENT:
196 case Token::Kind::TK_PERCENTEQ:
197 if (contains_constant_zero(right)) {
198 context.fErrors.error(offset, "division by zero");
199 return true;
200 }
201 return false;
202 default:
203 return false;
204 }
205}
206
John Stilese80e1692021-03-02 17:02:25 -0500207const Expression* ConstantFolder::GetConstantValueForVariable(const Expression& inExpr) {
208 for (const Expression* expr = &inExpr;;) {
209 if (!expr->is<VariableReference>()) {
210 break;
211 }
212 const VariableReference& varRef = expr->as<VariableReference>();
213 if (varRef.refKind() != VariableRefKind::kRead) {
214 break;
215 }
216 const Variable& var = *varRef.variable();
217 if (!(var.modifiers().fFlags & Modifiers::kConst_Flag)) {
218 break;
219 }
220 expr = var.initialValue();
221 SkASSERT(expr);
222 if (expr->isCompileTimeConstant()) {
223 return expr;
224 }
225 if (!expr->is<VariableReference>()) {
226 break;
227 }
228 }
229 // We didn't find a compile-time constant at the end. Return the expression as-is.
230 return &inExpr;
231}
232
John Stiles8f440b42021-03-05 16:48:56 -0500233static std::unique_ptr<Expression> simplify_no_op_arithmetic(const Context& context,
234 const Expression& left,
235 Operator op,
236 const Expression& right,
237 const Type& resultType) {
238 switch (op.kind()) {
239 case Token::Kind::TK_PLUS:
240 if (is_constant_value(right, 0.0)) { // x + 0
241 return cast_expression(context, left, resultType);
242 }
243 if (is_constant_value(left, 0.0)) { // 0 + x
244 return cast_expression(context, right, resultType);
245 }
246 break;
247
248 case Token::Kind::TK_STAR:
249 if (is_constant_value(right, 1.0)) { // x * 1
250 return cast_expression(context, left, resultType);
251 }
252 if (is_constant_value(left, 1.0)) { // 1 * x
253 return cast_expression(context, right, resultType);
254 }
255 if (is_constant_value(right, 0.0) && !left.hasSideEffects()) { // x * 0
256 return cast_expression(context, right, resultType);
257 }
258 if (is_constant_value(left, 0.0) && !right.hasSideEffects()) { // 0 * x
259 return cast_expression(context, left, resultType);
260 }
261 break;
262
263 case Token::Kind::TK_MINUS:
264 if (is_constant_value(right, 0.0)) { // x - 0
265 return cast_expression(context, left, resultType);
266 }
267 if (is_constant_value(left, 0.0)) { // 0 - x (to `-x`)
268 return PrefixExpression::Make(context, Token::Kind::TK_MINUS,
269 cast_expression(context, right, resultType));
270 }
271 break;
272
273 case Token::Kind::TK_SLASH:
274 if (is_constant_value(right, 1.0)) { // x / 1
275 return cast_expression(context, left, resultType);
276 }
277 if (is_constant_value(left, 0.0) &&
278 !is_constant_value(right, 0.0) &&
279 !right.hasSideEffects()) { // 0 / x (where x is not 0)
280 return cast_expression(context, left, resultType);
281 }
282 break;
283
284 case Token::Kind::TK_PLUSEQ:
285 case Token::Kind::TK_MINUSEQ:
286 if (is_constant_value(right, 0.0)) { // x += 0, x -= 0
287 std::unique_ptr<Expression> result = cast_expression(context, left, resultType);
288 Analysis::UpdateRefKind(result.get(), VariableRefKind::kRead);
289 return result;
290 }
291 break;
292
293 case Token::Kind::TK_STAREQ:
294 case Token::Kind::TK_SLASHEQ:
295 if (is_constant_value(right, 1.0)) { // x *= 1, x /= 1
296 std::unique_ptr<Expression> result = cast_expression(context, left, resultType);
297 Analysis::UpdateRefKind(result.get(), VariableRefKind::kRead);
298 return result;
299 }
300 break;
301
302 default:
303 break;
304 }
305
306 return nullptr;
307}
308
John Stilesdc8ec312021-01-11 11:05:21 -0500309std::unique_ptr<Expression> ConstantFolder::Simplify(const Context& context,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500310 int offset,
John Stilese80e1692021-03-02 17:02:25 -0500311 const Expression& leftExpr,
John Stiles45990502021-02-16 10:55:27 -0500312 Operator op,
John Stiles8f440b42021-03-05 16:48:56 -0500313 const Expression& rightExpr,
314 const Type& resultType) {
John Stilese80e1692021-03-02 17:02:25 -0500315 // Replace constant variables with trivial initial-values.
316 const Expression* left = GetConstantValueForVariable(leftExpr);
317 const Expression* right = GetConstantValueForVariable(rightExpr);
318
John Stilesabc3b782021-02-05 10:07:19 -0500319 // If this is the comma operator, the left side is evaluated but not otherwise used in any way.
320 // So if the left side has no side effects, it can just be eliminated entirely.
John Stilese80e1692021-03-02 17:02:25 -0500321 if (op.kind() == Token::Kind::TK_COMMA && !left->hasSideEffects()) {
322 return right->clone();
John Stilesabc3b782021-02-05 10:07:19 -0500323 }
324
John Stiles95d0bad2021-03-01 17:02:28 -0500325 // If this is the assignment operator, and both sides are the same trivial expression, this is
326 // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`).
327 // This can happen when other parts of the assignment are optimized away.
John Stiles5676c572021-03-08 17:10:52 -0500328 if (op.kind() == Token::Kind::TK_EQ && Analysis::IsSameExpressionTree(*left, *right)) {
John Stilese80e1692021-03-02 17:02:25 -0500329 return right->clone();
John Stiles95d0bad2021-03-01 17:02:28 -0500330 }
331
John Stiles8a9da732021-01-20 14:32:33 -0500332 // Simplify the expression when both sides are constant Boolean literals.
John Stilese80e1692021-03-02 17:02:25 -0500333 if (left->is<BoolLiteral>() && right->is<BoolLiteral>()) {
334 bool leftVal = left->as<BoolLiteral>().value();
335 bool rightVal = right->as<BoolLiteral>().value();
John Stilesdc8ec312021-01-11 11:05:21 -0500336 bool result;
John Stiles45990502021-02-16 10:55:27 -0500337 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500338 case Token::Kind::TK_LOGICALAND: result = leftVal && rightVal; break;
339 case Token::Kind::TK_LOGICALOR: result = leftVal || rightVal; break;
340 case Token::Kind::TK_LOGICALXOR: result = leftVal ^ rightVal; break;
John Stiles26fdcbb2021-01-19 19:00:31 -0500341 case Token::Kind::TK_EQEQ: result = leftVal == rightVal; break;
342 case Token::Kind::TK_NEQ: result = leftVal != rightVal; break;
John Stilesdc8ec312021-01-11 11:05:21 -0500343 default: return nullptr;
344 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500345 return std::make_unique<BoolLiteral>(context, offset, result);
John Stilesdc8ec312021-01-11 11:05:21 -0500346 }
347
John Stiles8a9da732021-01-20 14:32:33 -0500348 // If the left side is a Boolean literal, apply short-circuit optimizations.
John Stilese80e1692021-03-02 17:02:25 -0500349 if (left->is<BoolLiteral>()) {
350 return short_circuit_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500351 }
352
353 // If the right side is a Boolean literal...
John Stilese80e1692021-03-02 17:02:25 -0500354 if (right->is<BoolLiteral>()) {
John Stiles8a9da732021-01-20 14:32:33 -0500355 // ... and the left side has no side effects...
John Stilese80e1692021-03-02 17:02:25 -0500356 if (!left->hasSideEffects()) {
John Stiles8a9da732021-01-20 14:32:33 -0500357 // We can reverse the expressions and short-circuit optimizations are still valid.
John Stilese80e1692021-03-02 17:02:25 -0500358 return short_circuit_boolean(*right, op, *left);
John Stiles8a9da732021-01-20 14:32:33 -0500359 }
360
361 // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions.
John Stilese80e1692021-03-02 17:02:25 -0500362 return eliminate_no_op_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500363 }
364
John Stiles5676c572021-03-08 17:10:52 -0500365 if (op.kind() == Token::Kind::TK_EQEQ && Analysis::IsSameExpressionTree(*left, *right)) {
366 // With == comparison, if both sides are the same trivial expression, this is self-
367 // comparison and is always true. (We are not concerned with NaN.)
368 return std::make_unique<BoolLiteral>(context, leftExpr.fOffset, /*value=*/true);
369 }
370
371 if (op.kind() == Token::Kind::TK_NEQ && Analysis::IsSameExpressionTree(*left, *right)) {
372 // With != comparison, if both sides are the same trivial expression, this is self-
373 // comparison and is always false. (We are not concerned with NaN.)
374 return std::make_unique<BoolLiteral>(context, leftExpr.fOffset, /*value=*/false);
375 }
376
John Stilese80e1692021-03-02 17:02:25 -0500377 if (ErrorOnDivideByZero(context, offset, op, *right)) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500378 return nullptr;
379 }
380
John Stiles8f440b42021-03-05 16:48:56 -0500381 // Optimize away no-op arithmetic like `x * 1`, `x *= 1`, `x + 0`, `x * 0`, `0 / x`, etc.
382 const Type& leftType = left->type();
383 const Type& rightType = right->type();
384 if ((leftType.isScalar() || leftType.isVector()) &&
385 (rightType.isScalar() || rightType.isVector())) {
386 std::unique_ptr<Expression> expr = simplify_no_op_arithmetic(context, *left, op, *right,
387 resultType);
388 if (expr) {
389 return expr;
390 }
391 }
392
393 // Other than the cases above, constant folding requires both sides to be constant.
John Stilese80e1692021-03-02 17:02:25 -0500394 if (!left->isCompileTimeConstant() || !right->isCompileTimeConstant()) {
John Stiles8a9da732021-01-20 14:32:33 -0500395 return nullptr;
396 }
397
John Stilesdc8ec312021-01-11 11:05:21 -0500398 // Note that we expressly do not worry about precision and overflow here -- we use the maximum
399 // precision to calculate the results and hope the result makes sense.
400 // TODO: detect and handle integer overflow properly.
Brian Osman21d2b6a2021-02-01 13:48:50 -0500401 using SKSL_UINT = uint64_t;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500402 #define RESULT(t, op) std::make_unique<t ## Literal>(context, offset, \
John Stilesdc8ec312021-01-11 11:05:21 -0500403 leftVal op rightVal)
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500404 #define URESULT(t, op) std::make_unique<t ## Literal>(context, offset, \
Brian Osman21d2b6a2021-02-01 13:48:50 -0500405 (SKSL_UINT) leftVal op \
406 (SKSL_UINT) rightVal)
John Stilese80e1692021-03-02 17:02:25 -0500407 if (left->is<IntLiteral>() && right->is<IntLiteral>()) {
408 SKSL_INT leftVal = left->as<IntLiteral>().value();
409 SKSL_INT rightVal = right->as<IntLiteral>().value();
John Stiles45990502021-02-16 10:55:27 -0500410 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500411 case Token::Kind::TK_PLUS: return URESULT(Int, +);
412 case Token::Kind::TK_MINUS: return URESULT(Int, -);
413 case Token::Kind::TK_STAR: return URESULT(Int, *);
414 case Token::Kind::TK_SLASH:
415 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500416 context.fErrors.error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500417 return nullptr;
418 }
419 return RESULT(Int, /);
420 case Token::Kind::TK_PERCENT:
421 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500422 context.fErrors.error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500423 return nullptr;
424 }
425 return RESULT(Int, %);
426 case Token::Kind::TK_BITWISEAND: return RESULT(Int, &);
427 case Token::Kind::TK_BITWISEOR: return RESULT(Int, |);
428 case Token::Kind::TK_BITWISEXOR: return RESULT(Int, ^);
429 case Token::Kind::TK_EQEQ: return RESULT(Bool, ==);
430 case Token::Kind::TK_NEQ: return RESULT(Bool, !=);
431 case Token::Kind::TK_GT: return RESULT(Bool, >);
432 case Token::Kind::TK_GTEQ: return RESULT(Bool, >=);
433 case Token::Kind::TK_LT: return RESULT(Bool, <);
434 case Token::Kind::TK_LTEQ: return RESULT(Bool, <=);
435 case Token::Kind::TK_SHL:
436 if (rightVal >= 0 && rightVal <= 31) {
Brian Osmana7eb6812021-02-01 11:43:05 -0500437 // Left-shifting a negative (or really, any signed) value is undefined behavior
438 // in C++, but not GLSL. Do the shift on unsigned values, to avoid UBSAN.
439 return URESULT(Int, <<);
John Stilesdc8ec312021-01-11 11:05:21 -0500440 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500441 context.fErrors.error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500442 return nullptr;
443 case Token::Kind::TK_SHR:
444 if (rightVal >= 0 && rightVal <= 31) {
445 return RESULT(Int, >>);
446 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500447 context.fErrors.error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500448 return nullptr;
449
450 default:
451 return nullptr;
452 }
453 }
454
455 // Perform constant folding on pairs of floating-point literals.
John Stilese80e1692021-03-02 17:02:25 -0500456 if (left->is<FloatLiteral>() && right->is<FloatLiteral>()) {
457 SKSL_FLOAT leftVal = left->as<FloatLiteral>().value();
458 SKSL_FLOAT rightVal = right->as<FloatLiteral>().value();
John Stiles45990502021-02-16 10:55:27 -0500459 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500460 case Token::Kind::TK_PLUS: return RESULT(Float, +);
461 case Token::Kind::TK_MINUS: return RESULT(Float, -);
462 case Token::Kind::TK_STAR: return RESULT(Float, *);
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500463 case Token::Kind::TK_SLASH: return RESULT(Float, /);
John Stilesdc8ec312021-01-11 11:05:21 -0500464 case Token::Kind::TK_EQEQ: return RESULT(Bool, ==);
465 case Token::Kind::TK_NEQ: return RESULT(Bool, !=);
466 case Token::Kind::TK_GT: return RESULT(Bool, >);
467 case Token::Kind::TK_GTEQ: return RESULT(Bool, >=);
468 case Token::Kind::TK_LT: return RESULT(Bool, <);
469 case Token::Kind::TK_LTEQ: return RESULT(Bool, <=);
470 default: return nullptr;
471 }
472 }
473
474 // Perform constant folding on pairs of vectors.
John Stilesdc8ec312021-01-11 11:05:21 -0500475 if (leftType.isVector() && leftType == rightType) {
476 if (leftType.componentType().isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500477 return simplify_vector<SKSL_FLOAT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500478 }
479 if (leftType.componentType().isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500480 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500481 }
482 return nullptr;
483 }
484
John Stiles508eba72021-01-11 13:07:47 -0500485 // Perform constant folding on vectors against scalars, e.g.: half4(2) + 2
486 if (leftType.isVector() && leftType.componentType() == rightType) {
487 if (rightType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500488 return simplify_vector<SKSL_FLOAT>(context, *left, op,
489 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500490 }
491 if (rightType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500492 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op,
493 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500494 }
495 return nullptr;
496 }
497
498 // Perform constant folding on scalars against vectors, e.g.: 2 + half4(2)
499 if (rightType.isVector() && rightType.componentType() == leftType) {
500 if (leftType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500501 return simplify_vector<SKSL_FLOAT>(context, splat_scalar(*left, right->type()), op,
502 *right);
John Stiles508eba72021-01-11 13:07:47 -0500503 }
504 if (leftType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500505 return simplify_vector<SKSL_INT, SKSL_UINT>(context, splat_scalar(*left, right->type()),
506 op, *right);
John Stiles508eba72021-01-11 13:07:47 -0500507 }
508 return nullptr;
509 }
510
John Stilesdc8ec312021-01-11 11:05:21 -0500511 // Perform constant folding on pairs of matrices.
512 if (leftType.isMatrix() && rightType.isMatrix()) {
513 bool equality;
John Stiles45990502021-02-16 10:55:27 -0500514 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500515 case Token::Kind::TK_EQEQ:
516 equality = true;
517 break;
518 case Token::Kind::TK_NEQ:
519 equality = false;
520 break;
521 default:
522 return nullptr;
523 }
524
John Stilese80e1692021-03-02 17:02:25 -0500525 switch (left->compareConstant(*right)) {
John Stilesdc8ec312021-01-11 11:05:21 -0500526 case Expression::ComparisonResult::kNotEqual:
527 equality = !equality;
528 [[fallthrough]];
529
530 case Expression::ComparisonResult::kEqual:
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500531 return std::make_unique<BoolLiteral>(context, offset, equality);
John Stilesdc8ec312021-01-11 11:05:21 -0500532
533 case Expression::ComparisonResult::kUnknown:
534 return nullptr;
535 }
536 }
537
538 // We aren't able to constant-fold.
539 #undef RESULT
540 #undef URESULT
541 return nullptr;
542}
543
544} // namespace SkSL