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