xref: /aosp_15_r20/external/skia/src/sksl/SkSLConstantFolder.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2020 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLConstantFolder.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkFloatingPoint.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPosition.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBinaryExpression.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorCompound.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorSplat.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpression.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLiteral.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifierFlags.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPrefixExpression.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLType.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariable.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariableReference.h"
29*c8dee2aaSAndroid Build Coastguard Worker 
30*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
31*c8dee2aaSAndroid Build Coastguard Worker #include <float.h>
32*c8dee2aaSAndroid Build Coastguard Worker #include <limits>
33*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
34*c8dee2aaSAndroid Build Coastguard Worker #include <string>
35*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
36*c8dee2aaSAndroid Build Coastguard Worker 
37*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
38*c8dee2aaSAndroid Build Coastguard Worker 
39*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
40*c8dee2aaSAndroid Build Coastguard Worker 
is_vec_or_mat(const Type & type)41*c8dee2aaSAndroid Build Coastguard Worker static bool is_vec_or_mat(const Type& type) {
42*c8dee2aaSAndroid Build Coastguard Worker     switch (type.typeKind()) {
43*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kMatrix:
44*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kVector:
45*c8dee2aaSAndroid Build Coastguard Worker             return true;
46*c8dee2aaSAndroid Build Coastguard Worker 
47*c8dee2aaSAndroid Build Coastguard Worker         default:
48*c8dee2aaSAndroid Build Coastguard Worker             return false;
49*c8dee2aaSAndroid Build Coastguard Worker     }
50*c8dee2aaSAndroid Build Coastguard Worker }
51*c8dee2aaSAndroid Build Coastguard Worker 
eliminate_no_op_boolean(Position pos,const Expression & left,Operator op,const Expression & right)52*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> eliminate_no_op_boolean(Position pos,
53*c8dee2aaSAndroid Build Coastguard Worker                                                            const Expression& left,
54*c8dee2aaSAndroid Build Coastguard Worker                                                            Operator op,
55*c8dee2aaSAndroid Build Coastguard Worker                                                            const Expression& right) {
56*c8dee2aaSAndroid Build Coastguard Worker     bool rightVal = right.as<Literal>().boolValue();
57*c8dee2aaSAndroid Build Coastguard Worker 
58*c8dee2aaSAndroid Build Coastguard Worker     // Detect no-op Boolean expressions and optimize them away.
59*c8dee2aaSAndroid Build Coastguard Worker     if ((op.kind() == Operator::Kind::LOGICALAND && rightVal)  ||  // (expr && true)  -> (expr)
60*c8dee2aaSAndroid Build Coastguard Worker         (op.kind() == Operator::Kind::LOGICALOR  && !rightVal) ||  // (expr || false) -> (expr)
61*c8dee2aaSAndroid Build Coastguard Worker         (op.kind() == Operator::Kind::LOGICALXOR && !rightVal) ||  // (expr ^^ false) -> (expr)
62*c8dee2aaSAndroid Build Coastguard Worker         (op.kind() == Operator::Kind::EQEQ       && rightVal)  ||  // (expr == true)  -> (expr)
63*c8dee2aaSAndroid Build Coastguard Worker         (op.kind() == Operator::Kind::NEQ        && !rightVal)) {  // (expr != false) -> (expr)
64*c8dee2aaSAndroid Build Coastguard Worker 
65*c8dee2aaSAndroid Build Coastguard Worker         return left.clone(pos);
66*c8dee2aaSAndroid Build Coastguard Worker     }
67*c8dee2aaSAndroid Build Coastguard Worker 
68*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
69*c8dee2aaSAndroid Build Coastguard Worker }
70*c8dee2aaSAndroid Build Coastguard Worker 
short_circuit_boolean(Position pos,const Expression & left,Operator op,const Expression & right)71*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> short_circuit_boolean(Position pos,
72*c8dee2aaSAndroid Build Coastguard Worker                                                          const Expression& left,
73*c8dee2aaSAndroid Build Coastguard Worker                                                          Operator op,
74*c8dee2aaSAndroid Build Coastguard Worker                                                          const Expression& right) {
75*c8dee2aaSAndroid Build Coastguard Worker     bool leftVal = left.as<Literal>().boolValue();
76*c8dee2aaSAndroid Build Coastguard Worker 
77*c8dee2aaSAndroid Build Coastguard Worker     // When the literal is on the left, we can sometimes eliminate the other expression entirely.
78*c8dee2aaSAndroid Build Coastguard Worker     if ((op.kind() == Operator::Kind::LOGICALAND && !leftVal) ||  // (false && expr) -> (false)
79*c8dee2aaSAndroid Build Coastguard Worker         (op.kind() == Operator::Kind::LOGICALOR  && leftVal)) {   // (true  || expr) -> (true)
80*c8dee2aaSAndroid Build Coastguard Worker 
81*c8dee2aaSAndroid Build Coastguard Worker         return left.clone(pos);
82*c8dee2aaSAndroid Build Coastguard Worker     }
83*c8dee2aaSAndroid Build Coastguard Worker 
84*c8dee2aaSAndroid Build Coastguard Worker     // We can't eliminate the right-side expression via short-circuit, but we might still be able to
85*c8dee2aaSAndroid Build Coastguard Worker     // simplify away a no-op expression.
86*c8dee2aaSAndroid Build Coastguard Worker     return eliminate_no_op_boolean(pos, right, op, left);
87*c8dee2aaSAndroid Build Coastguard Worker }
88*c8dee2aaSAndroid Build Coastguard Worker 
simplify_constant_equality(const Context & context,Position pos,const Expression & left,Operator op,const Expression & right)89*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> simplify_constant_equality(const Context& context,
90*c8dee2aaSAndroid Build Coastguard Worker                                                               Position pos,
91*c8dee2aaSAndroid Build Coastguard Worker                                                               const Expression& left,
92*c8dee2aaSAndroid Build Coastguard Worker                                                               Operator op,
93*c8dee2aaSAndroid Build Coastguard Worker                                                               const Expression& right) {
94*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == Operator::Kind::EQEQ || op.kind() == Operator::Kind::NEQ) {
95*c8dee2aaSAndroid Build Coastguard Worker         bool equality = (op.kind() == Operator::Kind::EQEQ);
96*c8dee2aaSAndroid Build Coastguard Worker 
97*c8dee2aaSAndroid Build Coastguard Worker         switch (left.compareConstant(right)) {
98*c8dee2aaSAndroid Build Coastguard Worker             case Expression::ComparisonResult::kNotEqual:
99*c8dee2aaSAndroid Build Coastguard Worker                 equality = !equality;
100*c8dee2aaSAndroid Build Coastguard Worker                 [[fallthrough]];
101*c8dee2aaSAndroid Build Coastguard Worker 
102*c8dee2aaSAndroid Build Coastguard Worker             case Expression::ComparisonResult::kEqual:
103*c8dee2aaSAndroid Build Coastguard Worker                 return Literal::MakeBool(context, pos, equality);
104*c8dee2aaSAndroid Build Coastguard Worker 
105*c8dee2aaSAndroid Build Coastguard Worker             case Expression::ComparisonResult::kUnknown:
106*c8dee2aaSAndroid Build Coastguard Worker                 break;
107*c8dee2aaSAndroid Build Coastguard Worker         }
108*c8dee2aaSAndroid Build Coastguard Worker     }
109*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker 
simplify_matrix_multiplication(const Context & context,Position pos,const Expression & left,const Expression & right,int leftColumns,int leftRows,int rightColumns,int rightRows)112*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> simplify_matrix_multiplication(const Context& context,
113*c8dee2aaSAndroid Build Coastguard Worker                                                                   Position pos,
114*c8dee2aaSAndroid Build Coastguard Worker                                                                   const Expression& left,
115*c8dee2aaSAndroid Build Coastguard Worker                                                                   const Expression& right,
116*c8dee2aaSAndroid Build Coastguard Worker                                                                   int leftColumns,
117*c8dee2aaSAndroid Build Coastguard Worker                                                                   int leftRows,
118*c8dee2aaSAndroid Build Coastguard Worker                                                                   int rightColumns,
119*c8dee2aaSAndroid Build Coastguard Worker                                                                   int rightRows) {
120*c8dee2aaSAndroid Build Coastguard Worker     const Type& componentType = left.type().componentType();
121*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(componentType.matches(right.type().componentType()));
122*c8dee2aaSAndroid Build Coastguard Worker 
123*c8dee2aaSAndroid Build Coastguard Worker     // Fetch the left matrix.
124*c8dee2aaSAndroid Build Coastguard Worker     double leftVals[4][4];
125*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < leftColumns; ++c) {
126*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < leftRows; ++r) {
127*c8dee2aaSAndroid Build Coastguard Worker             leftVals[c][r] = *left.getConstantValue((c * leftRows) + r);
128*c8dee2aaSAndroid Build Coastguard Worker         }
129*c8dee2aaSAndroid Build Coastguard Worker     }
130*c8dee2aaSAndroid Build Coastguard Worker     // Fetch the right matrix.
131*c8dee2aaSAndroid Build Coastguard Worker     double rightVals[4][4];
132*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < rightColumns; ++c) {
133*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < rightRows; ++r) {
134*c8dee2aaSAndroid Build Coastguard Worker             rightVals[c][r] = *right.getConstantValue((c * rightRows) + r);
135*c8dee2aaSAndroid Build Coastguard Worker         }
136*c8dee2aaSAndroid Build Coastguard Worker     }
137*c8dee2aaSAndroid Build Coastguard Worker 
138*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(leftColumns == rightRows);
139*c8dee2aaSAndroid Build Coastguard Worker     int outColumns   = rightColumns,
140*c8dee2aaSAndroid Build Coastguard Worker         outRows      = leftRows;
141*c8dee2aaSAndroid Build Coastguard Worker 
142*c8dee2aaSAndroid Build Coastguard Worker     double args[16];
143*c8dee2aaSAndroid Build Coastguard Worker     int argIndex = 0;
144*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < outColumns; ++c) {
145*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < outRows; ++r) {
146*c8dee2aaSAndroid Build Coastguard Worker             // Compute a dot product for this position.
147*c8dee2aaSAndroid Build Coastguard Worker             double val = 0;
148*c8dee2aaSAndroid Build Coastguard Worker             for (int dotIdx = 0; dotIdx < leftColumns; ++dotIdx) {
149*c8dee2aaSAndroid Build Coastguard Worker                 val += leftVals[dotIdx][r] * rightVals[c][dotIdx];
150*c8dee2aaSAndroid Build Coastguard Worker             }
151*c8dee2aaSAndroid Build Coastguard Worker 
152*c8dee2aaSAndroid Build Coastguard Worker             if (val >= -FLT_MAX && val <= FLT_MAX) {
153*c8dee2aaSAndroid Build Coastguard Worker                 args[argIndex++] = val;
154*c8dee2aaSAndroid Build Coastguard Worker             } else {
155*c8dee2aaSAndroid Build Coastguard Worker                 // The value is outside the 32-bit float range, or is NaN; do not optimize.
156*c8dee2aaSAndroid Build Coastguard Worker                 return nullptr;
157*c8dee2aaSAndroid Build Coastguard Worker             }
158*c8dee2aaSAndroid Build Coastguard Worker         }
159*c8dee2aaSAndroid Build Coastguard Worker     }
160*c8dee2aaSAndroid Build Coastguard Worker 
161*c8dee2aaSAndroid Build Coastguard Worker     if (outColumns == 1) {
162*c8dee2aaSAndroid Build Coastguard Worker         // Matrix-times-vector conceptually makes a 1-column N-row matrix, but we return vecN.
163*c8dee2aaSAndroid Build Coastguard Worker         std::swap(outColumns, outRows);
164*c8dee2aaSAndroid Build Coastguard Worker     }
165*c8dee2aaSAndroid Build Coastguard Worker 
166*c8dee2aaSAndroid Build Coastguard Worker     const Type& resultType = componentType.toCompound(context, outColumns, outRows);
167*c8dee2aaSAndroid Build Coastguard Worker     return ConstructorCompound::MakeFromConstants(context, pos, resultType, args);
168*c8dee2aaSAndroid Build Coastguard Worker }
169*c8dee2aaSAndroid Build Coastguard Worker 
simplify_matrix_times_matrix(const Context & context,Position pos,const Expression & left,const Expression & right)170*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> simplify_matrix_times_matrix(const Context& context,
171*c8dee2aaSAndroid Build Coastguard Worker                                                                 Position pos,
172*c8dee2aaSAndroid Build Coastguard Worker                                                                 const Expression& left,
173*c8dee2aaSAndroid Build Coastguard Worker                                                                 const Expression& right) {
174*c8dee2aaSAndroid Build Coastguard Worker     const Type& leftType = left.type();
175*c8dee2aaSAndroid Build Coastguard Worker     const Type& rightType = right.type();
176*c8dee2aaSAndroid Build Coastguard Worker 
177*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(leftType.isMatrix());
178*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(rightType.isMatrix());
179*c8dee2aaSAndroid Build Coastguard Worker 
180*c8dee2aaSAndroid Build Coastguard Worker     return simplify_matrix_multiplication(context, pos, left, right,
181*c8dee2aaSAndroid Build Coastguard Worker                                           leftType.columns(), leftType.rows(),
182*c8dee2aaSAndroid Build Coastguard Worker                                           rightType.columns(), rightType.rows());
183*c8dee2aaSAndroid Build Coastguard Worker }
184*c8dee2aaSAndroid Build Coastguard Worker 
simplify_vector_times_matrix(const Context & context,Position pos,const Expression & left,const Expression & right)185*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> simplify_vector_times_matrix(const Context& context,
186*c8dee2aaSAndroid Build Coastguard Worker                                                                 Position pos,
187*c8dee2aaSAndroid Build Coastguard Worker                                                                 const Expression& left,
188*c8dee2aaSAndroid Build Coastguard Worker                                                                 const Expression& right) {
189*c8dee2aaSAndroid Build Coastguard Worker     const Type& leftType = left.type();
190*c8dee2aaSAndroid Build Coastguard Worker     const Type& rightType = right.type();
191*c8dee2aaSAndroid Build Coastguard Worker 
192*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(leftType.isVector());
193*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(rightType.isMatrix());
194*c8dee2aaSAndroid Build Coastguard Worker 
195*c8dee2aaSAndroid Build Coastguard Worker     return simplify_matrix_multiplication(context, pos, left, right,
196*c8dee2aaSAndroid Build Coastguard Worker                                           /*leftColumns=*/leftType.columns(), /*leftRows=*/1,
197*c8dee2aaSAndroid Build Coastguard Worker                                           rightType.columns(), rightType.rows());
198*c8dee2aaSAndroid Build Coastguard Worker }
199*c8dee2aaSAndroid Build Coastguard Worker 
simplify_matrix_times_vector(const Context & context,Position pos,const Expression & left,const Expression & right)200*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> simplify_matrix_times_vector(const Context& context,
201*c8dee2aaSAndroid Build Coastguard Worker                                                                 Position pos,
202*c8dee2aaSAndroid Build Coastguard Worker                                                                 const Expression& left,
203*c8dee2aaSAndroid Build Coastguard Worker                                                                 const Expression& right) {
204*c8dee2aaSAndroid Build Coastguard Worker     const Type& leftType = left.type();
205*c8dee2aaSAndroid Build Coastguard Worker     const Type& rightType = right.type();
206*c8dee2aaSAndroid Build Coastguard Worker 
207*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(leftType.isMatrix());
208*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(rightType.isVector());
209*c8dee2aaSAndroid Build Coastguard Worker 
210*c8dee2aaSAndroid Build Coastguard Worker     return simplify_matrix_multiplication(context, pos, left, right,
211*c8dee2aaSAndroid Build Coastguard Worker                                           leftType.columns(), leftType.rows(),
212*c8dee2aaSAndroid Build Coastguard Worker                                           /*rightColumns=*/1, /*rightRows=*/rightType.columns());
213*c8dee2aaSAndroid Build Coastguard Worker }
214*c8dee2aaSAndroid Build Coastguard Worker 
simplify_componentwise(const Context & context,Position pos,const Expression & left,Operator op,const Expression & right)215*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> simplify_componentwise(const Context& context,
216*c8dee2aaSAndroid Build Coastguard Worker                                                           Position pos,
217*c8dee2aaSAndroid Build Coastguard Worker                                                           const Expression& left,
218*c8dee2aaSAndroid Build Coastguard Worker                                                           Operator op,
219*c8dee2aaSAndroid Build Coastguard Worker                                                           const Expression& right) {
220*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(is_vec_or_mat(left.type()));
221*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(left.type().matches(right.type()));
222*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = left.type();
223*c8dee2aaSAndroid Build Coastguard Worker 
224*c8dee2aaSAndroid Build Coastguard Worker     // Handle equality operations: == !=
225*c8dee2aaSAndroid Build Coastguard Worker     if (std::unique_ptr<Expression> result = simplify_constant_equality(context, pos, left, op,
226*c8dee2aaSAndroid Build Coastguard Worker             right)) {
227*c8dee2aaSAndroid Build Coastguard Worker         return result;
228*c8dee2aaSAndroid Build Coastguard Worker     }
229*c8dee2aaSAndroid Build Coastguard Worker 
230*c8dee2aaSAndroid Build Coastguard Worker     // Handle floating-point arithmetic: + - * /
231*c8dee2aaSAndroid Build Coastguard Worker     using FoldFn = double (*)(double, double);
232*c8dee2aaSAndroid Build Coastguard Worker     FoldFn foldFn;
233*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
234*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PLUS:  foldFn = +[](double a, double b) { return a + b; }; break;
235*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::MINUS: foldFn = +[](double a, double b) { return a - b; }; break;
236*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::STAR:  foldFn = +[](double a, double b) { return a * b; }; break;
237*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::SLASH: foldFn = +[](double a, double b) { return a / b; }; break;
238*c8dee2aaSAndroid Build Coastguard Worker         default:
239*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
240*c8dee2aaSAndroid Build Coastguard Worker     }
241*c8dee2aaSAndroid Build Coastguard Worker 
242*c8dee2aaSAndroid Build Coastguard Worker     const Type& componentType = type.componentType();
243*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(componentType.isNumber());
244*c8dee2aaSAndroid Build Coastguard Worker 
245*c8dee2aaSAndroid Build Coastguard Worker     double minimumValue = componentType.minimumValue();
246*c8dee2aaSAndroid Build Coastguard Worker     double maximumValue = componentType.maximumValue();
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker     double args[16];
249*c8dee2aaSAndroid Build Coastguard Worker     int numSlots = type.slotCount();
250*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < numSlots; i++) {
251*c8dee2aaSAndroid Build Coastguard Worker         double value = foldFn(*left.getConstantValue(i), *right.getConstantValue(i));
252*c8dee2aaSAndroid Build Coastguard Worker         if (value < minimumValue || value > maximumValue) {
253*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
254*c8dee2aaSAndroid Build Coastguard Worker         }
255*c8dee2aaSAndroid Build Coastguard Worker         args[i] = value;
256*c8dee2aaSAndroid Build Coastguard Worker     }
257*c8dee2aaSAndroid Build Coastguard Worker     return ConstructorCompound::MakeFromConstants(context, pos, type, args);
258*c8dee2aaSAndroid Build Coastguard Worker }
259*c8dee2aaSAndroid Build Coastguard Worker 
splat_scalar(const Context & context,const Expression & scalar,const Type & type)260*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> splat_scalar(const Context& context,
261*c8dee2aaSAndroid Build Coastguard Worker                                                 const Expression& scalar,
262*c8dee2aaSAndroid Build Coastguard Worker                                                 const Type& type) {
263*c8dee2aaSAndroid Build Coastguard Worker     if (type.isVector()) {
264*c8dee2aaSAndroid Build Coastguard Worker         return ConstructorSplat::Make(context, scalar.fPosition, type, scalar.clone());
265*c8dee2aaSAndroid Build Coastguard Worker     }
266*c8dee2aaSAndroid Build Coastguard Worker     if (type.isMatrix()) {
267*c8dee2aaSAndroid Build Coastguard Worker         int numSlots = type.slotCount();
268*c8dee2aaSAndroid Build Coastguard Worker         ExpressionArray splatMatrix;
269*c8dee2aaSAndroid Build Coastguard Worker         splatMatrix.reserve_exact(numSlots);
270*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 0; index < numSlots; ++index) {
271*c8dee2aaSAndroid Build Coastguard Worker             splatMatrix.push_back(scalar.clone());
272*c8dee2aaSAndroid Build Coastguard Worker         }
273*c8dee2aaSAndroid Build Coastguard Worker         return ConstructorCompound::Make(context, scalar.fPosition, type, std::move(splatMatrix));
274*c8dee2aaSAndroid Build Coastguard Worker     }
275*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGFAILF("unsupported type %s", type.description().c_str());
276*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
277*c8dee2aaSAndroid Build Coastguard Worker }
278*c8dee2aaSAndroid Build Coastguard Worker 
cast_expression(const Context & context,Position pos,const Expression & expr,const Type & type)279*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> cast_expression(const Context& context,
280*c8dee2aaSAndroid Build Coastguard Worker                                                    Position pos,
281*c8dee2aaSAndroid Build Coastguard Worker                                                    const Expression& expr,
282*c8dee2aaSAndroid Build Coastguard Worker                                                    const Type& type) {
283*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.componentType().matches(expr.type().componentType()));
284*c8dee2aaSAndroid Build Coastguard Worker     if (expr.type().isScalar()) {
285*c8dee2aaSAndroid Build Coastguard Worker         if (type.isMatrix()) {
286*c8dee2aaSAndroid Build Coastguard Worker             return ConstructorDiagonalMatrix::Make(context, pos, type, expr.clone());
287*c8dee2aaSAndroid Build Coastguard Worker         }
288*c8dee2aaSAndroid Build Coastguard Worker         if (type.isVector()) {
289*c8dee2aaSAndroid Build Coastguard Worker             return ConstructorSplat::Make(context, pos, type, expr.clone());
290*c8dee2aaSAndroid Build Coastguard Worker         }
291*c8dee2aaSAndroid Build Coastguard Worker     }
292*c8dee2aaSAndroid Build Coastguard Worker     if (type.matches(expr.type())) {
293*c8dee2aaSAndroid Build Coastguard Worker         return expr.clone(pos);
294*c8dee2aaSAndroid Build Coastguard Worker     }
295*c8dee2aaSAndroid Build Coastguard Worker     // We can't cast matrices into vectors or vice-versa.
296*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
297*c8dee2aaSAndroid Build Coastguard Worker }
298*c8dee2aaSAndroid Build Coastguard Worker 
zero_expression(const Context & context,Position pos,const Type & type)299*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> zero_expression(const Context& context,
300*c8dee2aaSAndroid Build Coastguard Worker                                                    Position pos,
301*c8dee2aaSAndroid Build Coastguard Worker                                                    const Type& type) {
302*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Expression> zero = Literal::Make(pos, 0.0, &type.componentType());
303*c8dee2aaSAndroid Build Coastguard Worker     if (type.isScalar()) {
304*c8dee2aaSAndroid Build Coastguard Worker         return zero;
305*c8dee2aaSAndroid Build Coastguard Worker     }
306*c8dee2aaSAndroid Build Coastguard Worker     if (type.isVector()) {
307*c8dee2aaSAndroid Build Coastguard Worker         return ConstructorSplat::Make(context, pos, type, std::move(zero));
308*c8dee2aaSAndroid Build Coastguard Worker     }
309*c8dee2aaSAndroid Build Coastguard Worker     if (type.isMatrix()) {
310*c8dee2aaSAndroid Build Coastguard Worker         return ConstructorDiagonalMatrix::Make(context, pos, type, std::move(zero));
311*c8dee2aaSAndroid Build Coastguard Worker     }
312*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGFAILF("unsupported type %s", type.description().c_str());
313*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
314*c8dee2aaSAndroid Build Coastguard Worker }
315*c8dee2aaSAndroid Build Coastguard Worker 
negate_expression(const Context & context,Position pos,const Expression & expr,const Type & type)316*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> negate_expression(const Context& context,
317*c8dee2aaSAndroid Build Coastguard Worker                                                      Position pos,
318*c8dee2aaSAndroid Build Coastguard Worker                                                      const Expression& expr,
319*c8dee2aaSAndroid Build Coastguard Worker                                                      const Type& type) {
320*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Expression> ctor = cast_expression(context, pos, expr, type);
321*c8dee2aaSAndroid Build Coastguard Worker     return ctor ? PrefixExpression::Make(context, pos, Operator::Kind::MINUS, std::move(ctor))
322*c8dee2aaSAndroid Build Coastguard Worker                 : nullptr;
323*c8dee2aaSAndroid Build Coastguard Worker }
324*c8dee2aaSAndroid Build Coastguard Worker 
GetConstantInt(const Expression & value,SKSL_INT * out)325*c8dee2aaSAndroid Build Coastguard Worker bool ConstantFolder::GetConstantInt(const Expression& value, SKSL_INT* out) {
326*c8dee2aaSAndroid Build Coastguard Worker     const Expression* expr = GetConstantValueForVariable(value);
327*c8dee2aaSAndroid Build Coastguard Worker     if (!expr->isIntLiteral()) {
328*c8dee2aaSAndroid Build Coastguard Worker         return false;
329*c8dee2aaSAndroid Build Coastguard Worker     }
330*c8dee2aaSAndroid Build Coastguard Worker     *out = expr->as<Literal>().intValue();
331*c8dee2aaSAndroid Build Coastguard Worker     return true;
332*c8dee2aaSAndroid Build Coastguard Worker }
333*c8dee2aaSAndroid Build Coastguard Worker 
GetConstantValue(const Expression & value,double * out)334*c8dee2aaSAndroid Build Coastguard Worker bool ConstantFolder::GetConstantValue(const Expression& value, double* out) {
335*c8dee2aaSAndroid Build Coastguard Worker     const Expression* expr = GetConstantValueForVariable(value);
336*c8dee2aaSAndroid Build Coastguard Worker     if (!expr->is<Literal>()) {
337*c8dee2aaSAndroid Build Coastguard Worker         return false;
338*c8dee2aaSAndroid Build Coastguard Worker     }
339*c8dee2aaSAndroid Build Coastguard Worker     *out = expr->as<Literal>().value();
340*c8dee2aaSAndroid Build Coastguard Worker     return true;
341*c8dee2aaSAndroid Build Coastguard Worker }
342*c8dee2aaSAndroid Build Coastguard Worker 
contains_constant_zero(const Expression & expr)343*c8dee2aaSAndroid Build Coastguard Worker static bool contains_constant_zero(const Expression& expr) {
344*c8dee2aaSAndroid Build Coastguard Worker     int numSlots = expr.type().slotCount();
345*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < numSlots; ++index) {
346*c8dee2aaSAndroid Build Coastguard Worker         std::optional<double> slotVal = expr.getConstantValue(index);
347*c8dee2aaSAndroid Build Coastguard Worker         if (slotVal.has_value() && *slotVal == 0.0) {
348*c8dee2aaSAndroid Build Coastguard Worker             return true;
349*c8dee2aaSAndroid Build Coastguard Worker         }
350*c8dee2aaSAndroid Build Coastguard Worker     }
351*c8dee2aaSAndroid Build Coastguard Worker     return false;
352*c8dee2aaSAndroid Build Coastguard Worker }
353*c8dee2aaSAndroid Build Coastguard Worker 
IsConstantSplat(const Expression & expr,double value)354*c8dee2aaSAndroid Build Coastguard Worker bool ConstantFolder::IsConstantSplat(const Expression& expr, double value) {
355*c8dee2aaSAndroid Build Coastguard Worker     int numSlots = expr.type().slotCount();
356*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < numSlots; ++index) {
357*c8dee2aaSAndroid Build Coastguard Worker         std::optional<double> slotVal = expr.getConstantValue(index);
358*c8dee2aaSAndroid Build Coastguard Worker         if (!slotVal.has_value() || *slotVal != value) {
359*c8dee2aaSAndroid Build Coastguard Worker             return false;
360*c8dee2aaSAndroid Build Coastguard Worker         }
361*c8dee2aaSAndroid Build Coastguard Worker     }
362*c8dee2aaSAndroid Build Coastguard Worker     return true;
363*c8dee2aaSAndroid Build Coastguard Worker }
364*c8dee2aaSAndroid Build Coastguard Worker 
365*c8dee2aaSAndroid Build Coastguard Worker // Returns true if the expression is a square diagonal matrix containing `value`.
is_constant_diagonal(const Expression & expr,double value)366*c8dee2aaSAndroid Build Coastguard Worker static bool is_constant_diagonal(const Expression& expr, double value) {
367*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(expr.type().isMatrix());
368*c8dee2aaSAndroid Build Coastguard Worker     int columns = expr.type().columns();
369*c8dee2aaSAndroid Build Coastguard Worker     int rows = expr.type().rows();
370*c8dee2aaSAndroid Build Coastguard Worker     if (columns != rows) {
371*c8dee2aaSAndroid Build Coastguard Worker         return false;
372*c8dee2aaSAndroid Build Coastguard Worker     }
373*c8dee2aaSAndroid Build Coastguard Worker     int slotIdx = 0;
374*c8dee2aaSAndroid Build Coastguard Worker     for (int c = 0; c < columns; ++c) {
375*c8dee2aaSAndroid Build Coastguard Worker         for (int r = 0; r < rows; ++r) {
376*c8dee2aaSAndroid Build Coastguard Worker             double expectation = (c == r) ? value : 0;
377*c8dee2aaSAndroid Build Coastguard Worker             std::optional<double> slotVal = expr.getConstantValue(slotIdx++);
378*c8dee2aaSAndroid Build Coastguard Worker             if (!slotVal.has_value() || *slotVal != expectation) {
379*c8dee2aaSAndroid Build Coastguard Worker                 return false;
380*c8dee2aaSAndroid Build Coastguard Worker             }
381*c8dee2aaSAndroid Build Coastguard Worker         }
382*c8dee2aaSAndroid Build Coastguard Worker     }
383*c8dee2aaSAndroid Build Coastguard Worker     return true;
384*c8dee2aaSAndroid Build Coastguard Worker }
385*c8dee2aaSAndroid Build Coastguard Worker 
386*c8dee2aaSAndroid Build Coastguard Worker // Returns true if the expression is a scalar, vector, or diagonal matrix containing `value`.
is_constant_value(const Expression & expr,double value)387*c8dee2aaSAndroid Build Coastguard Worker static bool is_constant_value(const Expression& expr, double value) {
388*c8dee2aaSAndroid Build Coastguard Worker     return expr.type().isMatrix() ? is_constant_diagonal(expr, value)
389*c8dee2aaSAndroid Build Coastguard Worker                                   : ConstantFolder::IsConstantSplat(expr, value);
390*c8dee2aaSAndroid Build Coastguard Worker }
391*c8dee2aaSAndroid Build Coastguard Worker 
392*c8dee2aaSAndroid Build Coastguard Worker // The expression represents the right-hand side of a division op. If the division can be
393*c8dee2aaSAndroid Build Coastguard Worker // strength-reduced into multiplication by a reciprocal, returns that reciprocal as an expression.
394*c8dee2aaSAndroid Build Coastguard Worker // Note that this only supports literal values with safe-to-use reciprocals, and returns null if
395*c8dee2aaSAndroid Build Coastguard Worker // Expression contains anything else.
make_reciprocal_expression(const Context & context,const Expression & right)396*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> make_reciprocal_expression(const Context& context,
397*c8dee2aaSAndroid Build Coastguard Worker                                                               const Expression& right) {
398*c8dee2aaSAndroid Build Coastguard Worker     if (right.type().isMatrix() || !right.type().componentType().isFloat()) {
399*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
400*c8dee2aaSAndroid Build Coastguard Worker     }
401*c8dee2aaSAndroid Build Coastguard Worker     // Verify that each slot contains a finite, non-zero literal, take its reciprocal.
402*c8dee2aaSAndroid Build Coastguard Worker     double values[4];
403*c8dee2aaSAndroid Build Coastguard Worker     int nslots = right.type().slotCount();
404*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < nslots; ++index) {
405*c8dee2aaSAndroid Build Coastguard Worker         std::optional<double> value = right.getConstantValue(index);
406*c8dee2aaSAndroid Build Coastguard Worker         if (!value) {
407*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
408*c8dee2aaSAndroid Build Coastguard Worker         }
409*c8dee2aaSAndroid Build Coastguard Worker         *value = sk_ieee_double_divide(1.0, *value);
410*c8dee2aaSAndroid Build Coastguard Worker         if (*value >= -FLT_MAX && *value <= FLT_MAX && *value != 0.0) {
411*c8dee2aaSAndroid Build Coastguard Worker             // The reciprocal can be represented safely as a finite 32-bit float.
412*c8dee2aaSAndroid Build Coastguard Worker             values[index] = *value;
413*c8dee2aaSAndroid Build Coastguard Worker         } else {
414*c8dee2aaSAndroid Build Coastguard Worker             // The value is outside the 32-bit float range, or is NaN; do not optimize.
415*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
416*c8dee2aaSAndroid Build Coastguard Worker         }
417*c8dee2aaSAndroid Build Coastguard Worker     }
418*c8dee2aaSAndroid Build Coastguard Worker     // Turn the expression array into a compound constructor. (If this is a single-slot expression,
419*c8dee2aaSAndroid Build Coastguard Worker     // this will return the literal as-is.)
420*c8dee2aaSAndroid Build Coastguard Worker     return ConstructorCompound::MakeFromConstants(context, right.fPosition, right.type(), values);
421*c8dee2aaSAndroid Build Coastguard Worker }
422*c8dee2aaSAndroid Build Coastguard Worker 
error_on_divide_by_zero(const Context & context,Position pos,Operator op,const Expression & right)423*c8dee2aaSAndroid Build Coastguard Worker static bool error_on_divide_by_zero(const Context& context, Position pos, Operator op,
424*c8dee2aaSAndroid Build Coastguard Worker                                     const Expression& right) {
425*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
426*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::SLASH:
427*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::SLASHEQ:
428*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PERCENT:
429*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PERCENTEQ:
430*c8dee2aaSAndroid Build Coastguard Worker             if (contains_constant_zero(right)) {
431*c8dee2aaSAndroid Build Coastguard Worker                 context.fErrors->error(pos, "division by zero");
432*c8dee2aaSAndroid Build Coastguard Worker                 return true;
433*c8dee2aaSAndroid Build Coastguard Worker             }
434*c8dee2aaSAndroid Build Coastguard Worker             return false;
435*c8dee2aaSAndroid Build Coastguard Worker         default:
436*c8dee2aaSAndroid Build Coastguard Worker             return false;
437*c8dee2aaSAndroid Build Coastguard Worker     }
438*c8dee2aaSAndroid Build Coastguard Worker }
439*c8dee2aaSAndroid Build Coastguard Worker 
GetConstantValueOrNull(const Expression & inExpr)440*c8dee2aaSAndroid Build Coastguard Worker const Expression* ConstantFolder::GetConstantValueOrNull(const Expression& inExpr) {
441*c8dee2aaSAndroid Build Coastguard Worker     const Expression* expr = &inExpr;
442*c8dee2aaSAndroid Build Coastguard Worker     while (expr->is<VariableReference>()) {
443*c8dee2aaSAndroid Build Coastguard Worker         const VariableReference& varRef = expr->as<VariableReference>();
444*c8dee2aaSAndroid Build Coastguard Worker         if (varRef.refKind() != VariableRefKind::kRead) {
445*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
446*c8dee2aaSAndroid Build Coastguard Worker         }
447*c8dee2aaSAndroid Build Coastguard Worker         const Variable& var = *varRef.variable();
448*c8dee2aaSAndroid Build Coastguard Worker         if (!var.modifierFlags().isConst()) {
449*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
450*c8dee2aaSAndroid Build Coastguard Worker         }
451*c8dee2aaSAndroid Build Coastguard Worker         expr = var.initialValue();
452*c8dee2aaSAndroid Build Coastguard Worker         if (!expr) {
453*c8dee2aaSAndroid Build Coastguard Worker             // Generally, const variables must have initial values. However, function parameters are
454*c8dee2aaSAndroid Build Coastguard Worker             // an exception; they can be const but won't have an initial value.
455*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
456*c8dee2aaSAndroid Build Coastguard Worker         }
457*c8dee2aaSAndroid Build Coastguard Worker     }
458*c8dee2aaSAndroid Build Coastguard Worker     return Analysis::IsCompileTimeConstant(*expr) ? expr : nullptr;
459*c8dee2aaSAndroid Build Coastguard Worker }
460*c8dee2aaSAndroid Build Coastguard Worker 
GetConstantValueForVariable(const Expression & inExpr)461*c8dee2aaSAndroid Build Coastguard Worker const Expression* ConstantFolder::GetConstantValueForVariable(const Expression& inExpr) {
462*c8dee2aaSAndroid Build Coastguard Worker     const Expression* expr = GetConstantValueOrNull(inExpr);
463*c8dee2aaSAndroid Build Coastguard Worker     return expr ? expr : &inExpr;
464*c8dee2aaSAndroid Build Coastguard Worker }
465*c8dee2aaSAndroid Build Coastguard Worker 
MakeConstantValueForVariable(Position pos,std::unique_ptr<Expression> inExpr)466*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> ConstantFolder::MakeConstantValueForVariable(
467*c8dee2aaSAndroid Build Coastguard Worker         Position pos, std::unique_ptr<Expression> inExpr) {
468*c8dee2aaSAndroid Build Coastguard Worker     const Expression* expr = GetConstantValueOrNull(*inExpr);
469*c8dee2aaSAndroid Build Coastguard Worker     return expr ? expr->clone(pos) : std::move(inExpr);
470*c8dee2aaSAndroid Build Coastguard Worker }
471*c8dee2aaSAndroid Build Coastguard Worker 
is_scalar_op_matrix(const Expression & left,const Expression & right)472*c8dee2aaSAndroid Build Coastguard Worker static bool is_scalar_op_matrix(const Expression& left, const Expression& right) {
473*c8dee2aaSAndroid Build Coastguard Worker     return left.type().isScalar() && right.type().isMatrix();
474*c8dee2aaSAndroid Build Coastguard Worker }
475*c8dee2aaSAndroid Build Coastguard Worker 
is_matrix_op_scalar(const Expression & left,const Expression & right)476*c8dee2aaSAndroid Build Coastguard Worker static bool is_matrix_op_scalar(const Expression& left, const Expression& right) {
477*c8dee2aaSAndroid Build Coastguard Worker     return is_scalar_op_matrix(right, left);
478*c8dee2aaSAndroid Build Coastguard Worker }
479*c8dee2aaSAndroid Build Coastguard Worker 
simplify_arithmetic(const Context & context,Position pos,const Expression & left,Operator op,const Expression & right,const Type & resultType)480*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> simplify_arithmetic(const Context& context,
481*c8dee2aaSAndroid Build Coastguard Worker                                                        Position pos,
482*c8dee2aaSAndroid Build Coastguard Worker                                                        const Expression& left,
483*c8dee2aaSAndroid Build Coastguard Worker                                                        Operator op,
484*c8dee2aaSAndroid Build Coastguard Worker                                                        const Expression& right,
485*c8dee2aaSAndroid Build Coastguard Worker                                                        const Type& resultType) {
486*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
487*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PLUS:
488*c8dee2aaSAndroid Build Coastguard Worker             if (!is_scalar_op_matrix(left, right) &&
489*c8dee2aaSAndroid Build Coastguard Worker                 ConstantFolder::IsConstantSplat(right, 0.0)) {  // x + 0
490*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = cast_expression(context, pos, left,
491*c8dee2aaSAndroid Build Coastguard Worker                                                                        resultType)) {
492*c8dee2aaSAndroid Build Coastguard Worker                     return expr;
493*c8dee2aaSAndroid Build Coastguard Worker                 }
494*c8dee2aaSAndroid Build Coastguard Worker             }
495*c8dee2aaSAndroid Build Coastguard Worker             if (!is_matrix_op_scalar(left, right) &&
496*c8dee2aaSAndroid Build Coastguard Worker                 ConstantFolder::IsConstantSplat(left, 0.0)) {  // 0 + x
497*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = cast_expression(context, pos, right,
498*c8dee2aaSAndroid Build Coastguard Worker                                                                        resultType)) {
499*c8dee2aaSAndroid Build Coastguard Worker                     return expr;
500*c8dee2aaSAndroid Build Coastguard Worker                 }
501*c8dee2aaSAndroid Build Coastguard Worker             }
502*c8dee2aaSAndroid Build Coastguard Worker             break;
503*c8dee2aaSAndroid Build Coastguard Worker 
504*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::STAR:
505*c8dee2aaSAndroid Build Coastguard Worker             if (is_constant_value(right, 1.0)) {  // x * 1
506*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = cast_expression(context, pos, left,
507*c8dee2aaSAndroid Build Coastguard Worker                                                                        resultType)) {
508*c8dee2aaSAndroid Build Coastguard Worker                     return expr;
509*c8dee2aaSAndroid Build Coastguard Worker                 }
510*c8dee2aaSAndroid Build Coastguard Worker             }
511*c8dee2aaSAndroid Build Coastguard Worker             if (is_constant_value(left, 1.0)) {   // 1 * x
512*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = cast_expression(context, pos, right,
513*c8dee2aaSAndroid Build Coastguard Worker                                                                        resultType)) {
514*c8dee2aaSAndroid Build Coastguard Worker                     return expr;
515*c8dee2aaSAndroid Build Coastguard Worker                 }
516*c8dee2aaSAndroid Build Coastguard Worker             }
517*c8dee2aaSAndroid Build Coastguard Worker             if (is_constant_value(right, 0.0) && !Analysis::HasSideEffects(left)) {  // x * 0
518*c8dee2aaSAndroid Build Coastguard Worker                 return zero_expression(context, pos, resultType);
519*c8dee2aaSAndroid Build Coastguard Worker             }
520*c8dee2aaSAndroid Build Coastguard Worker             if (is_constant_value(left, 0.0) && !Analysis::HasSideEffects(right)) {  // 0 * x
521*c8dee2aaSAndroid Build Coastguard Worker                 return zero_expression(context, pos, resultType);
522*c8dee2aaSAndroid Build Coastguard Worker             }
523*c8dee2aaSAndroid Build Coastguard Worker             if (is_constant_value(right, -1.0)) {  // x * -1 (to `-x`)
524*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = negate_expression(context, pos, left,
525*c8dee2aaSAndroid Build Coastguard Worker                                                                          resultType)) {
526*c8dee2aaSAndroid Build Coastguard Worker                     return expr;
527*c8dee2aaSAndroid Build Coastguard Worker                 }
528*c8dee2aaSAndroid Build Coastguard Worker             }
529*c8dee2aaSAndroid Build Coastguard Worker             if (is_constant_value(left, -1.0)) {  // -1 * x (to `-x`)
530*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = negate_expression(context, pos, right,
531*c8dee2aaSAndroid Build Coastguard Worker                                                                          resultType)) {
532*c8dee2aaSAndroid Build Coastguard Worker                     return expr;
533*c8dee2aaSAndroid Build Coastguard Worker                 }
534*c8dee2aaSAndroid Build Coastguard Worker             }
535*c8dee2aaSAndroid Build Coastguard Worker             break;
536*c8dee2aaSAndroid Build Coastguard Worker 
537*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::MINUS:
538*c8dee2aaSAndroid Build Coastguard Worker             if (!is_scalar_op_matrix(left, right) &&
539*c8dee2aaSAndroid Build Coastguard Worker                 ConstantFolder::IsConstantSplat(right, 0.0)) {  // x - 0
540*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = cast_expression(context, pos, left,
541*c8dee2aaSAndroid Build Coastguard Worker                                                                        resultType)) {
542*c8dee2aaSAndroid Build Coastguard Worker                     return expr;
543*c8dee2aaSAndroid Build Coastguard Worker                 }
544*c8dee2aaSAndroid Build Coastguard Worker             }
545*c8dee2aaSAndroid Build Coastguard Worker             if (!is_matrix_op_scalar(left, right) &&
546*c8dee2aaSAndroid Build Coastguard Worker                 ConstantFolder::IsConstantSplat(left, 0.0)) {  // 0 - x
547*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = negate_expression(context, pos, right,
548*c8dee2aaSAndroid Build Coastguard Worker                                                                          resultType)) {
549*c8dee2aaSAndroid Build Coastguard Worker                     return expr;
550*c8dee2aaSAndroid Build Coastguard Worker                 }
551*c8dee2aaSAndroid Build Coastguard Worker             }
552*c8dee2aaSAndroid Build Coastguard Worker             break;
553*c8dee2aaSAndroid Build Coastguard Worker 
554*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::SLASH:
555*c8dee2aaSAndroid Build Coastguard Worker             if (!is_scalar_op_matrix(left, right) &&
556*c8dee2aaSAndroid Build Coastguard Worker                 ConstantFolder::IsConstantSplat(right, 1.0)) {  // x / 1
557*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = cast_expression(context, pos, left,
558*c8dee2aaSAndroid Build Coastguard Worker                                                                        resultType)) {
559*c8dee2aaSAndroid Build Coastguard Worker                     return expr;
560*c8dee2aaSAndroid Build Coastguard Worker                 }
561*c8dee2aaSAndroid Build Coastguard Worker             }
562*c8dee2aaSAndroid Build Coastguard Worker             if (!left.type().isMatrix()) {  // convert `x / 2` into `x * 0.5`
563*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> expr = make_reciprocal_expression(context, right)) {
564*c8dee2aaSAndroid Build Coastguard Worker                     return BinaryExpression::Make(context, pos, left.clone(), Operator::Kind::STAR,
565*c8dee2aaSAndroid Build Coastguard Worker                                                   std::move(expr));
566*c8dee2aaSAndroid Build Coastguard Worker                 }
567*c8dee2aaSAndroid Build Coastguard Worker             }
568*c8dee2aaSAndroid Build Coastguard Worker             break;
569*c8dee2aaSAndroid Build Coastguard Worker 
570*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PLUSEQ:
571*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::MINUSEQ:
572*c8dee2aaSAndroid Build Coastguard Worker             if (ConstantFolder::IsConstantSplat(right, 0.0)) {  // x += 0, x -= 0
573*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> var = cast_expression(context, pos, left,
574*c8dee2aaSAndroid Build Coastguard Worker                                                                       resultType)) {
575*c8dee2aaSAndroid Build Coastguard Worker                     Analysis::UpdateVariableRefKind(var.get(), VariableRefKind::kRead);
576*c8dee2aaSAndroid Build Coastguard Worker                     return var;
577*c8dee2aaSAndroid Build Coastguard Worker                 }
578*c8dee2aaSAndroid Build Coastguard Worker             }
579*c8dee2aaSAndroid Build Coastguard Worker             break;
580*c8dee2aaSAndroid Build Coastguard Worker 
581*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::STAREQ:
582*c8dee2aaSAndroid Build Coastguard Worker             if (is_constant_value(right, 1.0)) {  // x *= 1
583*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> var = cast_expression(context, pos, left,
584*c8dee2aaSAndroid Build Coastguard Worker                                                                       resultType)) {
585*c8dee2aaSAndroid Build Coastguard Worker                     Analysis::UpdateVariableRefKind(var.get(), VariableRefKind::kRead);
586*c8dee2aaSAndroid Build Coastguard Worker                     return var;
587*c8dee2aaSAndroid Build Coastguard Worker                 }
588*c8dee2aaSAndroid Build Coastguard Worker             }
589*c8dee2aaSAndroid Build Coastguard Worker             break;
590*c8dee2aaSAndroid Build Coastguard Worker 
591*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::SLASHEQ:
592*c8dee2aaSAndroid Build Coastguard Worker             if (ConstantFolder::IsConstantSplat(right, 1.0)) {  // x /= 1
593*c8dee2aaSAndroid Build Coastguard Worker                 if (std::unique_ptr<Expression> var = cast_expression(context, pos, left,
594*c8dee2aaSAndroid Build Coastguard Worker                                                                       resultType)) {
595*c8dee2aaSAndroid Build Coastguard Worker                     Analysis::UpdateVariableRefKind(var.get(), VariableRefKind::kRead);
596*c8dee2aaSAndroid Build Coastguard Worker                     return var;
597*c8dee2aaSAndroid Build Coastguard Worker                 }
598*c8dee2aaSAndroid Build Coastguard Worker             }
599*c8dee2aaSAndroid Build Coastguard Worker             if (std::unique_ptr<Expression> expr = make_reciprocal_expression(context, right)) {
600*c8dee2aaSAndroid Build Coastguard Worker                 return BinaryExpression::Make(context, pos, left.clone(), Operator::Kind::STAREQ,
601*c8dee2aaSAndroid Build Coastguard Worker                                               std::move(expr));
602*c8dee2aaSAndroid Build Coastguard Worker             }
603*c8dee2aaSAndroid Build Coastguard Worker             break;
604*c8dee2aaSAndroid Build Coastguard Worker 
605*c8dee2aaSAndroid Build Coastguard Worker         default:
606*c8dee2aaSAndroid Build Coastguard Worker             break;
607*c8dee2aaSAndroid Build Coastguard Worker     }
608*c8dee2aaSAndroid Build Coastguard Worker 
609*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
610*c8dee2aaSAndroid Build Coastguard Worker }
611*c8dee2aaSAndroid Build Coastguard Worker 
612*c8dee2aaSAndroid Build Coastguard Worker // The expression must be scalar, and represents the right-hand side of a division op. It can
613*c8dee2aaSAndroid Build Coastguard Worker // contain anything, not just literal values. This returns the binary expression `1.0 / expr`. The
614*c8dee2aaSAndroid Build Coastguard Worker // expression might be further simplified by the constant folding, if possible.
one_over_scalar(const Context & context,const Expression & right)615*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> one_over_scalar(const Context& context,
616*c8dee2aaSAndroid Build Coastguard Worker                                                    const Expression& right) {
617*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(right.type().isScalar());
618*c8dee2aaSAndroid Build Coastguard Worker     Position pos = right.fPosition;
619*c8dee2aaSAndroid Build Coastguard Worker     return BinaryExpression::Make(context, pos,
620*c8dee2aaSAndroid Build Coastguard Worker                                   Literal::Make(pos, 1.0, &right.type()),
621*c8dee2aaSAndroid Build Coastguard Worker                                   Operator::Kind::SLASH,
622*c8dee2aaSAndroid Build Coastguard Worker                                   right.clone());
623*c8dee2aaSAndroid Build Coastguard Worker }
624*c8dee2aaSAndroid Build Coastguard Worker 
simplify_matrix_division(const Context & context,Position pos,const Expression & left,Operator op,const Expression & right,const Type & resultType)625*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> simplify_matrix_division(const Context& context,
626*c8dee2aaSAndroid Build Coastguard Worker                                                             Position pos,
627*c8dee2aaSAndroid Build Coastguard Worker                                                             const Expression& left,
628*c8dee2aaSAndroid Build Coastguard Worker                                                             Operator op,
629*c8dee2aaSAndroid Build Coastguard Worker                                                             const Expression& right,
630*c8dee2aaSAndroid Build Coastguard Worker                                                             const Type& resultType) {
631*c8dee2aaSAndroid Build Coastguard Worker     // Convert matrix-over-scalar `x /= y` into `x *= (1.0 / y)`. This generates better
632*c8dee2aaSAndroid Build Coastguard Worker     // code in SPIR-V and Metal, and should be roughly equivalent elsewhere.
633*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
634*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::SLASH:
635*c8dee2aaSAndroid Build Coastguard Worker         case OperatorKind::SLASHEQ:
636*c8dee2aaSAndroid Build Coastguard Worker             if (left.type().isMatrix() && right.type().isScalar()) {
637*c8dee2aaSAndroid Build Coastguard Worker                 Operator multiplyOp = op.isAssignment() ? OperatorKind::STAREQ
638*c8dee2aaSAndroid Build Coastguard Worker                                                         : OperatorKind::STAR;
639*c8dee2aaSAndroid Build Coastguard Worker                 return BinaryExpression::Make(context, pos,
640*c8dee2aaSAndroid Build Coastguard Worker                                               left.clone(),
641*c8dee2aaSAndroid Build Coastguard Worker                                               multiplyOp,
642*c8dee2aaSAndroid Build Coastguard Worker                                               one_over_scalar(context, right));
643*c8dee2aaSAndroid Build Coastguard Worker             }
644*c8dee2aaSAndroid Build Coastguard Worker             break;
645*c8dee2aaSAndroid Build Coastguard Worker 
646*c8dee2aaSAndroid Build Coastguard Worker         default:
647*c8dee2aaSAndroid Build Coastguard Worker             break;
648*c8dee2aaSAndroid Build Coastguard Worker     }
649*c8dee2aaSAndroid Build Coastguard Worker 
650*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
651*c8dee2aaSAndroid Build Coastguard Worker }
652*c8dee2aaSAndroid Build Coastguard Worker 
fold_expression(Position pos,double result,const Type * resultType)653*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> fold_expression(Position pos,
654*c8dee2aaSAndroid Build Coastguard Worker                                                    double result,
655*c8dee2aaSAndroid Build Coastguard Worker                                                    const Type* resultType) {
656*c8dee2aaSAndroid Build Coastguard Worker     if (resultType->isNumber()) {
657*c8dee2aaSAndroid Build Coastguard Worker         if (result >= resultType->minimumValue() && result <= resultType->maximumValue()) {
658*c8dee2aaSAndroid Build Coastguard Worker             // This result will fit inside its type.
659*c8dee2aaSAndroid Build Coastguard Worker         } else {
660*c8dee2aaSAndroid Build Coastguard Worker             // The value is outside the range or is NaN (all if-checks fail); do not optimize.
661*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
662*c8dee2aaSAndroid Build Coastguard Worker         }
663*c8dee2aaSAndroid Build Coastguard Worker     }
664*c8dee2aaSAndroid Build Coastguard Worker 
665*c8dee2aaSAndroid Build Coastguard Worker     return Literal::Make(pos, result, resultType);
666*c8dee2aaSAndroid Build Coastguard Worker }
667*c8dee2aaSAndroid Build Coastguard Worker 
fold_two_constants(const Context & context,Position pos,const Expression * left,Operator op,const Expression * right,const Type & resultType)668*c8dee2aaSAndroid Build Coastguard Worker static std::unique_ptr<Expression> fold_two_constants(const Context& context,
669*c8dee2aaSAndroid Build Coastguard Worker                                                       Position pos,
670*c8dee2aaSAndroid Build Coastguard Worker                                                       const Expression* left,
671*c8dee2aaSAndroid Build Coastguard Worker                                                       Operator op,
672*c8dee2aaSAndroid Build Coastguard Worker                                                       const Expression* right,
673*c8dee2aaSAndroid Build Coastguard Worker                                                       const Type& resultType) {
674*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(Analysis::IsCompileTimeConstant(*left));
675*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(Analysis::IsCompileTimeConstant(*right));
676*c8dee2aaSAndroid Build Coastguard Worker     const Type& leftType = left->type();
677*c8dee2aaSAndroid Build Coastguard Worker     const Type& rightType = right->type();
678*c8dee2aaSAndroid Build Coastguard Worker 
679*c8dee2aaSAndroid Build Coastguard Worker     // Handle pairs of integer literals.
680*c8dee2aaSAndroid Build Coastguard Worker     if (left->isIntLiteral() && right->isIntLiteral()) {
681*c8dee2aaSAndroid Build Coastguard Worker         using SKSL_UINT = uint64_t;
682*c8dee2aaSAndroid Build Coastguard Worker         SKSL_INT leftVal  = left->as<Literal>().intValue();
683*c8dee2aaSAndroid Build Coastguard Worker         SKSL_INT rightVal = right->as<Literal>().intValue();
684*c8dee2aaSAndroid Build Coastguard Worker 
685*c8dee2aaSAndroid Build Coastguard Worker         // Note that fold_expression returns null if the result would overflow its type.
686*c8dee2aaSAndroid Build Coastguard Worker         #define RESULT(Op)   fold_expression(pos, (SKSL_INT)(leftVal) Op \
687*c8dee2aaSAndroid Build Coastguard Worker                                                   (SKSL_INT)(rightVal), &resultType)
688*c8dee2aaSAndroid Build Coastguard Worker         #define URESULT(Op)  fold_expression(pos, (SKSL_INT)((SKSL_UINT)(leftVal) Op \
689*c8dee2aaSAndroid Build Coastguard Worker                                                   (SKSL_UINT)(rightVal)), &resultType)
690*c8dee2aaSAndroid Build Coastguard Worker         switch (op.kind()) {
691*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::PLUS:       return URESULT(+);
692*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::MINUS:      return URESULT(-);
693*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::STAR:       return URESULT(*);
694*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::SLASH:
695*c8dee2aaSAndroid Build Coastguard Worker                 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
696*c8dee2aaSAndroid Build Coastguard Worker                     context.fErrors->error(pos, "arithmetic overflow");
697*c8dee2aaSAndroid Build Coastguard Worker                     return nullptr;
698*c8dee2aaSAndroid Build Coastguard Worker                 }
699*c8dee2aaSAndroid Build Coastguard Worker                 return RESULT(/);
700*c8dee2aaSAndroid Build Coastguard Worker 
701*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::PERCENT:
702*c8dee2aaSAndroid Build Coastguard Worker                 if (leftVal == std::numeric_limits<SKSL_INT>::min() && rightVal == -1) {
703*c8dee2aaSAndroid Build Coastguard Worker                     context.fErrors->error(pos, "arithmetic overflow");
704*c8dee2aaSAndroid Build Coastguard Worker                     return nullptr;
705*c8dee2aaSAndroid Build Coastguard Worker                 }
706*c8dee2aaSAndroid Build Coastguard Worker                 return RESULT(%);
707*c8dee2aaSAndroid Build Coastguard Worker 
708*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::BITWISEAND: return RESULT(&);
709*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::BITWISEOR:  return RESULT(|);
710*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::BITWISEXOR: return RESULT(^);
711*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::EQEQ:       return RESULT(==);
712*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::NEQ:        return RESULT(!=);
713*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::GT:         return RESULT(>);
714*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::GTEQ:       return RESULT(>=);
715*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::LT:         return RESULT(<);
716*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::LTEQ:       return RESULT(<=);
717*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::SHL:
718*c8dee2aaSAndroid Build Coastguard Worker                 if (rightVal >= 0 && rightVal <= 31) {
719*c8dee2aaSAndroid Build Coastguard Worker                     // Left-shifting a negative (or really, any signed) value is undefined behavior
720*c8dee2aaSAndroid Build Coastguard Worker                     // in C++, but not in GLSL. Do the shift on unsigned values to avoid triggering
721*c8dee2aaSAndroid Build Coastguard Worker                     // an UBSAN error.
722*c8dee2aaSAndroid Build Coastguard Worker                     return URESULT(<<);
723*c8dee2aaSAndroid Build Coastguard Worker                 }
724*c8dee2aaSAndroid Build Coastguard Worker                 context.fErrors->error(pos, "shift value out of range");
725*c8dee2aaSAndroid Build Coastguard Worker                 return nullptr;
726*c8dee2aaSAndroid Build Coastguard Worker 
727*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::SHR:
728*c8dee2aaSAndroid Build Coastguard Worker                 if (rightVal >= 0 && rightVal <= 31) {
729*c8dee2aaSAndroid Build Coastguard Worker                     return RESULT(>>);
730*c8dee2aaSAndroid Build Coastguard Worker                 }
731*c8dee2aaSAndroid Build Coastguard Worker                 context.fErrors->error(pos, "shift value out of range");
732*c8dee2aaSAndroid Build Coastguard Worker                 return nullptr;
733*c8dee2aaSAndroid Build Coastguard Worker 
734*c8dee2aaSAndroid Build Coastguard Worker             default:
735*c8dee2aaSAndroid Build Coastguard Worker                 break;
736*c8dee2aaSAndroid Build Coastguard Worker         }
737*c8dee2aaSAndroid Build Coastguard Worker         #undef RESULT
738*c8dee2aaSAndroid Build Coastguard Worker         #undef URESULT
739*c8dee2aaSAndroid Build Coastguard Worker 
740*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
741*c8dee2aaSAndroid Build Coastguard Worker     }
742*c8dee2aaSAndroid Build Coastguard Worker 
743*c8dee2aaSAndroid Build Coastguard Worker     // Handle pairs of floating-point literals.
744*c8dee2aaSAndroid Build Coastguard Worker     if (left->isFloatLiteral() && right->isFloatLiteral()) {
745*c8dee2aaSAndroid Build Coastguard Worker         SKSL_FLOAT leftVal  = left->as<Literal>().floatValue();
746*c8dee2aaSAndroid Build Coastguard Worker         SKSL_FLOAT rightVal = right->as<Literal>().floatValue();
747*c8dee2aaSAndroid Build Coastguard Worker 
748*c8dee2aaSAndroid Build Coastguard Worker         #define RESULT(Op) fold_expression(pos, leftVal Op rightVal, &resultType)
749*c8dee2aaSAndroid Build Coastguard Worker         switch (op.kind()) {
750*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::PLUS:  return RESULT(+);
751*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::MINUS: return RESULT(-);
752*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::STAR:  return RESULT(*);
753*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::SLASH: return RESULT(/);
754*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::EQEQ:  return RESULT(==);
755*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::NEQ:   return RESULT(!=);
756*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::GT:    return RESULT(>);
757*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::GTEQ:  return RESULT(>=);
758*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::LT:    return RESULT(<);
759*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::LTEQ:  return RESULT(<=);
760*c8dee2aaSAndroid Build Coastguard Worker             default:                    break;
761*c8dee2aaSAndroid Build Coastguard Worker         }
762*c8dee2aaSAndroid Build Coastguard Worker         #undef RESULT
763*c8dee2aaSAndroid Build Coastguard Worker 
764*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
765*c8dee2aaSAndroid Build Coastguard Worker     }
766*c8dee2aaSAndroid Build Coastguard Worker 
767*c8dee2aaSAndroid Build Coastguard Worker     // Perform matrix multiplication.
768*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == Operator::Kind::STAR) {
769*c8dee2aaSAndroid Build Coastguard Worker         if (leftType.isMatrix() && rightType.isMatrix()) {
770*c8dee2aaSAndroid Build Coastguard Worker             return simplify_matrix_times_matrix(context, pos, *left, *right);
771*c8dee2aaSAndroid Build Coastguard Worker         }
772*c8dee2aaSAndroid Build Coastguard Worker         if (leftType.isVector() && rightType.isMatrix()) {
773*c8dee2aaSAndroid Build Coastguard Worker             return simplify_vector_times_matrix(context, pos, *left, *right);
774*c8dee2aaSAndroid Build Coastguard Worker         }
775*c8dee2aaSAndroid Build Coastguard Worker         if (leftType.isMatrix() && rightType.isVector()) {
776*c8dee2aaSAndroid Build Coastguard Worker             return simplify_matrix_times_vector(context, pos, *left, *right);
777*c8dee2aaSAndroid Build Coastguard Worker         }
778*c8dee2aaSAndroid Build Coastguard Worker     }
779*c8dee2aaSAndroid Build Coastguard Worker 
780*c8dee2aaSAndroid Build Coastguard Worker     // Perform constant folding on pairs of vectors/matrices.
781*c8dee2aaSAndroid Build Coastguard Worker     if (is_vec_or_mat(leftType) && leftType.matches(rightType)) {
782*c8dee2aaSAndroid Build Coastguard Worker         return simplify_componentwise(context, pos, *left, op, *right);
783*c8dee2aaSAndroid Build Coastguard Worker     }
784*c8dee2aaSAndroid Build Coastguard Worker 
785*c8dee2aaSAndroid Build Coastguard Worker     // Perform constant folding on vectors/matrices against scalars, e.g.: half4(2) + 2
786*c8dee2aaSAndroid Build Coastguard Worker     if (rightType.isScalar() && is_vec_or_mat(leftType) &&
787*c8dee2aaSAndroid Build Coastguard Worker         leftType.componentType().matches(rightType)) {
788*c8dee2aaSAndroid Build Coastguard Worker         return simplify_componentwise(context, pos,
789*c8dee2aaSAndroid Build Coastguard Worker                                       *left, op, *splat_scalar(context, *right, left->type()));
790*c8dee2aaSAndroid Build Coastguard Worker     }
791*c8dee2aaSAndroid Build Coastguard Worker 
792*c8dee2aaSAndroid Build Coastguard Worker     // Perform constant folding on scalars against vectors/matrices, e.g.: 2 + half4(2)
793*c8dee2aaSAndroid Build Coastguard Worker     if (leftType.isScalar() && is_vec_or_mat(rightType) &&
794*c8dee2aaSAndroid Build Coastguard Worker         rightType.componentType().matches(leftType)) {
795*c8dee2aaSAndroid Build Coastguard Worker         return simplify_componentwise(context, pos,
796*c8dee2aaSAndroid Build Coastguard Worker                                       *splat_scalar(context, *left, right->type()), op, *right);
797*c8dee2aaSAndroid Build Coastguard Worker     }
798*c8dee2aaSAndroid Build Coastguard Worker 
799*c8dee2aaSAndroid Build Coastguard Worker     // Perform constant folding on pairs of matrices, arrays or structs.
800*c8dee2aaSAndroid Build Coastguard Worker     if ((leftType.isMatrix() && rightType.isMatrix()) ||
801*c8dee2aaSAndroid Build Coastguard Worker         (leftType.isArray() && rightType.isArray()) ||
802*c8dee2aaSAndroid Build Coastguard Worker         (leftType.isStruct() && rightType.isStruct())) {
803*c8dee2aaSAndroid Build Coastguard Worker         return simplify_constant_equality(context, pos, *left, op, *right);
804*c8dee2aaSAndroid Build Coastguard Worker     }
805*c8dee2aaSAndroid Build Coastguard Worker 
806*c8dee2aaSAndroid Build Coastguard Worker     // We aren't able to constant-fold these expressions.
807*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
808*c8dee2aaSAndroid Build Coastguard Worker }
809*c8dee2aaSAndroid Build Coastguard Worker 
Simplify(const Context & context,Position pos,const Expression & leftExpr,Operator op,const Expression & rightExpr,const Type & resultType)810*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> ConstantFolder::Simplify(const Context& context,
811*c8dee2aaSAndroid Build Coastguard Worker                                                      Position pos,
812*c8dee2aaSAndroid Build Coastguard Worker                                                      const Expression& leftExpr,
813*c8dee2aaSAndroid Build Coastguard Worker                                                      Operator op,
814*c8dee2aaSAndroid Build Coastguard Worker                                                      const Expression& rightExpr,
815*c8dee2aaSAndroid Build Coastguard Worker                                                      const Type& resultType) {
816*c8dee2aaSAndroid Build Coastguard Worker     // Replace constant variables with their literal values.
817*c8dee2aaSAndroid Build Coastguard Worker     const Expression* left = GetConstantValueForVariable(leftExpr);
818*c8dee2aaSAndroid Build Coastguard Worker     const Expression* right = GetConstantValueForVariable(rightExpr);
819*c8dee2aaSAndroid Build Coastguard Worker 
820*c8dee2aaSAndroid Build Coastguard Worker     // If this is the assignment operator, and both sides are the same trivial expression, this is
821*c8dee2aaSAndroid Build Coastguard Worker     // self-assignment (i.e., `var = var`) and can be reduced to just a variable reference (`var`).
822*c8dee2aaSAndroid Build Coastguard Worker     // This can happen when other parts of the assignment are optimized away.
823*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == Operator::Kind::EQ && Analysis::IsSameExpressionTree(*left, *right)) {
824*c8dee2aaSAndroid Build Coastguard Worker         return right->clone(pos);
825*c8dee2aaSAndroid Build Coastguard Worker     }
826*c8dee2aaSAndroid Build Coastguard Worker 
827*c8dee2aaSAndroid Build Coastguard Worker     // Simplify the expression when both sides are constant Boolean literals.
828*c8dee2aaSAndroid Build Coastguard Worker     if (left->isBoolLiteral() && right->isBoolLiteral()) {
829*c8dee2aaSAndroid Build Coastguard Worker         bool leftVal  = left->as<Literal>().boolValue();
830*c8dee2aaSAndroid Build Coastguard Worker         bool rightVal = right->as<Literal>().boolValue();
831*c8dee2aaSAndroid Build Coastguard Worker         bool result;
832*c8dee2aaSAndroid Build Coastguard Worker         switch (op.kind()) {
833*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::LOGICALAND: result = leftVal && rightVal; break;
834*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::LOGICALOR:  result = leftVal || rightVal; break;
835*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::LOGICALXOR: result = leftVal ^  rightVal; break;
836*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::EQEQ:       result = leftVal == rightVal; break;
837*c8dee2aaSAndroid Build Coastguard Worker             case Operator::Kind::NEQ:        result = leftVal != rightVal; break;
838*c8dee2aaSAndroid Build Coastguard Worker             default: return nullptr;
839*c8dee2aaSAndroid Build Coastguard Worker         }
840*c8dee2aaSAndroid Build Coastguard Worker         return Literal::MakeBool(context, pos, result);
841*c8dee2aaSAndroid Build Coastguard Worker     }
842*c8dee2aaSAndroid Build Coastguard Worker 
843*c8dee2aaSAndroid Build Coastguard Worker     // If the left side is a Boolean literal, apply short-circuit optimizations.
844*c8dee2aaSAndroid Build Coastguard Worker     if (left->isBoolLiteral()) {
845*c8dee2aaSAndroid Build Coastguard Worker         return short_circuit_boolean(pos, *left, op, *right);
846*c8dee2aaSAndroid Build Coastguard Worker     }
847*c8dee2aaSAndroid Build Coastguard Worker 
848*c8dee2aaSAndroid Build Coastguard Worker     // If the right side is a Boolean literal...
849*c8dee2aaSAndroid Build Coastguard Worker     if (right->isBoolLiteral()) {
850*c8dee2aaSAndroid Build Coastguard Worker         // ... and the left side has no side effects...
851*c8dee2aaSAndroid Build Coastguard Worker         if (!Analysis::HasSideEffects(*left)) {
852*c8dee2aaSAndroid Build Coastguard Worker             // We can reverse the expressions and short-circuit optimizations are still valid.
853*c8dee2aaSAndroid Build Coastguard Worker             return short_circuit_boolean(pos, *right, op, *left);
854*c8dee2aaSAndroid Build Coastguard Worker         }
855*c8dee2aaSAndroid Build Coastguard Worker 
856*c8dee2aaSAndroid Build Coastguard Worker         // We can't use short-circuiting, but we can still optimize away no-op Boolean expressions.
857*c8dee2aaSAndroid Build Coastguard Worker         return eliminate_no_op_boolean(pos, *left, op, *right);
858*c8dee2aaSAndroid Build Coastguard Worker     }
859*c8dee2aaSAndroid Build Coastguard Worker 
860*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == Operator::Kind::EQEQ && Analysis::IsSameExpressionTree(*left, *right)) {
861*c8dee2aaSAndroid Build Coastguard Worker         // With == comparison, if both sides are the same trivial expression, this is self-
862*c8dee2aaSAndroid Build Coastguard Worker         // comparison and is always true. (We are not concerned with NaN.)
863*c8dee2aaSAndroid Build Coastguard Worker         return Literal::MakeBool(context, pos, /*value=*/true);
864*c8dee2aaSAndroid Build Coastguard Worker     }
865*c8dee2aaSAndroid Build Coastguard Worker 
866*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == Operator::Kind::NEQ && Analysis::IsSameExpressionTree(*left, *right)) {
867*c8dee2aaSAndroid Build Coastguard Worker         // With != comparison, if both sides are the same trivial expression, this is self-
868*c8dee2aaSAndroid Build Coastguard Worker         // comparison and is always false. (We are not concerned with NaN.)
869*c8dee2aaSAndroid Build Coastguard Worker         return Literal::MakeBool(context, pos, /*value=*/false);
870*c8dee2aaSAndroid Build Coastguard Worker     }
871*c8dee2aaSAndroid Build Coastguard Worker 
872*c8dee2aaSAndroid Build Coastguard Worker     if (error_on_divide_by_zero(context, pos, op, *right)) {
873*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
874*c8dee2aaSAndroid Build Coastguard Worker     }
875*c8dee2aaSAndroid Build Coastguard Worker 
876*c8dee2aaSAndroid Build Coastguard Worker     // Perform full constant folding when both sides are compile-time constants.
877*c8dee2aaSAndroid Build Coastguard Worker     bool leftSideIsConstant = Analysis::IsCompileTimeConstant(*left);
878*c8dee2aaSAndroid Build Coastguard Worker     bool rightSideIsConstant = Analysis::IsCompileTimeConstant(*right);
879*c8dee2aaSAndroid Build Coastguard Worker     if (leftSideIsConstant && rightSideIsConstant) {
880*c8dee2aaSAndroid Build Coastguard Worker         return fold_two_constants(context, pos, left, op, right, resultType);
881*c8dee2aaSAndroid Build Coastguard Worker     }
882*c8dee2aaSAndroid Build Coastguard Worker 
883*c8dee2aaSAndroid Build Coastguard Worker     if (context.fConfig->fSettings.fOptimize) {
884*c8dee2aaSAndroid Build Coastguard Worker         // If just one side is constant, we might still be able to simplify arithmetic expressions
885*c8dee2aaSAndroid Build Coastguard Worker         // like `x * 1`, `x *= 1`, `x + 0`, `x * 0`, `0 / x`, etc.
886*c8dee2aaSAndroid Build Coastguard Worker         if (leftSideIsConstant || rightSideIsConstant) {
887*c8dee2aaSAndroid Build Coastguard Worker             if (std::unique_ptr<Expression> expr = simplify_arithmetic(context, pos, *left, op,
888*c8dee2aaSAndroid Build Coastguard Worker                                                                        *right, resultType)) {
889*c8dee2aaSAndroid Build Coastguard Worker                 return expr;
890*c8dee2aaSAndroid Build Coastguard Worker             }
891*c8dee2aaSAndroid Build Coastguard Worker         }
892*c8dee2aaSAndroid Build Coastguard Worker 
893*c8dee2aaSAndroid Build Coastguard Worker         // We can simplify some forms of matrix division even when neither side is constant.
894*c8dee2aaSAndroid Build Coastguard Worker         if (std::unique_ptr<Expression> expr = simplify_matrix_division(context, pos, *left, op,
895*c8dee2aaSAndroid Build Coastguard Worker                                                                         *right, resultType)) {
896*c8dee2aaSAndroid Build Coastguard Worker             return expr;
897*c8dee2aaSAndroid Build Coastguard Worker         }
898*c8dee2aaSAndroid Build Coastguard Worker     }
899*c8dee2aaSAndroid Build Coastguard Worker 
900*c8dee2aaSAndroid Build Coastguard Worker     // We aren't able to constant-fold.
901*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
902*c8dee2aaSAndroid Build Coastguard Worker }
903*c8dee2aaSAndroid Build Coastguard Worker 
904*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
905