blob: f1344d0486a849e5f27f8215790819b45ade7617 [file] [log] [blame]
Brian Osman00185012021-02-04 16:07:11 -05001/*
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"
John Stiles074a0162022-02-01 15:31:57 -05009#include "include/private/SkStringView.h"
John Stiles0b750f22021-02-26 20:13:07 -050010#include "src/sksl/SkSLContext.h"
Brian Osman00185012021-02-04 16:07:11 -050011#include "src/sksl/SkSLOperators.h"
John Stiles0b750f22021-02-26 20:13:07 -050012#include "src/sksl/SkSLProgramSettings.h"
13#include "src/sksl/ir/SkSLType.h"
Brian Osman00185012021-02-04 16:07:11 -050014
15namespace SkSL {
Brian Osman00185012021-02-04 16:07:11 -050016
John Stiles45990502021-02-16 10:55:27 -050017Operator::Precedence Operator::getBinaryPrecedence() const {
John Stiles0b750f22021-02-26 20:13:07 -050018 switch (this->kind()) {
John Stiles45990502021-02-16 10:55:27 -050019 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;
Brian Osman00185012021-02-04 16:07:11 -050050 default: SK_ABORT("unsupported binary operator");
51 }
52}
53
John Stiles45990502021-02-16 10:55:27 -050054bool 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:
Brian Osman00185012021-02-04 16:07:11 -050091 return true;
92 default:
93 return false;
94 }
95}
96
John Stiles45990502021-02-16 10:55:27 -050097const char* Operator::operatorName() const {
John Stiles0b750f22021-02-26 20:13:07 -050098 switch (this->kind()) {
John Stilesd6806902022-01-24 11:06:00 -050099 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 " >> ";
John Stiles45990502021-02-16 10:55:27 -0500106 case Kind::TK_LOGICALNOT: return "!";
John Stilesd6806902022-01-24 11:06:00 -0500107 case Kind::TK_LOGICALAND: return " && ";
108 case Kind::TK_LOGICALOR: return " || ";
109 case Kind::TK_LOGICALXOR: return " ^^ ";
John Stiles45990502021-02-16 10:55:27 -0500110 case Kind::TK_BITWISENOT: return "~";
John Stilesd6806902022-01-24 11:06:00 -0500111 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 " ^= ";
John Stiles45990502021-02-16 10:55:27 -0500131 case Kind::TK_PLUSPLUS: return "++";
132 case Kind::TK_MINUSMINUS: return "--";
John Stilesd6806902022-01-24 11:06:00 -0500133 case Kind::TK_COMMA: return ", ";
John Stiles45990502021-02-16 10:55:27 -0500134 default:
135 SK_ABORT("unsupported operator: %d\n", (int) fKind);
Brian Osman00185012021-02-04 16:07:11 -0500136 }
137}
138
John Stiles074a0162022-02-01 15:31:57 -0500139std::string_view Operator::tightOperatorName() const {
140 std::string_view name = this->operatorName();
John Stiles2c764e12022-01-31 21:45:35 -0500141 if (skstd::starts_with(name, ' ')) {
John Stilesd6806902022-01-24 11:06:00 -0500142 name.remove_prefix(1);
143 }
John Stiles2c764e12022-01-31 21:45:35 -0500144 if (skstd::ends_with(name, ' ')) {
John Stilesd6806902022-01-24 11:06:00 -0500145 name.remove_suffix(1);
146 }
147 return name;
148}
149
John Stiles45990502021-02-16 10:55:27 -0500150bool Operator::isAssignment() const {
John Stiles0b750f22021-02-26 20:13:07 -0500151 switch (this->kind()) {
John Stiles45990502021-02-16 10:55:27 -0500152 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
169Operator Operator::removeAssignment() const {
John Stiles0b750f22021-02-26 20:13:07 -0500170 switch (this->kind()) {
John Stiles45990502021-02-16 10:55:27 -0500171 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
185bool Operator::isLogical() const {
John Stiles0b750f22021-02-26 20:13:07 -0500186 switch (this->kind()) {
John Stiles45990502021-02-16 10:55:27 -0500187 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
197bool Operator::isOnlyValidForIntegralTypes() const {
John Stiles0b750f22021-02-26 20:13:07 -0500198 switch (this->kind()) {
John Stiles45990502021-02-16 10:55:27 -0500199 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
217bool Operator::isValidForMatrixOrVector() const {
John Stiles0b750f22021-02-26 20:13:07 -0500218 switch (this->kind()) {
John Stiles45990502021-02-16 10:55:27 -0500219 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
John Stilesa9419012022-02-07 14:13:02 -0500245bool Operator::isMatrixMultiply(const Type& left, const Type& right) const {
John Stiles0b750f22021-02-26 20:13:07 -0500246 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 */
259bool Operator::determineBinaryType(const Context& context,
260 const Type& left,
261 const Type& right,
262 const Type** outLeftType,
263 const Type** outRightType,
John Stilesa9419012022-02-07 14:13:02 -0500264 const Type** outResultType) const {
John Stiles0b750f22021-02-26 20:13:07 -0500265 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
John Stiles85f42262021-11-17 10:01:57 -0500304 if (left.isOpaque() || right.isOpaque()) {
305 return false;
306 }
John Stiles0b750f22021-02-26 20:13:07 -0500307 *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
Brian Osman00185012021-02-04 16:07:11 -0500418} // namespace SkSL