blob: e324a6142e6dc7d0f34fa7c0cf3a3e6647861c43 [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/SkSLConstructor.h"
#include "src/sksl/ir/SkSLBoolLiteral.h"
#include "src/sksl/ir/SkSLFloatLiteral.h"
#include "src/sksl/ir/SkSLIntLiteral.h"
#include "src/sksl/ir/SkSLPrefixExpression.h"
namespace SkSL {
std::unique_ptr<Expression> Constructor::constantPropagate(const IRGenerator& irGenerator,
const DefinitionMap& definitions) {
// Handle conversion constructors of literal values.
if (this->arguments().size() == 1) {
return SimplifyConversion(this->type(), *this->arguments().front());
}
return nullptr;
}
std::unique_ptr<Expression> Constructor::SimplifyConversion(const Type& constructorType,
const Expression& expr) {
if (expr.is<IntLiteral>()) {
SKSL_INT value = expr.as<IntLiteral>().value();
if (constructorType.isFloat()) {
// promote float(1) to 1.0
return std::make_unique<FloatLiteral>(expr.fOffset, (SKSL_FLOAT)value,
&constructorType);
} else if (constructorType.isInteger()) {
// promote uint(1) to 1u
return std::make_unique<IntLiteral>(expr.fOffset, value, &constructorType);
} else if (constructorType.isBoolean()) {
// promote bool(1) to true/false
return std::make_unique<BoolLiteral>(expr.fOffset, value != 0, &constructorType);
}
} else if (expr.is<FloatLiteral>()) {
float value = expr.as<FloatLiteral>().value();
if (constructorType.isFloat()) {
// promote float(1.23) to 1.23
return std::make_unique<FloatLiteral>(expr.fOffset, value, &constructorType);
} else if (constructorType.isInteger()) {
// promote uint(1.23) to 1u
return std::make_unique<IntLiteral>(expr.fOffset, (SKSL_INT)value, &constructorType);
} else if (constructorType.isBoolean()) {
// promote bool(1.23) to true/false
return std::make_unique<BoolLiteral>(expr.fOffset, value != 0.0f, &constructorType);
}
} else if (expr.is<BoolLiteral>()) {
bool value = expr.as<BoolLiteral>().value();
if (constructorType.isFloat()) {
// promote float(true) to 1.0
return std::make_unique<FloatLiteral>(expr.fOffset, value ? 1.0f : 0.0f,
&constructorType);
} else if (constructorType.isInteger()) {
// promote uint(true) to 1u
return std::make_unique<IntLiteral>(expr.fOffset, value ? 1 : 0, &constructorType);
} else if (constructorType.isBoolean()) {
// promote bool(true) to true/false
return std::make_unique<BoolLiteral>(expr.fOffset, value, &constructorType);
}
}
return nullptr;
}
Expression::ComparisonResult Constructor::compareConstant(const Context& context,
const Expression& other) const {
if (!other.is<Constructor>()) {
return ComparisonResult::kUnknown;
}
const Constructor& c = other.as<Constructor>();
const Type& myType = this->type();
const Type& otherType = c.type();
SkASSERT(myType == otherType);
if (otherType.isVector()) {
bool isFloat = otherType.columns() > 1 ? otherType.componentType().isFloat()
: otherType.isFloat();
for (int i = 0; i < myType.columns(); i++) {
if (isFloat) {
if (this->getFVecComponent(i) != c.getFVecComponent(i)) {
return ComparisonResult::kNotEqual;
}
} else if (this->getIVecComponent(i) != c.getIVecComponent(i)) {
return ComparisonResult::kNotEqual;
}
}
return ComparisonResult::kEqual;
}
// shouldn't be possible to have a constant constructor that isn't a vector or matrix;
// a constant scalar constructor should have been collapsed down to the appropriate
// literal
SkASSERT(myType.isMatrix());
for (int col = 0; col < myType.columns(); col++) {
for (int row = 0; row < myType.rows(); row++) {
if (getMatComponent(col, row) != c.getMatComponent(col, row)) {
return ComparisonResult::kNotEqual;
}
}
}
return ComparisonResult::kEqual;
}
template <typename ResultType>
ResultType Constructor::getVecComponent(int index) const {
SkASSERT(this->type().isVector());
if (this->arguments().size() == 1 &&
this->arguments()[0]->type().isScalar()) {
// This constructor just wraps a scalar. Propagate out the value.
if (std::is_floating_point<ResultType>::value) {
return this->arguments()[0]->getConstantFloat();
} else {
return this->arguments()[0]->getConstantInt();
}
}
// Walk through all the constructor arguments until we reach the index we're searching for.
int current = 0;
for (const std::unique_ptr<Expression>& arg : this->arguments()) {
if (current > index) {
// Somehow, we went past the argument we're looking for. Bail.
break;
}
if (arg->type().isScalar()) {
if (index == current) {
// We're on the proper argument, and it's a scalar; fetch it.
if (std::is_floating_point<ResultType>::value) {
return arg->getConstantFloat();
} else {
return arg->getConstantInt();
}
}
current++;
continue;
}
switch (arg->kind()) {
case Kind::kConstructor: {
const Constructor& constructor = arg->as<Constructor>();
if (current + constructor.type().columns() > index) {
// We've found a constructor that overlaps the proper argument. Descend into
// it, honoring the type.
return constructor.componentType().isFloat()
? ResultType(constructor.getVecComponent<SKSL_FLOAT>(index - current))
: ResultType(constructor.getVecComponent<SKSL_INT>(index - current));
}
break;
}
case Kind::kPrefix: {
const PrefixExpression& prefix = arg->as<PrefixExpression>();
if (current + prefix.type().columns() > index) {
// We found a prefix operator that contains the proper argument. Descend
// into it. We only support for constant propagation of the unary minus, so
// we shouldn't see any other tokens here.
SkASSERT(prefix.getOperator() == Token::Kind::TK_MINUS);
const Expression& operand = *prefix.operand();
if (operand.type().isVector()) {
return operand.type().componentType().isFloat()
? -ResultType(operand.getVecComponent<SKSL_FLOAT>(index - current))
: -ResultType(operand.getVecComponent<SKSL_INT>(index - current));
} else {
return operand.type().isFloat()
? -ResultType(operand.getConstantFloat())
: -ResultType(operand.getConstantInt());
}
}
break;
}
default: {
SkDEBUGFAILF("unexpected component %d { %s } in %s\n",
index, arg->description().c_str(), description().c_str());
break;
}
}
current += arg->type().columns();
}
SkDEBUGFAILF("failed to find vector component %d in %s\n", index, description().c_str());
return -1;
}
template int Constructor::getVecComponent(int) const;
template float Constructor::getVecComponent(int) const;
SKSL_FLOAT Constructor::getMatComponent(int col, int row) const {
SkDEBUGCODE(const Type& myType = this->type();)
SkASSERT(this->isCompileTimeConstant());
SkASSERT(myType.isMatrix());
SkASSERT(col < myType.columns() && row < myType.rows());
if (this->arguments().size() == 1) {
const Type& argType = this->arguments()[0]->type();
if (argType.isScalar()) {
// single scalar argument, so matrix is of the form:
// x 0 0
// 0 x 0
// 0 0 x
// return x if col == row
return col == row ? this->arguments()[0]->getConstantFloat() : 0.0;
}
if (argType.isMatrix()) {
SkASSERT(this->arguments()[0]->kind() == Expression::Kind::kConstructor);
// single matrix argument. make sure we're within the argument's bounds.
if (col < argType.columns() && row < argType.rows()) {
// within bounds, defer to argument
return ((Constructor&) *this->arguments()[0]).getMatComponent(col, row);
}
// out of bounds
return 0.0;
}
}
int currentIndex = 0;
int targetIndex = col * this->type().rows() + row;
for (const auto& arg : this->arguments()) {
const Type& argType = arg->type();
SkASSERT(targetIndex >= currentIndex);
SkASSERT(argType.rows() == 1);
if (currentIndex + argType.columns() > targetIndex) {
if (argType.columns() == 1) {
return arg->getConstantFloat();
} else {
return arg->getFVecComponent(targetIndex - currentIndex);
}
}
currentIndex += argType.columns();
}
ABORT("can't happen, matrix component out of bounds");
}
SKSL_INT Constructor::getConstantInt() const {
// We're looking for scalar integer constructors only, i.e. `int(1)`.
SkASSERT(this->arguments().size() == 1);
SkASSERT(this->type().columns() == 1);
SkASSERT(this->type().isInteger());
// The inner argument might actually be a float! `int(1.0)` is a valid cast.
const Expression& expr = *this->arguments().front();
SkASSERT(expr.type().isScalar());
return expr.type().isInteger() ? expr.getConstantInt() : (SKSL_INT)expr.getConstantFloat();
}
SKSL_FLOAT Constructor::getConstantFloat() const {
// We're looking for scalar integer constructors only, i.e. `float(1.0)`.
SkASSERT(this->arguments().size() == 1);
SkASSERT(this->type().columns() == 1);
SkASSERT(this->type().isFloat());
// The inner argument might actually be an integer! `float(1)` is a valid cast.
const Expression& expr = *this->arguments().front();
SkASSERT(expr.type().isScalar());
return expr.type().isFloat() ? expr.getConstantFloat() : (SKSL_FLOAT)expr.getConstantInt();
}
} // namespace SkSL