| /* | |
| * Copyright 2021 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkTypes.h" | |
| #include "src/sksl/SkSLContext.h" | |
| #include "src/sksl/SkSLOperators.h" | |
| #include "src/sksl/SkSLProgramSettings.h" | |
| #include "src/sksl/ir/SkSLType.h" | |
| namespace SkSL { | |
| Operator::Precedence Operator::getBinaryPrecedence() const { | |
| switch (this->kind()) { | |
| case Kind::TK_STAR: // fall through | |
| case Kind::TK_SLASH: // fall through | |
| case Kind::TK_PERCENT: return Precedence::kMultiplicative; | |
| case Kind::TK_PLUS: // fall through | |
| case Kind::TK_MINUS: return Precedence::kAdditive; | |
| case Kind::TK_SHL: // fall through | |
| case Kind::TK_SHR: return Precedence::kShift; | |
| case Kind::TK_LT: // fall through | |
| case Kind::TK_GT: // fall through | |
| case Kind::TK_LTEQ: // fall through | |
| case Kind::TK_GTEQ: return Precedence::kRelational; | |
| case Kind::TK_EQEQ: // fall through | |
| case Kind::TK_NEQ: return Precedence::kEquality; | |
| case Kind::TK_BITWISEAND: return Precedence::kBitwiseAnd; | |
| case Kind::TK_BITWISEXOR: return Precedence::kBitwiseXor; | |
| case Kind::TK_BITWISEOR: return Precedence::kBitwiseOr; | |
| case Kind::TK_LOGICALAND: return Precedence::kLogicalAnd; | |
| case Kind::TK_LOGICALXOR: return Precedence::kLogicalXor; | |
| case Kind::TK_LOGICALOR: return Precedence::kLogicalOr; | |
| case Kind::TK_EQ: // fall through | |
| case Kind::TK_PLUSEQ: // fall through | |
| case Kind::TK_MINUSEQ: // fall through | |
| case Kind::TK_STAREQ: // fall through | |
| case Kind::TK_SLASHEQ: // fall through | |
| case Kind::TK_PERCENTEQ: // fall through | |
| case Kind::TK_SHLEQ: // fall through | |
| case Kind::TK_SHREQ: // fall through | |
| case Kind::TK_BITWISEANDEQ: // fall through | |
| case Kind::TK_BITWISEXOREQ: // fall through | |
| case Kind::TK_BITWISEOREQ: return Precedence::kAssignment; | |
| case Kind::TK_COMMA: return Precedence::kSequence; | |
| default: SK_ABORT("unsupported binary operator"); | |
| } | |
| } | |
| bool Operator::isOperator() const { | |
| switch (this->kind()) { | |
| case Kind::TK_PLUS: | |
| case Kind::TK_MINUS: | |
| case Kind::TK_STAR: | |
| case Kind::TK_SLASH: | |
| case Kind::TK_PERCENT: | |
| case Kind::TK_SHL: | |
| case Kind::TK_SHR: | |
| case Kind::TK_LOGICALNOT: | |
| case Kind::TK_LOGICALAND: | |
| case Kind::TK_LOGICALOR: | |
| case Kind::TK_LOGICALXOR: | |
| case Kind::TK_BITWISENOT: | |
| case Kind::TK_BITWISEAND: | |
| case Kind::TK_BITWISEOR: | |
| case Kind::TK_BITWISEXOR: | |
| case Kind::TK_EQ: | |
| case Kind::TK_EQEQ: | |
| case Kind::TK_NEQ: | |
| case Kind::TK_LT: | |
| case Kind::TK_GT: | |
| case Kind::TK_LTEQ: | |
| case Kind::TK_GTEQ: | |
| case Kind::TK_PLUSEQ: | |
| case Kind::TK_MINUSEQ: | |
| case Kind::TK_STAREQ: | |
| case Kind::TK_SLASHEQ: | |
| case Kind::TK_PERCENTEQ: | |
| case Kind::TK_SHLEQ: | |
| case Kind::TK_SHREQ: | |
| case Kind::TK_BITWISEANDEQ: | |
| case Kind::TK_BITWISEOREQ: | |
| case Kind::TK_BITWISEXOREQ: | |
| case Kind::TK_PLUSPLUS: | |
| case Kind::TK_MINUSMINUS: | |
| case Kind::TK_COMMA: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| const char* Operator::operatorName() const { | |
| switch (this->kind()) { | |
| case Kind::TK_PLUS: return "+"; | |
| case Kind::TK_MINUS: return "-"; | |
| case Kind::TK_STAR: return "*"; | |
| case Kind::TK_SLASH: return "/"; | |
| case Kind::TK_PERCENT: return "%"; | |
| case Kind::TK_SHL: return "<<"; | |
| case Kind::TK_SHR: return ">>"; | |
| case Kind::TK_LOGICALNOT: return "!"; | |
| case Kind::TK_LOGICALAND: return "&&"; | |
| case Kind::TK_LOGICALOR: return "||"; | |
| case Kind::TK_LOGICALXOR: return "^^"; | |
| case Kind::TK_BITWISENOT: return "~"; | |
| case Kind::TK_BITWISEAND: return "&"; | |
| case Kind::TK_BITWISEOR: return "|"; | |
| case Kind::TK_BITWISEXOR: return "^"; | |
| case Kind::TK_EQ: return "="; | |
| case Kind::TK_EQEQ: return "=="; | |
| case Kind::TK_NEQ: return "!="; | |
| case Kind::TK_LT: return "<"; | |
| case Kind::TK_GT: return ">"; | |
| case Kind::TK_LTEQ: return "<="; | |
| case Kind::TK_GTEQ: return ">="; | |
| case Kind::TK_PLUSEQ: return "+="; | |
| case Kind::TK_MINUSEQ: return "-="; | |
| case Kind::TK_STAREQ: return "*="; | |
| case Kind::TK_SLASHEQ: return "/="; | |
| case Kind::TK_PERCENTEQ: return "%="; | |
| case Kind::TK_SHLEQ: return "<<="; | |
| case Kind::TK_SHREQ: return ">>="; | |
| case Kind::TK_BITWISEANDEQ: return "&="; | |
| case Kind::TK_BITWISEOREQ: return "|="; | |
| case Kind::TK_BITWISEXOREQ: return "^="; | |
| case Kind::TK_PLUSPLUS: return "++"; | |
| case Kind::TK_MINUSMINUS: return "--"; | |
| case Kind::TK_COMMA: return ","; | |
| default: | |
| SK_ABORT("unsupported operator: %d\n", (int) fKind); | |
| } | |
| } | |
| bool Operator::isAssignment() const { | |
| switch (this->kind()) { | |
| case Kind::TK_EQ: // fall through | |
| case Kind::TK_PLUSEQ: // fall through | |
| case Kind::TK_MINUSEQ: // fall through | |
| case Kind::TK_STAREQ: // fall through | |
| case Kind::TK_SLASHEQ: // fall through | |
| case Kind::TK_PERCENTEQ: // fall through | |
| case Kind::TK_SHLEQ: // fall through | |
| case Kind::TK_SHREQ: // fall through | |
| case Kind::TK_BITWISEOREQ: // fall through | |
| case Kind::TK_BITWISEXOREQ: // fall through | |
| case Kind::TK_BITWISEANDEQ: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| Operator Operator::removeAssignment() const { | |
| switch (this->kind()) { | |
| case Kind::TK_PLUSEQ: return Operator{Kind::TK_PLUS}; | |
| case Kind::TK_MINUSEQ: return Operator{Kind::TK_MINUS}; | |
| case Kind::TK_STAREQ: return Operator{Kind::TK_STAR}; | |
| case Kind::TK_SLASHEQ: return Operator{Kind::TK_SLASH}; | |
| case Kind::TK_PERCENTEQ: return Operator{Kind::TK_PERCENT}; | |
| case Kind::TK_SHLEQ: return Operator{Kind::TK_SHL}; | |
| case Kind::TK_SHREQ: return Operator{Kind::TK_SHR}; | |
| case Kind::TK_BITWISEOREQ: return Operator{Kind::TK_BITWISEOR}; | |
| case Kind::TK_BITWISEXOREQ: return Operator{Kind::TK_BITWISEXOR}; | |
| case Kind::TK_BITWISEANDEQ: return Operator{Kind::TK_BITWISEAND}; | |
| default: return *this; | |
| } | |
| } | |
| bool Operator::isLogical() const { | |
| switch (this->kind()) { | |
| case Token::Kind::TK_LT: | |
| case Token::Kind::TK_GT: | |
| case Token::Kind::TK_LTEQ: | |
| case Token::Kind::TK_GTEQ: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool Operator::isOnlyValidForIntegralTypes() const { | |
| switch (this->kind()) { | |
| case Token::Kind::TK_SHL: | |
| case Token::Kind::TK_SHR: | |
| case Token::Kind::TK_BITWISEAND: | |
| case Token::Kind::TK_BITWISEOR: | |
| case Token::Kind::TK_BITWISEXOR: | |
| case Token::Kind::TK_PERCENT: | |
| case Token::Kind::TK_SHLEQ: | |
| case Token::Kind::TK_SHREQ: | |
| case Token::Kind::TK_BITWISEANDEQ: | |
| case Token::Kind::TK_BITWISEOREQ: | |
| case Token::Kind::TK_BITWISEXOREQ: | |
| case Token::Kind::TK_PERCENTEQ: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool Operator::isValidForMatrixOrVector() const { | |
| switch (this->kind()) { | |
| case Token::Kind::TK_PLUS: | |
| case Token::Kind::TK_MINUS: | |
| case Token::Kind::TK_STAR: | |
| case Token::Kind::TK_SLASH: | |
| case Token::Kind::TK_PERCENT: | |
| case Token::Kind::TK_SHL: | |
| case Token::Kind::TK_SHR: | |
| case Token::Kind::TK_BITWISEAND: | |
| case Token::Kind::TK_BITWISEOR: | |
| case Token::Kind::TK_BITWISEXOR: | |
| case Token::Kind::TK_PLUSEQ: | |
| case Token::Kind::TK_MINUSEQ: | |
| case Token::Kind::TK_STAREQ: | |
| case Token::Kind::TK_SLASHEQ: | |
| case Token::Kind::TK_PERCENTEQ: | |
| case Token::Kind::TK_SHLEQ: | |
| case Token::Kind::TK_SHREQ: | |
| case Token::Kind::TK_BITWISEANDEQ: | |
| case Token::Kind::TK_BITWISEOREQ: | |
| case Token::Kind::TK_BITWISEXOREQ: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool Operator::isMatrixMultiply(const Type& left, const Type& right) { | |
| if (this->kind() != Token::Kind::TK_STAR && this->kind() != Token::Kind::TK_STAREQ) { | |
| return false; | |
| } | |
| if (left.isMatrix()) { | |
| return right.isMatrix() || right.isVector(); | |
| } | |
| return left.isVector() && right.isMatrix(); | |
| } | |
| /** | |
| * Determines the operand and result types of a binary expression. Returns true if the expression is | |
| * legal, false otherwise. If false, the values of the out parameters are undefined. | |
| */ | |
| bool Operator::determineBinaryType(const Context& context, | |
| const Type& left, | |
| const Type& right, | |
| const Type** outLeftType, | |
| const Type** outRightType, | |
| const Type** outResultType) { | |
| const bool allowNarrowing = context.fConfig->fSettings.fAllowNarrowingConversions; | |
| switch (this->kind()) { | |
| case Token::Kind::TK_EQ: // left = right | |
| *outLeftType = &left; | |
| *outRightType = &left; | |
| *outResultType = &left; | |
| return right.canCoerceTo(left, allowNarrowing); | |
| case Token::Kind::TK_EQEQ: // left == right | |
| case Token::Kind::TK_NEQ: { // left != right | |
| CoercionCost rightToLeft = right.coercionCost(left), | |
| leftToRight = left.coercionCost(right); | |
| if (rightToLeft < leftToRight) { | |
| if (rightToLeft.isPossible(allowNarrowing)) { | |
| *outLeftType = &left; | |
| *outRightType = &left; | |
| *outResultType = context.fTypes.fBool.get(); | |
| return true; | |
| } | |
| } else { | |
| if (leftToRight.isPossible(allowNarrowing)) { | |
| *outLeftType = &right; | |
| *outRightType = &right; | |
| *outResultType = context.fTypes.fBool.get(); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| case Token::Kind::TK_LOGICALOR: // left || right | |
| case Token::Kind::TK_LOGICALAND: // left && right | |
| case Token::Kind::TK_LOGICALXOR: // left ^^ right | |
| *outLeftType = context.fTypes.fBool.get(); | |
| *outRightType = context.fTypes.fBool.get(); | |
| *outResultType = context.fTypes.fBool.get(); | |
| return left.canCoerceTo(*context.fTypes.fBool, allowNarrowing) && | |
| right.canCoerceTo(*context.fTypes.fBool, allowNarrowing); | |
| case Token::Kind::TK_COMMA: // left, right | |
| *outLeftType = &left; | |
| *outRightType = &right; | |
| *outResultType = &right; | |
| return true; | |
| default: | |
| break; | |
| } | |
| // Boolean types only support the operators listed above (, = == != || && ^^). | |
| // If we've gotten this far with a boolean, we have an unsupported operator. | |
| const Type& leftComponentType = left.componentType(); | |
| const Type& rightComponentType = right.componentType(); | |
| if (leftComponentType.isBoolean() || rightComponentType.isBoolean()) { | |
| return false; | |
| } | |
| bool isAssignment = this->isAssignment(); | |
| if (this->isMatrixMultiply(left, right)) { // left * right | |
| // Determine final component type. | |
| if (!this->determineBinaryType(context, left.componentType(), right.componentType(), | |
| outLeftType, outRightType, outResultType)) { | |
| return false; | |
| } | |
| // Convert component type to compound. | |
| *outLeftType = &(*outResultType)->toCompound(context, left.columns(), left.rows()); | |
| *outRightType = &(*outResultType)->toCompound(context, right.columns(), right.rows()); | |
| int leftColumns = left.columns(), leftRows = left.rows(); | |
| int rightColumns = right.columns(), rightRows = right.rows(); | |
| if (right.isVector()) { | |
| // `matrix * vector` treats the vector as a column vector; we need to transpose it. | |
| std::swap(rightColumns, rightRows); | |
| SkASSERT(rightColumns == 1); | |
| } | |
| if (rightColumns > 1) { | |
| *outResultType = &(*outResultType)->toCompound(context, rightColumns, leftRows); | |
| } else { | |
| // The result was a column vector. Transpose it back to a row. | |
| *outResultType = &(*outResultType)->toCompound(context, leftRows, rightColumns); | |
| } | |
| if (isAssignment && ((*outResultType)->columns() != leftColumns || | |
| (*outResultType)->rows() != leftRows)) { | |
| return false; | |
| } | |
| return leftColumns == rightRows; | |
| } | |
| bool leftIsVectorOrMatrix = left.isVector() || left.isMatrix(); | |
| bool validMatrixOrVectorOp = this->isValidForMatrixOrVector(); | |
| if (leftIsVectorOrMatrix && validMatrixOrVectorOp && right.isScalar()) { | |
| // Determine final component type. | |
| if (!this->determineBinaryType(context, left.componentType(), right, | |
| outLeftType, outRightType, outResultType)) { | |
| return false; | |
| } | |
| // Convert component type to compound. | |
| *outLeftType = &(*outLeftType)->toCompound(context, left.columns(), left.rows()); | |
| if (!this->isLogical()) { | |
| *outResultType = &(*outResultType)->toCompound(context, left.columns(), left.rows()); | |
| } | |
| return true; | |
| } | |
| bool rightIsVectorOrMatrix = right.isVector() || right.isMatrix(); | |
| if (!isAssignment && rightIsVectorOrMatrix && validMatrixOrVectorOp && left.isScalar()) { | |
| // Determine final component type. | |
| if (!this->determineBinaryType(context, left, right.componentType(), | |
| outLeftType, outRightType, outResultType)) { | |
| return false; | |
| } | |
| // Convert component type to compound. | |
| *outRightType = &(*outRightType)->toCompound(context, right.columns(), right.rows()); | |
| if (!this->isLogical()) { | |
| *outResultType = &(*outResultType)->toCompound(context, right.columns(), right.rows()); | |
| } | |
| return true; | |
| } | |
| CoercionCost rightToLeftCost = right.coercionCost(left); | |
| CoercionCost leftToRightCost = isAssignment ? CoercionCost::Impossible() | |
| : left.coercionCost(right); | |
| if ((left.isScalar() && right.isScalar()) || (leftIsVectorOrMatrix && validMatrixOrVectorOp)) { | |
| if (this->isOnlyValidForIntegralTypes()) { | |
| if (!leftComponentType.isInteger() || !rightComponentType.isInteger()) { | |
| return false; | |
| } | |
| } | |
| if (rightToLeftCost.isPossible(allowNarrowing) && rightToLeftCost < leftToRightCost) { | |
| // Right-to-Left conversion is possible and cheaper | |
| *outLeftType = &left; | |
| *outRightType = &left; | |
| *outResultType = &left; | |
| } else if (leftToRightCost.isPossible(allowNarrowing)) { | |
| // Left-to-Right conversion is possible (and at least as cheap as Right-to-Left) | |
| *outLeftType = &right; | |
| *outRightType = &right; | |
| *outResultType = &right; | |
| } else { | |
| return false; | |
| } | |
| if (this->isLogical()) { | |
| *outResultType = context.fTypes.fBool.get(); | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| } // namespace SkSL |