Flutter Engine
The Flutter Engine
SkSLOperator.cpp
Go to the documentation of this file.
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
9
16
17#include <memory>
18
19namespace SkSL {
20
22 switch (this->kind()) {
23 case Kind::STAR: // fall through
24 case Kind::SLASH: // fall through
25 case Kind::PERCENT: return OperatorPrecedence::kMultiplicative;
26 case Kind::PLUS: // fall through
27 case Kind::MINUS: return OperatorPrecedence::kAdditive;
28 case Kind::SHL: // fall through
29 case Kind::SHR: return OperatorPrecedence::kShift;
30 case Kind::LT: // fall through
31 case Kind::GT: // fall through
32 case Kind::LTEQ: // fall through
33 case Kind::GTEQ: return OperatorPrecedence::kRelational;
34 case Kind::EQEQ: // fall through
35 case Kind::NEQ: return OperatorPrecedence::kEquality;
36 case Kind::BITWISEAND: return OperatorPrecedence::kBitwiseAnd;
37 case Kind::BITWISEXOR: return OperatorPrecedence::kBitwiseXor;
38 case Kind::BITWISEOR: return OperatorPrecedence::kBitwiseOr;
39 case Kind::LOGICALAND: return OperatorPrecedence::kLogicalAnd;
40 case Kind::LOGICALXOR: return OperatorPrecedence::kLogicalXor;
41 case Kind::LOGICALOR: return OperatorPrecedence::kLogicalOr;
42 case Kind::EQ: // fall through
43 case Kind::PLUSEQ: // fall through
44 case Kind::MINUSEQ: // fall through
45 case Kind::STAREQ: // fall through
46 case Kind::SLASHEQ: // fall through
47 case Kind::PERCENTEQ: // fall through
48 case Kind::SHLEQ: // fall through
49 case Kind::SHREQ: // fall through
50 case Kind::BITWISEANDEQ: // fall through
51 case Kind::BITWISEXOREQ: // fall through
52 case Kind::BITWISEOREQ: return OperatorPrecedence::kAssignment;
54 default: SK_ABORT("unsupported binary operator");
55 }
56}
57
58const char* Operator::operatorName() const {
59 switch (this->kind()) {
60 case Kind::PLUS: return " + ";
61 case Kind::MINUS: return " - ";
62 case Kind::STAR: return " * ";
63 case Kind::SLASH: return " / ";
64 case Kind::PERCENT: return " % ";
65 case Kind::SHL: return " << ";
66 case Kind::SHR: return " >> ";
67 case Kind::LOGICALNOT: return "!";
68 case Kind::LOGICALAND: return " && ";
69 case Kind::LOGICALOR: return " || ";
70 case Kind::LOGICALXOR: return " ^^ ";
71 case Kind::BITWISENOT: return "~";
72 case Kind::BITWISEAND: return " & ";
73 case Kind::BITWISEOR: return " | ";
74 case Kind::BITWISEXOR: return " ^ ";
75 case Kind::EQ: return " = ";
76 case Kind::EQEQ: return " == ";
77 case Kind::NEQ: return " != ";
78 case Kind::LT: return " < ";
79 case Kind::GT: return " > ";
80 case Kind::LTEQ: return " <= ";
81 case Kind::GTEQ: return " >= ";
82 case Kind::PLUSEQ: return " += ";
83 case Kind::MINUSEQ: return " -= ";
84 case Kind::STAREQ: return " *= ";
85 case Kind::SLASHEQ: return " /= ";
86 case Kind::PERCENTEQ: return " %= ";
87 case Kind::SHLEQ: return " <<= ";
88 case Kind::SHREQ: return " >>= ";
89 case Kind::BITWISEANDEQ: return " &= ";
90 case Kind::BITWISEOREQ: return " |= ";
91 case Kind::BITWISEXOREQ: return " ^= ";
92 case Kind::PLUSPLUS: return "++";
93 case Kind::MINUSMINUS: return "--";
94 case Kind::COMMA: return ", ";
95 default: SkUNREACHABLE;
96 }
97}
98
99std::string_view Operator::tightOperatorName() const {
100 std::string_view name = this->operatorName();
101 if (skstd::starts_with(name, ' ')) {
102 name.remove_prefix(1);
103 }
104 if (skstd::ends_with(name, ' ')) {
105 name.remove_suffix(1);
106 }
107 return name;
108}
109
111 switch (this->kind()) {
112 case Kind::EQ: // fall through
113 case Kind::PLUSEQ: // fall through
114 case Kind::MINUSEQ: // fall through
115 case Kind::STAREQ: // fall through
116 case Kind::SLASHEQ: // fall through
117 case Kind::PERCENTEQ: // fall through
118 case Kind::SHLEQ: // fall through
119 case Kind::SHREQ: // fall through
120 case Kind::BITWISEOREQ: // fall through
121 case Kind::BITWISEXOREQ: // fall through
122 case Kind::BITWISEANDEQ:
123 return true;
124 default:
125 return false;
126 }
127}
128
130 return this->isAssignment() && this->kind() != Kind::EQ;
131}
132
134 switch (this->kind()) {
135 case Kind::PLUSEQ: return Kind::PLUS;
136 case Kind::MINUSEQ: return Kind::MINUS;
137 case Kind::STAREQ: return Kind::STAR;
138 case Kind::SLASHEQ: return Kind::SLASH;
139 case Kind::PERCENTEQ: return Kind::PERCENT;
140 case Kind::SHLEQ: return Kind::SHL;
141 case Kind::SHREQ: return Kind::SHR;
142 case Kind::BITWISEOREQ: return Kind::BITWISEOR;
143 case Kind::BITWISEXOREQ: return Kind::BITWISEXOR;
144 case Kind::BITWISEANDEQ: return Kind::BITWISEAND;
145 default: return *this;
146 }
147}
148
150 switch (this->kind()) {
151 case Kind::LT:
152 case Kind::GT:
153 case Kind::LTEQ:
154 case Kind::GTEQ:
155 return true;
156 default:
157 return false;
158 }
159}
160
162 switch (this->kind()) {
163 case Kind::SHL:
164 case Kind::SHR:
165 case Kind::BITWISEAND:
166 case Kind::BITWISEOR:
167 case Kind::BITWISEXOR:
168 case Kind::PERCENT:
169 case Kind::SHLEQ:
170 case Kind::SHREQ:
171 case Kind::BITWISEANDEQ:
172 case Kind::BITWISEOREQ:
173 case Kind::BITWISEXOREQ:
174 case Kind::PERCENTEQ:
175 return true;
176 default:
177 return false;
178 }
179}
180
182 switch (this->kind()) {
183 case Kind::PLUS:
184 case Kind::MINUS:
185 case Kind::STAR:
186 case Kind::SLASH:
187 case Kind::PERCENT:
188 case Kind::SHL:
189 case Kind::SHR:
190 case Kind::BITWISEAND:
191 case Kind::BITWISEOR:
192 case Kind::BITWISEXOR:
193 case Kind::PLUSEQ:
194 case Kind::MINUSEQ:
195 case Kind::STAREQ:
196 case Kind::SLASHEQ:
197 case Kind::PERCENTEQ:
198 case Kind::SHLEQ:
199 case Kind::SHREQ:
200 case Kind::BITWISEANDEQ:
201 case Kind::BITWISEOREQ:
202 case Kind::BITWISEXOREQ:
203 return true;
204 default:
205 return false;
206 }
207}
208
209bool Operator::isMatrixMultiply(const Type& left, const Type& right) const {
210 if (this->kind() != Kind::STAR && this->kind() != Kind::STAREQ) {
211 return false;
212 }
213 if (left.isMatrix()) {
214 return right.isMatrix() || right.isVector();
215 }
216 return left.isVector() && right.isMatrix();
217}
218
219/**
220 * Determines the operand and result types of a binary expression. Returns true if the expression is
221 * legal, false otherwise. If false, the values of the out parameters are undefined.
222 */
224 const Type& left,
225 const Type& right,
226 const Type** outLeftType,
227 const Type** outRightType,
228 const Type** outResultType) const {
229 const bool allowNarrowing = context.fConfig->fSettings.fAllowNarrowingConversions;
230 switch (this->kind()) {
231 case Kind::EQ: // left = right
232 if (left.isVoid()) {
233 return false;
234 }
235 *outLeftType = &left;
236 *outRightType = &left;
237 *outResultType = &left;
238 return right.canCoerceTo(left, allowNarrowing);
239
240 case Kind::EQEQ: // left == right
241 case Kind::NEQ: { // left != right
242 if (left.isVoid() || left.isOpaque()) {
243 return false;
244 }
245 CoercionCost rightToLeft = right.coercionCost(left),
246 leftToRight = left.coercionCost(right);
247 if (rightToLeft < leftToRight) {
248 if (rightToLeft.isPossible(allowNarrowing)) {
249 *outLeftType = &left;
250 *outRightType = &left;
251 *outResultType = context.fTypes.fBool.get();
252 return true;
253 }
254 } else {
255 if (leftToRight.isPossible(allowNarrowing)) {
256 *outLeftType = &right;
257 *outRightType = &right;
258 *outResultType = context.fTypes.fBool.get();
259 return true;
260 }
261 }
262 return false;
263 }
264 case Kind::LOGICALOR: // left || right
265 case Kind::LOGICALAND: // left && right
266 case Kind::LOGICALXOR: // left ^^ right
267 *outLeftType = context.fTypes.fBool.get();
268 *outRightType = context.fTypes.fBool.get();
269 *outResultType = context.fTypes.fBool.get();
270 return left.canCoerceTo(*context.fTypes.fBool, allowNarrowing) &&
271 right.canCoerceTo(*context.fTypes.fBool, allowNarrowing);
272
273 case Operator::Kind::COMMA: // left, right
274 if (left.isOpaque() || right.isOpaque()) {
275 return false;
276 }
277 *outLeftType = &left;
278 *outRightType = &right;
279 *outResultType = &right;
280 return true;
281
282 default:
283 break;
284 }
285
286 // Boolean types only support the operators listed above (, = == != || && ^^).
287 // If we've gotten this far with a boolean, we have an unsupported operator.
288 const Type& leftComponentType = left.componentType();
289 const Type& rightComponentType = right.componentType();
290 if (leftComponentType.isBoolean() || rightComponentType.isBoolean()) {
291 return false;
292 }
293
294 bool isAssignment = this->isAssignment();
295 if (this->isMatrixMultiply(left, right)) { // left * right
296 // Determine final component type.
297 if (!this->determineBinaryType(context, left.componentType(), right.componentType(),
298 outLeftType, outRightType, outResultType)) {
299 return false;
300 }
301 // Convert component type to compound.
302 *outLeftType = &(*outResultType)->toCompound(context, left.columns(), left.rows());
303 *outRightType = &(*outResultType)->toCompound(context, right.columns(), right.rows());
304 int leftColumns = left.columns(), leftRows = left.rows();
305 int rightColumns = right.columns(), rightRows = right.rows();
306 if (right.isVector()) {
307 // `matrix * vector` treats the vector as a column vector; we need to transpose it.
308 std::swap(rightColumns, rightRows);
309 SkASSERT(rightColumns == 1);
310 }
311 if (rightColumns > 1) {
312 *outResultType = &(*outResultType)->toCompound(context, rightColumns, leftRows);
313 } else {
314 // The result was a column vector. Transpose it back to a row.
315 *outResultType = &(*outResultType)->toCompound(context, leftRows, rightColumns);
316 }
317 if (isAssignment && ((*outResultType)->columns() != leftColumns ||
318 (*outResultType)->rows() != leftRows)) {
319 return false;
320 }
321 return leftColumns == rightRows;
322 }
323
324 bool leftIsVectorOrMatrix = left.isVector() || left.isMatrix();
325 bool validMatrixOrVectorOp = this->isValidForMatrixOrVector();
326
327 if (leftIsVectorOrMatrix && validMatrixOrVectorOp && right.isScalar()) {
328 // Determine final component type.
329 if (!this->determineBinaryType(context, left.componentType(), right,
330 outLeftType, outRightType, outResultType)) {
331 return false;
332 }
333 // Convert component type to compound.
334 *outLeftType = &(*outLeftType)->toCompound(context, left.columns(), left.rows());
335 if (!this->isRelational()) {
336 *outResultType = &(*outResultType)->toCompound(context, left.columns(), left.rows());
337 }
338 return true;
339 }
340
341 bool rightIsVectorOrMatrix = right.isVector() || right.isMatrix();
342
343 if (!isAssignment && rightIsVectorOrMatrix && validMatrixOrVectorOp && left.isScalar()) {
344 // Determine final component type.
345 if (!this->determineBinaryType(context, left, right.componentType(),
346 outLeftType, outRightType, outResultType)) {
347 return false;
348 }
349 // Convert component type to compound.
350 *outRightType = &(*outRightType)->toCompound(context, right.columns(), right.rows());
351 if (!this->isRelational()) {
352 *outResultType = &(*outResultType)->toCompound(context, right.columns(), right.rows());
353 }
354 return true;
355 }
356
357 CoercionCost rightToLeftCost = right.coercionCost(left);
359 : left.coercionCost(right);
360
361 if ((left.isScalar() && right.isScalar()) || (leftIsVectorOrMatrix && validMatrixOrVectorOp)) {
362 if (this->isOnlyValidForIntegralTypes()) {
363 if (!leftComponentType.isInteger() || !rightComponentType.isInteger()) {
364 return false;
365 }
366 }
367 if (rightToLeftCost.isPossible(allowNarrowing) && rightToLeftCost < leftToRightCost) {
368 // Right-to-Left conversion is possible and cheaper
369 *outLeftType = &left;
370 *outRightType = &left;
371 *outResultType = &left;
372 } else if (leftToRightCost.isPossible(allowNarrowing)) {
373 // Left-to-Right conversion is possible (and at least as cheap as Right-to-Left)
374 *outLeftType = &right;
375 *outRightType = &right;
376 *outResultType = &right;
377 } else {
378 return false;
379 }
380 if (this->isRelational()) {
381 *outResultType = context.fTypes.fBool.get();
382 }
383 return true;
384 }
385 return false;
386}
387
388} // namespace SkSL
#define COMMA
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
const std::unique_ptr< Type > fBool
const BuiltinTypes & fTypes
Definition: SkSLContext.h:30
ProgramConfig * fConfig
Definition: SkSLContext.h:33
Kind kind() const
Definition: SkSLOperator.h:85
std::string_view tightOperatorName() const
OperatorPrecedence getBinaryPrecedence() const
bool isRelational() const
const char * operatorName() const
Operator removeAssignment() const
bool isCompoundAssignment() const
bool isOnlyValidForIntegralTypes() const
bool determineBinaryType(const Context &context, const Type &left, const Type &right, const Type **outLeftType, const Type **outRightType, const Type **outResultType) const
bool isValidForMatrixOrVector() const
bool isAssignment() const
bool isBoolean() const
Definition: SkSLType.h:297
bool isVoid() const
Definition: SkSLType.h:496
const Type & toCompound(const Context &context, int columns, int rows) const
bool isInteger() const
Definition: SkSLType.h:339
OperatorPrecedence
Definition: SkSLOperator.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
constexpr bool starts_with(std::string_view str, std::string_view prefix)
Definition: SkStringView.h:17
constexpr bool ends_with(std::string_view str, std::string_view suffix)
Definition: SkStringView.h:28
static CoercionCost Impossible()
Definition: SkSLType.h:42
bool isPossible(bool allowNarrowing) const
Definition: SkSLType.h:44
ProgramSettings fSettings