xref: /aosp_15_r20/external/skia/src/sksl/ir/SkSLBinaryExpression.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2021 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/ir/SkSLBinaryExpression.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLConstantFolder.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFieldAccess.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIndexExpression.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwizzle.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLTernaryExpression.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLType.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariableReference.h"
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
23*c8dee2aaSAndroid Build Coastguard Worker 
Convert(const Context & context,Position pos,std::unique_ptr<Expression> left,Operator op,std::unique_ptr<Expression> right)24*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> BinaryExpression::Convert(const Context& context,
25*c8dee2aaSAndroid Build Coastguard Worker                                                       Position pos,
26*c8dee2aaSAndroid Build Coastguard Worker                                                       std::unique_ptr<Expression> left,
27*c8dee2aaSAndroid Build Coastguard Worker                                                       Operator op,
28*c8dee2aaSAndroid Build Coastguard Worker                                                       std::unique_ptr<Expression> right) {
29*c8dee2aaSAndroid Build Coastguard Worker     if (!left || !right) {
30*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
31*c8dee2aaSAndroid Build Coastguard Worker     }
32*c8dee2aaSAndroid Build Coastguard Worker     const Type* rawLeftType = (left->isIntLiteral() && right->type().isInteger())
33*c8dee2aaSAndroid Build Coastguard Worker             ? &right->type()
34*c8dee2aaSAndroid Build Coastguard Worker             : &left->type();
35*c8dee2aaSAndroid Build Coastguard Worker     const Type* rawRightType = (right->isIntLiteral() && left->type().isInteger())
36*c8dee2aaSAndroid Build Coastguard Worker             ? &left->type()
37*c8dee2aaSAndroid Build Coastguard Worker             : &right->type();
38*c8dee2aaSAndroid Build Coastguard Worker 
39*c8dee2aaSAndroid Build Coastguard Worker     bool isAssignment = op.isAssignment();
40*c8dee2aaSAndroid Build Coastguard Worker     if (isAssignment &&
41*c8dee2aaSAndroid Build Coastguard Worker         !Analysis::UpdateVariableRefKind(left.get(),
42*c8dee2aaSAndroid Build Coastguard Worker                                          op.kind() != Operator::Kind::EQ
43*c8dee2aaSAndroid Build Coastguard Worker                                                  ? VariableReference::RefKind::kReadWrite
44*c8dee2aaSAndroid Build Coastguard Worker                                                  : VariableReference::RefKind::kWrite,
45*c8dee2aaSAndroid Build Coastguard Worker                                          context.fErrors)) {
46*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
47*c8dee2aaSAndroid Build Coastguard Worker     }
48*c8dee2aaSAndroid Build Coastguard Worker 
49*c8dee2aaSAndroid Build Coastguard Worker     const Type* leftType;
50*c8dee2aaSAndroid Build Coastguard Worker     const Type* rightType;
51*c8dee2aaSAndroid Build Coastguard Worker     const Type* resultType;
52*c8dee2aaSAndroid Build Coastguard Worker     if (!op.determineBinaryType(context, *rawLeftType, *rawRightType,
53*c8dee2aaSAndroid Build Coastguard Worker                                 &leftType, &rightType, &resultType)) {
54*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "type mismatch: '" + std::string(op.tightOperatorName()) +
55*c8dee2aaSAndroid Build Coastguard Worker                                     "' cannot operate on '" + left->type().displayName() + "', '" +
56*c8dee2aaSAndroid Build Coastguard Worker                                     right->type().displayName() + "'");
57*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
58*c8dee2aaSAndroid Build Coastguard Worker     }
59*c8dee2aaSAndroid Build Coastguard Worker 
60*c8dee2aaSAndroid Build Coastguard Worker     if (isAssignment && (leftType->componentType().isOpaque() || leftType->isOrContainsAtomic())) {
61*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "assignments to opaque type '" + left->type().displayName() +
62*c8dee2aaSAndroid Build Coastguard Worker                                     "' are not permitted");
63*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
64*c8dee2aaSAndroid Build Coastguard Worker     }
65*c8dee2aaSAndroid Build Coastguard Worker     if (context.fConfig->strictES2Mode() && !op.isAllowedInStrictES2Mode()) {
66*c8dee2aaSAndroid Build Coastguard Worker         context.fErrors->error(pos, "operator '" + std::string(op.tightOperatorName()) +
67*c8dee2aaSAndroid Build Coastguard Worker                                     "' is not allowed");
68*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
69*c8dee2aaSAndroid Build Coastguard Worker     }
70*c8dee2aaSAndroid Build Coastguard Worker     if (context.fConfig->strictES2Mode() || op.kind() == OperatorKind::COMMA) {
71*c8dee2aaSAndroid Build Coastguard Worker         // Most operators are already rejected on arrays, but GLSL ES 1.0 is very explicit that the
72*c8dee2aaSAndroid Build Coastguard Worker         // *only* operator allowed on arrays is subscripting (and the rules against assignment,
73*c8dee2aaSAndroid Build Coastguard Worker         // comparison, and even sequence apply to structs containing arrays as well).
74*c8dee2aaSAndroid Build Coastguard Worker         // WebGL2 also restricts the usage of the sequence operator with arrays (section 5.26,
75*c8dee2aaSAndroid Build Coastguard Worker         // "Disallowed variants of GLSL ES 3.00 operators"). Since there is very little practical
76*c8dee2aaSAndroid Build Coastguard Worker         // application for sequenced array expressions, we disallow it in SkSL.
77*c8dee2aaSAndroid Build Coastguard Worker         const Expression* arrayExpr =  leftType->isOrContainsArray() ? left.get() :
78*c8dee2aaSAndroid Build Coastguard Worker                                       rightType->isOrContainsArray() ? right.get() :
79*c8dee2aaSAndroid Build Coastguard Worker                                                                        nullptr;
80*c8dee2aaSAndroid Build Coastguard Worker         if (arrayExpr) {
81*c8dee2aaSAndroid Build Coastguard Worker             context.fErrors->error(arrayExpr->position(),
82*c8dee2aaSAndroid Build Coastguard Worker                                    "operator '" + std::string(op.tightOperatorName()) +
83*c8dee2aaSAndroid Build Coastguard Worker                                    "' can not operate on arrays (or structs containing arrays)");
84*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
85*c8dee2aaSAndroid Build Coastguard Worker         }
86*c8dee2aaSAndroid Build Coastguard Worker     }
87*c8dee2aaSAndroid Build Coastguard Worker 
88*c8dee2aaSAndroid Build Coastguard Worker     left = leftType->coerceExpression(std::move(left), context);
89*c8dee2aaSAndroid Build Coastguard Worker     right = rightType->coerceExpression(std::move(right), context);
90*c8dee2aaSAndroid Build Coastguard Worker     if (!left || !right) {
91*c8dee2aaSAndroid Build Coastguard Worker         return nullptr;
92*c8dee2aaSAndroid Build Coastguard Worker     }
93*c8dee2aaSAndroid Build Coastguard Worker 
94*c8dee2aaSAndroid Build Coastguard Worker     return BinaryExpression::Make(context, pos, std::move(left), op, std::move(right), resultType);
95*c8dee2aaSAndroid Build Coastguard Worker }
96*c8dee2aaSAndroid Build Coastguard Worker 
Make(const Context & context,Position pos,std::unique_ptr<Expression> left,Operator op,std::unique_ptr<Expression> right)97*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> BinaryExpression::Make(const Context& context,
98*c8dee2aaSAndroid Build Coastguard Worker                                                    Position pos,
99*c8dee2aaSAndroid Build Coastguard Worker                                                    std::unique_ptr<Expression> left,
100*c8dee2aaSAndroid Build Coastguard Worker                                                    Operator op,
101*c8dee2aaSAndroid Build Coastguard Worker                                                    std::unique_ptr<Expression> right) {
102*c8dee2aaSAndroid Build Coastguard Worker     // Determine the result type of the binary expression.
103*c8dee2aaSAndroid Build Coastguard Worker     const Type* leftType;
104*c8dee2aaSAndroid Build Coastguard Worker     const Type* rightType;
105*c8dee2aaSAndroid Build Coastguard Worker     const Type* resultType;
106*c8dee2aaSAndroid Build Coastguard Worker     SkAssertResult(op.determineBinaryType(context, left->type(), right->type(),
107*c8dee2aaSAndroid Build Coastguard Worker                                           &leftType, &rightType, &resultType));
108*c8dee2aaSAndroid Build Coastguard Worker 
109*c8dee2aaSAndroid Build Coastguard Worker     return BinaryExpression::Make(context, pos, std::move(left), op, std::move(right), resultType);
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker 
Make(const Context & context,Position pos,std::unique_ptr<Expression> left,Operator op,std::unique_ptr<Expression> right,const Type * resultType)112*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> BinaryExpression::Make(const Context& context,
113*c8dee2aaSAndroid Build Coastguard Worker                                                    Position pos,
114*c8dee2aaSAndroid Build Coastguard Worker                                                    std::unique_ptr<Expression> left,
115*c8dee2aaSAndroid Build Coastguard Worker                                                    Operator op,
116*c8dee2aaSAndroid Build Coastguard Worker                                                    std::unique_ptr<Expression> right,
117*c8dee2aaSAndroid Build Coastguard Worker                                                    const Type* resultType) {
118*c8dee2aaSAndroid Build Coastguard Worker     // We should have detected non-ES2 compliant behavior in Convert.
119*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!context.fConfig->strictES2Mode() || op.isAllowedInStrictES2Mode());
120*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!context.fConfig->strictES2Mode() || !left->type().isOrContainsArray());
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker     // We should have detected non-assignable assignment expressions in Convert.
123*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!op.isAssignment() || Analysis::IsAssignable(*left));
124*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!op.isAssignment() || !left->type().componentType().isOpaque());
125*c8dee2aaSAndroid Build Coastguard Worker 
126*c8dee2aaSAndroid Build Coastguard Worker     // For simple assignments, detect and report out-of-range literal values.
127*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == Operator::Kind::EQ) {
128*c8dee2aaSAndroid Build Coastguard Worker         left->type().checkForOutOfRangeLiteral(context, *right);
129*c8dee2aaSAndroid Build Coastguard Worker     }
130*c8dee2aaSAndroid Build Coastguard Worker 
131*c8dee2aaSAndroid Build Coastguard Worker     // Perform constant-folding on the expression.
132*c8dee2aaSAndroid Build Coastguard Worker     if (std::unique_ptr<Expression> result = ConstantFolder::Simplify(context, pos, *left,
133*c8dee2aaSAndroid Build Coastguard Worker                                                                       op, *right, *resultType)) {
134*c8dee2aaSAndroid Build Coastguard Worker         return result;
135*c8dee2aaSAndroid Build Coastguard Worker     }
136*c8dee2aaSAndroid Build Coastguard Worker 
137*c8dee2aaSAndroid Build Coastguard Worker     return std::make_unique<BinaryExpression>(pos, std::move(left), op,
138*c8dee2aaSAndroid Build Coastguard Worker                                               std::move(right), resultType);
139*c8dee2aaSAndroid Build Coastguard Worker }
140*c8dee2aaSAndroid Build Coastguard Worker 
CheckRef(const Expression & expr)141*c8dee2aaSAndroid Build Coastguard Worker bool BinaryExpression::CheckRef(const Expression& expr) {
142*c8dee2aaSAndroid Build Coastguard Worker     switch (expr.kind()) {
143*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFieldAccess:
144*c8dee2aaSAndroid Build Coastguard Worker             return CheckRef(*expr.as<FieldAccess>().base());
145*c8dee2aaSAndroid Build Coastguard Worker 
146*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kIndex:
147*c8dee2aaSAndroid Build Coastguard Worker             return CheckRef(*expr.as<IndexExpression>().base());
148*c8dee2aaSAndroid Build Coastguard Worker 
149*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kSwizzle:
150*c8dee2aaSAndroid Build Coastguard Worker             return CheckRef(*expr.as<Swizzle>().base());
151*c8dee2aaSAndroid Build Coastguard Worker 
152*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kTernary: {
153*c8dee2aaSAndroid Build Coastguard Worker             const TernaryExpression& t = expr.as<TernaryExpression>();
154*c8dee2aaSAndroid Build Coastguard Worker             return CheckRef(*t.ifTrue()) && CheckRef(*t.ifFalse());
155*c8dee2aaSAndroid Build Coastguard Worker         }
156*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kVariableReference: {
157*c8dee2aaSAndroid Build Coastguard Worker             const VariableReference& ref = expr.as<VariableReference>();
158*c8dee2aaSAndroid Build Coastguard Worker             return ref.refKind() == VariableRefKind::kWrite ||
159*c8dee2aaSAndroid Build Coastguard Worker                    ref.refKind() == VariableRefKind::kReadWrite;
160*c8dee2aaSAndroid Build Coastguard Worker         }
161*c8dee2aaSAndroid Build Coastguard Worker         default:
162*c8dee2aaSAndroid Build Coastguard Worker             return false;
163*c8dee2aaSAndroid Build Coastguard Worker     }
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker 
clone(Position pos) const166*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> BinaryExpression::clone(Position pos) const {
167*c8dee2aaSAndroid Build Coastguard Worker     return std::make_unique<BinaryExpression>(pos,
168*c8dee2aaSAndroid Build Coastguard Worker                                               this->left()->clone(),
169*c8dee2aaSAndroid Build Coastguard Worker                                               this->getOperator(),
170*c8dee2aaSAndroid Build Coastguard Worker                                               this->right()->clone(),
171*c8dee2aaSAndroid Build Coastguard Worker                                               &this->type());
172*c8dee2aaSAndroid Build Coastguard Worker }
173*c8dee2aaSAndroid Build Coastguard Worker 
description(OperatorPrecedence parentPrecedence) const174*c8dee2aaSAndroid Build Coastguard Worker std::string BinaryExpression::description(OperatorPrecedence parentPrecedence) const {
175*c8dee2aaSAndroid Build Coastguard Worker     OperatorPrecedence operatorPrecedence = this->getOperator().getBinaryPrecedence();
176*c8dee2aaSAndroid Build Coastguard Worker     bool needsParens = (operatorPrecedence >= parentPrecedence);
177*c8dee2aaSAndroid Build Coastguard Worker     return std::string(needsParens ? "(" : "") +
178*c8dee2aaSAndroid Build Coastguard Worker            this->left()->description(operatorPrecedence) +
179*c8dee2aaSAndroid Build Coastguard Worker            this->getOperator().operatorName() +
180*c8dee2aaSAndroid Build Coastguard Worker            this->right()->description(operatorPrecedence) +
181*c8dee2aaSAndroid Build Coastguard Worker            std::string(needsParens ? ")" : "");
182*c8dee2aaSAndroid Build Coastguard Worker }
183*c8dee2aaSAndroid Build Coastguard Worker 
isAssignmentIntoVariable()184*c8dee2aaSAndroid Build Coastguard Worker VariableReference* BinaryExpression::isAssignmentIntoVariable() {
185*c8dee2aaSAndroid Build Coastguard Worker     if (this->getOperator().isAssignment()) {
186*c8dee2aaSAndroid Build Coastguard Worker         Analysis::AssignmentInfo assignmentInfo;
187*c8dee2aaSAndroid Build Coastguard Worker         if (Analysis::IsAssignable(*this->left(), &assignmentInfo, /*errors=*/nullptr)) {
188*c8dee2aaSAndroid Build Coastguard Worker             return assignmentInfo.fAssignedVar;
189*c8dee2aaSAndroid Build Coastguard Worker         }
190*c8dee2aaSAndroid Build Coastguard Worker     }
191*c8dee2aaSAndroid Build Coastguard Worker     return nullptr;
192*c8dee2aaSAndroid Build Coastguard Worker }
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
195