blob: 57a5194c7ebc3f36508f63b33c15c5b877747268 [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"
20#include "src/sksl/ir/SkSLType.h"
21#include "src/sksl/ir/SkSLVariable.h"
22#include "src/sksl/ir/SkSLVariableReference.h"
23
24namespace SkSL {
25
John Stiles8a9da732021-01-20 14:32:33 -050026static std::unique_ptr<Expression> eliminate_no_op_boolean(const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050027 Operator op,
John Stiles8a9da732021-01-20 14:32:33 -050028 const Expression& right) {
29 SkASSERT(right.is<BoolLiteral>());
30 bool rightVal = right.as<BoolLiteral>().value();
31
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) {
48 SkASSERT(left.is<BoolLiteral>());
49 bool leftVal = left.as<BoolLiteral>().value();
50
John Stiles8a9da732021-01-20 14:32:33 -050051 // When the literal is on the left, we can sometimes eliminate the other expression entirely.
John Stiles45990502021-02-16 10:55:27 -050052 if ((op.kind() == Token::Kind::TK_LOGICALAND && !leftVal) || // (false && expr) -> (false)
53 (op.kind() == Token::Kind::TK_LOGICALOR && leftVal)) { // (true || expr) -> (true)
John Stiles8a9da732021-01-20 14:32:33 -050054
55 return left.clone();
John Stilesdc8ec312021-01-11 11:05:21 -050056 }
57
John Stiles8a9da732021-01-20 14:32:33 -050058 // We can't eliminate the right-side expression via short-circuit, but we might still be able to
59 // simplify away a no-op expression.
60 return eliminate_no_op_boolean(right, op, left);
John Stilesdc8ec312021-01-11 11:05:21 -050061}
62
Brian Osman21d2b6a2021-02-01 13:48:50 -050063// 'T' is the actual stored type of the literal data (SKSL_FLOAT or SKSL_INT).
64// 'U' is an unsigned version of that, used to perform addition, subtraction, and multiplication,
65// to avoid signed-integer overflow errors. This mimics the use of URESULT vs. RESULT when doing
66// scalar folding in Simplify, later in this file.
67template <typename T, typename U = T>
John Stilesdc8ec312021-01-11 11:05:21 -050068static std::unique_ptr<Expression> simplify_vector(const Context& context,
John Stilesdc8ec312021-01-11 11:05:21 -050069 const Expression& left,
John Stiles45990502021-02-16 10:55:27 -050070 Operator op,
John Stilesdc8ec312021-01-11 11:05:21 -050071 const Expression& right) {
John Stiles508eba72021-01-11 13:07:47 -050072 SkASSERT(left.type().isVector());
John Stilesdc8ec312021-01-11 11:05:21 -050073 SkASSERT(left.type() == right.type());
74 const Type& type = left.type();
75
76 // Handle boolean operations: == !=
John Stiles45990502021-02-16 10:55:27 -050077 if (op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ) {
78 bool equality = (op.kind() == Token::Kind::TK_EQEQ);
John Stilesdc8ec312021-01-11 11:05:21 -050079
80 switch (left.compareConstant(right)) {
81 case Expression::ComparisonResult::kNotEqual:
82 equality = !equality;
83 [[fallthrough]];
84
85 case Expression::ComparisonResult::kEqual:
86 return std::make_unique<BoolLiteral>(context, left.fOffset, equality);
87
88 case Expression::ComparisonResult::kUnknown:
89 return nullptr;
90 }
91 }
92
93 // Handle floating-point arithmetic: + - * /
John Stiles54f00492021-02-19 11:46:10 -050094 const auto vectorComponentwiseFold = [&](auto foldFn) -> std::unique_ptr<Expression> {
John Stilesdc8ec312021-01-11 11:05:21 -050095 const Type& componentType = type.componentType();
96 ExpressionArray args;
97 args.reserve_back(type.columns());
98 for (int i = 0; i < type.columns(); i++) {
Brian Osman21d2b6a2021-02-01 13:48:50 -050099 U value = foldFn(left.getVecComponent<T>(i), right.getVecComponent<T>(i));
John Stilesdc8ec312021-01-11 11:05:21 -0500100 args.push_back(std::make_unique<Literal<T>>(left.fOffset, value, &componentType));
101 }
John Stiles54f00492021-02-19 11:46:10 -0500102 return Constructor::Make(context, left.fOffset, type, std::move(args));
John Stilesdc8ec312021-01-11 11:05:21 -0500103 };
104
John Stiles45990502021-02-16 10:55:27 -0500105 switch (op.kind()) {
Brian Osman21d2b6a2021-02-01 13:48:50 -0500106 case Token::Kind::TK_PLUS: return vectorComponentwiseFold([](U a, U b) { return a + b; });
107 case Token::Kind::TK_MINUS: return vectorComponentwiseFold([](U a, U b) { return a - b; });
108 case Token::Kind::TK_STAR: return vectorComponentwiseFold([](U a, U b) { return a * b; });
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500109 case Token::Kind::TK_SLASH: return vectorComponentwiseFold([](T a, T b) { return a / b; });
John Stilesdc8ec312021-01-11 11:05:21 -0500110 default:
111 return nullptr;
112 }
113}
114
John Stiles508eba72021-01-11 13:07:47 -0500115static Constructor splat_scalar(const Expression& scalar, const Type& type) {
116 SkASSERT(type.isVector());
117 SkASSERT(type.componentType() == scalar.type());
118
119 // Use a Constructor to splat the scalar expression across a vector.
120 ExpressionArray arg;
121 arg.push_back(scalar.clone());
John Stiles54f00492021-02-19 11:46:10 -0500122 return Constructor{scalar.fOffset, type, std::move(arg)};
John Stiles508eba72021-01-11 13:07:47 -0500123}
124
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500125bool ConstantFolder::GetConstantInt(const Expression& value, SKSL_INT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500126 const Expression* expr = GetConstantValueForVariable(value);
127 if (!expr->is<IntLiteral>()) {
128 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500129 }
John Stilese80e1692021-03-02 17:02:25 -0500130 *out = expr->as<IntLiteral>().value();
131 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500132}
133
134bool ConstantFolder::GetConstantFloat(const Expression& value, SKSL_FLOAT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500135 const Expression* expr = GetConstantValueForVariable(value);
136 if (!expr->is<FloatLiteral>()) {
137 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500138 }
John Stilese80e1692021-03-02 17:02:25 -0500139 *out = expr->as<FloatLiteral>().value();
140 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500141}
142
143static bool contains_constant_zero(const Expression& expr) {
144 if (expr.is<Constructor>()) {
145 for (const auto& arg : expr.as<Constructor>().arguments()) {
146 if (contains_constant_zero(*arg)) {
147 return true;
148 }
149 }
150 return false;
151 }
John Stilese80e1692021-03-02 17:02:25 -0500152 const Expression* value = ConstantFolder::GetConstantValueForVariable(expr);
153 return (value->is<IntLiteral>() && value->as<IntLiteral>().value() == 0.0) ||
154 (value->is<FloatLiteral>() && value->as<FloatLiteral>().value() == 0.0);
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500155}
156
John Stiles45990502021-02-16 10:55:27 -0500157bool ConstantFolder::ErrorOnDivideByZero(const Context& context, int offset, Operator op,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500158 const Expression& right) {
John Stiles45990502021-02-16 10:55:27 -0500159 switch (op.kind()) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500160 case Token::Kind::TK_SLASH:
161 case Token::Kind::TK_SLASHEQ:
162 case Token::Kind::TK_PERCENT:
163 case Token::Kind::TK_PERCENTEQ:
164 if (contains_constant_zero(right)) {
165 context.fErrors.error(offset, "division by zero");
166 return true;
167 }
168 return false;
169 default:
170 return false;
171 }
172}
173
John Stilese80e1692021-03-02 17:02:25 -0500174const Expression* ConstantFolder::GetConstantValueForVariable(const Expression& inExpr) {
175 for (const Expression* expr = &inExpr;;) {
176 if (!expr->is<VariableReference>()) {
177 break;
178 }
179 const VariableReference& varRef = expr->as<VariableReference>();
180 if (varRef.refKind() != VariableRefKind::kRead) {
181 break;
182 }
183 const Variable& var = *varRef.variable();
184 if (!(var.modifiers().fFlags & Modifiers::kConst_Flag)) {
185 break;
186 }
187 expr = var.initialValue();
188 SkASSERT(expr);
189 if (expr->isCompileTimeConstant()) {
190 return expr;
191 }
192 if (!expr->is<VariableReference>()) {
193 break;
194 }
195 }
196 // We didn't find a compile-time constant at the end. Return the expression as-is.
197 return &inExpr;
198}
199
John Stilesdc8ec312021-01-11 11:05:21 -0500200std::unique_ptr<Expression> ConstantFolder::Simplify(const Context& context,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500201 int offset,
John Stilese80e1692021-03-02 17:02:25 -0500202 const Expression& leftExpr,
John Stiles45990502021-02-16 10:55:27 -0500203 Operator op,
John Stilese80e1692021-03-02 17:02:25 -0500204 const Expression& rightExpr) {
205 // Replace constant variables with trivial initial-values.
206 const Expression* left = GetConstantValueForVariable(leftExpr);
207 const Expression* right = GetConstantValueForVariable(rightExpr);
208
John Stilesabc3b782021-02-05 10:07:19 -0500209 // If this is the comma operator, the left side is evaluated but not otherwise used in any way.
210 // So if the left side has no side effects, it can just be eliminated entirely.
John Stilese80e1692021-03-02 17:02:25 -0500211 if (op.kind() == Token::Kind::TK_COMMA && !left->hasSideEffects()) {
212 return right->clone();
John Stilesabc3b782021-02-05 10:07:19 -0500213 }
214
John Stiles95d0bad2021-03-01 17:02:28 -0500215 // If this is the assignment operator, and both sides are the same trivial expression, this is
216 // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`).
217 // This can happen when other parts of the assignment are optimized away.
John Stilese80e1692021-03-02 17:02:25 -0500218 if (op.kind() == Token::Kind::TK_EQ && Analysis::IsSelfAssignment(*left, *right)) {
219 return right->clone();
John Stiles95d0bad2021-03-01 17:02:28 -0500220 }
221
John Stiles8a9da732021-01-20 14:32:33 -0500222 // Simplify the expression when both sides are constant Boolean literals.
John Stilese80e1692021-03-02 17:02:25 -0500223 if (left->is<BoolLiteral>() && right->is<BoolLiteral>()) {
224 bool leftVal = left->as<BoolLiteral>().value();
225 bool rightVal = right->as<BoolLiteral>().value();
John Stilesdc8ec312021-01-11 11:05:21 -0500226 bool result;
John Stiles45990502021-02-16 10:55:27 -0500227 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500228 case Token::Kind::TK_LOGICALAND: result = leftVal && rightVal; break;
229 case Token::Kind::TK_LOGICALOR: result = leftVal || rightVal; break;
230 case Token::Kind::TK_LOGICALXOR: result = leftVal ^ rightVal; break;
John Stiles26fdcbb2021-01-19 19:00:31 -0500231 case Token::Kind::TK_EQEQ: result = leftVal == rightVal; break;
232 case Token::Kind::TK_NEQ: result = leftVal != rightVal; break;
John Stilesdc8ec312021-01-11 11:05:21 -0500233 default: return nullptr;
234 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500235 return std::make_unique<BoolLiteral>(context, offset, result);
John Stilesdc8ec312021-01-11 11:05:21 -0500236 }
237
John Stiles8a9da732021-01-20 14:32:33 -0500238 // If the left side is a Boolean literal, apply short-circuit optimizations.
John Stilese80e1692021-03-02 17:02:25 -0500239 if (left->is<BoolLiteral>()) {
240 return short_circuit_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500241 }
242
243 // If the right side is a Boolean literal...
John Stilese80e1692021-03-02 17:02:25 -0500244 if (right->is<BoolLiteral>()) {
John Stiles8a9da732021-01-20 14:32:33 -0500245 // ... and the left side has no side effects...
John Stilese80e1692021-03-02 17:02:25 -0500246 if (!left->hasSideEffects()) {
John Stiles8a9da732021-01-20 14:32:33 -0500247 // We can reverse the expressions and short-circuit optimizations are still valid.
John Stilese80e1692021-03-02 17:02:25 -0500248 return short_circuit_boolean(*right, op, *left);
John Stiles8a9da732021-01-20 14:32:33 -0500249 }
250
251 // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions.
John Stilese80e1692021-03-02 17:02:25 -0500252 return eliminate_no_op_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500253 }
254
John Stilese80e1692021-03-02 17:02:25 -0500255 if (ErrorOnDivideByZero(context, offset, op, *right)) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500256 return nullptr;
257 }
258
John Stiles8a9da732021-01-20 14:32:33 -0500259 // Other than the short-circuit cases above, constant folding requires both sides to be constant
John Stilese80e1692021-03-02 17:02:25 -0500260 if (!left->isCompileTimeConstant() || !right->isCompileTimeConstant()) {
John Stiles8a9da732021-01-20 14:32:33 -0500261 return nullptr;
262 }
263
John Stilesdc8ec312021-01-11 11:05:21 -0500264 // Note that we expressly do not worry about precision and overflow here -- we use the maximum
265 // precision to calculate the results and hope the result makes sense.
266 // TODO: detect and handle integer overflow properly.
Brian Osman21d2b6a2021-02-01 13:48:50 -0500267 using SKSL_UINT = uint64_t;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500268 #define RESULT(t, op) std::make_unique<t ## Literal>(context, offset, \
John Stilesdc8ec312021-01-11 11:05:21 -0500269 leftVal op rightVal)
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500270 #define URESULT(t, op) std::make_unique<t ## Literal>(context, offset, \
Brian Osman21d2b6a2021-02-01 13:48:50 -0500271 (SKSL_UINT) leftVal op \
272 (SKSL_UINT) rightVal)
John Stilese80e1692021-03-02 17:02:25 -0500273 if (left->is<IntLiteral>() && right->is<IntLiteral>()) {
274 SKSL_INT leftVal = left->as<IntLiteral>().value();
275 SKSL_INT rightVal = right->as<IntLiteral>().value();
John Stiles45990502021-02-16 10:55:27 -0500276 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500277 case Token::Kind::TK_PLUS: return URESULT(Int, +);
278 case Token::Kind::TK_MINUS: return URESULT(Int, -);
279 case Token::Kind::TK_STAR: return URESULT(Int, *);
280 case Token::Kind::TK_SLASH:
281 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500282 context.fErrors.error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500283 return nullptr;
284 }
285 return RESULT(Int, /);
286 case Token::Kind::TK_PERCENT:
287 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500288 context.fErrors.error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500289 return nullptr;
290 }
291 return RESULT(Int, %);
292 case Token::Kind::TK_BITWISEAND: return RESULT(Int, &);
293 case Token::Kind::TK_BITWISEOR: return RESULT(Int, |);
294 case Token::Kind::TK_BITWISEXOR: return RESULT(Int, ^);
295 case Token::Kind::TK_EQEQ: return RESULT(Bool, ==);
296 case Token::Kind::TK_NEQ: return RESULT(Bool, !=);
297 case Token::Kind::TK_GT: return RESULT(Bool, >);
298 case Token::Kind::TK_GTEQ: return RESULT(Bool, >=);
299 case Token::Kind::TK_LT: return RESULT(Bool, <);
300 case Token::Kind::TK_LTEQ: return RESULT(Bool, <=);
301 case Token::Kind::TK_SHL:
302 if (rightVal >= 0 && rightVal <= 31) {
Brian Osmana7eb6812021-02-01 11:43:05 -0500303 // Left-shifting a negative (or really, any signed) value is undefined behavior
304 // in C++, but not GLSL. Do the shift on unsigned values, to avoid UBSAN.
305 return URESULT(Int, <<);
John Stilesdc8ec312021-01-11 11:05:21 -0500306 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500307 context.fErrors.error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500308 return nullptr;
309 case Token::Kind::TK_SHR:
310 if (rightVal >= 0 && rightVal <= 31) {
311 return RESULT(Int, >>);
312 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500313 context.fErrors.error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500314 return nullptr;
315
316 default:
317 return nullptr;
318 }
319 }
320
321 // Perform constant folding on pairs of floating-point literals.
John Stilese80e1692021-03-02 17:02:25 -0500322 if (left->is<FloatLiteral>() && right->is<FloatLiteral>()) {
323 SKSL_FLOAT leftVal = left->as<FloatLiteral>().value();
324 SKSL_FLOAT rightVal = right->as<FloatLiteral>().value();
John Stiles45990502021-02-16 10:55:27 -0500325 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500326 case Token::Kind::TK_PLUS: return RESULT(Float, +);
327 case Token::Kind::TK_MINUS: return RESULT(Float, -);
328 case Token::Kind::TK_STAR: return RESULT(Float, *);
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500329 case Token::Kind::TK_SLASH: return RESULT(Float, /);
John Stilesdc8ec312021-01-11 11:05:21 -0500330 case Token::Kind::TK_EQEQ: return RESULT(Bool, ==);
331 case Token::Kind::TK_NEQ: return RESULT(Bool, !=);
332 case Token::Kind::TK_GT: return RESULT(Bool, >);
333 case Token::Kind::TK_GTEQ: return RESULT(Bool, >=);
334 case Token::Kind::TK_LT: return RESULT(Bool, <);
335 case Token::Kind::TK_LTEQ: return RESULT(Bool, <=);
336 default: return nullptr;
337 }
338 }
339
340 // Perform constant folding on pairs of vectors.
John Stilese80e1692021-03-02 17:02:25 -0500341 const Type& leftType = left->type();
342 const Type& rightType = right->type();
John Stilesdc8ec312021-01-11 11:05:21 -0500343 if (leftType.isVector() && leftType == rightType) {
344 if (leftType.componentType().isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500345 return simplify_vector<SKSL_FLOAT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500346 }
347 if (leftType.componentType().isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500348 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500349 }
350 return nullptr;
351 }
352
John Stiles508eba72021-01-11 13:07:47 -0500353 // Perform constant folding on vectors against scalars, e.g.: half4(2) + 2
354 if (leftType.isVector() && leftType.componentType() == rightType) {
355 if (rightType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500356 return simplify_vector<SKSL_FLOAT>(context, *left, op,
357 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500358 }
359 if (rightType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500360 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op,
361 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500362 }
363 return nullptr;
364 }
365
366 // Perform constant folding on scalars against vectors, e.g.: 2 + half4(2)
367 if (rightType.isVector() && rightType.componentType() == leftType) {
368 if (leftType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500369 return simplify_vector<SKSL_FLOAT>(context, splat_scalar(*left, right->type()), op,
370 *right);
John Stiles508eba72021-01-11 13:07:47 -0500371 }
372 if (leftType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500373 return simplify_vector<SKSL_INT, SKSL_UINT>(context, splat_scalar(*left, right->type()),
374 op, *right);
John Stiles508eba72021-01-11 13:07:47 -0500375 }
376 return nullptr;
377 }
378
John Stilesdc8ec312021-01-11 11:05:21 -0500379 // Perform constant folding on pairs of matrices.
380 if (leftType.isMatrix() && rightType.isMatrix()) {
381 bool equality;
John Stiles45990502021-02-16 10:55:27 -0500382 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500383 case Token::Kind::TK_EQEQ:
384 equality = true;
385 break;
386 case Token::Kind::TK_NEQ:
387 equality = false;
388 break;
389 default:
390 return nullptr;
391 }
392
John Stilese80e1692021-03-02 17:02:25 -0500393 switch (left->compareConstant(*right)) {
John Stilesdc8ec312021-01-11 11:05:21 -0500394 case Expression::ComparisonResult::kNotEqual:
395 equality = !equality;
396 [[fallthrough]];
397
398 case Expression::ComparisonResult::kEqual:
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500399 return std::make_unique<BoolLiteral>(context, offset, equality);
John Stilesdc8ec312021-01-11 11:05:21 -0500400
401 case Expression::ComparisonResult::kUnknown:
402 return nullptr;
403 }
404 }
405
406 // We aren't able to constant-fold.
407 #undef RESULT
408 #undef URESULT
409 return nullptr;
410}
411
412} // namespace SkSL