| /* |
| * Copyright 2020 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "src/sksl/ir/SkSLPrefixExpression.h" |
| |
| #include "include/core/SkTypes.h" |
| #include "src/sksl/SkSLAnalysis.h" |
| #include "src/sksl/SkSLConstantFolder.h" |
| #include "src/sksl/SkSLContext.h" |
| #include "src/sksl/SkSLDefines.h" |
| #include "src/sksl/SkSLErrorReporter.h" |
| #include "src/sksl/SkSLOperator.h" |
| #include "src/sksl/SkSLProgramSettings.h" |
| #include "src/sksl/ir/SkSLBinaryExpression.h" |
| #include "src/sksl/ir/SkSLConstructorArray.h" |
| #include "src/sksl/ir/SkSLConstructorCompound.h" |
| #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h" |
| #include "src/sksl/ir/SkSLLiteral.h" |
| #include "src/sksl/ir/SkSLType.h" |
| #include "src/sksl/ir/SkSLVariableReference.h" |
| |
| #include <cstddef> |
| #include <optional> |
| |
| namespace SkSL { |
| |
| static ExpressionArray negate_operands(const Context& context, |
| Position pos, |
| const ExpressionArray& operands); |
| |
| static double negate_value(double value) { |
| return -value; |
| } |
| |
| static double bitwise_not_value(double value) { |
| return ~static_cast<SKSL_INT>(value); |
| } |
| |
| static std::unique_ptr<Expression> apply_to_elements(const Context& context, |
| Position pos, |
| const Expression& expr, |
| double (*fn)(double)) { |
| const Type& elementType = expr.type().componentType(); |
| |
| double values[16]; |
| size_t numSlots = expr.type().slotCount(); |
| if (numSlots > std::size(values)) { |
| // The expression has more slots than we expected. |
| return nullptr; |
| } |
| |
| for (size_t index = 0; index < numSlots; ++index) { |
| if (std::optional<double> slotValue = expr.getConstantValue(index)) { |
| values[index] = fn(*slotValue); |
| if (elementType.checkForOutOfRangeLiteral(context, values[index], pos)) { |
| // We can't simplify the expression if the new value is out-of-range for the type. |
| return nullptr; |
| } |
| } else { |
| // There's a non-constant element; we can't simplify this expression. |
| return nullptr; |
| } |
| } |
| return ConstructorCompound::MakeFromConstants(context, pos, expr.type(), values); |
| } |
| |
| static std::unique_ptr<Expression> simplify_negation(const Context& context, |
| Position pos, |
| const Expression& originalExpr) { |
| const Expression* value = ConstantFolder::GetConstantValueForVariable(originalExpr); |
| switch (value->kind()) { |
| case Expression::Kind::kLiteral: |
| case Expression::Kind::kConstructorSplat: |
| case Expression::Kind::kConstructorCompound: { |
| // Convert `-vecN(literal, ...)` into `vecN(-literal, ...)`. |
| if (std::unique_ptr<Expression> expr = apply_to_elements(context, pos, *value, |
| negate_value)) { |
| return expr; |
| } |
| break; |
| } |
| case Expression::Kind::kPrefix: { |
| // Convert `-(-expression)` into `expression`. |
| const PrefixExpression& prefix = value->as<PrefixExpression>(); |
| if (prefix.getOperator().kind() == Operator::Kind::MINUS) { |
| return prefix.operand()->clone(pos); |
| } |
| break; |
| } |
| case Expression::Kind::kConstructorArray: |
| // Convert `-array[N](literal, ...)` into `array[N](-literal, ...)`. |
| if (Analysis::IsCompileTimeConstant(*value)) { |
| const ConstructorArray& ctor = value->as<ConstructorArray>(); |
| return ConstructorArray::Make(context, pos, ctor.type(), |
| negate_operands(context, pos, ctor.arguments())); |
| } |
| break; |
| |
| case Expression::Kind::kConstructorDiagonalMatrix: |
| // Convert `-matrix(literal)` into `matrix(-literal)`. |
| if (Analysis::IsCompileTimeConstant(*value)) { |
| const ConstructorDiagonalMatrix& ctor = value->as<ConstructorDiagonalMatrix>(); |
| if (std::unique_ptr<Expression> simplified = simplify_negation(context, |
| pos, |
| *ctor.argument())) { |
| return ConstructorDiagonalMatrix::Make(context, pos, ctor.type(), |
| std::move(simplified)); |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| return nullptr; |
| } |
| |
| static ExpressionArray negate_operands(const Context& context, |
| Position pos, |
| const ExpressionArray& array) { |
| ExpressionArray replacement; |
| replacement.reserve_exact(array.size()); |
| for (const std::unique_ptr<Expression>& expr : array) { |
| // The logic below is very similar to `negate_operand`, but with different ownership rules. |
| if (std::unique_ptr<Expression> simplified = simplify_negation(context, pos, *expr)) { |
| replacement.push_back(std::move(simplified)); |
| } else { |
| replacement.push_back(std::make_unique<PrefixExpression>(pos, Operator::Kind::MINUS, |
| expr->clone())); |
| } |
| } |
| return replacement; |
| } |
| |
| static std::unique_ptr<Expression> negate_operand(const Context& context, |
| Position pos, |
| std::unique_ptr<Expression> value) { |
| // Attempt to simplify this negation (e.g. eliminate double negation, literal values) |
| if (std::unique_ptr<Expression> simplified = simplify_negation(context, pos, *value)) { |
| return simplified; |
| } |
| |
| // No simplified form; convert expression to Prefix(MINUS, expression). |
| return std::make_unique<PrefixExpression>(pos, Operator::Kind::MINUS, std::move(value)); |
| } |
| |
| static std::unique_ptr<Expression> logical_not_operand(const Context& context, |
| Position pos, |
| std::unique_ptr<Expression> operand) { |
| const Expression* value = ConstantFolder::GetConstantValueForVariable(*operand); |
| switch (value->kind()) { |
| case Expression::Kind::kLiteral: { |
| // Convert !boolLiteral(true) to boolLiteral(false). |
| SkASSERT(value->type().isBoolean()); |
| const Literal& b = value->as<Literal>(); |
| return Literal::MakeBool(pos, !b.boolValue(), &operand->type()); |
| } |
| case Expression::Kind::kPrefix: { |
| // Convert `!(!expression)` into `expression`. |
| PrefixExpression& prefix = operand->as<PrefixExpression>(); |
| if (prefix.getOperator().kind() == Operator::Kind::LOGICALNOT) { |
| prefix.operand()->fPosition = pos; |
| return std::move(prefix.operand()); |
| } |
| break; |
| } |
| case Expression::Kind::kBinary: { |
| BinaryExpression& binary = operand->as<BinaryExpression>(); |
| std::optional<Operator> replacement; |
| switch (binary.getOperator().kind()) { |
| case OperatorKind::EQEQ: replacement = OperatorKind::NEQ; break; |
| case OperatorKind::NEQ: replacement = OperatorKind::EQEQ; break; |
| case OperatorKind::LT: replacement = OperatorKind::GTEQ; break; |
| case OperatorKind::LTEQ: replacement = OperatorKind::GT; break; |
| case OperatorKind::GT: replacement = OperatorKind::LTEQ; break; |
| case OperatorKind::GTEQ: replacement = OperatorKind::LT; break; |
| default: break; |
| } |
| if (replacement.has_value()) { |
| return BinaryExpression::Make(context, pos, std::move(binary.left()), |
| *replacement, std::move(binary.right()), |
| &binary.type()); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| |
| // No simplified form; convert expression to Prefix(LOGICALNOT, expression). |
| return std::make_unique<PrefixExpression>(pos, Operator::Kind::LOGICALNOT, std::move(operand)); |
| } |
| |
| static std::unique_ptr<Expression> bitwise_not_operand(const Context& context, |
| Position pos, |
| std::unique_ptr<Expression> operand) { |
| SkASSERT(operand->type().componentType().isInteger()); |
| |
| const Expression* value = ConstantFolder::GetConstantValueForVariable(*operand); |
| |
| switch (value->kind()) { |
| case Expression::Kind::kLiteral: |
| case Expression::Kind::kConstructorSplat: |
| case Expression::Kind::kConstructorCompound: { |
| // Convert ~vecN(1, 2, ...) to vecN(~1, ~2, ...). |
| if (std::unique_ptr<Expression> expr = apply_to_elements(context, pos, *value, |
| bitwise_not_value)) { |
| return expr; |
| } |
| break; |
| } |
| case Expression::Kind::kPrefix: { |
| // Convert `~(~expression)` into `expression`. |
| PrefixExpression& prefix = operand->as<PrefixExpression>(); |
| if (prefix.getOperator().kind() == Operator::Kind::BITWISENOT) { |
| prefix.operand()->fPosition = pos; |
| return std::move(prefix.operand()); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| |
| // No simplified form; convert expression to Prefix(BITWISENOT, expression). |
| return std::make_unique<PrefixExpression>(pos, Operator::Kind::BITWISENOT, std::move(operand)); |
| } |
| |
| std::unique_ptr<Expression> PrefixExpression::Convert(const Context& context, |
| Position pos, |
| Operator op, |
| std::unique_ptr<Expression> base) { |
| const Type& baseType = base->type(); |
| switch (op.kind()) { |
| case Operator::Kind::PLUS: |
| if (baseType.isArray() || !baseType.componentType().isNumber()) { |
| context.fErrors->error(pos, |
| "'+' cannot operate on '" + baseType.displayName() + "'"); |
| return nullptr; |
| } |
| break; |
| |
| case Operator::Kind::MINUS: |
| if (baseType.isArray() || !baseType.componentType().isNumber()) { |
| context.fErrors->error(pos, |
| "'-' cannot operate on '" + baseType.displayName() + "'"); |
| return nullptr; |
| } |
| break; |
| |
| case Operator::Kind::PLUSPLUS: |
| case Operator::Kind::MINUSMINUS: |
| if (baseType.isArray() || !baseType.componentType().isNumber()) { |
| context.fErrors->error(pos, |
| "'" + std::string(op.tightOperatorName()) + |
| "' cannot operate on '" + baseType.displayName() + "'"); |
| return nullptr; |
| } |
| if (!Analysis::UpdateVariableRefKind(base.get(), VariableReference::RefKind::kReadWrite, |
| context.fErrors)) { |
| return nullptr; |
| } |
| break; |
| |
| case Operator::Kind::LOGICALNOT: |
| if (!baseType.isBoolean()) { |
| context.fErrors->error(pos, |
| "'" + std::string(op.tightOperatorName()) + |
| "' cannot operate on '" + baseType.displayName() + "'"); |
| return nullptr; |
| } |
| break; |
| |
| case Operator::Kind::BITWISENOT: |
| if (context.fConfig->strictES2Mode()) { |
| // GLSL ES 1.00, Section 5.1 |
| context.fErrors->error( |
| pos, |
| "operator '" + std::string(op.tightOperatorName()) + "' is not allowed"); |
| return nullptr; |
| } |
| if (baseType.isArray() || !baseType.componentType().isInteger()) { |
| context.fErrors->error(pos, |
| "'" + std::string(op.tightOperatorName()) + |
| "' cannot operate on '" + baseType.displayName() + "'"); |
| return nullptr; |
| } |
| break; |
| |
| default: |
| SK_ABORT("unsupported prefix operator"); |
| } |
| |
| std::unique_ptr<Expression> result = PrefixExpression::Make(context, pos, op, std::move(base)); |
| SkASSERT(result->fPosition == pos); |
| return result; |
| } |
| |
| std::unique_ptr<Expression> PrefixExpression::Make(const Context& context, |
| Position pos, |
| Operator op, |
| std::unique_ptr<Expression> base) { |
| const Type& baseType = base->type(); |
| switch (op.kind()) { |
| case Operator::Kind::PLUS: |
| SkASSERT(!baseType.isArray()); |
| SkASSERT(baseType.componentType().isNumber()); |
| base->fPosition = pos; |
| return base; |
| |
| case Operator::Kind::MINUS: |
| SkASSERT(!baseType.isArray()); |
| SkASSERT(baseType.componentType().isNumber()); |
| return negate_operand(context, pos, std::move(base)); |
| |
| case Operator::Kind::LOGICALNOT: |
| SkASSERT(baseType.isBoolean()); |
| return logical_not_operand(context, pos, std::move(base)); |
| |
| case Operator::Kind::PLUSPLUS: |
| case Operator::Kind::MINUSMINUS: |
| SkASSERT(!baseType.isArray()); |
| SkASSERT(baseType.componentType().isNumber()); |
| SkASSERT(Analysis::IsAssignable(*base)); |
| break; |
| |
| case Operator::Kind::BITWISENOT: |
| SkASSERT(!context.fConfig->strictES2Mode()); |
| SkASSERT(!baseType.isArray()); |
| SkASSERT(baseType.componentType().isInteger()); |
| if (baseType.isLiteral()) { |
| // The expression `~123` is no longer a literal; coerce to the actual type. |
| base = baseType.scalarTypeForLiteral().coerceExpression(std::move(base), context); |
| SkASSERT(base); |
| } |
| return bitwise_not_operand(context, pos, std::move(base)); |
| |
| default: |
| SkDEBUGFAILF("unsupported prefix operator: %s", op.operatorName()); |
| } |
| |
| return std::make_unique<PrefixExpression>(pos, op, std::move(base)); |
| } |
| |
| std::string PrefixExpression::description(OperatorPrecedence parentPrecedence) const { |
| bool needsParens = (OperatorPrecedence::kPrefix >= parentPrecedence); |
| return std::string(needsParens ? "(" : "") + |
| std::string(this->getOperator().tightOperatorName()) + |
| this->operand()->description(OperatorPrecedence::kPrefix) + |
| std::string(needsParens ? ")" : ""); |
| } |
| |
| } // namespace SkSL |