• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkTypes.h"
9 #include "src/sksl/SkSLContext.h"
10 #include "src/sksl/SkSLOperators.h"
11 #include "src/sksl/SkSLProgramSettings.h"
12 #include "src/sksl/ir/SkSLType.h"
13 
14 namespace SkSL {
15 
getBinaryPrecedence() const16 Operator::Precedence Operator::getBinaryPrecedence() const {
17     switch (this->kind()) {
18         case Kind::TK_STAR:         // fall through
19         case Kind::TK_SLASH:        // fall through
20         case Kind::TK_PERCENT:      return Precedence::kMultiplicative;
21         case Kind::TK_PLUS:         // fall through
22         case Kind::TK_MINUS:        return Precedence::kAdditive;
23         case Kind::TK_SHL:          // fall through
24         case Kind::TK_SHR:          return Precedence::kShift;
25         case Kind::TK_LT:           // fall through
26         case Kind::TK_GT:           // fall through
27         case Kind::TK_LTEQ:         // fall through
28         case Kind::TK_GTEQ:         return Precedence::kRelational;
29         case Kind::TK_EQEQ:         // fall through
30         case Kind::TK_NEQ:          return Precedence::kEquality;
31         case Kind::TK_BITWISEAND:   return Precedence::kBitwiseAnd;
32         case Kind::TK_BITWISEXOR:   return Precedence::kBitwiseXor;
33         case Kind::TK_BITWISEOR:    return Precedence::kBitwiseOr;
34         case Kind::TK_LOGICALAND:   return Precedence::kLogicalAnd;
35         case Kind::TK_LOGICALXOR:   return Precedence::kLogicalXor;
36         case Kind::TK_LOGICALOR:    return Precedence::kLogicalOr;
37         case Kind::TK_EQ:           // fall through
38         case Kind::TK_PLUSEQ:       // fall through
39         case Kind::TK_MINUSEQ:      // fall through
40         case Kind::TK_STAREQ:       // fall through
41         case Kind::TK_SLASHEQ:      // fall through
42         case Kind::TK_PERCENTEQ:    // fall through
43         case Kind::TK_SHLEQ:        // fall through
44         case Kind::TK_SHREQ:        // fall through
45         case Kind::TK_BITWISEANDEQ: // fall through
46         case Kind::TK_BITWISEXOREQ: // fall through
47         case Kind::TK_BITWISEOREQ:  return Precedence::kAssignment;
48         case Kind::TK_COMMA:        return Precedence::kSequence;
49         default: SK_ABORT("unsupported binary operator");
50     }
51 }
52 
isOperator() const53 bool Operator::isOperator() const {
54     switch (this->kind()) {
55         case Kind::TK_PLUS:
56         case Kind::TK_MINUS:
57         case Kind::TK_STAR:
58         case Kind::TK_SLASH:
59         case Kind::TK_PERCENT:
60         case Kind::TK_SHL:
61         case Kind::TK_SHR:
62         case Kind::TK_LOGICALNOT:
63         case Kind::TK_LOGICALAND:
64         case Kind::TK_LOGICALOR:
65         case Kind::TK_LOGICALXOR:
66         case Kind::TK_BITWISENOT:
67         case Kind::TK_BITWISEAND:
68         case Kind::TK_BITWISEOR:
69         case Kind::TK_BITWISEXOR:
70         case Kind::TK_EQ:
71         case Kind::TK_EQEQ:
72         case Kind::TK_NEQ:
73         case Kind::TK_LT:
74         case Kind::TK_GT:
75         case Kind::TK_LTEQ:
76         case Kind::TK_GTEQ:
77         case Kind::TK_PLUSEQ:
78         case Kind::TK_MINUSEQ:
79         case Kind::TK_STAREQ:
80         case Kind::TK_SLASHEQ:
81         case Kind::TK_PERCENTEQ:
82         case Kind::TK_SHLEQ:
83         case Kind::TK_SHREQ:
84         case Kind::TK_BITWISEANDEQ:
85         case Kind::TK_BITWISEOREQ:
86         case Kind::TK_BITWISEXOREQ:
87         case Kind::TK_PLUSPLUS:
88         case Kind::TK_MINUSMINUS:
89         case Kind::TK_COMMA:
90             return true;
91         default:
92             return false;
93     }
94 }
95 
operatorName() const96 const char* Operator::operatorName() const {
97     switch (this->kind()) {
98         case Kind::TK_PLUS:         return "+";
99         case Kind::TK_MINUS:        return "-";
100         case Kind::TK_STAR:         return "*";
101         case Kind::TK_SLASH:        return "/";
102         case Kind::TK_PERCENT:      return "%";
103         case Kind::TK_SHL:          return "<<";
104         case Kind::TK_SHR:          return ">>";
105         case Kind::TK_LOGICALNOT:   return "!";
106         case Kind::TK_LOGICALAND:   return "&&";
107         case Kind::TK_LOGICALOR:    return "||";
108         case Kind::TK_LOGICALXOR:   return "^^";
109         case Kind::TK_BITWISENOT:   return "~";
110         case Kind::TK_BITWISEAND:   return "&";
111         case Kind::TK_BITWISEOR:    return "|";
112         case Kind::TK_BITWISEXOR:   return "^";
113         case Kind::TK_EQ:           return "=";
114         case Kind::TK_EQEQ:         return "==";
115         case Kind::TK_NEQ:          return "!=";
116         case Kind::TK_LT:           return "<";
117         case Kind::TK_GT:           return ">";
118         case Kind::TK_LTEQ:         return "<=";
119         case Kind::TK_GTEQ:         return ">=";
120         case Kind::TK_PLUSEQ:       return "+=";
121         case Kind::TK_MINUSEQ:      return "-=";
122         case Kind::TK_STAREQ:       return "*=";
123         case Kind::TK_SLASHEQ:      return "/=";
124         case Kind::TK_PERCENTEQ:    return "%=";
125         case Kind::TK_SHLEQ:        return "<<=";
126         case Kind::TK_SHREQ:        return ">>=";
127         case Kind::TK_BITWISEANDEQ: return "&=";
128         case Kind::TK_BITWISEOREQ:  return "|=";
129         case Kind::TK_BITWISEXOREQ: return "^=";
130         case Kind::TK_PLUSPLUS:     return "++";
131         case Kind::TK_MINUSMINUS:   return "--";
132         case Kind::TK_COMMA:        return ",";
133         default:
134             SK_ABORT("unsupported operator: %d\n", (int) fKind);
135     }
136 }
137 
isAssignment() const138 bool Operator::isAssignment() const {
139     switch (this->kind()) {
140         case Kind::TK_EQ:           // fall through
141         case Kind::TK_PLUSEQ:       // fall through
142         case Kind::TK_MINUSEQ:      // fall through
143         case Kind::TK_STAREQ:       // fall through
144         case Kind::TK_SLASHEQ:      // fall through
145         case Kind::TK_PERCENTEQ:    // fall through
146         case Kind::TK_SHLEQ:        // fall through
147         case Kind::TK_SHREQ:        // fall through
148         case Kind::TK_BITWISEOREQ:  // fall through
149         case Kind::TK_BITWISEXOREQ: // fall through
150         case Kind::TK_BITWISEANDEQ:
151             return true;
152         default:
153             return false;
154     }
155 }
156 
removeAssignment() const157 Operator Operator::removeAssignment() const {
158     switch (this->kind()) {
159         case Kind::TK_PLUSEQ:       return Operator{Kind::TK_PLUS};
160         case Kind::TK_MINUSEQ:      return Operator{Kind::TK_MINUS};
161         case Kind::TK_STAREQ:       return Operator{Kind::TK_STAR};
162         case Kind::TK_SLASHEQ:      return Operator{Kind::TK_SLASH};
163         case Kind::TK_PERCENTEQ:    return Operator{Kind::TK_PERCENT};
164         case Kind::TK_SHLEQ:        return Operator{Kind::TK_SHL};
165         case Kind::TK_SHREQ:        return Operator{Kind::TK_SHR};
166         case Kind::TK_BITWISEOREQ:  return Operator{Kind::TK_BITWISEOR};
167         case Kind::TK_BITWISEXOREQ: return Operator{Kind::TK_BITWISEXOR};
168         case Kind::TK_BITWISEANDEQ: return Operator{Kind::TK_BITWISEAND};
169         default: return *this;
170     }
171 }
172 
isLogical() const173 bool Operator::isLogical() const {
174     switch (this->kind()) {
175         case Token::Kind::TK_LT:
176         case Token::Kind::TK_GT:
177         case Token::Kind::TK_LTEQ:
178         case Token::Kind::TK_GTEQ:
179             return true;
180         default:
181             return false;
182     }
183 }
184 
isOnlyValidForIntegralTypes() const185 bool Operator::isOnlyValidForIntegralTypes() const {
186     switch (this->kind()) {
187         case Token::Kind::TK_SHL:
188         case Token::Kind::TK_SHR:
189         case Token::Kind::TK_BITWISEAND:
190         case Token::Kind::TK_BITWISEOR:
191         case Token::Kind::TK_BITWISEXOR:
192         case Token::Kind::TK_PERCENT:
193         case Token::Kind::TK_SHLEQ:
194         case Token::Kind::TK_SHREQ:
195         case Token::Kind::TK_BITWISEANDEQ:
196         case Token::Kind::TK_BITWISEOREQ:
197         case Token::Kind::TK_BITWISEXOREQ:
198         case Token::Kind::TK_PERCENTEQ:
199             return true;
200         default:
201             return false;
202     }
203 }
204 
isValidForMatrixOrVector() const205 bool Operator::isValidForMatrixOrVector() const {
206     switch (this->kind()) {
207         case Token::Kind::TK_PLUS:
208         case Token::Kind::TK_MINUS:
209         case Token::Kind::TK_STAR:
210         case Token::Kind::TK_SLASH:
211         case Token::Kind::TK_PERCENT:
212         case Token::Kind::TK_SHL:
213         case Token::Kind::TK_SHR:
214         case Token::Kind::TK_BITWISEAND:
215         case Token::Kind::TK_BITWISEOR:
216         case Token::Kind::TK_BITWISEXOR:
217         case Token::Kind::TK_PLUSEQ:
218         case Token::Kind::TK_MINUSEQ:
219         case Token::Kind::TK_STAREQ:
220         case Token::Kind::TK_SLASHEQ:
221         case Token::Kind::TK_PERCENTEQ:
222         case Token::Kind::TK_SHLEQ:
223         case Token::Kind::TK_SHREQ:
224         case Token::Kind::TK_BITWISEANDEQ:
225         case Token::Kind::TK_BITWISEOREQ:
226         case Token::Kind::TK_BITWISEXOREQ:
227             return true;
228         default:
229             return false;
230     }
231 }
232 
isMatrixMultiply(const Type & left,const Type & right)233 bool Operator::isMatrixMultiply(const Type& left, const Type& right) {
234     if (this->kind() != Token::Kind::TK_STAR && this->kind() != Token::Kind::TK_STAREQ) {
235         return false;
236     }
237     if (left.isMatrix()) {
238         return right.isMatrix() || right.isVector();
239     }
240     return left.isVector() && right.isMatrix();
241 }
242 
243 /**
244  * Determines the operand and result types of a binary expression. Returns true if the expression is
245  * legal, false otherwise. If false, the values of the out parameters are undefined.
246  */
determineBinaryType(const Context & context,const Type & left,const Type & right,const Type ** outLeftType,const Type ** outRightType,const Type ** outResultType)247 bool Operator::determineBinaryType(const Context& context,
248                                    const Type& left,
249                                    const Type& right,
250                                    const Type** outLeftType,
251                                    const Type** outRightType,
252                                    const Type** outResultType) {
253     const bool allowNarrowing = context.fConfig->fSettings.fAllowNarrowingConversions;
254     switch (this->kind()) {
255         case Token::Kind::TK_EQ:  // left = right
256             *outLeftType = &left;
257             *outRightType = &left;
258             *outResultType = &left;
259             return right.canCoerceTo(left, allowNarrowing);
260 
261         case Token::Kind::TK_EQEQ:   // left == right
262         case Token::Kind::TK_NEQ: {  // left != right
263             CoercionCost rightToLeft = right.coercionCost(left),
264                          leftToRight = left.coercionCost(right);
265             if (rightToLeft < leftToRight) {
266                 if (rightToLeft.isPossible(allowNarrowing)) {
267                     *outLeftType = &left;
268                     *outRightType = &left;
269                     *outResultType = context.fTypes.fBool.get();
270                     return true;
271                 }
272             } else {
273                 if (leftToRight.isPossible(allowNarrowing)) {
274                     *outLeftType = &right;
275                     *outRightType = &right;
276                     *outResultType = context.fTypes.fBool.get();
277                     return true;
278                 }
279             }
280             return false;
281         }
282         case Token::Kind::TK_LOGICALOR:   // left || right
283         case Token::Kind::TK_LOGICALAND:  // left && right
284         case Token::Kind::TK_LOGICALXOR:  // left ^^ right
285             *outLeftType = context.fTypes.fBool.get();
286             *outRightType = context.fTypes.fBool.get();
287             *outResultType = context.fTypes.fBool.get();
288             return left.canCoerceTo(*context.fTypes.fBool, allowNarrowing) &&
289                    right.canCoerceTo(*context.fTypes.fBool, allowNarrowing);
290 
291         case Token::Kind::TK_COMMA:  // left, right
292             *outLeftType = &left;
293             *outRightType = &right;
294             *outResultType = &right;
295             return true;
296 
297         default:
298             break;
299     }
300 
301     // Boolean types only support the operators listed above (, = == != || && ^^).
302     // If we've gotten this far with a boolean, we have an unsupported operator.
303     const Type& leftComponentType = left.componentType();
304     const Type& rightComponentType = right.componentType();
305     if (leftComponentType.isBoolean() || rightComponentType.isBoolean()) {
306         return false;
307     }
308 
309     bool isAssignment = this->isAssignment();
310     if (this->isMatrixMultiply(left, right)) {  // left * right
311         // Determine final component type.
312         if (!this->determineBinaryType(context, left.componentType(), right.componentType(),
313                                        outLeftType, outRightType, outResultType)) {
314             return false;
315         }
316         // Convert component type to compound.
317         *outLeftType = &(*outResultType)->toCompound(context, left.columns(), left.rows());
318         *outRightType = &(*outResultType)->toCompound(context, right.columns(), right.rows());
319         int leftColumns = left.columns(), leftRows = left.rows();
320         int rightColumns = right.columns(), rightRows = right.rows();
321         if (right.isVector()) {
322             // `matrix * vector` treats the vector as a column vector; we need to transpose it.
323             std::swap(rightColumns, rightRows);
324             SkASSERT(rightColumns == 1);
325         }
326         if (rightColumns > 1) {
327             *outResultType = &(*outResultType)->toCompound(context, rightColumns, leftRows);
328         } else {
329             // The result was a column vector. Transpose it back to a row.
330             *outResultType = &(*outResultType)->toCompound(context, leftRows, rightColumns);
331         }
332         if (isAssignment && ((*outResultType)->columns() != leftColumns ||
333                              (*outResultType)->rows() != leftRows)) {
334             return false;
335         }
336         return leftColumns == rightRows;
337     }
338 
339     bool leftIsVectorOrMatrix = left.isVector() || left.isMatrix();
340     bool validMatrixOrVectorOp = this->isValidForMatrixOrVector();
341 
342     if (leftIsVectorOrMatrix && validMatrixOrVectorOp && right.isScalar()) {
343         // Determine final component type.
344         if (!this->determineBinaryType(context, left.componentType(), right,
345                                        outLeftType, outRightType, outResultType)) {
346             return false;
347         }
348         // Convert component type to compound.
349         *outLeftType = &(*outLeftType)->toCompound(context, left.columns(), left.rows());
350         if (!this->isLogical()) {
351             *outResultType = &(*outResultType)->toCompound(context, left.columns(), left.rows());
352         }
353         return true;
354     }
355 
356     bool rightIsVectorOrMatrix = right.isVector() || right.isMatrix();
357 
358     if (!isAssignment && rightIsVectorOrMatrix && validMatrixOrVectorOp && left.isScalar()) {
359         // Determine final component type.
360         if (!this->determineBinaryType(context, left, right.componentType(),
361                                        outLeftType, outRightType, outResultType)) {
362             return false;
363         }
364         // Convert component type to compound.
365         *outRightType = &(*outRightType)->toCompound(context, right.columns(), right.rows());
366         if (!this->isLogical()) {
367             *outResultType = &(*outResultType)->toCompound(context, right.columns(), right.rows());
368         }
369         return true;
370     }
371 
372     CoercionCost rightToLeftCost = right.coercionCost(left);
373     CoercionCost leftToRightCost = isAssignment ? CoercionCost::Impossible()
374                                                 : left.coercionCost(right);
375 
376     if ((left.isScalar() && right.isScalar()) || (leftIsVectorOrMatrix && validMatrixOrVectorOp)) {
377         if (this->isOnlyValidForIntegralTypes()) {
378             if (!leftComponentType.isInteger() || !rightComponentType.isInteger()) {
379                 return false;
380             }
381         }
382         if (rightToLeftCost.isPossible(allowNarrowing) && rightToLeftCost < leftToRightCost) {
383             // Right-to-Left conversion is possible and cheaper
384             *outLeftType = &left;
385             *outRightType = &left;
386             *outResultType = &left;
387         } else if (leftToRightCost.isPossible(allowNarrowing)) {
388             // Left-to-Right conversion is possible (and at least as cheap as Right-to-Left)
389             *outLeftType = &right;
390             *outRightType = &right;
391             *outResultType = &right;
392         } else {
393             return false;
394         }
395         if (this->isLogical()) {
396             *outResultType = context.fTypes.fBool.get();
397         }
398         return true;
399     }
400     return false;
401 }
402 
403 }  // namespace SkSL
404