Index: src/sksl/SkSLCFGGenerator.cpp |
diff --git a/src/sksl/SkSLCFGGenerator.cpp b/src/sksl/SkSLCFGGenerator.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..964a8dc84ab87d880dc073faedbc89d57e450bc7 |
--- /dev/null |
+++ b/src/sksl/SkSLCFGGenerator.cpp |
@@ -0,0 +1,343 @@ |
+/* |
+ * Copyright 2016 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "SkSLCFGGenerator.h" |
+ |
+#include "ir/SkSLConstructor.h" |
+#include "ir/SkSLBinaryExpression.h" |
+#include "ir/SkSLDoStatement.h" |
+#include "ir/SkSLExpressionStatement.h" |
+#include "ir/SkSLFieldAccess.h" |
+#include "ir/SkSLForStatement.h" |
+#include "ir/SkSLFunctionCall.h" |
+#include "ir/SkSLIfStatement.h" |
+#include "ir/SkSLIndexExpression.h" |
+#include "ir/SkSLPostfixExpression.h" |
+#include "ir/SkSLPrefixExpression.h" |
+#include "ir/SkSLReturnStatement.h" |
+#include "ir/SkSLSwizzle.h" |
+#include "ir/SkSLTernaryExpression.h" |
+#include "ir/SkSLVarDeclarationsStatement.h" |
+#include "ir/SkSLWhileStatement.h" |
+ |
+namespace SkSL { |
+ |
+BlockId CFG::newBlock() { |
+ BlockId result = fBlocks.size(); |
+ fBlocks.emplace_back(); |
+ if (fBlocks.size() > 1) { |
+ this->addExit(fCurrent, result); |
+ } |
+ fCurrent = result; |
+ return result; |
+} |
+ |
+BlockId CFG::newIsolatedBlock() { |
+ BlockId result = fBlocks.size(); |
+ fBlocks.emplace_back(); |
+ return result; |
+} |
+ |
+void CFG::addExit(BlockId from, BlockId to) { |
+ if (from == 0 || fBlocks[from].fEntrances.size()) { |
+ fBlocks[from].fExits.insert(to); |
+ fBlocks[to].fEntrances.insert(from); |
+ } |
+} |
+ |
+void CFG::dump() { |
+ for (size_t i = 0; i < fBlocks.size(); i++) { |
+ printf("Block %d\n-------\nBefore: ", (int) i); |
+ const char* separator = ""; |
+ for (auto iter = fBlocks[i].fBefore.begin(); iter != fBlocks[i].fBefore.end(); iter++) { |
+ printf("%s%s = %s", separator, iter->first->description().c_str(), |
+ iter->second ? iter->second->description().c_str() : "<undefined>"); |
+ separator = ", "; |
+ } |
+ printf("\nEntrances: "); |
+ separator = ""; |
+ for (BlockId b : fBlocks[i].fEntrances) { |
+ printf("%s%d", separator, (int) b); |
+ separator = ", "; |
+ } |
+ printf("\n"); |
+ for (size_t j = 0; j < fBlocks[i].fNodes.size(); j++) { |
+ printf("Node %d: %s\n", (int) j, fBlocks[i].fNodes[j].fNode->description().c_str()); |
+ } |
+ printf("Exits: "); |
+ separator = ""; |
+ for (BlockId b : fBlocks[i].fExits) { |
+ printf("%s%d", separator, (int) b); |
+ separator = ", "; |
+ } |
+ printf("\n\n"); |
+ } |
+} |
+ |
+void CFGGenerator::addExpression(CFG& cfg, const Expression* e) { |
+ switch (e->fKind) { |
+ case Expression::kBinary_Kind: { |
+ const BinaryExpression* b = (const BinaryExpression*) e; |
+ switch (b->fOperator) { |
+ case Token::LOGICALAND: // fall through |
+ case Token::LOGICALOR: { |
+ // this isn't as precise as it could be -- we don't bother to track that if we |
+ // early exit from a logical and/or, we know which branch of an 'if' we're going |
+ // to hit -- but it won't make much difference in practice. |
+ this->addExpression(cfg, b->fLeft.get()); |
+ BlockId start = cfg.fCurrent; |
+ cfg.newBlock(); |
+ this->addExpression(cfg, b->fRight.get()); |
+ cfg.newBlock(); |
+ cfg.addExit(start, cfg.fCurrent); |
+ break; |
+ } |
+ case Token::EQ: { |
+ this->addExpression(cfg, b->fRight.get()); |
+ this->addLValue(cfg, b->fLeft.get()); |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ |
+ BasicBlock::Node::kExpression_Kind, |
+ b |
+ }); |
+ break; |
+ } |
+ default: |
+ this->addExpression(cfg, b->fLeft.get()); |
+ this->addExpression(cfg, b->fRight.get()); |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ |
+ BasicBlock::Node::kExpression_Kind, |
+ b |
+ }); |
+ } |
+ break; |
+ } |
+ case Expression::kConstructor_Kind: { |
+ const Constructor* c = (const Constructor*) e; |
+ for (const auto& arg : c->fArguments) { |
+ this->addExpression(cfg, arg.get()); |
+ } |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind, c }); |
+ break; |
+ } |
+ case Expression::kFunctionCall_Kind: { |
+ const FunctionCall* c = (const FunctionCall*) e; |
+ for (const auto& arg : c->fArguments) { |
+ this->addExpression(cfg, arg.get()); |
+ } |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind, c }); |
+ break; |
+ } |
+ case Expression::kFieldAccess_Kind: |
+ this->addExpression(cfg, ((const FieldAccess*) e)->fBase.get()); |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind, e }); |
+ break; |
+ case Expression::kIndex_Kind: |
+ this->addExpression(cfg, ((const IndexExpression*) e)->fBase.get()); |
+ this->addExpression(cfg, ((const IndexExpression*) e)->fIndex.get()); |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind, e }); |
+ break; |
+ case Expression::kPrefix_Kind: |
+ this->addExpression(cfg, ((const PrefixExpression*) e)->fOperand.get()); |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind, e }); |
+ break; |
+ case Expression::kPostfix_Kind: |
+ this->addExpression(cfg, ((const PostfixExpression*) e)->fOperand.get()); |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind, e }); |
+ break; |
+ case Expression::kSwizzle_Kind: |
+ this->addExpression(cfg, ((const Swizzle*) e)->fBase.get()); |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind, e }); |
+ break; |
+ case Expression::kBoolLiteral_Kind: // fall through |
+ case Expression::kFloatLiteral_Kind: // fall through |
+ case Expression::kIntLiteral_Kind: // fall through |
+ case Expression::kVariableReference_Kind: |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind, e }); |
+ break; |
+ case Expression::kTernary_Kind: { |
+ const TernaryExpression* t = (const TernaryExpression*) e; |
+ this->addExpression(cfg, t->fTest.get()); |
+ BlockId start = cfg.fCurrent; |
+ cfg.newBlock(); |
+ this->addExpression(cfg, t->fIfTrue.get()); |
+ BlockId next = cfg.newBlock(); |
+ cfg.fCurrent = start; |
+ cfg.newBlock(); |
+ this->addExpression(cfg, t->fIfFalse.get()); |
+ cfg.addExit(cfg.fCurrent, next); |
+ cfg.fCurrent = next; |
+ break; |
+ } |
+ case Expression::kFunctionReference_Kind: // fall through |
+ case Expression::kTypeReference_Kind: // fall through |
+ case Expression::kDefined_Kind: |
+ ASSERT(false); |
+ break; |
+ } |
+} |
+ |
+// adds expressions that are evaluated as part of resolving an lvalue |
+void CFGGenerator::addLValue(CFG& cfg, const Expression* e) { |
+ switch (e->fKind) { |
+ case Expression::kFieldAccess_Kind: |
+ this->addLValue(cfg, ((const FieldAccess*) e)->fBase.get()); |
+ break; |
+ case Expression::kIndex_Kind: |
+ this->addLValue(cfg, ((const IndexExpression*) e)->fBase.get()); |
+ this->addExpression(cfg, ((const IndexExpression*) e)->fIndex.get()); |
+ break; |
+ case Expression::kSwizzle_Kind: |
+ this->addLValue(cfg, ((const Swizzle*) e)->fBase.get()); |
+ break; |
+ case Expression::kVariableReference_Kind: |
+ break; |
+ default: |
+ // not an lvalue, can't happen |
+ ASSERT(false); |
+ break; |
+ } |
+} |
+ |
+void CFGGenerator::addStatement(CFG& cfg, const Statement* s) { |
+ switch (s->fKind) { |
+ case Statement::kBlock_Kind: |
+ for (const auto& child : ((const Block*) s)->fStatements) { |
+ addStatement(cfg, child.get()); |
+ } |
+ break; |
+ case Statement::kIf_Kind: { |
+ const IfStatement* ifs = (const IfStatement*) s; |
+ this->addExpression(cfg, ifs->fTest.get()); |
+ BlockId start = cfg.fCurrent; |
+ cfg.newBlock(); |
+ this->addStatement(cfg, ifs->fIfTrue.get()); |
+ BlockId next = cfg.newBlock(); |
+ if (ifs->fIfFalse) { |
+ cfg.fCurrent = start; |
+ cfg.newBlock(); |
+ this->addStatement(cfg, ifs->fIfFalse.get()); |
+ cfg.addExit(cfg.fCurrent, next); |
+ cfg.fCurrent = next; |
+ } else { |
+ cfg.addExit(start, next); |
+ } |
+ break; |
+ } |
+ case Statement::kExpression_Kind: { |
+ this->addExpression(cfg, ((ExpressionStatement&) *s).fExpression.get()); |
+ break; |
+ } |
+ case Statement::kVarDeclarations_Kind: { |
+ const VarDeclarationsStatement& decls = ((VarDeclarationsStatement&) *s); |
+ for (const auto& vd : decls.fDeclaration->fVars) { |
+ if (vd.fValue) { |
+ this->addExpression(cfg, vd.fValue.get()); |
+ } |
+ } |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, s }); |
+ break; |
+ } |
+ case Statement::kDiscard_Kind: |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, s }); |
+ cfg.fCurrent = cfg.newIsolatedBlock(); |
+ break; |
+ case Statement::kReturn_Kind: { |
+ const ReturnStatement& r = ((ReturnStatement&) *s); |
+ if (r.fExpression) { |
+ this->addExpression(cfg, r.fExpression.get()); |
+ } |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, s }); |
+ cfg.fCurrent = cfg.newIsolatedBlock(); |
+ break; |
+ } |
+ case Statement::kBreak_Kind: |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, s }); |
+ cfg.addExit(cfg.fCurrent, fLoopExits.top()); |
+ cfg.fCurrent = cfg.newIsolatedBlock(); |
+ break; |
+ case Statement::kContinue_Kind: |
+ cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, s }); |
+ cfg.addExit(cfg.fCurrent, fLoopContinues.top()); |
+ cfg.fCurrent = cfg.newIsolatedBlock(); |
+ break; |
+ case Statement::kWhile_Kind: { |
+ const WhileStatement* w = (const WhileStatement*) s; |
+ BlockId loopStart = cfg.newBlock(); |
+ fLoopContinues.push(loopStart); |
+ BlockId loopExit = cfg.newIsolatedBlock(); |
+ fLoopExits.push(loopExit); |
+ this->addExpression(cfg, w->fTest.get()); |
+ BlockId test = cfg.fCurrent; |
+ cfg.addExit(test, loopExit); |
+ cfg.newBlock(); |
+ this->addStatement(cfg, w->fStatement.get()); |
+ cfg.addExit(cfg.fCurrent, loopStart); |
+ fLoopContinues.pop(); |
+ fLoopExits.pop(); |
+ cfg.fCurrent = loopExit; |
+ break; |
+ } |
+ case Statement::kDo_Kind: { |
+ const DoStatement* d = (const DoStatement*) s; |
+ BlockId loopStart = cfg.newBlock(); |
+ fLoopContinues.push(loopStart); |
+ BlockId loopExit = cfg.newIsolatedBlock(); |
+ fLoopExits.push(loopExit); |
+ this->addStatement(cfg, d->fStatement.get()); |
+ this->addExpression(cfg, d->fTest.get()); |
+ cfg.addExit(cfg.fCurrent, loopExit); |
+ cfg.addExit(cfg.fCurrent, loopStart); |
+ fLoopContinues.pop(); |
+ fLoopExits.pop(); |
+ cfg.fCurrent = loopExit; |
+ break; |
+ } |
+ case Statement::kFor_Kind: { |
+ const ForStatement* f = (const ForStatement*) s; |
+ if (f->fInitializer) { |
+ this->addStatement(cfg, f->fInitializer.get()); |
+ } |
+ BlockId loopStart = cfg.newBlock(); |
+ BlockId next = cfg.newIsolatedBlock(); |
+ fLoopContinues.push(next); |
+ BlockId loopExit = cfg.newIsolatedBlock(); |
+ fLoopExits.push(loopExit); |
+ if (f->fTest) { |
+ this->addExpression(cfg, f->fTest.get()); |
+ BlockId test = cfg.fCurrent; |
+ cfg.addExit(test, loopExit); |
+ } |
+ cfg.newBlock(); |
+ this->addStatement(cfg, f->fStatement.get()); |
+ cfg.addExit(cfg.fCurrent, next); |
+ cfg.fCurrent = next; |
+ if (f->fNext) { |
+ this->addExpression(cfg, f->fNext.get()); |
+ } |
+ cfg.addExit(next, loopStart); |
+ fLoopContinues.pop(); |
+ fLoopExits.pop(); |
+ cfg.fCurrent = loopExit; |
+ break; |
+ } |
+ default: |
+ printf("statement: %s\n", s->description().c_str()); |
+ ABORT("unsupported statement kind"); |
+ } |
+} |
+ |
+CFG CFGGenerator::getCFG(const FunctionDefinition& f) { |
+ CFG result; |
+ result.fStart = result.newBlock(); |
+ result.fCurrent = result.fStart; |
+ this->addStatement(result, f.fBody.get()); |
+ result.newBlock(); |
+ result.fExit = result.fCurrent; |
+ return result; |
+} |
+ |
+} // namespace |