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