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