| /* |
| * 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 "src/sksl/SkSLCFGGenerator.h" |
| |
| #include "src/sksl/ir/SkSLBinaryExpression.h" |
| #include "src/sksl/ir/SkSLConstructor.h" |
| #include "src/sksl/ir/SkSLDoStatement.h" |
| #include "src/sksl/ir/SkSLExpressionStatement.h" |
| #include "src/sksl/ir/SkSLExternalFunctionCall.h" |
| #include "src/sksl/ir/SkSLFieldAccess.h" |
| #include "src/sksl/ir/SkSLForStatement.h" |
| #include "src/sksl/ir/SkSLFunctionCall.h" |
| #include "src/sksl/ir/SkSLIfStatement.h" |
| #include "src/sksl/ir/SkSLIndexExpression.h" |
| #include "src/sksl/ir/SkSLPostfixExpression.h" |
| #include "src/sksl/ir/SkSLPrefixExpression.h" |
| #include "src/sksl/ir/SkSLReturnStatement.h" |
| #include "src/sksl/ir/SkSLSwitchStatement.h" |
| #include "src/sksl/ir/SkSLSwizzle.h" |
| #include "src/sksl/ir/SkSLTernaryExpression.h" |
| #include "src/sksl/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].fIsReachable) { |
| BasicBlock::ExitArray& exits = fBlocks[from].fExits; |
| if (std::find(exits.begin(), exits.end(), to) == exits.end()) { |
| exits.push_back(to); |
| } |
| fBlocks[to].fIsReachable = true; |
| } |
| } |
| |
| #ifdef SK_DEBUG |
| void CFG::dump() const { |
| for (size_t i = 0; i < fBlocks.size(); i++) { |
| printf("Block %zu\n-------\n", i); |
| fBlocks[i].dump(); |
| } |
| } |
| |
| void BasicBlock::dump() const { |
| printf("Before: ["); |
| const char* separator = ""; |
| fBefore.foreach([&](const Variable* var, std::unique_ptr<Expression>* expr) { |
| printf("%s%s = %s", separator, |
| var->description().c_str(), |
| expr ? (*expr)->description().c_str() : "<undefined>"); |
| separator = ", "; |
| }); |
| printf("]\nIs Reachable: [%s]\n", fIsReachable ? "yes" : "no"); |
| for (size_t j = 0; j < fNodes.size(); j++) { |
| const BasicBlock::Node& n = fNodes[j]; |
| printf("Node %zu (%p): %s\n", j, &n, n.description().c_str()); |
| } |
| printf("Exits: ["); |
| separator = ""; |
| for (BlockId b : fExits) { |
| printf("%s%zu", separator, b); |
| separator = ", "; |
| } |
| printf("]\n\n"); |
| } |
| #endif |
| |
| bool BasicBlock::tryRemoveExpressionBefore(std::vector<BasicBlock::Node>::iterator* iter, |
| Expression* e) { |
| if (e->kind() == Expression::Kind::kTernary) { |
| return false; |
| } |
| bool result; |
| if ((*iter)->isExpression()) { |
| SkASSERT((*iter)->expression()->get() != e); |
| Expression* old = (*iter)->expression()->get(); |
| do { |
| if ((*iter) == fNodes.begin()) { |
| return false; |
| } |
| --(*iter); |
| } while (!(*iter)->isExpression() || (*iter)->expression()->get() != e); |
| |
| result = this->tryRemoveExpression(iter); |
| |
| while (!(*iter)->isExpression() || (*iter)->expression()->get() != old) { |
| SkASSERT(*iter != fNodes.end()); |
| ++(*iter); |
| } |
| } else { |
| Statement* old = (*iter)->statement()->get(); |
| do { |
| if ((*iter) == fNodes.begin()) { |
| return false; |
| } |
| --(*iter); |
| } while (!(*iter)->isExpression() || (*iter)->expression()->get() != e); |
| |
| result = this->tryRemoveExpression(iter); |
| |
| while (!(*iter)->isStatement() || (*iter)->statement()->get() != old) { |
| SkASSERT(*iter != fNodes.end()); |
| ++(*iter); |
| } |
| } |
| return result; |
| } |
| |
| bool BasicBlock::tryRemoveLValueBefore(std::vector<BasicBlock::Node>::iterator* iter, |
| Expression* lvalue) { |
| switch (lvalue->kind()) { |
| case Expression::Kind::kExternalValue: // fall through |
| case Expression::Kind::kVariableReference: |
| return true; |
| case Expression::Kind::kSwizzle: |
| return this->tryRemoveLValueBefore(iter, lvalue->as<Swizzle>().base().get()); |
| case Expression::Kind::kFieldAccess: |
| return this->tryRemoveLValueBefore(iter, lvalue->as<FieldAccess>().base().get()); |
| case Expression::Kind::kIndex: { |
| IndexExpression& indexExpr = lvalue->as<IndexExpression>(); |
| if (!this->tryRemoveLValueBefore(iter, indexExpr.base().get())) { |
| return false; |
| } |
| return this->tryRemoveExpressionBefore(iter, indexExpr.index().get()); |
| } |
| case Expression::Kind::kTernary: { |
| TernaryExpression& ternary = lvalue->as<TernaryExpression>(); |
| if (!this->tryRemoveExpressionBefore(iter, ternary.test().get())) { |
| return false; |
| } |
| if (!this->tryRemoveLValueBefore(iter, ternary.ifTrue().get())) { |
| return false; |
| } |
| return this->tryRemoveLValueBefore(iter, ternary.ifFalse().get()); |
| } |
| default: |
| #ifdef SK_DEBUG |
| ABORT("invalid lvalue: %s\n", lvalue->description().c_str()); |
| #endif |
| return false; |
| } |
| } |
| |
| bool BasicBlock::tryRemoveExpression(std::vector<BasicBlock::Node>::iterator* iter) { |
| Expression* expr = (*iter)->expression()->get(); |
| switch (expr->kind()) { |
| case Expression::Kind::kBinary: { |
| BinaryExpression& b = expr->as<BinaryExpression>(); |
| if (b.getOperator() == Token::Kind::TK_EQ) { |
| if (!this->tryRemoveLValueBefore(iter, &b.left())) { |
| return false; |
| } |
| } else if (!this->tryRemoveExpressionBefore(iter, &b.left())) { |
| return false; |
| } |
| if (!this->tryRemoveExpressionBefore(iter, &b.right())) { |
| return false; |
| } |
| SkASSERT((*iter)->expression()->get() == expr); |
| *iter = fNodes.erase(*iter); |
| return true; |
| } |
| case Expression::Kind::kTernary: { |
| // ternaries cross basic block boundaries, must regenerate the CFG to remove it |
| return false; |
| } |
| case Expression::Kind::kFieldAccess: { |
| FieldAccess& f = expr->as<FieldAccess>(); |
| if (!this->tryRemoveExpressionBefore(iter, f.base().get())) { |
| return false; |
| } |
| *iter = fNodes.erase(*iter); |
| return true; |
| } |
| case Expression::Kind::kSwizzle: { |
| Swizzle& s = expr->as<Swizzle>(); |
| if (s.base() && !this->tryRemoveExpressionBefore(iter, s.base().get())) { |
| return false; |
| } |
| *iter = fNodes.erase(*iter); |
| return true; |
| } |
| case Expression::Kind::kIndex: { |
| IndexExpression& idx = expr->as<IndexExpression>(); |
| if (!this->tryRemoveExpressionBefore(iter, idx.base().get())) { |
| return false; |
| } |
| if (!this->tryRemoveExpressionBefore(iter, idx.index().get())) { |
| return false; |
| } |
| *iter = fNodes.erase(*iter); |
| return true; |
| } |
| case Expression::Kind::kConstructor: { |
| Constructor& c = expr->as<Constructor>(); |
| for (auto& arg : c.arguments()) { |
| if (!this->tryRemoveExpressionBefore(iter, arg.get())) { |
| return false; |
| } |
| SkASSERT((*iter)->expression()->get() == expr); |
| } |
| *iter = fNodes.erase(*iter); |
| return true; |
| } |
| case Expression::Kind::kFunctionCall: { |
| FunctionCall& f = expr->as<FunctionCall>(); |
| for (auto& arg : f.arguments()) { |
| if (!this->tryRemoveExpressionBefore(iter, arg.get())) { |
| return false; |
| } |
| SkASSERT((*iter)->expression()->get() == expr); |
| } |
| *iter = fNodes.erase(*iter); |
| return true; |
| } |
| case Expression::Kind::kPrefix: |
| if (!this->tryRemoveExpressionBefore(iter, |
| expr->as<PrefixExpression>().operand().get())) { |
| return false; |
| } |
| *iter = fNodes.erase(*iter); |
| return true; |
| case Expression::Kind::kPostfix: |
| if (!this->tryRemoveExpressionBefore(iter, |
| expr->as<PrefixExpression>().operand().get())) { |
| return false; |
| } |
| *iter = fNodes.erase(*iter); |
| return true; |
| case Expression::Kind::kBoolLiteral: // fall through |
| case Expression::Kind::kFloatLiteral: // fall through |
| case Expression::Kind::kIntLiteral: // fall through |
| case Expression::Kind::kSetting: // fall through |
| case Expression::Kind::kVariableReference: |
| *iter = fNodes.erase(*iter); |
| return true; |
| default: |
| #ifdef SK_DEBUG |
| ABORT("unhandled expression: %s\n", expr->description().c_str()); |
| #endif |
| return false; |
| } |
| } |
| |
| bool BasicBlock::tryInsertExpression(std::vector<BasicBlock::Node>::iterator* iter, |
| std::unique_ptr<Expression>* expr) { |
| switch ((*expr)->kind()) { |
| case Expression::Kind::kBinary: { |
| BinaryExpression& b = expr->get()->as<BinaryExpression>(); |
| if (!this->tryInsertExpression(iter, &b.rightPointer())) { |
| return false; |
| } |
| |
| ++(*iter); |
| if (!this->tryInsertExpression(iter, &b.leftPointer())) { |
| return false; |
| } |
| ++(*iter); |
| *iter = fNodes.insert(*iter, |
| BasicBlock::MakeExpression(expr, /*constantPropagation=*/true)); |
| return true; |
| } |
| case Expression::Kind::kBoolLiteral: // fall through |
| case Expression::Kind::kFloatLiteral: // fall through |
| case Expression::Kind::kIntLiteral: // fall through |
| case Expression::Kind::kVariableReference: { |
| *iter = fNodes.insert(*iter, |
| BasicBlock::MakeExpression(expr, /*constantPropagation=*/true)); |
| return true; |
| } |
| case Expression::Kind::kConstructor: { |
| Constructor& c = expr->get()->as<Constructor>(); |
| for (auto& arg : c.arguments()) { |
| if (!this->tryInsertExpression(iter, &arg)) { |
| return false; |
| } |
| ++(*iter); |
| } |
| *iter = fNodes.insert(*iter, |
| BasicBlock::MakeExpression(expr, /*constantPropagation=*/true)); |
| return true; |
| } |
| case Expression::Kind::kSwizzle: { |
| Swizzle& s = expr->get()->as<Swizzle>(); |
| if (!this->tryInsertExpression(iter, &s.base())) { |
| return false; |
| } |
| ++(*iter); |
| *iter = fNodes.insert(*iter, |
| BasicBlock::MakeExpression(expr, /*constantPropagation=*/true)); |
| return true; |
| } |
| default: |
| return false; |
| } |
| } |
| |
| void CFGGenerator::addExpression(CFG& cfg, std::unique_ptr<Expression>* e, bool constantPropagate) { |
| SkASSERT(e); |
| switch ((*e)->kind()) { |
| case Expression::Kind::kBinary: { |
| BinaryExpression& b = e->get()->as<BinaryExpression>(); |
| Token::Kind op = b.getOperator(); |
| switch (op) { |
| case Token::Kind::TK_LOGICALAND: // fall through |
| case Token::Kind::TK_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.leftPointer(), constantPropagate); |
| BlockId start = cfg.fCurrent; |
| cfg.newBlock(); |
| this->addExpression(cfg, &b.rightPointer(), constantPropagate); |
| cfg.newBlock(); |
| cfg.addExit(start, cfg.fCurrent); |
| cfg.currentBlock().fNodes.push_back( |
| BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| } |
| case Token::Kind::TK_EQ: { |
| this->addExpression(cfg, &b.rightPointer(), constantPropagate); |
| this->addLValue(cfg, &b.leftPointer()); |
| cfg.currentBlock().fNodes.push_back( |
| BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| } |
| default: |
| this->addExpression(cfg, &b.leftPointer(), |
| !Compiler::IsAssignment(b.getOperator())); |
| this->addExpression(cfg, &b.rightPointer(), constantPropagate); |
| cfg.currentBlock().fNodes.push_back( |
| BasicBlock::MakeExpression(e, constantPropagate)); |
| } |
| break; |
| } |
| case Expression::Kind::kConstructor: { |
| Constructor& c = e->get()->as<Constructor>(); |
| for (auto& arg : c.arguments()) { |
| this->addExpression(cfg, &arg, constantPropagate); |
| } |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| } |
| case Expression::Kind::kExternalFunctionCall: { |
| ExternalFunctionCall& c = e->get()->as<ExternalFunctionCall>(); |
| for (auto& arg : c.arguments()) { |
| this->addExpression(cfg, &arg, constantPropagate); |
| } |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| } |
| case Expression::Kind::kFunctionCall: { |
| FunctionCall& c = e->get()->as<FunctionCall>(); |
| for (auto& arg : c.arguments()) { |
| this->addExpression(cfg, &arg, constantPropagate); |
| } |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| } |
| case Expression::Kind::kFieldAccess: { |
| this->addExpression(cfg, &e->get()->as<FieldAccess>().base(), constantPropagate); |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| } |
| case Expression::Kind::kIndex: { |
| IndexExpression& indexExpr = e->get()->as<IndexExpression>(); |
| |
| this->addExpression(cfg, &indexExpr.base(), constantPropagate); |
| this->addExpression(cfg, &indexExpr.index(), constantPropagate); |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| } |
| case Expression::Kind::kPrefix: { |
| PrefixExpression& p = e->get()->as<PrefixExpression>(); |
| this->addExpression(cfg, &p.operand(), constantPropagate && |
| p.getOperator() != Token::Kind::TK_PLUSPLUS && |
| p.getOperator() != Token::Kind::TK_MINUSMINUS); |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| } |
| case Expression::Kind::kPostfix: |
| this->addExpression(cfg, &e->get()->as<PostfixExpression>().operand(), false); |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| case Expression::Kind::kSwizzle: |
| this->addExpression(cfg, &e->get()->as<Swizzle>().base(), constantPropagate); |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| case Expression::Kind::kBoolLiteral: // fall through |
| case Expression::Kind::kExternalValue: // fall through |
| case Expression::Kind::kFloatLiteral: // fall through |
| case Expression::Kind::kIntLiteral: // fall through |
| case Expression::Kind::kNullLiteral: // fall through |
| case Expression::Kind::kSetting: // fall through |
| case Expression::Kind::kVariableReference: |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| break; |
| case Expression::Kind::kTernary: { |
| TernaryExpression& t = e->get()->as<TernaryExpression>(); |
| this->addExpression(cfg, &t.test(), constantPropagate); |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeExpression(e, constantPropagate)); |
| BlockId start = cfg.fCurrent; |
| cfg.newBlock(); |
| this->addExpression(cfg, &t.ifTrue(), constantPropagate); |
| BlockId next = cfg.newBlock(); |
| cfg.fCurrent = start; |
| cfg.newBlock(); |
| this->addExpression(cfg, &t.ifFalse(), constantPropagate); |
| cfg.addExit(cfg.fCurrent, next); |
| cfg.fCurrent = next; |
| break; |
| } |
| case Expression::Kind::kFunctionReference: // fall through |
| case Expression::Kind::kTypeReference: // fall through |
| case Expression::Kind::kDefined: |
| SkASSERT(false); |
| break; |
| } |
| } |
| |
| // adds expressions that are evaluated as part of resolving an lvalue |
| void CFGGenerator::addLValue(CFG& cfg, std::unique_ptr<Expression>* e) { |
| switch ((*e)->kind()) { |
| case Expression::Kind::kFieldAccess: |
| this->addLValue(cfg, &e->get()->as<FieldAccess>().base()); |
| break; |
| case Expression::Kind::kIndex: { |
| IndexExpression& indexExpr = e->get()->as<IndexExpression>(); |
| this->addLValue(cfg, &indexExpr.base()); |
| this->addExpression(cfg, &indexExpr.index(), /*constantPropagate=*/true); |
| break; |
| } |
| case Expression::Kind::kSwizzle: |
| this->addLValue(cfg, &e->get()->as<Swizzle>().base()); |
| break; |
| case Expression::Kind::kExternalValue: // fall through |
| case Expression::Kind::kVariableReference: |
| break; |
| case Expression::Kind::kTernary: { |
| TernaryExpression& ternary = e->get()->as<TernaryExpression>(); |
| this->addExpression(cfg, &ternary.test(), /*constantPropagate=*/true); |
| // Technically we will of course only evaluate one or the other, but if the test turns |
| // out to be constant, the ternary will get collapsed down to just one branch anyway. So |
| // it should be ok to pretend that we always evaluate both branches here. |
| this->addLValue(cfg, &ternary.ifTrue()); |
| this->addLValue(cfg, &ternary.ifFalse()); |
| break; |
| } |
| default: |
| // not an lvalue, can't happen |
| SkASSERT(false); |
| break; |
| } |
| } |
| |
| static bool is_true(Expression& expr) { |
| return expr.is<BoolLiteral>() && expr.as<BoolLiteral>().value(); |
| } |
| |
| void CFGGenerator::addStatement(CFG& cfg, std::unique_ptr<Statement>* s) { |
| switch ((*s)->kind()) { |
| case Statement::Kind::kBlock: { |
| Block& block = (*s)->as<Block>(); |
| for (std::unique_ptr<Statement>& child : block.children()) { |
| addStatement(cfg, &child); |
| } |
| break; |
| } |
| case Statement::Kind::kIf: { |
| IfStatement& ifs = (*s)->as<IfStatement>(); |
| this->addExpression(cfg, &ifs.test(), /*constantPropagate=*/true); |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeStatement(s)); |
| BlockId start = cfg.fCurrent; |
| cfg.newBlock(); |
| this->addStatement(cfg, &ifs.ifTrue()); |
| BlockId next = cfg.newBlock(); |
| if (ifs.ifFalse()) { |
| cfg.fCurrent = start; |
| cfg.newBlock(); |
| this->addStatement(cfg, &ifs.ifFalse()); |
| cfg.addExit(cfg.fCurrent, next); |
| cfg.fCurrent = next; |
| } else { |
| cfg.addExit(start, next); |
| } |
| break; |
| } |
| case Statement::Kind::kExpression: { |
| this->addExpression(cfg, &(*s)->as<ExpressionStatement>().expression(), |
| /*constantPropagate=*/true); |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeStatement(s)); |
| break; |
| } |
| case Statement::Kind::kVarDeclaration: { |
| VarDeclaration& vd = (*s)->as<VarDeclaration>(); |
| if (vd.value()) { |
| this->addExpression(cfg, &vd.value(), /*constantPropagate=*/true); |
| } |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeStatement(s)); |
| break; |
| } |
| case Statement::Kind::kDiscard: |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeStatement(s)); |
| cfg.fCurrent = cfg.newIsolatedBlock(); |
| break; |
| case Statement::Kind::kReturn: { |
| ReturnStatement& r = (*s)->as<ReturnStatement>(); |
| if (r.expression()) { |
| this->addExpression(cfg, &r.expression(), /*constantPropagate=*/true); |
| } |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeStatement(s)); |
| cfg.fCurrent = cfg.newIsolatedBlock(); |
| break; |
| } |
| case Statement::Kind::kBreak: |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeStatement(s)); |
| cfg.addExit(cfg.fCurrent, fLoopExits.top()); |
| cfg.fCurrent = cfg.newIsolatedBlock(); |
| break; |
| case Statement::Kind::kContinue: |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeStatement(s)); |
| cfg.addExit(cfg.fCurrent, fLoopContinues.top()); |
| cfg.fCurrent = cfg.newIsolatedBlock(); |
| break; |
| case Statement::Kind::kWhile: { |
| WhileStatement& w = (*s)->as<WhileStatement>(); |
| BlockId loopStart = cfg.newBlock(); |
| fLoopContinues.push(loopStart); |
| BlockId loopExit = cfg.newIsolatedBlock(); |
| fLoopExits.push(loopExit); |
| this->addExpression(cfg, &w.test(), /*constantPropagate=*/true); |
| BlockId test = cfg.fCurrent; |
| if (!is_true(*w.test())) { |
| cfg.addExit(test, loopExit); |
| } |
| cfg.newBlock(); |
| this->addStatement(cfg, &w.statement()); |
| cfg.addExit(cfg.fCurrent, loopStart); |
| fLoopContinues.pop(); |
| fLoopExits.pop(); |
| cfg.fCurrent = loopExit; |
| break; |
| } |
| case Statement::Kind::kDo: { |
| DoStatement& d = (*s)->as<DoStatement>(); |
| BlockId loopStart = cfg.newBlock(); |
| fLoopContinues.push(loopStart); |
| BlockId loopExit = cfg.newIsolatedBlock(); |
| fLoopExits.push(loopExit); |
| this->addStatement(cfg, &d.statement()); |
| this->addExpression(cfg, &d.test(), /*constantPropagate=*/true); |
| cfg.addExit(cfg.fCurrent, loopExit); |
| cfg.addExit(cfg.fCurrent, loopStart); |
| fLoopContinues.pop(); |
| fLoopExits.pop(); |
| cfg.fCurrent = loopExit; |
| break; |
| } |
| case Statement::Kind::kFor: { |
| ForStatement& f = (*s)->as<ForStatement>(); |
| if (f.initializer()) { |
| this->addStatement(cfg, &f.initializer()); |
| } |
| BlockId loopStart = cfg.newBlock(); |
| BlockId next = cfg.newIsolatedBlock(); |
| fLoopContinues.push(next); |
| BlockId loopExit = cfg.newIsolatedBlock(); |
| fLoopExits.push(loopExit); |
| if (f.test()) { |
| this->addExpression(cfg, &f.test(), /*constantPropagate=*/true); |
| // this isn't quite right; we should have an exit from here to the loop exit, and |
| // remove the exit from the loop body to the loop exit. Structuring it like this |
| // forces the optimizer to believe that the loop body is always executed at least |
| // once. While not strictly correct, this avoids incorrect "variable not assigned" |
| // errors on variables which are assigned within the loop. The correct solution to |
| // this is to analyze the loop to see whether or not at least one iteration is |
| // guaranteed to happen, but for the time being we take the easy way out. |
| } |
| cfg.newBlock(); |
| this->addStatement(cfg, &f.statement()); |
| cfg.addExit(cfg.fCurrent, next); |
| cfg.fCurrent = next; |
| if (f.next()) { |
| this->addExpression(cfg, &f.next(), /*constantPropagate=*/true); |
| } |
| cfg.addExit(cfg.fCurrent, loopStart); |
| cfg.addExit(cfg.fCurrent, loopExit); |
| fLoopContinues.pop(); |
| fLoopExits.pop(); |
| cfg.fCurrent = loopExit; |
| break; |
| } |
| case Statement::Kind::kSwitch: { |
| SwitchStatement& ss = (*s)->as<SwitchStatement>(); |
| this->addExpression(cfg, &ss.fValue, /*constantPropagate=*/true); |
| cfg.currentBlock().fNodes.push_back(BasicBlock::MakeStatement(s)); |
| BlockId start = cfg.fCurrent; |
| BlockId switchExit = cfg.newIsolatedBlock(); |
| fLoopExits.push(switchExit); |
| for (const auto& c : ss.fCases) { |
| cfg.newBlock(); |
| cfg.addExit(start, cfg.fCurrent); |
| if (c->fValue) { |
| // technically this should go in the start block, but it doesn't actually matter |
| // because it must be constant. Not worth running two loops for. |
| this->addExpression(cfg, &c->fValue, /*constantPropagate=*/true); |
| } |
| for (auto& caseStatement : c->fStatements) { |
| this->addStatement(cfg, &caseStatement); |
| } |
| } |
| cfg.addExit(cfg.fCurrent, switchExit); |
| // note that unlike GLSL, our grammar requires the default case to be last |
| if (ss.fCases.empty() || ss.fCases.back()->fValue) { |
| // switch does not have a default clause, mark that it can skip straight to the end |
| cfg.addExit(start, switchExit); |
| } |
| fLoopExits.pop(); |
| cfg.fCurrent = switchExit; |
| break; |
| } |
| case Statement::Kind::kInlineMarker: |
| case Statement::Kind::kNop: |
| break; |
| default: |
| #ifdef SK_DEBUG |
| ABORT("unsupported statement: %s\n", (*s)->description().c_str()); |
| #endif |
| break; |
| } |
| } |
| |
| CFG CFGGenerator::getCFG(FunctionDefinition& f) { |
| CFG result; |
| result.fStart = result.newBlock(); |
| result.fCurrent = result.fStart; |
| this->addStatement(result, &f.body()); |
| result.newBlock(); |
| result.fExit = result.fCurrent; |
| return result; |
| } |
| |
| } // namespace SkSL |