1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2020 Google LLC
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "src/sksl/SkSLConstantFolder.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include <limits>
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ci#include "include/sksl/SkSLErrorReporter.h"
13cb93a386Sopenharmony_ci#include "src/sksl/SkSLAnalysis.h"
14cb93a386Sopenharmony_ci#include "src/sksl/SkSLContext.h"
15cb93a386Sopenharmony_ci#include "src/sksl/SkSLProgramSettings.h"
16cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLBinaryExpression.h"
17cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructor.h"
18cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorCompound.h"
19cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLConstructorSplat.h"
20cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLExpression.h"
21cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLLiteral.h"
22cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLPrefixExpression.h"
23cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLType.h"
24cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLVariable.h"
25cb93a386Sopenharmony_ci#include "src/sksl/ir/SkSLVariableReference.h"
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_cinamespace SkSL {
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> eliminate_no_op_boolean(const Expression& left,
30cb93a386Sopenharmony_ci                                                           Operator op,
31cb93a386Sopenharmony_ci                                                           const Expression& right) {
32cb93a386Sopenharmony_ci    bool rightVal = right.as<Literal>().boolValue();
33cb93a386Sopenharmony_ci
34cb93a386Sopenharmony_ci    // Detect no-op Boolean expressions and optimize them away.
35cb93a386Sopenharmony_ci    if ((op.kind() == Token::Kind::TK_LOGICALAND && rightVal)  ||  // (expr && true)  -> (expr)
36cb93a386Sopenharmony_ci        (op.kind() == Token::Kind::TK_LOGICALOR  && !rightVal) ||  // (expr || false) -> (expr)
37cb93a386Sopenharmony_ci        (op.kind() == Token::Kind::TK_LOGICALXOR && !rightVal) ||  // (expr ^^ false) -> (expr)
38cb93a386Sopenharmony_ci        (op.kind() == Token::Kind::TK_EQEQ       && rightVal)  ||  // (expr == true)  -> (expr)
39cb93a386Sopenharmony_ci        (op.kind() == Token::Kind::TK_NEQ        && !rightVal)) {  // (expr != false) -> (expr)
40cb93a386Sopenharmony_ci
41cb93a386Sopenharmony_ci        return left.clone();
42cb93a386Sopenharmony_ci    }
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci    return nullptr;
45cb93a386Sopenharmony_ci}
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> short_circuit_boolean(const Expression& left,
48cb93a386Sopenharmony_ci                                                         Operator op,
49cb93a386Sopenharmony_ci                                                         const Expression& right) {
50cb93a386Sopenharmony_ci    bool leftVal = left.as<Literal>().boolValue();
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci    // When the literal is on the left, we can sometimes eliminate the other expression entirely.
53cb93a386Sopenharmony_ci    if ((op.kind() == Token::Kind::TK_LOGICALAND && !leftVal) ||  // (false && expr) -> (false)
54cb93a386Sopenharmony_ci        (op.kind() == Token::Kind::TK_LOGICALOR  && leftVal)) {   // (true  || expr) -> (true)
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci        return left.clone();
57cb93a386Sopenharmony_ci    }
58cb93a386Sopenharmony_ci
59cb93a386Sopenharmony_ci    // We can't eliminate the right-side expression via short-circuit, but we might still be able to
60cb93a386Sopenharmony_ci    // simplify away a no-op expression.
61cb93a386Sopenharmony_ci    return eliminate_no_op_boolean(right, op, left);
62cb93a386Sopenharmony_ci}
63cb93a386Sopenharmony_ci
64cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> simplify_vector_equality(const Context& context,
65cb93a386Sopenharmony_ci                                                            const Expression& left,
66cb93a386Sopenharmony_ci                                                            Operator op,
67cb93a386Sopenharmony_ci                                                            const Expression& right) {
68cb93a386Sopenharmony_ci    if (op.kind() == Token::Kind::TK_EQEQ || op.kind() == Token::Kind::TK_NEQ) {
69cb93a386Sopenharmony_ci        bool equality = (op.kind() == Token::Kind::TK_EQEQ);
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci        switch (left.compareConstant(right)) {
72cb93a386Sopenharmony_ci            case Expression::ComparisonResult::kNotEqual:
73cb93a386Sopenharmony_ci                equality = !equality;
74cb93a386Sopenharmony_ci                [[fallthrough]];
75cb93a386Sopenharmony_ci
76cb93a386Sopenharmony_ci            case Expression::ComparisonResult::kEqual:
77cb93a386Sopenharmony_ci                return Literal::MakeBool(context, left.fLine, equality);
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci            case Expression::ComparisonResult::kUnknown:
80cb93a386Sopenharmony_ci                break;
81cb93a386Sopenharmony_ci        }
82cb93a386Sopenharmony_ci    }
83cb93a386Sopenharmony_ci    return nullptr;
84cb93a386Sopenharmony_ci}
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> simplify_vector(const Context& context,
87cb93a386Sopenharmony_ci                                                   const Expression& left,
88cb93a386Sopenharmony_ci                                                   Operator op,
89cb93a386Sopenharmony_ci                                                   const Expression& right) {
90cb93a386Sopenharmony_ci    SkASSERT(left.type().isVector());
91cb93a386Sopenharmony_ci    SkASSERT(left.type() == right.type());
92cb93a386Sopenharmony_ci    const Type& type = left.type();
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_ci    // Handle equality operations: == !=
95cb93a386Sopenharmony_ci    if (std::unique_ptr<Expression> result = simplify_vector_equality(context, left, op, right)) {
96cb93a386Sopenharmony_ci        return result;
97cb93a386Sopenharmony_ci    }
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci    // Handle floating-point arithmetic: + - * /
100cb93a386Sopenharmony_ci    using FoldFn = double (*)(double, double);
101cb93a386Sopenharmony_ci    FoldFn foldFn;
102cb93a386Sopenharmony_ci    switch (op.kind()) {
103cb93a386Sopenharmony_ci        case Token::Kind::TK_PLUS:  foldFn = +[](double a, double b) { return a + b; }; break;
104cb93a386Sopenharmony_ci        case Token::Kind::TK_MINUS: foldFn = +[](double a, double b) { return a - b; }; break;
105cb93a386Sopenharmony_ci        case Token::Kind::TK_STAR:  foldFn = +[](double a, double b) { return a * b; }; break;
106cb93a386Sopenharmony_ci        case Token::Kind::TK_SLASH: foldFn = +[](double a, double b) { return a / b; }; break;
107cb93a386Sopenharmony_ci        default:
108cb93a386Sopenharmony_ci            return nullptr;
109cb93a386Sopenharmony_ci    }
110cb93a386Sopenharmony_ci
111cb93a386Sopenharmony_ci    const Type& componentType = type.componentType();
112cb93a386Sopenharmony_ci    double minimumValue = -INFINITY, maximumValue = INFINITY;
113cb93a386Sopenharmony_ci    if (componentType.isInteger()) {
114cb93a386Sopenharmony_ci        minimumValue = componentType.minimumValue();
115cb93a386Sopenharmony_ci        maximumValue = componentType.maximumValue();
116cb93a386Sopenharmony_ci    }
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_ci    ExpressionArray args;
119cb93a386Sopenharmony_ci    args.reserve_back(type.columns());
120cb93a386Sopenharmony_ci    for (int i = 0; i < type.columns(); i++) {
121cb93a386Sopenharmony_ci        double value = foldFn(*left.getConstantValue(i), *right.getConstantValue(i));
122cb93a386Sopenharmony_ci        if (value < minimumValue || value > maximumValue) {
123cb93a386Sopenharmony_ci            return nullptr;
124cb93a386Sopenharmony_ci        }
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ci        args.push_back(Literal::Make(left.fLine, value, &componentType));
127cb93a386Sopenharmony_ci    }
128cb93a386Sopenharmony_ci    return ConstructorCompound::Make(context, left.fLine, type, std::move(args));
129cb93a386Sopenharmony_ci}
130cb93a386Sopenharmony_ci
131cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> cast_expression(const Context& context,
132cb93a386Sopenharmony_ci                                                   const Expression& expr,
133cb93a386Sopenharmony_ci                                                   const Type& type) {
134cb93a386Sopenharmony_ci    ExpressionArray ctorArgs;
135cb93a386Sopenharmony_ci    ctorArgs.push_back(expr.clone());
136cb93a386Sopenharmony_ci    return Constructor::Convert(context, expr.fLine, type, std::move(ctorArgs));
137cb93a386Sopenharmony_ci}
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_cibool ConstantFolder::GetConstantInt(const Expression& value, SKSL_INT* out) {
140cb93a386Sopenharmony_ci    const Expression* expr = GetConstantValueForVariable(value);
141cb93a386Sopenharmony_ci    if (!expr->isIntLiteral()) {
142cb93a386Sopenharmony_ci        return false;
143cb93a386Sopenharmony_ci    }
144cb93a386Sopenharmony_ci    *out = expr->as<Literal>().intValue();
145cb93a386Sopenharmony_ci    return true;
146cb93a386Sopenharmony_ci}
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_cibool ConstantFolder::GetConstantValue(const Expression& value, double* out) {
149cb93a386Sopenharmony_ci    const Expression* expr = GetConstantValueForVariable(value);
150cb93a386Sopenharmony_ci    if (!expr->is<Literal>()) {
151cb93a386Sopenharmony_ci        return false;
152cb93a386Sopenharmony_ci    }
153cb93a386Sopenharmony_ci    *out = expr->as<Literal>().value();
154cb93a386Sopenharmony_ci    return true;
155cb93a386Sopenharmony_ci}
156cb93a386Sopenharmony_ci
157cb93a386Sopenharmony_cistatic bool contains_constant_zero(const Expression& expr) {
158cb93a386Sopenharmony_ci    int numSlots = expr.type().slotCount();
159cb93a386Sopenharmony_ci    for (int index = 0; index < numSlots; ++index) {
160cb93a386Sopenharmony_ci        skstd::optional<double> slotVal = expr.getConstantValue(index);
161cb93a386Sopenharmony_ci        if (slotVal.has_value() && *slotVal == 0.0) {
162cb93a386Sopenharmony_ci            return true;
163cb93a386Sopenharmony_ci        }
164cb93a386Sopenharmony_ci    }
165cb93a386Sopenharmony_ci    return false;
166cb93a386Sopenharmony_ci}
167cb93a386Sopenharmony_ci
168cb93a386Sopenharmony_cistatic bool is_constant_value(const Expression& expr, double value) {
169cb93a386Sopenharmony_ci    int numSlots = expr.type().slotCount();
170cb93a386Sopenharmony_ci    for (int index = 0; index < numSlots; ++index) {
171cb93a386Sopenharmony_ci        skstd::optional<double> slotVal = expr.getConstantValue(index);
172cb93a386Sopenharmony_ci        if (!slotVal.has_value() || *slotVal != value) {
173cb93a386Sopenharmony_ci            return false;
174cb93a386Sopenharmony_ci        }
175cb93a386Sopenharmony_ci    }
176cb93a386Sopenharmony_ci    return true;
177cb93a386Sopenharmony_ci}
178cb93a386Sopenharmony_ci
179cb93a386Sopenharmony_cistatic bool error_on_divide_by_zero(const Context& context, int line, Operator op,
180cb93a386Sopenharmony_ci                                    const Expression& right) {
181cb93a386Sopenharmony_ci    switch (op.kind()) {
182cb93a386Sopenharmony_ci        case Token::Kind::TK_SLASH:
183cb93a386Sopenharmony_ci        case Token::Kind::TK_SLASHEQ:
184cb93a386Sopenharmony_ci        case Token::Kind::TK_PERCENT:
185cb93a386Sopenharmony_ci        case Token::Kind::TK_PERCENTEQ:
186cb93a386Sopenharmony_ci            if (contains_constant_zero(right)) {
187cb93a386Sopenharmony_ci                context.fErrors->error(line, "division by zero");
188cb93a386Sopenharmony_ci                return true;
189cb93a386Sopenharmony_ci            }
190cb93a386Sopenharmony_ci            return false;
191cb93a386Sopenharmony_ci        default:
192cb93a386Sopenharmony_ci            return false;
193cb93a386Sopenharmony_ci    }
194cb93a386Sopenharmony_ci}
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_ciconst Expression* ConstantFolder::GetConstantValueForVariable(const Expression& inExpr) {
197cb93a386Sopenharmony_ci    for (const Expression* expr = &inExpr;;) {
198cb93a386Sopenharmony_ci        if (!expr->is<VariableReference>()) {
199cb93a386Sopenharmony_ci            break;
200cb93a386Sopenharmony_ci        }
201cb93a386Sopenharmony_ci        const VariableReference& varRef = expr->as<VariableReference>();
202cb93a386Sopenharmony_ci        if (varRef.refKind() != VariableRefKind::kRead) {
203cb93a386Sopenharmony_ci            break;
204cb93a386Sopenharmony_ci        }
205cb93a386Sopenharmony_ci        const Variable& var = *varRef.variable();
206cb93a386Sopenharmony_ci        if (!(var.modifiers().fFlags & Modifiers::kConst_Flag)) {
207cb93a386Sopenharmony_ci            break;
208cb93a386Sopenharmony_ci        }
209cb93a386Sopenharmony_ci        expr = var.initialValue();
210cb93a386Sopenharmony_ci        if (!expr) {
211cb93a386Sopenharmony_ci            // Function parameters can be const but won't have an initial value.
212cb93a386Sopenharmony_ci            break;
213cb93a386Sopenharmony_ci        }
214cb93a386Sopenharmony_ci        if (expr->isCompileTimeConstant()) {
215cb93a386Sopenharmony_ci            return expr;
216cb93a386Sopenharmony_ci        }
217cb93a386Sopenharmony_ci    }
218cb93a386Sopenharmony_ci    // We didn't find a compile-time constant at the end. Return the expression as-is.
219cb93a386Sopenharmony_ci    return &inExpr;
220cb93a386Sopenharmony_ci}
221cb93a386Sopenharmony_ci
222cb93a386Sopenharmony_cistd::unique_ptr<Expression> ConstantFolder::MakeConstantValueForVariable(
223cb93a386Sopenharmony_ci        std::unique_ptr<Expression> expr) {
224cb93a386Sopenharmony_ci    const Expression* constantExpr = GetConstantValueForVariable(*expr);
225cb93a386Sopenharmony_ci    if (constantExpr != expr.get()) {
226cb93a386Sopenharmony_ci        expr = constantExpr->clone();
227cb93a386Sopenharmony_ci    }
228cb93a386Sopenharmony_ci    return expr;
229cb93a386Sopenharmony_ci}
230cb93a386Sopenharmony_ci
231cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> simplify_no_op_arithmetic(const Context& context,
232cb93a386Sopenharmony_ci                                                             const Expression& left,
233cb93a386Sopenharmony_ci                                                             Operator op,
234cb93a386Sopenharmony_ci                                                             const Expression& right,
235cb93a386Sopenharmony_ci                                                             const Type& resultType) {
236cb93a386Sopenharmony_ci    switch (op.kind()) {
237cb93a386Sopenharmony_ci        case Token::Kind::TK_PLUS:
238cb93a386Sopenharmony_ci            if (is_constant_value(right, 0.0)) {  // x + 0
239cb93a386Sopenharmony_ci                return cast_expression(context, left, resultType);
240cb93a386Sopenharmony_ci            }
241cb93a386Sopenharmony_ci            if (is_constant_value(left, 0.0)) {   // 0 + x
242cb93a386Sopenharmony_ci                return cast_expression(context, right, resultType);
243cb93a386Sopenharmony_ci            }
244cb93a386Sopenharmony_ci            break;
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci        case Token::Kind::TK_STAR:
247cb93a386Sopenharmony_ci            if (is_constant_value(right, 1.0)) {  // x * 1
248cb93a386Sopenharmony_ci                return cast_expression(context, left, resultType);
249cb93a386Sopenharmony_ci            }
250cb93a386Sopenharmony_ci            if (is_constant_value(left, 1.0)) {   // 1 * x
251cb93a386Sopenharmony_ci                return cast_expression(context, right, resultType);
252cb93a386Sopenharmony_ci            }
253cb93a386Sopenharmony_ci            if (is_constant_value(right, 0.0) && !left.hasSideEffects()) {  // x * 0
254cb93a386Sopenharmony_ci                return cast_expression(context, right, resultType);
255cb93a386Sopenharmony_ci            }
256cb93a386Sopenharmony_ci            if (is_constant_value(left, 0.0) && !right.hasSideEffects()) {  // 0 * x
257cb93a386Sopenharmony_ci                return cast_expression(context, left, resultType);
258cb93a386Sopenharmony_ci            }
259cb93a386Sopenharmony_ci            break;
260cb93a386Sopenharmony_ci
261cb93a386Sopenharmony_ci        case Token::Kind::TK_MINUS:
262cb93a386Sopenharmony_ci            if (is_constant_value(right, 0.0)) {  // x - 0
263cb93a386Sopenharmony_ci                return cast_expression(context, left, resultType);
264cb93a386Sopenharmony_ci            }
265cb93a386Sopenharmony_ci            if (is_constant_value(left, 0.0)) {   // 0 - x (to `-x`)
266cb93a386Sopenharmony_ci                if (std::unique_ptr<Expression> val = cast_expression(context, right, resultType)) {
267cb93a386Sopenharmony_ci                    return PrefixExpression::Make(context, Token::Kind::TK_MINUS, std::move(val));
268cb93a386Sopenharmony_ci                }
269cb93a386Sopenharmony_ci            }
270cb93a386Sopenharmony_ci            break;
271cb93a386Sopenharmony_ci
272cb93a386Sopenharmony_ci        case Token::Kind::TK_SLASH:
273cb93a386Sopenharmony_ci            if (is_constant_value(right, 1.0)) {  // x / 1
274cb93a386Sopenharmony_ci                return cast_expression(context, left, resultType);
275cb93a386Sopenharmony_ci            }
276cb93a386Sopenharmony_ci            break;
277cb93a386Sopenharmony_ci
278cb93a386Sopenharmony_ci        case Token::Kind::TK_PLUSEQ:
279cb93a386Sopenharmony_ci        case Token::Kind::TK_MINUSEQ:
280cb93a386Sopenharmony_ci            if (is_constant_value(right, 0.0)) {  // x += 0, x -= 0
281cb93a386Sopenharmony_ci                if (std::unique_ptr<Expression> var = cast_expression(context, left, resultType)) {
282cb93a386Sopenharmony_ci                    Analysis::UpdateVariableRefKind(var.get(), VariableRefKind::kRead);
283cb93a386Sopenharmony_ci                    return var;
284cb93a386Sopenharmony_ci                }
285cb93a386Sopenharmony_ci            }
286cb93a386Sopenharmony_ci            break;
287cb93a386Sopenharmony_ci
288cb93a386Sopenharmony_ci        case Token::Kind::TK_STAREQ:
289cb93a386Sopenharmony_ci        case Token::Kind::TK_SLASHEQ:
290cb93a386Sopenharmony_ci            if (is_constant_value(right, 1.0)) {  // x *= 1, x /= 1
291cb93a386Sopenharmony_ci                if (std::unique_ptr<Expression> var = cast_expression(context, left, resultType)) {
292cb93a386Sopenharmony_ci                    Analysis::UpdateVariableRefKind(var.get(), VariableRefKind::kRead);
293cb93a386Sopenharmony_ci                    return var;
294cb93a386Sopenharmony_ci                }
295cb93a386Sopenharmony_ci            }
296cb93a386Sopenharmony_ci            break;
297cb93a386Sopenharmony_ci
298cb93a386Sopenharmony_ci        default:
299cb93a386Sopenharmony_ci            break;
300cb93a386Sopenharmony_ci    }
301cb93a386Sopenharmony_ci
302cb93a386Sopenharmony_ci    return nullptr;
303cb93a386Sopenharmony_ci}
304cb93a386Sopenharmony_ci
305cb93a386Sopenharmony_citemplate <typename T>
306cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> fold_float_expression(int line,
307cb93a386Sopenharmony_ci                                                         T result,
308cb93a386Sopenharmony_ci                                                         const Type* resultType) {
309cb93a386Sopenharmony_ci    // If constant-folding this expression would generate a NaN/infinite result, leave it as-is.
310cb93a386Sopenharmony_ci    if constexpr (!std::is_same<T, bool>::value) {
311cb93a386Sopenharmony_ci        if (!std::isfinite(result)) {
312cb93a386Sopenharmony_ci            return nullptr;
313cb93a386Sopenharmony_ci        }
314cb93a386Sopenharmony_ci    }
315cb93a386Sopenharmony_ci
316cb93a386Sopenharmony_ci    return Literal::Make(line, result, resultType);
317cb93a386Sopenharmony_ci}
318cb93a386Sopenharmony_ci
319cb93a386Sopenharmony_citemplate <typename T>
320cb93a386Sopenharmony_cistatic std::unique_ptr<Expression> fold_int_expression(int line,
321cb93a386Sopenharmony_ci                                                       T result,
322cb93a386Sopenharmony_ci                                                       const Type* resultType) {
323cb93a386Sopenharmony_ci    // If constant-folding this expression would overflow the result type, leave it as-is.
324cb93a386Sopenharmony_ci    if constexpr (!std::is_same<T, bool>::value) {
325cb93a386Sopenharmony_ci        if (result < resultType->minimumValue() || result > resultType->maximumValue()) {
326cb93a386Sopenharmony_ci            return nullptr;
327cb93a386Sopenharmony_ci        }
328cb93a386Sopenharmony_ci    }
329cb93a386Sopenharmony_ci
330cb93a386Sopenharmony_ci    return Literal::Make(line, result, resultType);
331cb93a386Sopenharmony_ci}
332cb93a386Sopenharmony_ci
333cb93a386Sopenharmony_cistd::unique_ptr<Expression> ConstantFolder::Simplify(const Context& context,
334cb93a386Sopenharmony_ci                                                     int line,
335cb93a386Sopenharmony_ci                                                     const Expression& leftExpr,
336cb93a386Sopenharmony_ci                                                     Operator op,
337cb93a386Sopenharmony_ci                                                     const Expression& rightExpr,
338cb93a386Sopenharmony_ci                                                     const Type& resultType) {
339cb93a386Sopenharmony_ci    // Replace constant variables with their literal values.
340cb93a386Sopenharmony_ci    const Expression* left = GetConstantValueForVariable(leftExpr);
341cb93a386Sopenharmony_ci    const Expression* right = GetConstantValueForVariable(rightExpr);
342cb93a386Sopenharmony_ci
343cb93a386Sopenharmony_ci    // If this is the comma operator, the left side is evaluated but not otherwise used in any way.
344cb93a386Sopenharmony_ci    // So if the left side has no side effects, it can just be eliminated entirely.
345cb93a386Sopenharmony_ci    if (op.kind() == Token::Kind::TK_COMMA && !left->hasSideEffects()) {
346cb93a386Sopenharmony_ci        return right->clone();
347cb93a386Sopenharmony_ci    }
348cb93a386Sopenharmony_ci
349cb93a386Sopenharmony_ci    // If this is the assignment operator, and both sides are the same trivial expression, this is
350cb93a386Sopenharmony_ci    // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`).
351cb93a386Sopenharmony_ci    // This can happen when other parts of the assignment are optimized away.
352cb93a386Sopenharmony_ci    if (op.kind() == Token::Kind::TK_EQ && Analysis::IsSameExpressionTree(*left, *right)) {
353cb93a386Sopenharmony_ci        return right->clone();
354cb93a386Sopenharmony_ci    }
355cb93a386Sopenharmony_ci
356cb93a386Sopenharmony_ci    // Simplify the expression when both sides are constant Boolean literals.
357cb93a386Sopenharmony_ci    if (left->isBoolLiteral() && right->isBoolLiteral()) {
358cb93a386Sopenharmony_ci        bool leftVal  = left->as<Literal>().boolValue();
359cb93a386Sopenharmony_ci        bool rightVal = right->as<Literal>().boolValue();
360cb93a386Sopenharmony_ci        bool result;
361cb93a386Sopenharmony_ci        switch (op.kind()) {
362cb93a386Sopenharmony_ci            case Token::Kind::TK_LOGICALAND: result = leftVal && rightVal; break;
363cb93a386Sopenharmony_ci            case Token::Kind::TK_LOGICALOR:  result = leftVal || rightVal; break;
364cb93a386Sopenharmony_ci            case Token::Kind::TK_LOGICALXOR: result = leftVal ^  rightVal; break;
365cb93a386Sopenharmony_ci            case Token::Kind::TK_EQEQ:       result = leftVal == rightVal; break;
366cb93a386Sopenharmony_ci            case Token::Kind::TK_NEQ:        result = leftVal != rightVal; break;
367cb93a386Sopenharmony_ci            default: return nullptr;
368cb93a386Sopenharmony_ci        }
369cb93a386Sopenharmony_ci        return Literal::MakeBool(context, line, result);
370cb93a386Sopenharmony_ci    }
371cb93a386Sopenharmony_ci
372cb93a386Sopenharmony_ci    // If the left side is a Boolean literal, apply short-circuit optimizations.
373cb93a386Sopenharmony_ci    if (left->isBoolLiteral()) {
374cb93a386Sopenharmony_ci        return short_circuit_boolean(*left, op, *right);
375cb93a386Sopenharmony_ci    }
376cb93a386Sopenharmony_ci
377cb93a386Sopenharmony_ci    // If the right side is a Boolean literal...
378cb93a386Sopenharmony_ci    if (right->isBoolLiteral()) {
379cb93a386Sopenharmony_ci        // ... and the left side has no side effects...
380cb93a386Sopenharmony_ci        if (!left->hasSideEffects()) {
381cb93a386Sopenharmony_ci            // We can reverse the expressions and short-circuit optimizations are still valid.
382cb93a386Sopenharmony_ci            return short_circuit_boolean(*right, op, *left);
383cb93a386Sopenharmony_ci        }
384cb93a386Sopenharmony_ci
385cb93a386Sopenharmony_ci        // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions.
386cb93a386Sopenharmony_ci        return eliminate_no_op_boolean(*left, op, *right);
387cb93a386Sopenharmony_ci    }
388cb93a386Sopenharmony_ci
389cb93a386Sopenharmony_ci    if (op.kind() == Token::Kind::TK_EQEQ && Analysis::IsSameExpressionTree(*left, *right)) {
390cb93a386Sopenharmony_ci        // With == comparison, if both sides are the same trivial expression, this is self-
391cb93a386Sopenharmony_ci        // comparison and is always true. (We are not concerned with NaN.)
392cb93a386Sopenharmony_ci        return Literal::MakeBool(context, leftExpr.fLine, /*value=*/true);
393cb93a386Sopenharmony_ci    }
394cb93a386Sopenharmony_ci
395cb93a386Sopenharmony_ci    if (op.kind() == Token::Kind::TK_NEQ && Analysis::IsSameExpressionTree(*left, *right)) {
396cb93a386Sopenharmony_ci        // With != comparison, if both sides are the same trivial expression, this is self-
397cb93a386Sopenharmony_ci        // comparison and is always false. (We are not concerned with NaN.)
398cb93a386Sopenharmony_ci        return Literal::MakeBool(context, leftExpr.fLine, /*value=*/false);
399cb93a386Sopenharmony_ci    }
400cb93a386Sopenharmony_ci
401cb93a386Sopenharmony_ci    if (error_on_divide_by_zero(context, line, op, *right)) {
402cb93a386Sopenharmony_ci        return nullptr;
403cb93a386Sopenharmony_ci    }
404cb93a386Sopenharmony_ci
405cb93a386Sopenharmony_ci    // Optimize away no-op arithmetic like `x * 1`, `x *= 1`, `x + 0`, `x * 0`, `0 / x`, etc.
406cb93a386Sopenharmony_ci    const Type& leftType = left->type();
407cb93a386Sopenharmony_ci    const Type& rightType = right->type();
408cb93a386Sopenharmony_ci    if ((leftType.isScalar() || leftType.isVector()) &&
409cb93a386Sopenharmony_ci        (rightType.isScalar() || rightType.isVector())) {
410cb93a386Sopenharmony_ci        std::unique_ptr<Expression> expr = simplify_no_op_arithmetic(context, *left, op, *right,
411cb93a386Sopenharmony_ci                                                                     resultType);
412cb93a386Sopenharmony_ci        if (expr) {
413cb93a386Sopenharmony_ci            return expr;
414cb93a386Sopenharmony_ci        }
415cb93a386Sopenharmony_ci    }
416cb93a386Sopenharmony_ci
417cb93a386Sopenharmony_ci    // Other than the cases above, constant folding requires both sides to be constant.
418cb93a386Sopenharmony_ci    if (!left->isCompileTimeConstant() || !right->isCompileTimeConstant()) {
419cb93a386Sopenharmony_ci        return nullptr;
420cb93a386Sopenharmony_ci    }
421cb93a386Sopenharmony_ci
422cb93a386Sopenharmony_ci    // Note that we expressly do not worry about precision and overflow here -- we use the maximum
423cb93a386Sopenharmony_ci    // precision to calculate the results and hope the result makes sense.
424cb93a386Sopenharmony_ci    // TODO(skia:10932): detect and handle integer overflow properly.
425cb93a386Sopenharmony_ci    using SKSL_UINT = uint64_t;
426cb93a386Sopenharmony_ci    if (left->isIntLiteral() && right->isIntLiteral()) {
427cb93a386Sopenharmony_ci        SKSL_INT leftVal  = left->as<Literal>().intValue();
428cb93a386Sopenharmony_ci        SKSL_INT rightVal = right->as<Literal>().intValue();
429cb93a386Sopenharmony_ci
430cb93a386Sopenharmony_ci        #define RESULT(Op)   fold_int_expression(line, \
431cb93a386Sopenharmony_ci                                        (SKSL_INT)(leftVal) Op (SKSL_INT)(rightVal), &resultType)
432cb93a386Sopenharmony_ci        #define URESULT(Op)  fold_int_expression(line, \
433cb93a386Sopenharmony_ci                             (SKSL_INT)((SKSL_UINT)(leftVal) Op (SKSL_UINT)(rightVal)), &resultType)
434cb93a386Sopenharmony_ci        switch (op.kind()) {
435cb93a386Sopenharmony_ci            case Token::Kind::TK_PLUS:       return URESULT(+);
436cb93a386Sopenharmony_ci            case Token::Kind::TK_MINUS:      return URESULT(-);
437cb93a386Sopenharmony_ci            case Token::Kind::TK_STAR:       return URESULT(*);
438cb93a386Sopenharmony_ci            case Token::Kind::TK_SLASH:
439cb93a386Sopenharmony_ci                if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
440cb93a386Sopenharmony_ci                    context.fErrors->error(line, "arithmetic overflow");
441cb93a386Sopenharmony_ci                    return nullptr;
442cb93a386Sopenharmony_ci                }
443cb93a386Sopenharmony_ci                return RESULT(/);
444cb93a386Sopenharmony_ci            case Token::Kind::TK_PERCENT:
445cb93a386Sopenharmony_ci                if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
446cb93a386Sopenharmony_ci                    context.fErrors->error(line, "arithmetic overflow");
447cb93a386Sopenharmony_ci                    return nullptr;
448cb93a386Sopenharmony_ci                }
449cb93a386Sopenharmony_ci                return RESULT(%);
450cb93a386Sopenharmony_ci            case Token::Kind::TK_BITWISEAND: return RESULT(&);
451cb93a386Sopenharmony_ci            case Token::Kind::TK_BITWISEOR:  return RESULT(|);
452cb93a386Sopenharmony_ci            case Token::Kind::TK_BITWISEXOR: return RESULT(^);
453cb93a386Sopenharmony_ci            case Token::Kind::TK_EQEQ:       return RESULT(==);
454cb93a386Sopenharmony_ci            case Token::Kind::TK_NEQ:        return RESULT(!=);
455cb93a386Sopenharmony_ci            case Token::Kind::TK_GT:         return RESULT(>);
456cb93a386Sopenharmony_ci            case Token::Kind::TK_GTEQ:       return RESULT(>=);
457cb93a386Sopenharmony_ci            case Token::Kind::TK_LT:         return RESULT(<);
458cb93a386Sopenharmony_ci            case Token::Kind::TK_LTEQ:       return RESULT(<=);
459cb93a386Sopenharmony_ci            case Token::Kind::TK_SHL:
460cb93a386Sopenharmony_ci                if (rightVal >= 0 && rightVal <= 31) {
461cb93a386Sopenharmony_ci                    // Left-shifting a negative (or really, any signed) value is undefined behavior
462cb93a386Sopenharmony_ci                    // in C++, but not GLSL. Do the shift on unsigned values, to avoid UBSAN.
463cb93a386Sopenharmony_ci                    return URESULT(<<);
464cb93a386Sopenharmony_ci                }
465cb93a386Sopenharmony_ci                context.fErrors->error(line, "shift value out of range");
466cb93a386Sopenharmony_ci                return nullptr;
467cb93a386Sopenharmony_ci            case Token::Kind::TK_SHR:
468cb93a386Sopenharmony_ci                if (rightVal >= 0 && rightVal <= 31) {
469cb93a386Sopenharmony_ci                    return RESULT(>>);
470cb93a386Sopenharmony_ci                }
471cb93a386Sopenharmony_ci                context.fErrors->error(line, "shift value out of range");
472cb93a386Sopenharmony_ci                return nullptr;
473cb93a386Sopenharmony_ci
474cb93a386Sopenharmony_ci            default:
475cb93a386Sopenharmony_ci                return nullptr;
476cb93a386Sopenharmony_ci        }
477cb93a386Sopenharmony_ci        #undef RESULT
478cb93a386Sopenharmony_ci        #undef URESULT
479cb93a386Sopenharmony_ci    }
480cb93a386Sopenharmony_ci
481cb93a386Sopenharmony_ci    // Perform constant folding on pairs of floating-point literals.
482cb93a386Sopenharmony_ci    if (left->isFloatLiteral() && right->isFloatLiteral()) {
483cb93a386Sopenharmony_ci        SKSL_FLOAT leftVal  = left->as<Literal>().floatValue();
484cb93a386Sopenharmony_ci        SKSL_FLOAT rightVal = right->as<Literal>().floatValue();
485cb93a386Sopenharmony_ci
486cb93a386Sopenharmony_ci        #define RESULT(Op) fold_float_expression(line, leftVal Op rightVal, &resultType)
487cb93a386Sopenharmony_ci        switch (op.kind()) {
488cb93a386Sopenharmony_ci            case Token::Kind::TK_PLUS:  return RESULT(+);
489cb93a386Sopenharmony_ci            case Token::Kind::TK_MINUS: return RESULT(-);
490cb93a386Sopenharmony_ci            case Token::Kind::TK_STAR:  return RESULT(*);
491cb93a386Sopenharmony_ci            case Token::Kind::TK_SLASH: return RESULT(/);
492cb93a386Sopenharmony_ci            case Token::Kind::TK_EQEQ:  return RESULT(==);
493cb93a386Sopenharmony_ci            case Token::Kind::TK_NEQ:   return RESULT(!=);
494cb93a386Sopenharmony_ci            case Token::Kind::TK_GT:    return RESULT(>);
495cb93a386Sopenharmony_ci            case Token::Kind::TK_GTEQ:  return RESULT(>=);
496cb93a386Sopenharmony_ci            case Token::Kind::TK_LT:    return RESULT(<);
497cb93a386Sopenharmony_ci            case Token::Kind::TK_LTEQ:  return RESULT(<=);
498cb93a386Sopenharmony_ci            default:                    return nullptr;
499cb93a386Sopenharmony_ci        }
500cb93a386Sopenharmony_ci        #undef RESULT
501cb93a386Sopenharmony_ci    }
502cb93a386Sopenharmony_ci
503cb93a386Sopenharmony_ci    // Perform constant folding on pairs of vectors.
504cb93a386Sopenharmony_ci    if (leftType.isVector() && leftType == rightType) {
505cb93a386Sopenharmony_ci        if (leftType.componentType().isFloat()) {
506cb93a386Sopenharmony_ci            return simplify_vector(context, *left, op, *right);
507cb93a386Sopenharmony_ci        }
508cb93a386Sopenharmony_ci        if (leftType.componentType().isInteger()) {
509cb93a386Sopenharmony_ci            return simplify_vector(context, *left, op, *right);
510cb93a386Sopenharmony_ci        }
511cb93a386Sopenharmony_ci        if (leftType.componentType().isBoolean()) {
512cb93a386Sopenharmony_ci            return simplify_vector_equality(context, *left, op, *right);
513cb93a386Sopenharmony_ci        }
514cb93a386Sopenharmony_ci        return nullptr;
515cb93a386Sopenharmony_ci    }
516cb93a386Sopenharmony_ci
517cb93a386Sopenharmony_ci    // Perform constant folding on vectors against scalars, e.g.: half4(2) + 2
518cb93a386Sopenharmony_ci    if (leftType.isVector() && leftType.componentType() == rightType) {
519cb93a386Sopenharmony_ci        if (rightType.isFloat()) {
520cb93a386Sopenharmony_ci            return simplify_vector(context, *left, op, ConstructorSplat(*right, left->type()));
521cb93a386Sopenharmony_ci        }
522cb93a386Sopenharmony_ci        if (rightType.isInteger()) {
523cb93a386Sopenharmony_ci            return simplify_vector(context, *left, op, ConstructorSplat(*right, left->type()));
524cb93a386Sopenharmony_ci        }
525cb93a386Sopenharmony_ci        if (rightType.isBoolean()) {
526cb93a386Sopenharmony_ci            return simplify_vector_equality(context, *left, op,
527cb93a386Sopenharmony_ci                                            ConstructorSplat(*right, left->type()));
528cb93a386Sopenharmony_ci        }
529cb93a386Sopenharmony_ci        return nullptr;
530cb93a386Sopenharmony_ci    }
531cb93a386Sopenharmony_ci
532cb93a386Sopenharmony_ci    // Perform constant folding on scalars against vectors, e.g.: 2 + half4(2)
533cb93a386Sopenharmony_ci    if (rightType.isVector() && rightType.componentType() == leftType) {
534cb93a386Sopenharmony_ci        if (leftType.isFloat()) {
535cb93a386Sopenharmony_ci            return simplify_vector(context, ConstructorSplat(*left, right->type()), op, *right);
536cb93a386Sopenharmony_ci        }
537cb93a386Sopenharmony_ci        if (leftType.isInteger()) {
538cb93a386Sopenharmony_ci            return simplify_vector(context, ConstructorSplat(*left, right->type()), op, *right);
539cb93a386Sopenharmony_ci        }
540cb93a386Sopenharmony_ci        if (leftType.isBoolean()) {
541cb93a386Sopenharmony_ci            return simplify_vector_equality(context, ConstructorSplat(*left, right->type()),
542cb93a386Sopenharmony_ci                                            op, *right);
543cb93a386Sopenharmony_ci        }
544cb93a386Sopenharmony_ci        return nullptr;
545cb93a386Sopenharmony_ci    }
546cb93a386Sopenharmony_ci
547cb93a386Sopenharmony_ci    // Perform constant folding on pairs of matrices or arrays.
548cb93a386Sopenharmony_ci    if ((leftType.isMatrix() && rightType.isMatrix()) ||
549cb93a386Sopenharmony_ci        (leftType.isArray() && rightType.isArray())) {
550cb93a386Sopenharmony_ci        bool equality;
551cb93a386Sopenharmony_ci        switch (op.kind()) {
552cb93a386Sopenharmony_ci            case Token::Kind::TK_EQEQ:
553cb93a386Sopenharmony_ci                equality = true;
554cb93a386Sopenharmony_ci                break;
555cb93a386Sopenharmony_ci            case Token::Kind::TK_NEQ:
556cb93a386Sopenharmony_ci                equality = false;
557cb93a386Sopenharmony_ci                break;
558cb93a386Sopenharmony_ci            default:
559cb93a386Sopenharmony_ci                return nullptr;
560cb93a386Sopenharmony_ci        }
561cb93a386Sopenharmony_ci
562cb93a386Sopenharmony_ci        switch (left->compareConstant(*right)) {
563cb93a386Sopenharmony_ci            case Expression::ComparisonResult::kNotEqual:
564cb93a386Sopenharmony_ci                equality = !equality;
565cb93a386Sopenharmony_ci                [[fallthrough]];
566cb93a386Sopenharmony_ci
567cb93a386Sopenharmony_ci            case Expression::ComparisonResult::kEqual:
568cb93a386Sopenharmony_ci                return Literal::MakeBool(context, line, equality);
569cb93a386Sopenharmony_ci
570cb93a386Sopenharmony_ci            case Expression::ComparisonResult::kUnknown:
571cb93a386Sopenharmony_ci                return nullptr;
572cb93a386Sopenharmony_ci        }
573cb93a386Sopenharmony_ci    }
574cb93a386Sopenharmony_ci
575cb93a386Sopenharmony_ci    // We aren't able to constant-fold.
576cb93a386Sopenharmony_ci    return nullptr;
577cb93a386Sopenharmony_ci}
578cb93a386Sopenharmony_ci
579cb93a386Sopenharmony_ci}  // namespace SkSL
580