aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar ethannicholas <ethannicholas@google.com>2016-11-09 13:26:45 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2016-11-09 13:26:45 -0800
commit08a9211a8492a84e1f4a6899759f8f37ed5aec3e (patch)
tree8fe430d685b62467d0a59aa374ef7b98d6a3db4a /src
parentf2b024db6777a904d986c68a21ba0bc41f956f6e (diff)
added constant folding & branch elimination to skslc
Diffstat (limited to 'src')
-rw-r--r--src/sksl/SkSLIRGenerator.cpp112
-rw-r--r--src/sksl/SkSLIRGenerator.h5
2 files changed, 112 insertions, 5 deletions
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index ec64fa9348..1a4c775998 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -246,6 +246,19 @@ std::unique_ptr<Statement> IRGenerator::convertIf(const ASTIfStatement& s) {
return nullptr;
}
}
+ if (test->fKind == Expression::kBoolLiteral_Kind) {
+ // static boolean value, fold down to a single branch
+ if (((BoolLiteral&) *test).fValue) {
+ return ifTrue;
+ } else if (s.fIfFalse) {
+ return ifFalse;
+ } else {
+ // False & no else clause. Not an error, so don't return null!
+ std::vector<std::unique_ptr<Statement>> empty;
+ return std::unique_ptr<Statement>(new Block(s.fPosition, std::move(empty),
+ fSymbolTable));
+ }
+ }
return std::unique_ptr<Statement>(new IfStatement(s.fPosition, std::move(test),
std::move(ifTrue), std::move(ifFalse)));
}
@@ -794,6 +807,78 @@ static bool determine_binary_type(const Context& context,
return false;
}
+/**
+ * If both operands are compile-time constants and can be folded, returns an expression representing
+ * the folded value. Otherwise, returns null. Note that unlike most other functions here, null does
+ * not represent a compilation error.
+ */
+std::unique_ptr<Expression> IRGenerator::constantFold(const Expression& left,
+ Token::Kind op,
+ const Expression& right) {
+ // Note that we expressly do not worry about precision and overflow here -- we use the maximum
+ // precision to calculate the results and hope the result makes sense. The plan is to move the
+ // Skia caps into SkSL, so we have access to all of them including the precisions of the various
+ // types, which will let us be more intelligent about this.
+ if (left.fKind == Expression::kBoolLiteral_Kind &&
+ right.fKind == Expression::kBoolLiteral_Kind) {
+ bool leftVal = ((BoolLiteral&) left).fValue;
+ bool rightVal = ((BoolLiteral&) right).fValue;
+ bool result;
+ switch (op) {
+ case Token::LOGICALAND: result = leftVal && rightVal; break;
+ case Token::LOGICALOR: result = leftVal || rightVal; break;
+ case Token::LOGICALXOR: result = leftVal ^ rightVal; break;
+ default: return nullptr;
+ }
+ return std::unique_ptr<Expression>(new BoolLiteral(fContext, left.fPosition, result));
+ }
+ #define RESULT(t, op) std::unique_ptr<Expression>(new t ## Literal(fContext, left.fPosition, \
+ leftVal op rightVal))
+ if (left.fKind == Expression::kIntLiteral_Kind && right.fKind == Expression::kIntLiteral_Kind) {
+ int64_t leftVal = ((IntLiteral&) left).fValue;
+ int64_t rightVal = ((IntLiteral&) right).fValue;
+ switch (op) {
+ case Token::PLUS: return RESULT(Int, +);
+ case Token::MINUS: return RESULT(Int, -);
+ case Token::STAR: return RESULT(Int, *);
+ case Token::SLASH: return RESULT(Int, /);
+ case Token::PERCENT: return RESULT(Int, %);
+ case Token::BITWISEAND: return RESULT(Int, &);
+ case Token::BITWISEOR: return RESULT(Int, |);
+ case Token::BITWISEXOR: return RESULT(Int, ^);
+ case Token::SHL: return RESULT(Int, <<);
+ case Token::SHR: return RESULT(Int, >>);
+ case Token::EQEQ: return RESULT(Bool, ==);
+ case Token::NEQ: return RESULT(Bool, !=);
+ case Token::GT: return RESULT(Bool, >);
+ case Token::GTEQ: return RESULT(Bool, >=);
+ case Token::LT: return RESULT(Bool, <);
+ case Token::LTEQ: return RESULT(Bool, <=);
+ default: return nullptr;
+ }
+ }
+ if (left.fKind == Expression::kFloatLiteral_Kind &&
+ right.fKind == Expression::kFloatLiteral_Kind) {
+ double leftVal = ((FloatLiteral&) left).fValue;
+ double rightVal = ((FloatLiteral&) right).fValue;
+ switch (op) {
+ case Token::PLUS: return RESULT(Float, +);
+ case Token::MINUS: return RESULT(Float, -);
+ case Token::STAR: return RESULT(Float, *);
+ case Token::SLASH: return RESULT(Float, /);
+ case Token::EQEQ: return RESULT(Bool, ==);
+ case Token::NEQ: return RESULT(Bool, !=);
+ case Token::GT: return RESULT(Bool, >);
+ case Token::GTEQ: return RESULT(Bool, >=);
+ case Token::LT: return RESULT(Bool, <);
+ case Token::LTEQ: return RESULT(Bool, <=);
+ default: return nullptr;
+ }
+ }
+ #undef RESULT
+ return nullptr;
+}
+
std::unique_ptr<Expression> IRGenerator::convertBinaryExpression(
const ASTBinaryExpression& expression) {
std::unique_ptr<Expression> left = this->convertExpression(*expression.fLeft);
@@ -823,11 +908,16 @@ std::unique_ptr<Expression> IRGenerator::convertBinaryExpression(
if (!left || !right) {
return nullptr;
}
- return std::unique_ptr<Expression>(new BinaryExpression(expression.fPosition,
- std::move(left),
- expression.fOperator,
- std::move(right),
- *resultType));
+ std::unique_ptr<Expression> result = this->constantFold(*left.get(), expression.fOperator,
+ *right.get());
+ if (!result) {
+ result = std::unique_ptr<Expression>(new BinaryExpression(expression.fPosition,
+ std::move(left),
+ expression.fOperator,
+ std::move(right),
+ *resultType));
+ }
+ return result;
}
std::unique_ptr<Expression> IRGenerator::convertTernaryExpression(
@@ -858,6 +948,14 @@ std::unique_ptr<Expression> IRGenerator::convertTernaryExpression(
ASSERT(trueType == falseType);
ifTrue = this->coerce(std::move(ifTrue), *trueType);
ifFalse = this->coerce(std::move(ifFalse), *falseType);
+ if (test->fKind == Expression::kBoolLiteral_Kind) {
+ // static boolean test, just return one of the branches
+ if (((BoolLiteral&) *test).fValue) {
+ return ifTrue;
+ } else {
+ return ifFalse;
+ }
+ }
return std::unique_ptr<Expression>(new TernaryExpression(expression.fPosition,
std::move(test),
std::move(ifTrue),
@@ -1126,6 +1224,10 @@ std::unique_ptr<Expression> IRGenerator::convertPrefixExpression(
"' cannot operate on '" + base->fType.description() + "'");
return nullptr;
}
+ if (base->fKind == Expression::kBoolLiteral_Kind) {
+ return std::unique_ptr<Expression>(new BoolLiteral(fContext, base->fPosition,
+ !((BoolLiteral&) *base).fValue));
+ }
break;
case Token::BITWISENOT:
if (base->fType != *fContext.fInt_Type) {
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
index a1b86f4f54..036f24253b 100644
--- a/src/sksl/SkSLIRGenerator.h
+++ b/src/sksl/SkSLIRGenerator.h
@@ -89,6 +89,11 @@ private:
std::unique_ptr<Statement> convertDiscard(const ASTDiscardStatement& d);
std::unique_ptr<Statement> convertDo(const ASTDoStatement& d);
std::unique_ptr<Expression> convertBinaryExpression(const ASTBinaryExpression& expression);
+ // Returns null if it cannot fold the expression. Note that unlike most other functions here, a
+ // null return does not represent a compilation error.
+ std::unique_ptr<Expression> constantFold(const Expression& left,
+ Token::Kind op,
+ const Expression& right);
std::unique_ptr<Extension> convertExtension(const ASTExtension& e);
std::unique_ptr<Statement> convertExpressionStatement(const ASTExpressionStatement& s);
std::unique_ptr<Statement> convertFor(const ASTForStatement& f);