/*
|
* 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/SkSLSwitchStatement.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++) {
|
BasicBlock::Node& n = fBlocks[i].fNodes[j];
|
printf("Node %d (%p): %s\n", (int) j, &n, n.fKind == BasicBlock::Node::kExpression_Kind
|
? (*n.expression())->description().c_str()
|
: (*n.statement())->description().c_str());
|
}
|
printf("Exits: ");
|
separator = "";
|
for (BlockId b : fBlocks[i].fExits) {
|
printf("%s%d", separator, (int) b);
|
separator = ", ";
|
}
|
printf("\n\n");
|
}
|
}
|
|
bool BasicBlock::tryRemoveExpressionBefore(std::vector<BasicBlock::Node>::iterator* iter,
|
Expression* e) {
|
if (e->fKind == Expression::kTernary_Kind) {
|
return false;
|
}
|
bool result;
|
if ((*iter)->fKind == BasicBlock::Node::kExpression_Kind) {
|
SkASSERT((*iter)->expression()->get() != e);
|
Expression* old = (*iter)->expression()->get();
|
do {
|
if ((*iter) == fNodes.begin()) {
|
return false;
|
}
|
--(*iter);
|
} while ((*iter)->fKind != BasicBlock::Node::kExpression_Kind ||
|
(*iter)->expression()->get() != e);
|
result = this->tryRemoveExpression(iter);
|
while ((*iter)->fKind != BasicBlock::Node::kExpression_Kind ||
|
(*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)->fKind != BasicBlock::Node::kExpression_Kind ||
|
(*iter)->expression()->get() != e);
|
result = this->tryRemoveExpression(iter);
|
while ((*iter)->fKind != BasicBlock::Node::kStatement_Kind ||
|
(*iter)->statement()->get() != old) {
|
SkASSERT(*iter != fNodes.end());
|
++(*iter);
|
}
|
}
|
return result;
|
}
|
|
bool BasicBlock::tryRemoveLValueBefore(std::vector<BasicBlock::Node>::iterator* iter,
|
Expression* lvalue) {
|
switch (lvalue->fKind) {
|
case Expression::kVariableReference_Kind:
|
return true;
|
case Expression::kSwizzle_Kind:
|
return this->tryRemoveLValueBefore(iter, ((Swizzle*) lvalue)->fBase.get());
|
case Expression::kFieldAccess_Kind:
|
return this->tryRemoveLValueBefore(iter, ((FieldAccess*) lvalue)->fBase.get());
|
case Expression::kIndex_Kind:
|
if (!this->tryRemoveLValueBefore(iter, ((IndexExpression*) lvalue)->fBase.get())) {
|
return false;
|
}
|
return this->tryRemoveExpressionBefore(iter, ((IndexExpression*) lvalue)->fIndex.get());
|
case Expression::kTernary_Kind:
|
if (!this->tryRemoveExpressionBefore(iter,
|
((TernaryExpression*) lvalue)->fTest.get())) {
|
return false;
|
}
|
if (!this->tryRemoveLValueBefore(iter, ((TernaryExpression*) lvalue)->fIfTrue.get())) {
|
return false;
|
}
|
return this->tryRemoveLValueBefore(iter, ((TernaryExpression*) lvalue)->fIfFalse.get());
|
default:
|
ABORT("invalid lvalue: %s\n", lvalue->description().c_str());
|
}
|
}
|
|
bool BasicBlock::tryRemoveExpression(std::vector<BasicBlock::Node>::iterator* iter) {
|
Expression* expr = (*iter)->expression()->get();
|
switch (expr->fKind) {
|
case Expression::kBinary_Kind: {
|
BinaryExpression* b = (BinaryExpression*) expr;
|
if (b->fOperator == Token::EQ) {
|
if (!this->tryRemoveLValueBefore(iter, b->fLeft.get())) {
|
return false;
|
}
|
} else if (!this->tryRemoveExpressionBefore(iter, b->fLeft.get())) {
|
return false;
|
}
|
if (!this->tryRemoveExpressionBefore(iter, b->fRight.get())) {
|
return false;
|
}
|
SkASSERT((*iter)->expression()->get() == expr);
|
*iter = fNodes.erase(*iter);
|
return true;
|
}
|
case Expression::kTernary_Kind: {
|
// ternaries cross basic block boundaries, must regenerate the CFG to remove it
|
return false;
|
}
|
case Expression::kFieldAccess_Kind: {
|
FieldAccess* f = (FieldAccess*) expr;
|
if (!this->tryRemoveExpressionBefore(iter, f->fBase.get())) {
|
return false;
|
}
|
*iter = fNodes.erase(*iter);
|
return true;
|
}
|
case Expression::kSwizzle_Kind: {
|
Swizzle* s = (Swizzle*) expr;
|
if (!this->tryRemoveExpressionBefore(iter, s->fBase.get())) {
|
return false;
|
}
|
*iter = fNodes.erase(*iter);
|
return true;
|
}
|
case Expression::kIndex_Kind: {
|
IndexExpression* idx = (IndexExpression*) expr;
|
if (!this->tryRemoveExpressionBefore(iter, idx->fBase.get())) {
|
return false;
|
}
|
if (!this->tryRemoveExpressionBefore(iter, idx->fIndex.get())) {
|
return false;
|
}
|
*iter = fNodes.erase(*iter);
|
return true;
|
}
|
case Expression::kConstructor_Kind: {
|
Constructor* c = (Constructor*) expr;
|
for (auto& arg : c->fArguments) {
|
if (!this->tryRemoveExpressionBefore(iter, arg.get())) {
|
return false;
|
}
|
SkASSERT((*iter)->expression()->get() == expr);
|
}
|
*iter = fNodes.erase(*iter);
|
return true;
|
}
|
case Expression::kFunctionCall_Kind: {
|
FunctionCall* f = (FunctionCall*) expr;
|
for (auto& arg : f->fArguments) {
|
if (!this->tryRemoveExpressionBefore(iter, arg.get())) {
|
return false;
|
}
|
SkASSERT((*iter)->expression()->get() == expr);
|
}
|
*iter = fNodes.erase(*iter);
|
return true;
|
}
|
case Expression::kPrefix_Kind:
|
if (!this->tryRemoveExpressionBefore(iter,
|
((PrefixExpression*) expr)->fOperand.get())) {
|
return false;
|
}
|
*iter = fNodes.erase(*iter);
|
return true;
|
case Expression::kPostfix_Kind:
|
if (!this->tryRemoveExpressionBefore(iter,
|
((PrefixExpression*) expr)->fOperand.get())) {
|
return false;
|
}
|
*iter = fNodes.erase(*iter);
|
return true;
|
case Expression::kBoolLiteral_Kind: // fall through
|
case Expression::kFloatLiteral_Kind: // fall through
|
case Expression::kIntLiteral_Kind: // fall through
|
case Expression::kSetting_Kind: // fall through
|
case Expression::kVariableReference_Kind:
|
*iter = fNodes.erase(*iter);
|
return true;
|
default:
|
ABORT("unhandled expression: %s\n", expr->description().c_str());
|
}
|
}
|
|
bool BasicBlock::tryInsertExpression(std::vector<BasicBlock::Node>::iterator* iter,
|
std::unique_ptr<Expression>* expr) {
|
switch ((*expr)->fKind) {
|
case Expression::kBinary_Kind: {
|
BinaryExpression* b = (BinaryExpression*) expr->get();
|
if (!this->tryInsertExpression(iter, &b->fRight)) {
|
return false;
|
}
|
++(*iter);
|
if (!this->tryInsertExpression(iter, &b->fLeft)) {
|
return false;
|
}
|
++(*iter);
|
BasicBlock::Node node = { BasicBlock::Node::kExpression_Kind, true, expr, nullptr };
|
*iter = fNodes.insert(*iter, node);
|
return true;
|
}
|
case Expression::kBoolLiteral_Kind: // fall through
|
case Expression::kFloatLiteral_Kind: // fall through
|
case Expression::kIntLiteral_Kind: // fall through
|
case Expression::kVariableReference_Kind: {
|
BasicBlock::Node node = { BasicBlock::Node::kExpression_Kind, true, expr, nullptr };
|
*iter = fNodes.insert(*iter, node);
|
return true;
|
}
|
case Expression::kConstructor_Kind: {
|
Constructor* c = (Constructor*) expr->get();
|
for (auto& arg : c->fArguments) {
|
if (!this->tryInsertExpression(iter, &arg)) {
|
return false;
|
}
|
++(*iter);
|
}
|
BasicBlock::Node node = { BasicBlock::Node::kExpression_Kind, true, expr, nullptr };
|
*iter = fNodes.insert(*iter, node);
|
return true;
|
}
|
default:
|
return false;
|
}
|
}
|
|
void CFGGenerator::addExpression(CFG& cfg, std::unique_ptr<Expression>* e, bool constantPropagate) {
|
SkASSERT(e);
|
switch ((*e)->fKind) {
|
case Expression::kBinary_Kind: {
|
BinaryExpression* b = (BinaryExpression*) e->get();
|
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, constantPropagate);
|
BlockId start = cfg.fCurrent;
|
cfg.newBlock();
|
this->addExpression(cfg, &b->fRight, constantPropagate);
|
cfg.newBlock();
|
cfg.addExit(start, cfg.fCurrent);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({
|
BasicBlock::Node::kExpression_Kind,
|
constantPropagate,
|
e,
|
nullptr
|
});
|
break;
|
}
|
case Token::EQ: {
|
this->addExpression(cfg, &b->fRight, constantPropagate);
|
this->addLValue(cfg, &b->fLeft);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({
|
BasicBlock::Node::kExpression_Kind,
|
constantPropagate,
|
e,
|
nullptr
|
});
|
break;
|
}
|
default:
|
this->addExpression(cfg, &b->fLeft, !Compiler::IsAssignment(b->fOperator));
|
this->addExpression(cfg, &b->fRight, constantPropagate);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({
|
BasicBlock::Node::kExpression_Kind,
|
constantPropagate,
|
e,
|
nullptr
|
});
|
}
|
break;
|
}
|
case Expression::kConstructor_Kind: {
|
Constructor* c = (Constructor*) e->get();
|
for (auto& arg : c->fArguments) {
|
this->addExpression(cfg, &arg, constantPropagate);
|
}
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
|
constantPropagate, e, nullptr });
|
break;
|
}
|
case Expression::kFunctionCall_Kind: {
|
FunctionCall* c = (FunctionCall*) e->get();
|
for (auto& arg : c->fArguments) {
|
this->addExpression(cfg, &arg, constantPropagate);
|
}
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
|
constantPropagate, e, nullptr });
|
break;
|
}
|
case Expression::kFieldAccess_Kind:
|
this->addExpression(cfg, &((FieldAccess*) e->get())->fBase, constantPropagate);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
|
constantPropagate, e, nullptr });
|
break;
|
case Expression::kIndex_Kind:
|
this->addExpression(cfg, &((IndexExpression*) e->get())->fBase, constantPropagate);
|
this->addExpression(cfg, &((IndexExpression*) e->get())->fIndex, constantPropagate);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
|
constantPropagate, e, nullptr });
|
break;
|
case Expression::kPrefix_Kind: {
|
PrefixExpression* p = (PrefixExpression*) e->get();
|
this->addExpression(cfg, &p->fOperand, constantPropagate &&
|
p->fOperator != Token::PLUSPLUS &&
|
p->fOperator != Token::MINUSMINUS);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
|
constantPropagate, e, nullptr });
|
break;
|
}
|
case Expression::kPostfix_Kind:
|
this->addExpression(cfg, &((PostfixExpression*) e->get())->fOperand, false);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
|
constantPropagate, e, nullptr });
|
break;
|
case Expression::kSwizzle_Kind:
|
this->addExpression(cfg, &((Swizzle*) e->get())->fBase, constantPropagate);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
|
constantPropagate, e, nullptr });
|
break;
|
case Expression::kAppendStage_Kind: // fall through
|
case Expression::kBoolLiteral_Kind: // fall through
|
case Expression::kFloatLiteral_Kind: // fall through
|
case Expression::kIntLiteral_Kind: // fall through
|
case Expression::kNullLiteral_Kind: // fall through
|
case Expression::kSetting_Kind: // fall through
|
case Expression::kVariableReference_Kind:
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
|
constantPropagate, e, nullptr });
|
break;
|
case Expression::kTernary_Kind: {
|
TernaryExpression* t = (TernaryExpression*) e->get();
|
this->addExpression(cfg, &t->fTest, constantPropagate);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
|
constantPropagate, e, nullptr });
|
BlockId start = cfg.fCurrent;
|
cfg.newBlock();
|
this->addExpression(cfg, &t->fIfTrue, constantPropagate);
|
BlockId next = cfg.newBlock();
|
cfg.fCurrent = start;
|
cfg.newBlock();
|
this->addExpression(cfg, &t->fIfFalse, constantPropagate);
|
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:
|
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)->fKind) {
|
case Expression::kFieldAccess_Kind:
|
this->addLValue(cfg, &((FieldAccess&) **e).fBase);
|
break;
|
case Expression::kIndex_Kind:
|
this->addLValue(cfg, &((IndexExpression&) **e).fBase);
|
this->addExpression(cfg, &((IndexExpression&) **e).fIndex, true);
|
break;
|
case Expression::kSwizzle_Kind:
|
this->addLValue(cfg, &((Swizzle&) **e).fBase);
|
break;
|
case Expression::kVariableReference_Kind:
|
break;
|
case Expression::kTernary_Kind:
|
this->addExpression(cfg, &((TernaryExpression&) **e).fTest, 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, &((TernaryExpression&) **e).fIfTrue);
|
this->addLValue(cfg, &((TernaryExpression&) **e).fIfFalse);
|
break;
|
default:
|
// not an lvalue, can't happen
|
SkASSERT(false);
|
break;
|
}
|
}
|
|
void CFGGenerator::addStatement(CFG& cfg, std::unique_ptr<Statement>* s) {
|
switch ((*s)->fKind) {
|
case Statement::kBlock_Kind:
|
for (auto& child : ((Block&) **s).fStatements) {
|
addStatement(cfg, &child);
|
}
|
break;
|
case Statement::kIf_Kind: {
|
IfStatement& ifs = (IfStatement&) **s;
|
this->addExpression(cfg, &ifs.fTest, true);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
|
nullptr, s });
|
BlockId start = cfg.fCurrent;
|
cfg.newBlock();
|
this->addStatement(cfg, &ifs.fIfTrue);
|
BlockId next = cfg.newBlock();
|
if (ifs.fIfFalse) {
|
cfg.fCurrent = start;
|
cfg.newBlock();
|
this->addStatement(cfg, &ifs.fIfFalse);
|
cfg.addExit(cfg.fCurrent, next);
|
cfg.fCurrent = next;
|
} else {
|
cfg.addExit(start, next);
|
}
|
break;
|
}
|
case Statement::kExpression_Kind: {
|
this->addExpression(cfg, &((ExpressionStatement&) **s).fExpression, true);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
|
nullptr, s });
|
break;
|
}
|
case Statement::kVarDeclarations_Kind: {
|
VarDeclarationsStatement& decls = ((VarDeclarationsStatement&) **s);
|
for (auto& stmt : decls.fDeclaration->fVars) {
|
if (stmt->fKind == Statement::kNop_Kind) {
|
continue;
|
}
|
VarDeclaration& vd = (VarDeclaration&) *stmt;
|
if (vd.fValue) {
|
this->addExpression(cfg, &vd.fValue, true);
|
}
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind,
|
false, nullptr, &stmt });
|
}
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
|
nullptr, s });
|
break;
|
}
|
case Statement::kDiscard_Kind:
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
|
nullptr, s });
|
cfg.fCurrent = cfg.newIsolatedBlock();
|
break;
|
case Statement::kReturn_Kind: {
|
ReturnStatement& r = ((ReturnStatement&) **s);
|
if (r.fExpression) {
|
this->addExpression(cfg, &r.fExpression, true);
|
}
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
|
nullptr, s });
|
cfg.fCurrent = cfg.newIsolatedBlock();
|
break;
|
}
|
case Statement::kBreak_Kind:
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
|
nullptr, 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, false,
|
nullptr, s });
|
cfg.addExit(cfg.fCurrent, fLoopContinues.top());
|
cfg.fCurrent = cfg.newIsolatedBlock();
|
break;
|
case Statement::kWhile_Kind: {
|
WhileStatement& w = (WhileStatement&) **s;
|
BlockId loopStart = cfg.newBlock();
|
fLoopContinues.push(loopStart);
|
BlockId loopExit = cfg.newIsolatedBlock();
|
fLoopExits.push(loopExit);
|
this->addExpression(cfg, &w.fTest, true);
|
BlockId test = cfg.fCurrent;
|
cfg.addExit(test, loopExit);
|
cfg.newBlock();
|
this->addStatement(cfg, &w.fStatement);
|
cfg.addExit(cfg.fCurrent, loopStart);
|
fLoopContinues.pop();
|
fLoopExits.pop();
|
cfg.fCurrent = loopExit;
|
break;
|
}
|
case Statement::kDo_Kind: {
|
DoStatement& d = (DoStatement&) **s;
|
BlockId loopStart = cfg.newBlock();
|
fLoopContinues.push(loopStart);
|
BlockId loopExit = cfg.newIsolatedBlock();
|
fLoopExits.push(loopExit);
|
this->addStatement(cfg, &d.fStatement);
|
this->addExpression(cfg, &d.fTest, true);
|
cfg.addExit(cfg.fCurrent, loopExit);
|
cfg.addExit(cfg.fCurrent, loopStart);
|
fLoopContinues.pop();
|
fLoopExits.pop();
|
cfg.fCurrent = loopExit;
|
break;
|
}
|
case Statement::kFor_Kind: {
|
ForStatement& f = (ForStatement&) **s;
|
if (f.fInitializer) {
|
this->addStatement(cfg, &f.fInitializer);
|
}
|
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, 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.fStatement);
|
cfg.addExit(cfg.fCurrent, next);
|
cfg.fCurrent = next;
|
if (f.fNext) {
|
this->addExpression(cfg, &f.fNext, true);
|
}
|
cfg.addExit(cfg.fCurrent, loopStart);
|
cfg.addExit(cfg.fCurrent, loopExit);
|
fLoopContinues.pop();
|
fLoopExits.pop();
|
cfg.fCurrent = loopExit;
|
break;
|
}
|
case Statement::kSwitch_Kind: {
|
SwitchStatement& ss = (SwitchStatement&) **s;
|
this->addExpression(cfg, &ss.fValue, true);
|
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
|
nullptr, 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, 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 (0 == ss.fCases.size() || ss.fCases[ss.fCases.size() - 1]->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::kNop_Kind:
|
break;
|
default:
|
printf("statement: %s\n", (*s)->description().c_str());
|
ABORT("unsupported statement kind");
|
}
|
}
|
|
CFG CFGGenerator::getCFG(FunctionDefinition& f) {
|
CFG result;
|
result.fStart = result.newBlock();
|
result.fCurrent = result.fStart;
|
this->addStatement(result, &f.fBody);
|
result.newBlock();
|
result.fExit = result.fCurrent;
|
return result;
|
}
|
|
} // namespace
|