blob: 8e997795f4c2983bb09cdd1126d995820fa25378 [file] [log] [blame]
/*
* 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 "src/sksl/ir/SkSLBoolLiteral.h"
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLFloatLiteral.h"
#include "src/sksl/ir/SkSLIntLiteral.h"
namespace SkSL {
static std::unique_ptr<Expression> negate_operand(const Context& context,
std::unique_ptr<Expression> operand) {
switch (operand->kind()) {
case Expression::Kind::kFloatLiteral:
// Convert -floatLiteral(1) to floatLiteral(-1).
return std::make_unique<FloatLiteral>(operand->fOffset,
-operand->as<FloatLiteral>().value(),
&operand->type());
case Expression::Kind::kIntLiteral:
// Convert -intLiteral(1) to intLiteral(-1).
return std::make_unique<IntLiteral>(operand->fOffset,
-operand->as<IntLiteral>().value(),
&operand->type());
case Expression::Kind::kConstructor:
// To be consistent with prior behavior, the conversion of a negated constructor into a
// constructor of negative values is only performed when optimization is on.
// Conceptually it's pretty similar to the int/float optimizations above, though.
if (context.fConfig->fSettings.fOptimize && operand->isCompileTimeConstant()) {
Constructor& ctor = operand->as<Constructor>();
// We've found a negated constant constructor, e.g.:
// -float4(float3(floatLiteral(1)), floatLiteral(2))
// To optimize this, the outer negation is removed and each argument is negated:
// float4(-float3(floatLiteral(1)), floatLiteral(-2))
// Recursion will continue to push negation inwards as deeply as possible:
// float4(float3(floatLiteral(-1)), floatLiteral(-2))
ExpressionArray args;
args.reserve_back(ctor.arguments().size());
for (std::unique_ptr<Expression>& arg : ctor.arguments()) {
args.push_back(negate_operand(context, std::move(arg)));
}
return Constructor::Make(context, ctor.fOffset, ctor.type(), std::move(args));
}
break;
default:
break;
}
// No simplified form; convert expression to Prefix(TK_MINUS, expression).
return std::make_unique<PrefixExpression>(Token::Kind::TK_MINUS, std::move(operand));
}
static std::unique_ptr<Expression> logical_not_operand(const Context& context,
std::unique_ptr<Expression> operand) {
if (operand->is<BoolLiteral>()) {
// Convert !boolLiteral(true) to boolLiteral(false).
const BoolLiteral& b = operand->as<BoolLiteral>();
return std::make_unique<BoolLiteral>(operand->fOffset, !b.value(), &operand->type());
}
// No simplified form; convert expression to Prefix(TK_LOGICALNOT, expression).
return std::make_unique<PrefixExpression>(Token::Kind::TK_LOGICALNOT, std::move(operand));
}
std::unique_ptr<Expression> PrefixExpression::Make(const Context& context, Operator op,
std::unique_ptr<Expression> base) {
const Type& baseType = base->type();
switch (op.kind()) {
case Token::Kind::TK_PLUS:
if (!baseType.componentType().isNumber()) {
context.fErrors.error(base->fOffset,
"'+' cannot operate on '" + baseType.displayName() + "'");
return nullptr;
}
return base;
case Token::Kind::TK_MINUS:
if (!baseType.componentType().isNumber()) {
context.fErrors.error(base->fOffset,
"'-' cannot operate on '" + baseType.displayName() + "'");
return nullptr;
}
return negate_operand(context, std::move(base));
case Token::Kind::TK_PLUSPLUS:
if (!baseType.isNumber()) {
context.fErrors.error(base->fOffset,
String("'") + op.operatorName() + "' cannot operate on '" +
baseType.displayName() + "'");
return nullptr;
}
if (!Analysis::MakeAssignmentExpr(base.get(), VariableReference::RefKind::kReadWrite,
&context.fErrors)) {
return nullptr;
}
break;
case Token::Kind::TK_MINUSMINUS:
if (!baseType.isNumber()) {
context.fErrors.error(base->fOffset,
String("'") + op.operatorName() + "' cannot operate on '" +
baseType.displayName() + "'");
return nullptr;
}
if (!Analysis::MakeAssignmentExpr(base.get(), VariableReference::RefKind::kReadWrite,
&context.fErrors)) {
return nullptr;
}
break;
case Token::Kind::TK_LOGICALNOT:
if (!baseType.isBoolean()) {
context.fErrors.error(base->fOffset,
String("'") + op.operatorName() + "' cannot operate on '" +
baseType.displayName() + "'");
return nullptr;
}
return logical_not_operand(context, std::move(base));
case Token::Kind::TK_BITWISENOT:
if (context.fConfig->strictES2Mode()) {
// GLSL ES 1.00, Section 5.1
context.fErrors.error(
base->fOffset,
String("operator '") + op.operatorName() + "' is not allowed");
return nullptr;
}
if (!baseType.isInteger()) {
context.fErrors.error(base->fOffset,
String("'") + op.operatorName() + "' cannot operate on '" +
baseType.displayName() + "'");
return nullptr;
}
if (baseType.isLiteral()) {
// The expression `~123` is no longer a literal; coerce to the actual type.
base = baseType.scalarTypeForLiteral().coerceExpression(std::move(base), context);
}
break;
default:
SK_ABORT("unsupported prefix operator\n");
}
return std::make_unique<PrefixExpression>(op, std::move(base));
}
std::unique_ptr<Expression> PrefixExpression::constantPropagate(const IRGenerator& irGenerator,
const DefinitionMap& definitions) {
if (this->operand()->isCompileTimeConstant()) {
if (this->getOperator().kind() == Token::Kind::TK_MINUS) {
// Constant-propagate negation onto compile-time constants.
switch (this->operand()->kind()) {
case Expression::Kind::kFloatLiteral:
case Expression::Kind::kIntLiteral:
case Expression::Kind::kConstructor:
return negate_operand(irGenerator.fContext, this->operand()->clone());
default:
break;
}
} else if (this->getOperator().kind() == Token::Kind::TK_LOGICALNOT) {
if (this->operand()->is<BoolLiteral>()) {
return logical_not_operand(irGenerator.fContext, this->operand()->clone());
}
}
}
return nullptr;
}
} // namespace SkSL