blob: 887bc69fc62a1d9dd51b316b269a6ca4409223dd [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 Stiles23521a82021-03-02 17:02:51 -0500102 auto foldedCtor = Constructor::Convert(context, left.fOffset, type, std::move(args));
103 SkASSERT(foldedCtor);
104 return foldedCtor;
John Stilesdc8ec312021-01-11 11:05:21 -0500105 };
106
John Stiles45990502021-02-16 10:55:27 -0500107 switch (op.kind()) {
Brian Osman21d2b6a2021-02-01 13:48:50 -0500108 case Token::Kind::TK_PLUS: return vectorComponentwiseFold([](U a, U b) { return a + b; });
109 case Token::Kind::TK_MINUS: return vectorComponentwiseFold([](U a, U b) { return a - b; });
110 case Token::Kind::TK_STAR: return vectorComponentwiseFold([](U a, U b) { return a * b; });
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500111 case Token::Kind::TK_SLASH: return vectorComponentwiseFold([](T a, T b) { return a / b; });
John Stilesdc8ec312021-01-11 11:05:21 -0500112 default:
113 return nullptr;
114 }
115}
116
John Stiles508eba72021-01-11 13:07:47 -0500117static Constructor splat_scalar(const Expression& scalar, const Type& type) {
118 SkASSERT(type.isVector());
119 SkASSERT(type.componentType() == scalar.type());
120
121 // Use a Constructor to splat the scalar expression across a vector.
122 ExpressionArray arg;
123 arg.push_back(scalar.clone());
John Stiles54f00492021-02-19 11:46:10 -0500124 return Constructor{scalar.fOffset, type, std::move(arg)};
John Stiles508eba72021-01-11 13:07:47 -0500125}
126
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500127bool ConstantFolder::GetConstantInt(const Expression& value, SKSL_INT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500128 const Expression* expr = GetConstantValueForVariable(value);
129 if (!expr->is<IntLiteral>()) {
130 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500131 }
John Stilese80e1692021-03-02 17:02:25 -0500132 *out = expr->as<IntLiteral>().value();
133 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500134}
135
136bool ConstantFolder::GetConstantFloat(const Expression& value, SKSL_FLOAT* out) {
John Stilese80e1692021-03-02 17:02:25 -0500137 const Expression* expr = GetConstantValueForVariable(value);
138 if (!expr->is<FloatLiteral>()) {
139 return false;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500140 }
John Stilese80e1692021-03-02 17:02:25 -0500141 *out = expr->as<FloatLiteral>().value();
142 return true;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500143}
144
145static bool contains_constant_zero(const Expression& expr) {
146 if (expr.is<Constructor>()) {
147 for (const auto& arg : expr.as<Constructor>().arguments()) {
148 if (contains_constant_zero(*arg)) {
149 return true;
150 }
151 }
152 return false;
153 }
John Stilese80e1692021-03-02 17:02:25 -0500154 const Expression* value = ConstantFolder::GetConstantValueForVariable(expr);
155 return (value->is<IntLiteral>() && value->as<IntLiteral>().value() == 0.0) ||
156 (value->is<FloatLiteral>() && value->as<FloatLiteral>().value() == 0.0);
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500157}
158
John Stiles45990502021-02-16 10:55:27 -0500159bool ConstantFolder::ErrorOnDivideByZero(const Context& context, int offset, Operator op,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500160 const Expression& right) {
John Stiles45990502021-02-16 10:55:27 -0500161 switch (op.kind()) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500162 case Token::Kind::TK_SLASH:
163 case Token::Kind::TK_SLASHEQ:
164 case Token::Kind::TK_PERCENT:
165 case Token::Kind::TK_PERCENTEQ:
166 if (contains_constant_zero(right)) {
167 context.fErrors.error(offset, "division by zero");
168 return true;
169 }
170 return false;
171 default:
172 return false;
173 }
174}
175
John Stilese80e1692021-03-02 17:02:25 -0500176const Expression* ConstantFolder::GetConstantValueForVariable(const Expression& inExpr) {
177 for (const Expression* expr = &inExpr;;) {
178 if (!expr->is<VariableReference>()) {
179 break;
180 }
181 const VariableReference& varRef = expr->as<VariableReference>();
182 if (varRef.refKind() != VariableRefKind::kRead) {
183 break;
184 }
185 const Variable& var = *varRef.variable();
186 if (!(var.modifiers().fFlags & Modifiers::kConst_Flag)) {
187 break;
188 }
189 expr = var.initialValue();
190 SkASSERT(expr);
191 if (expr->isCompileTimeConstant()) {
192 return expr;
193 }
194 if (!expr->is<VariableReference>()) {
195 break;
196 }
197 }
198 // We didn't find a compile-time constant at the end. Return the expression as-is.
199 return &inExpr;
200}
201
John Stilesdc8ec312021-01-11 11:05:21 -0500202std::unique_ptr<Expression> ConstantFolder::Simplify(const Context& context,
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500203 int offset,
John Stilese80e1692021-03-02 17:02:25 -0500204 const Expression& leftExpr,
John Stiles45990502021-02-16 10:55:27 -0500205 Operator op,
John Stilese80e1692021-03-02 17:02:25 -0500206 const Expression& rightExpr) {
207 // Replace constant variables with trivial initial-values.
208 const Expression* left = GetConstantValueForVariable(leftExpr);
209 const Expression* right = GetConstantValueForVariable(rightExpr);
210
John Stilesabc3b782021-02-05 10:07:19 -0500211 // If this is the comma operator, the left side is evaluated but not otherwise used in any way.
212 // So if the left side has no side effects, it can just be eliminated entirely.
John Stilese80e1692021-03-02 17:02:25 -0500213 if (op.kind() == Token::Kind::TK_COMMA && !left->hasSideEffects()) {
214 return right->clone();
John Stilesabc3b782021-02-05 10:07:19 -0500215 }
216
John Stiles95d0bad2021-03-01 17:02:28 -0500217 // If this is the assignment operator, and both sides are the same trivial expression, this is
218 // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`).
219 // This can happen when other parts of the assignment are optimized away.
John Stilese80e1692021-03-02 17:02:25 -0500220 if (op.kind() == Token::Kind::TK_EQ && Analysis::IsSelfAssignment(*left, *right)) {
221 return right->clone();
John Stiles95d0bad2021-03-01 17:02:28 -0500222 }
223
John Stiles8a9da732021-01-20 14:32:33 -0500224 // Simplify the expression when both sides are constant Boolean literals.
John Stilese80e1692021-03-02 17:02:25 -0500225 if (left->is<BoolLiteral>() && right->is<BoolLiteral>()) {
226 bool leftVal = left->as<BoolLiteral>().value();
227 bool rightVal = right->as<BoolLiteral>().value();
John Stilesdc8ec312021-01-11 11:05:21 -0500228 bool result;
John Stiles45990502021-02-16 10:55:27 -0500229 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500230 case Token::Kind::TK_LOGICALAND: result = leftVal && rightVal; break;
231 case Token::Kind::TK_LOGICALOR: result = leftVal || rightVal; break;
232 case Token::Kind::TK_LOGICALXOR: result = leftVal ^ rightVal; break;
John Stiles26fdcbb2021-01-19 19:00:31 -0500233 case Token::Kind::TK_EQEQ: result = leftVal == rightVal; break;
234 case Token::Kind::TK_NEQ: result = leftVal != rightVal; break;
John Stilesdc8ec312021-01-11 11:05:21 -0500235 default: return nullptr;
236 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500237 return std::make_unique<BoolLiteral>(context, offset, result);
John Stilesdc8ec312021-01-11 11:05:21 -0500238 }
239
John Stiles8a9da732021-01-20 14:32:33 -0500240 // If the left side is a Boolean literal, apply short-circuit optimizations.
John Stilese80e1692021-03-02 17:02:25 -0500241 if (left->is<BoolLiteral>()) {
242 return short_circuit_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500243 }
244
245 // If the right side is a Boolean literal...
John Stilese80e1692021-03-02 17:02:25 -0500246 if (right->is<BoolLiteral>()) {
John Stiles8a9da732021-01-20 14:32:33 -0500247 // ... and the left side has no side effects...
John Stilese80e1692021-03-02 17:02:25 -0500248 if (!left->hasSideEffects()) {
John Stiles8a9da732021-01-20 14:32:33 -0500249 // We can reverse the expressions and short-circuit optimizations are still valid.
John Stilese80e1692021-03-02 17:02:25 -0500250 return short_circuit_boolean(*right, op, *left);
John Stiles8a9da732021-01-20 14:32:33 -0500251 }
252
253 // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions.
John Stilese80e1692021-03-02 17:02:25 -0500254 return eliminate_no_op_boolean(*left, op, *right);
John Stiles8a9da732021-01-20 14:32:33 -0500255 }
256
John Stilese80e1692021-03-02 17:02:25 -0500257 if (ErrorOnDivideByZero(context, offset, op, *right)) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500258 return nullptr;
259 }
260
John Stiles8a9da732021-01-20 14:32:33 -0500261 // Other than the short-circuit cases above, constant folding requires both sides to be constant
John Stilese80e1692021-03-02 17:02:25 -0500262 if (!left->isCompileTimeConstant() || !right->isCompileTimeConstant()) {
John Stiles8a9da732021-01-20 14:32:33 -0500263 return nullptr;
264 }
265
John Stilesdc8ec312021-01-11 11:05:21 -0500266 // Note that we expressly do not worry about precision and overflow here -- we use the maximum
267 // precision to calculate the results and hope the result makes sense.
268 // TODO: detect and handle integer overflow properly.
Brian Osman21d2b6a2021-02-01 13:48:50 -0500269 using SKSL_UINT = uint64_t;
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500270 #define RESULT(t, op) std::make_unique<t ## Literal>(context, offset, \
John Stilesdc8ec312021-01-11 11:05:21 -0500271 leftVal op rightVal)
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500272 #define URESULT(t, op) std::make_unique<t ## Literal>(context, offset, \
Brian Osman21d2b6a2021-02-01 13:48:50 -0500273 (SKSL_UINT) leftVal op \
274 (SKSL_UINT) rightVal)
John Stilese80e1692021-03-02 17:02:25 -0500275 if (left->is<IntLiteral>() && right->is<IntLiteral>()) {
276 SKSL_INT leftVal = left->as<IntLiteral>().value();
277 SKSL_INT rightVal = right->as<IntLiteral>().value();
John Stiles45990502021-02-16 10:55:27 -0500278 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500279 case Token::Kind::TK_PLUS: return URESULT(Int, +);
280 case Token::Kind::TK_MINUS: return URESULT(Int, -);
281 case Token::Kind::TK_STAR: return URESULT(Int, *);
282 case Token::Kind::TK_SLASH:
283 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500284 context.fErrors.error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500285 return nullptr;
286 }
287 return RESULT(Int, /);
288 case Token::Kind::TK_PERCENT:
289 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500290 context.fErrors.error(offset, "arithmetic overflow");
John Stilesdc8ec312021-01-11 11:05:21 -0500291 return nullptr;
292 }
293 return RESULT(Int, %);
294 case Token::Kind::TK_BITWISEAND: return RESULT(Int, &);
295 case Token::Kind::TK_BITWISEOR: return RESULT(Int, |);
296 case Token::Kind::TK_BITWISEXOR: return RESULT(Int, ^);
297 case Token::Kind::TK_EQEQ: return RESULT(Bool, ==);
298 case Token::Kind::TK_NEQ: return RESULT(Bool, !=);
299 case Token::Kind::TK_GT: return RESULT(Bool, >);
300 case Token::Kind::TK_GTEQ: return RESULT(Bool, >=);
301 case Token::Kind::TK_LT: return RESULT(Bool, <);
302 case Token::Kind::TK_LTEQ: return RESULT(Bool, <=);
303 case Token::Kind::TK_SHL:
304 if (rightVal >= 0 && rightVal <= 31) {
Brian Osmana7eb6812021-02-01 11:43:05 -0500305 // Left-shifting a negative (or really, any signed) value is undefined behavior
306 // in C++, but not GLSL. Do the shift on unsigned values, to avoid UBSAN.
307 return URESULT(Int, <<);
John Stilesdc8ec312021-01-11 11:05:21 -0500308 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500309 context.fErrors.error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500310 return nullptr;
311 case Token::Kind::TK_SHR:
312 if (rightVal >= 0 && rightVal <= 31) {
313 return RESULT(Int, >>);
314 }
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500315 context.fErrors.error(offset, "shift value out of range");
John Stilesdc8ec312021-01-11 11:05:21 -0500316 return nullptr;
317
318 default:
319 return nullptr;
320 }
321 }
322
323 // Perform constant folding on pairs of floating-point literals.
John Stilese80e1692021-03-02 17:02:25 -0500324 if (left->is<FloatLiteral>() && right->is<FloatLiteral>()) {
325 SKSL_FLOAT leftVal = left->as<FloatLiteral>().value();
326 SKSL_FLOAT rightVal = right->as<FloatLiteral>().value();
John Stiles45990502021-02-16 10:55:27 -0500327 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500328 case Token::Kind::TK_PLUS: return RESULT(Float, +);
329 case Token::Kind::TK_MINUS: return RESULT(Float, -);
330 case Token::Kind::TK_STAR: return RESULT(Float, *);
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500331 case Token::Kind::TK_SLASH: return RESULT(Float, /);
John Stilesdc8ec312021-01-11 11:05:21 -0500332 case Token::Kind::TK_EQEQ: return RESULT(Bool, ==);
333 case Token::Kind::TK_NEQ: return RESULT(Bool, !=);
334 case Token::Kind::TK_GT: return RESULT(Bool, >);
335 case Token::Kind::TK_GTEQ: return RESULT(Bool, >=);
336 case Token::Kind::TK_LT: return RESULT(Bool, <);
337 case Token::Kind::TK_LTEQ: return RESULT(Bool, <=);
338 default: return nullptr;
339 }
340 }
341
342 // Perform constant folding on pairs of vectors.
John Stilese80e1692021-03-02 17:02:25 -0500343 const Type& leftType = left->type();
344 const Type& rightType = right->type();
John Stilesdc8ec312021-01-11 11:05:21 -0500345 if (leftType.isVector() && leftType == rightType) {
346 if (leftType.componentType().isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500347 return simplify_vector<SKSL_FLOAT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500348 }
349 if (leftType.componentType().isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500350 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op, *right);
John Stilesdc8ec312021-01-11 11:05:21 -0500351 }
352 return nullptr;
353 }
354
John Stiles508eba72021-01-11 13:07:47 -0500355 // Perform constant folding on vectors against scalars, e.g.: half4(2) + 2
356 if (leftType.isVector() && leftType.componentType() == rightType) {
357 if (rightType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500358 return simplify_vector<SKSL_FLOAT>(context, *left, op,
359 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500360 }
361 if (rightType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500362 return simplify_vector<SKSL_INT, SKSL_UINT>(context, *left, op,
363 splat_scalar(*right, left->type()));
John Stiles508eba72021-01-11 13:07:47 -0500364 }
365 return nullptr;
366 }
367
368 // Perform constant folding on scalars against vectors, e.g.: 2 + half4(2)
369 if (rightType.isVector() && rightType.componentType() == leftType) {
370 if (leftType.isFloat()) {
John Stilese80e1692021-03-02 17:02:25 -0500371 return simplify_vector<SKSL_FLOAT>(context, splat_scalar(*left, right->type()), op,
372 *right);
John Stiles508eba72021-01-11 13:07:47 -0500373 }
374 if (leftType.isInteger()) {
John Stilese80e1692021-03-02 17:02:25 -0500375 return simplify_vector<SKSL_INT, SKSL_UINT>(context, splat_scalar(*left, right->type()),
376 op, *right);
John Stiles508eba72021-01-11 13:07:47 -0500377 }
378 return nullptr;
379 }
380
John Stilesdc8ec312021-01-11 11:05:21 -0500381 // Perform constant folding on pairs of matrices.
382 if (leftType.isMatrix() && rightType.isMatrix()) {
383 bool equality;
John Stiles45990502021-02-16 10:55:27 -0500384 switch (op.kind()) {
John Stilesdc8ec312021-01-11 11:05:21 -0500385 case Token::Kind::TK_EQEQ:
386 equality = true;
387 break;
388 case Token::Kind::TK_NEQ:
389 equality = false;
390 break;
391 default:
392 return nullptr;
393 }
394
John Stilese80e1692021-03-02 17:02:25 -0500395 switch (left->compareConstant(*right)) {
John Stilesdc8ec312021-01-11 11:05:21 -0500396 case Expression::ComparisonResult::kNotEqual:
397 equality = !equality;
398 [[fallthrough]];
399
400 case Expression::ComparisonResult::kEqual:
Ethan Nicholasc0f98152021-02-05 16:21:10 -0500401 return std::make_unique<BoolLiteral>(context, offset, equality);
John Stilesdc8ec312021-01-11 11:05:21 -0500402
403 case Expression::ComparisonResult::kUnknown:
404 return nullptr;
405 }
406 }
407
408 // We aren't able to constant-fold.
409 #undef RESULT
410 #undef URESULT
411 return nullptr;
412}
413
414} // namespace SkSL