Implement boolean short circuit folding in SkSL

Bug: skia:
Change-Id: I0bb0506bbe6973c004ced22648588d82d2bac497
Reviewed-on: https://skia-review.googlesource.com/151820
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2018-09-06 17:01:38 -04:00 committed by Skia Commit-Bot
parent 0b80e62a14
commit 7b429aed84
2 changed files with 84 additions and 1 deletions

View File

@ -1286,9 +1286,40 @@ static bool determine_binary_type(const Context& context,
return false;
}
static std::unique_ptr<Expression> short_circuit_boolean(const Context& context,
const Expression& left,
Token::Kind op,
const Expression& right) {
SkASSERT(left.fKind == Expression::kBoolLiteral_Kind);
bool leftVal = ((BoolLiteral&) left).fValue;
if (op == Token::LOGICALAND) {
// (true && expr) -> (expr) and (false && expr) -> (false)
return leftVal ? right.clone()
: std::unique_ptr<Expression>(new BoolLiteral(context, left.fOffset, false));
} else if (op == Token::LOGICALOR) {
// (true || expr) -> (true) and (false || expr) -> (expr)
return leftVal ? std::unique_ptr<Expression>(new BoolLiteral(context, left.fOffset, true))
: right.clone();
} else {
// Can't short circuit XOR
return nullptr;
}
}
std::unique_ptr<Expression> IRGenerator::constantFold(const Expression& left,
Token::Kind op,
const Expression& right) const {
// If the left side is a constant boolean literal, the right side does not need to be constant
// for short circuit optimizations to allow the constant to be folded.
if (left.fKind == Expression::kBoolLiteral_Kind && !right.isConstant()) {
return short_circuit_boolean(fContext, left, op, right);
} else if (right.fKind == Expression::kBoolLiteral_Kind && !left.isConstant()) {
// There aren't side effects in SKSL within expressions, so (left OP right) is equivalent to
// (right OP left) for short-circuit optimizations
return short_circuit_boolean(fContext, right, op, left);
}
// Other than the short-circuit cases above, constant folding requires both sides to be constant
if (!left.isConstant() || !right.isConstant()) {
return nullptr;
}

View File

@ -152,7 +152,7 @@ DEF_TEST(SkSLOperators, r) {
" x = -6.0;\n"
" y = -1.0;\n"
" z = 8;\n"
" bool b = false == true || 2.0 >= sqrt(2.0) && true;\n"
" bool b = false == true || 2.0 >= sqrt(2.0);\n"
" x += 12.0;\n"
" x -= 12.0;\n"
" x *= (y /= float(z = 10));\n"
@ -748,6 +748,58 @@ DEF_TEST(SkSLBoolFolding, r) {
"}\n");
}
DEF_TEST(SkSLShortCircuitBoolFolding, r) {
test(r,
"void main() {"
"bool expr1 = sk_FragCoord.x > 0;"
"bool expr2 = sk_FragCoord.y > 0;"
" if (true && expr1) {" // -> if (expr1)
" sk_FragColor.r = 1;"
" } else if (false && expr1) {" // -> if (false) -> block removed
" sk_FragColor.r = -2;"
" } else if (false || expr2) {" // -> if (expr2)
" sk_FragColor.r = 3;"
" } else if (true || expr2) {" // -> if (true) -> replaces unreachable else
" sk_FragColor.r = 4;"
" } else {" // removed
" sk_FragColor.r = -5;"
" }"
// Test short-circuiting of right hand side boolean literals
" if (expr1 && true) {" // -> if (expr1)
" sk_FragColor.r = 1;"
" } else if (expr1 && false) {" // -> if (false) -> block removed
" sk_FragColor.r = -2;"
" } else if (expr2 || false) {" // -> if (expr2)
" sk_FragColor.r = 3;"
" } else if (expr2 || true) {" // -> if (true) -> replaces unreachable else
" sk_FragColor.r = 4;"
" } else {" // removed
" sk_FragColor.r = -5;"
" }"
"}",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" bool expr1 = gl_FragCoord.x > 0.0;\n"
" bool expr2 = gl_FragCoord.y > 0.0;\n"
" if (expr1) {\n"
" sk_FragColor.x = 1.0;\n"
" } else if (expr2) {\n"
" sk_FragColor.x = 3.0;\n"
" } else {\n"
" sk_FragColor.x = 4.0;\n"
" }\n"
" if (expr1) {\n"
" sk_FragColor.x = 1.0;\n"
" } else if (expr2) {\n"
" sk_FragColor.x = 3.0;\n"
" } else {\n"
" sk_FragColor.x = 4.0;\n"
" }\n"
"}\n");
}
DEF_TEST(SkSLVecFolding, r) {
test(r,
"void main() {"