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