Improved skslc optimizer, particularly around vectors.

BUG=skia:

Change-Id: Idb364d9198f2ff84aad1eb68e236fb45ec1c86b7
Reviewed-on: https://skia-review.googlesource.com/8000
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Ben Wagner <benjaminwagner@google.com>
This commit is contained in:
Ethan Nicholas 2017-04-20 19:31:52 -04:00 committed by Skia Commit-Bot
parent e79b473714
commit cb67096b61
46 changed files with 1533 additions and 527 deletions

View File

@ -56,7 +56,7 @@ void CFG::dump() {
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>");
iter->second ? (*iter->second)->description().c_str() : "<undefined>");
separator = ", ";
}
printf("\nEntrances: ");
@ -68,9 +68,9 @@ void CFG::dump() {
printf("\n");
for (size_t j = 0; j < fBlocks[i].fNodes.size(); j++) {
BasicBlock::Node& n = fBlocks[i].fNodes[j];
printf("Node %d: %s\n", (int) j, n.fKind == BasicBlock::Node::kExpression_Kind
? (*n.fExpression)->description().c_str()
: n.fStatement->description().c_str());
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 = "";
@ -82,6 +82,207 @@ void CFG::dump() {
}
}
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) {
ASSERT((*iter)->expression()->get() != e);
Expression* old = (*iter)->expression()->get();
do {
if ((*iter) == fNodes.begin()) {
ABORT("couldn't find %s before %s\n", e->description().c_str(),
old->description().c_str());
}
--(*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) {
ASSERT(*iter != fNodes.end());
++(*iter);
}
} else {
Statement* old = (*iter)->statement()->get();
do {
if ((*iter) == fNodes.begin()) {
ABORT("couldn't find %s before %s\n", e->description().c_str(),
old->description().c_str());
}
--(*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) {
ASSERT(*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());
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;
}
ASSERT((*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;
}
ASSERT((*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;
}
ASSERT((*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::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) {
ASSERT(e);
switch ((*e)->fKind) {
@ -182,6 +383,8 @@ void CFGGenerator::addExpression(CFG& cfg, std::unique_ptr<Expression>* e, bool
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);
@ -223,24 +426,26 @@ void CFGGenerator::addLValue(CFG& cfg, std::unique_ptr<Expression>* e) {
}
}
void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
switch (s->fKind) {
void CFGGenerator::addStatement(CFG& cfg, std::unique_ptr<Statement>* s) {
switch ((*s)->fKind) {
case Statement::kBlock_Kind:
for (const auto& child : ((const Block*) s)->fStatements) {
addStatement(cfg, child.get());
for (auto& child : ((Block&) **s).fStatements) {
addStatement(cfg, &child);
}
break;
case Statement::kIf_Kind: {
IfStatement* ifs = (IfStatement*) s;
this->addExpression(cfg, &ifs->fTest, true);
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.get());
this->addStatement(cfg, &ifs.fIfTrue);
BlockId next = cfg.newBlock();
if (ifs->fIfFalse) {
if (ifs.fIfFalse) {
cfg.fCurrent = start;
cfg.newBlock();
this->addStatement(cfg, ifs->fIfFalse.get());
this->addStatement(cfg, &ifs.fIfFalse);
cfg.addExit(cfg.fCurrent, next);
cfg.fCurrent = next;
} else {
@ -249,14 +454,16 @@ void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
break;
}
case Statement::kExpression_Kind: {
this->addExpression(cfg, &((ExpressionStatement&) *s).fExpression, true);
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);
VarDeclarationsStatement& decls = ((VarDeclarationsStatement&) **s);
for (auto& vd : decls.fDeclaration->fVars) {
if (vd.fValue) {
this->addExpression(cfg, &vd.fValue, true);
if (vd->fValue) {
this->addExpression(cfg, &vd->fValue, true);
}
}
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kStatement_Kind, false,
@ -269,7 +476,7 @@ void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
cfg.fCurrent = cfg.newIsolatedBlock();
break;
case Statement::kReturn_Kind: {
ReturnStatement& r = ((ReturnStatement&) *s);
ReturnStatement& r = ((ReturnStatement&) **s);
if (r.fExpression) {
this->addExpression(cfg, &r.fExpression, true);
}
@ -291,16 +498,16 @@ void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
cfg.fCurrent = cfg.newIsolatedBlock();
break;
case Statement::kWhile_Kind: {
WhileStatement* w = (WhileStatement*) s;
WhileStatement& w = (WhileStatement&) **s;
BlockId loopStart = cfg.newBlock();
fLoopContinues.push(loopStart);
BlockId loopExit = cfg.newIsolatedBlock();
fLoopExits.push(loopExit);
this->addExpression(cfg, &w->fTest, true);
this->addExpression(cfg, &w.fTest, true);
BlockId test = cfg.fCurrent;
cfg.addExit(test, loopExit);
cfg.newBlock();
this->addStatement(cfg, w->fStatement.get());
this->addStatement(cfg, &w.fStatement);
cfg.addExit(cfg.fCurrent, loopStart);
fLoopContinues.pop();
fLoopExits.pop();
@ -308,13 +515,13 @@ void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
break;
}
case Statement::kDo_Kind: {
DoStatement* d = (DoStatement*) s;
DoStatement& d = (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, true);
this->addStatement(cfg, &d.fStatement);
this->addExpression(cfg, &d.fTest, true);
cfg.addExit(cfg.fCurrent, loopExit);
cfg.addExit(cfg.fCurrent, loopStart);
fLoopContinues.pop();
@ -323,26 +530,26 @@ void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
break;
}
case Statement::kFor_Kind: {
ForStatement* f = (ForStatement*) s;
if (f->fInitializer) {
this->addStatement(cfg, f->fInitializer.get());
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);
if (f.fTest) {
this->addExpression(cfg, &f.fTest, true);
BlockId test = cfg.fCurrent;
cfg.addExit(test, loopExit);
}
cfg.newBlock();
this->addStatement(cfg, f->fStatement.get());
this->addStatement(cfg, &f.fStatement);
cfg.addExit(cfg.fCurrent, next);
cfg.fCurrent = next;
if (f->fNext) {
this->addExpression(cfg, &f->fNext, true);
if (f.fNext) {
this->addExpression(cfg, &f.fNext, true);
}
cfg.addExit(cfg.fCurrent, loopStart);
fLoopContinues.pop();
@ -351,12 +558,12 @@ void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
break;
}
case Statement::kSwitch_Kind: {
SwitchStatement* ss = (SwitchStatement*) s;
this->addExpression(cfg, &ss->fValue, true);
SwitchStatement& ss = (SwitchStatement&) **s;
this->addExpression(cfg, &ss.fValue, true);
BlockId start = cfg.fCurrent;
BlockId switchExit = cfg.newIsolatedBlock();
fLoopExits.push(switchExit);
for (const auto& c : ss->fCases) {
for (const auto& c : ss.fCases) {
cfg.newBlock();
cfg.addExit(start, cfg.fCurrent);
if (c->fValue) {
@ -364,13 +571,13 @@ void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
// because it must be constant. Not worth running two loops for.
this->addExpression(cfg, &c->fValue, true);
}
for (const auto& caseStatement : c->fStatements) {
this->addStatement(cfg, caseStatement.get());
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) {
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);
}
@ -378,17 +585,19 @@ void CFGGenerator::addStatement(CFG& cfg, const Statement* s) {
cfg.fCurrent = switchExit;
break;
}
case Statement::kNop_Kind:
break;
default:
printf("statement: %s\n", s->description().c_str());
printf("statement: %s\n", (*s)->description().c_str());
ABORT("unsupported statement kind");
}
}
CFG CFGGenerator::getCFG(const FunctionDefinition& f) {
CFG CFGGenerator::getCFG(FunctionDefinition& f) {
CFG result;
result.fStart = result.newBlock();
result.fCurrent = result.fStart;
this->addStatement(result, f.fBody.get());
this->addStatement(result, &f.fBody);
result.newBlock();
result.fExit = result.fCurrent;
return result;

View File

@ -26,6 +26,42 @@ struct BasicBlock {
kExpression_Kind
};
Node(Kind kind, bool constantPropagation, std::unique_ptr<Expression>* expression,
std::unique_ptr<Statement>* statement)
: fKind(kind)
, fConstantPropagation(constantPropagation)
, fExpression(expression)
, fStatement(statement) {}
std::unique_ptr<Expression>* expression() const {
ASSERT(fKind == kExpression_Kind);
return fExpression;
}
void setExpression(std::unique_ptr<Expression> expr) {
ASSERT(fKind == kExpression_Kind);
*fExpression = std::move(expr);
}
std::unique_ptr<Statement>* statement() const {
ASSERT(fKind == kStatement_Kind);
return fStatement;
}
void setStatement(std::unique_ptr<Statement> stmt) {
ASSERT(fKind == kStatement_Kind);
*fStatement = std::move(stmt);
}
String description() const {
if (fKind == kStatement_Kind) {
return (*fStatement)->description();
} else {
ASSERT(fKind == kExpression_Kind);
return (*fExpression)->description();
}
}
Kind fKind;
// if false, this node should not be subject to constant propagation. This happens with
// compound assignment (i.e. x *= 2), in which the value x is used as an rvalue for
@ -35,10 +71,45 @@ struct BasicBlock {
// assignment if the target is constant (i.e. x = 1; x *= 2; should become x = 1; x = 1 * 2;
// and then collapse down to a simple x = 2;).
bool fConstantPropagation;
private:
// we store pointers to the unique_ptrs so that we can replace expressions or statements
// during optimization without having to regenerate the entire tree
std::unique_ptr<Expression>* fExpression;
const Statement* fStatement;
std::unique_ptr<Statement>* fStatement;
};
/**
* Attempts to remove the expression (and its subexpressions) pointed to by the iterator. If the
* expression can be cleanly removed, returns true and updates the iterator to point to the
* expression after the deleted expression. Otherwise returns false (and the CFG will need to be
* regenerated).
*/
bool tryRemoveExpression(std::vector<BasicBlock::Node>::iterator* iter);
/**
* Locates and attempts remove an expression occurring before the expression pointed to by iter.
* If the expression can be cleanly removed, returns true and resets iter to a valid iterator
* pointing to the same expression it did initially. Otherwise returns false (and the CFG will
* need to be regenerated).
*/
bool tryRemoveExpressionBefore(std::vector<BasicBlock::Node>::iterator* iter, Expression* e);
/**
* As tryRemoveExpressionBefore, but for lvalues. As lvalues are at most partially evaluated
* (for instance, x[i] = 0 evaluates i but not x) this will only look for the parts of the
* lvalue that are actually evaluated.
*/
bool tryRemoveLValueBefore(std::vector<BasicBlock::Node>::iterator* iter, Expression* lvalue);
/**
* Attempts to inserts a new expression before the node pointed to by iter. If the
* expression can be cleanly inserted, returns true and updates the iterator to point to the
* newly inserted expression. Otherwise returns false (and the CFG will need to be regenerated).
*/
bool tryInsertExpression(std::vector<BasicBlock::Node>::iterator* iter,
std::unique_ptr<Expression>* expr);
std::vector<Node> fNodes;
std::set<BlockId> fEntrances;
std::set<BlockId> fExits;
@ -81,10 +152,10 @@ class CFGGenerator {
public:
CFGGenerator() {}
CFG getCFG(const FunctionDefinition& f);
CFG getCFG(FunctionDefinition& f);
private:
void addStatement(CFG& cfg, const Statement* s);
void addStatement(CFG& cfg, std::unique_ptr<Statement>* s);
void addExpression(CFG& cfg, std::unique_ptr<Expression>* e, bool constantPropagate);

View File

@ -14,9 +14,12 @@
#include "SkSLParser.h"
#include "SkSLSPIRVCodeGenerator.h"
#include "ir/SkSLExpression.h"
#include "ir/SkSLExpressionStatement.h"
#include "ir/SkSLIntLiteral.h"
#include "ir/SkSLModifiersDeclaration.h"
#include "ir/SkSLNop.h"
#include "ir/SkSLSymbolTable.h"
#include "ir/SkSLTernaryExpression.h"
#include "ir/SkSLUnresolvedFunction.h"
#include "ir/SkSLVarDeclarations.h"
@ -207,8 +210,8 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
DefinitionMap* definitions) {
switch (node.fKind) {
case BasicBlock::Node::kExpression_Kind: {
ASSERT(node.fExpression);
const Expression* expr = (Expression*) node.fExpression->get();
ASSERT(node.expression());
const Expression* expr = (Expression*) node.expression()->get();
switch (expr->fKind) {
case Expression::kBinary_Kind: {
BinaryExpression* b = (BinaryExpression*) expr;
@ -240,22 +243,30 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
p->fOperand.get(),
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
definitions);
}
break;
}
case Expression::kVariableReference_Kind: {
const VariableReference* v = (VariableReference*) expr;
if (v->fRefKind != VariableReference::kRead_RefKind) {
this->addDefinition(
v,
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
definitions);
}
}
default:
break;
}
break;
}
case BasicBlock::Node::kStatement_Kind: {
const Statement* stmt = (Statement*) node.fStatement;
const Statement* stmt = (Statement*) node.statement()->get();
if (stmt->fKind == Statement::kVarDeclarations_Kind) {
VarDeclarationsStatement* vd = (VarDeclarationsStatement*) stmt;
for (VarDeclaration& decl : vd->fDeclaration->fVars) {
if (decl.fValue) {
(*definitions)[decl.fVar] = &decl.fValue;
for (const auto& decl : vd->fDeclaration->fVars) {
if (decl->fValue) {
(*definitions)[decl->fVar] = &decl->fValue;
}
}
}
@ -308,12 +319,12 @@ static DefinitionMap compute_start_state(const CFG& cfg) {
for (const auto& block : cfg.fBlocks) {
for (const auto& node : block.fNodes) {
if (node.fKind == BasicBlock::Node::kStatement_Kind) {
ASSERT(node.fStatement);
const Statement* s = node.fStatement;
ASSERT(node.statement());
const Statement* s = node.statement()->get();
if (s->fKind == Statement::kVarDeclarations_Kind) {
const VarDeclarationsStatement* vd = (const VarDeclarationsStatement*) s;
for (const VarDeclaration& decl : vd->fDeclaration->fVars) {
result[decl.fVar] = nullptr;
for (const auto& decl : vd->fDeclaration->fVars) {
result[decl->fVar] = nullptr;
}
}
}
@ -322,20 +333,290 @@ static DefinitionMap compute_start_state(const CFG& cfg) {
return result;
}
void Compiler::scanCFG(const FunctionDefinition& f) {
CFG cfg = CFGGenerator().getCFG(f);
/**
* Returns true if assigning to this lvalue has no effect.
*/
static bool is_dead(const Expression& lvalue) {
switch (lvalue.fKind) {
case Expression::kVariableReference_Kind:
return ((VariableReference&) lvalue).fVariable.dead();
case Expression::kSwizzle_Kind:
return is_dead(*((Swizzle&) lvalue).fBase);
case Expression::kFieldAccess_Kind:
return is_dead(*((FieldAccess&) lvalue).fBase);
case Expression::kIndex_Kind: {
const IndexExpression& idx = (IndexExpression&) lvalue;
return is_dead(*idx.fBase) && !idx.fIndex->hasSideEffects();
}
default:
ABORT("invalid lvalue: %s\n", lvalue.description().c_str());
}
}
// compute the data flow
cfg.fBlocks[cfg.fStart].fBefore = compute_start_state(cfg);
/**
* Returns true if this is an assignment which can be collapsed down to just the right hand side due
* to a dead target and lack of side effects on the left hand side.
*/
static bool dead_assignment(const BinaryExpression& b) {
if (!Token::IsAssignment(b.fOperator)) {
return false;
}
return is_dead(*b.fLeft);
}
void Compiler::computeDataFlow(CFG* cfg) {
cfg->fBlocks[cfg->fStart].fBefore = compute_start_state(*cfg);
std::set<BlockId> workList;
for (BlockId i = 0; i < cfg.fBlocks.size(); i++) {
for (BlockId i = 0; i < cfg->fBlocks.size(); i++) {
workList.insert(i);
}
while (workList.size()) {
BlockId next = *workList.begin();
workList.erase(workList.begin());
this->scanCFG(&cfg, next, &workList);
this->scanCFG(cfg, next, &workList);
}
}
/**
* Attempts to replace the expression pointed to by iter with a new one (in both the CFG and the
* IR). If the expression can be cleanly removed, returns true and updates the iterator to point to
* the newly-inserted element. Otherwise updates only the IR and returns false (and the CFG will
* need to be regenerated).
*/
bool try_replace_expression(BasicBlock* b,
std::vector<BasicBlock::Node>::iterator* iter,
std::unique_ptr<Expression>* newExpression) {
std::unique_ptr<Expression>* target = (*iter)->expression();
if (!b->tryRemoveExpression(iter)) {
*target = std::move(*newExpression);
return false;
}
*target = std::move(*newExpression);
return b->tryInsertExpression(iter, target);
}
/**
* Returns true if the expression is a constant numeric literal with the specified value.
*/
bool is_constant(Expression& expr, double value) {
switch (expr.fKind) {
case Expression::kIntLiteral_Kind:
return ((IntLiteral&) expr).fValue == value;
case Expression::kFloatLiteral_Kind:
return ((FloatLiteral&) expr).fValue == value;
default:
return false;
}
}
/**
* Collapses the binary expression pointed to by iter down to just the right side (in both the IR
* and CFG structures).
*/
void delete_left(BasicBlock* b,
std::vector<BasicBlock::Node>::iterator* iter,
bool* outUpdated,
bool* outNeedsRescan) {
*outUpdated = true;
if (!try_replace_expression(b, iter, &((BinaryExpression&) **(*iter)->expression()).fRight)) {
*outNeedsRescan = true;
}
}
/**
* Collapses the binary expression pointed to by iter down to just the left side (in both the IR and
* CFG structures).
*/
void delete_right(BasicBlock* b,
std::vector<BasicBlock::Node>::iterator* iter,
bool* outUpdated,
bool* outNeedsRescan) {
*outUpdated = true;
if (!try_replace_expression(b, iter, &((BinaryExpression&) **(*iter)->expression()).fLeft)) {
*outNeedsRescan = true;
}
}
void Compiler::simplifyExpression(DefinitionMap& definitions,
BasicBlock& b,
std::vector<BasicBlock::Node>::iterator* iter,
std::unordered_set<const Variable*>* undefinedVariables,
bool* outUpdated,
bool* outNeedsRescan) {
Expression* expr = (*iter)->expression()->get();
ASSERT(expr);
if ((*iter)->fConstantPropagation) {
std::unique_ptr<Expression> optimized = expr->constantPropagate(*fIRGenerator, definitions);
if (optimized) {
if (!try_replace_expression(&b, iter, &optimized)) {
*outNeedsRescan = true;
}
ASSERT((*iter)->fKind == BasicBlock::Node::kExpression_Kind);
expr = (*iter)->expression()->get();
*outUpdated = true;
}
}
switch (expr->fKind) {
case Expression::kVariableReference_Kind: {
const Variable& var = ((VariableReference*) expr)->fVariable;
if (var.fStorage == Variable::kLocal_Storage && !definitions[&var] &&
(*undefinedVariables).find(&var) == (*undefinedVariables).end()) {
(*undefinedVariables).insert(&var);
this->error(expr->fPosition,
"'" + var.fName + "' has not been assigned");
}
break;
}
case Expression::kTernary_Kind: {
TernaryExpression* t = (TernaryExpression*) expr;
if (t->fTest->fKind == Expression::kBoolLiteral_Kind) {
// ternary has a constant test, replace it with either the true or
// false branch
if (((BoolLiteral&) *t->fTest).fValue) {
(*iter)->setExpression(std::move(t->fIfTrue));
} else {
(*iter)->setExpression(std::move(t->fIfFalse));
}
*outUpdated = true;
*outNeedsRescan = true;
}
break;
}
case Expression::kBinary_Kind: {
// collapse useless expressions like x * 1 or x + 0
BinaryExpression* bin = (BinaryExpression*) expr;
switch (bin->fOperator) {
case Token::STAR:
if (is_constant(*bin->fLeft, 1)) {
delete_left(&b, iter, outUpdated, outNeedsRescan);
}
else if (is_constant(*bin->fRight, 1)) {
delete_right(&b, iter, outUpdated, outNeedsRescan);
}
break;
case Token::PLUS: // fall through
case Token::MINUS:
if (is_constant(*bin->fLeft, 0)) {
delete_left(&b, iter, outUpdated, outNeedsRescan);
}
else if (is_constant(*bin->fRight, 0)) {
delete_right(&b, iter, outUpdated, outNeedsRescan);
}
break;
case Token::SLASH:
if (is_constant(*bin->fRight, 1)) {
delete_right(&b, iter, outUpdated, outNeedsRescan);
}
break;
default:
break;
}
}
default:
break;
}
}
void Compiler::simplifyStatement(DefinitionMap& definitions,
BasicBlock& b,
std::vector<BasicBlock::Node>::iterator* iter,
std::unordered_set<const Variable*>* undefinedVariables,
bool* outUpdated,
bool* outNeedsRescan) {
Statement* stmt = (*iter)->statement()->get();
switch (stmt->fKind) {
case Statement::kVarDeclarations_Kind: {
VarDeclarations& vd = *((VarDeclarationsStatement&) *stmt).fDeclaration;
for (auto varIter = vd.fVars.begin(); varIter != vd.fVars.end(); ) {
const auto& varDecl = **varIter;
if (varDecl.fVar->dead() &&
(!varDecl.fValue ||
!varDecl.fValue->hasSideEffects())) {
if (varDecl.fValue) {
ASSERT((*iter)->statement()->get() == stmt);
if (!b.tryRemoveExpressionBefore(iter, varDecl.fValue.get())) {
*outNeedsRescan = true;
}
}
varIter = vd.fVars.erase(varIter);
*outUpdated = true;
} else {
++varIter;
}
}
if (vd.fVars.size() == 0) {
(*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
}
break;
}
case Statement::kIf_Kind: {
IfStatement& i = (IfStatement&) *stmt;
if (i.fIfFalse && i.fIfFalse->isEmpty()) {
// else block doesn't do anything, remove it
i.fIfFalse.reset();
*outUpdated = true;
*outNeedsRescan = true;
}
if (!i.fIfFalse && i.fIfTrue->isEmpty()) {
// if block doesn't do anything, no else block
if (i.fTest->hasSideEffects()) {
// test has side effects, keep it
(*iter)->setStatement(std::unique_ptr<Statement>(
new ExpressionStatement(std::move(i.fTest))));
} else {
// no if, no else, no test side effects, kill the whole if
// statement
(*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
}
*outUpdated = true;
*outNeedsRescan = true;
}
break;
}
case Statement::kExpression_Kind: {
ExpressionStatement& e = (ExpressionStatement&) *stmt;
ASSERT((*iter)->statement()->get() == &e);
if (e.fExpression->fKind == Expression::kBinary_Kind) {
BinaryExpression& bin = (BinaryExpression&) *e.fExpression;
if (dead_assignment(bin)) {
if (!b.tryRemoveExpressionBefore(iter, &bin)) {
*outNeedsRescan = true;
}
if (bin.fRight->hasSideEffects()) {
// still have to evaluate the right due to side effects,
// replace the binary expression with just the right side
e.fExpression = std::move(bin.fRight);
if (!b.tryInsertExpression(iter, &e.fExpression)) {
*outNeedsRescan = true;
}
} else {
// no side effects, kill the whole statement
ASSERT((*iter)->statement()->get() == stmt);
(*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
}
*outUpdated = true;
break;
}
}
if (!e.fExpression->hasSideEffects()) {
// Expression statement with no side effects, kill it
if (!b.tryRemoveExpressionBefore(iter, e.fExpression.get())) {
*outNeedsRescan = true;
}
ASSERT((*iter)->statement()->get() == stmt);
(*iter)->setStatement(std::unique_ptr<Statement>(new Nop()));
*outUpdated = true;
}
break;
}
default:
break;
}
}
void Compiler::scanCFG(FunctionDefinition& f) {
CFG cfg = CFGGenerator().getCFG(f);
this->computeDataFlow(&cfg);
// check for unreachable code
for (size_t i = 0; i < cfg.fBlocks.size(); i++) {
@ -344,10 +625,10 @@ void Compiler::scanCFG(const FunctionDefinition& f) {
Position p;
switch (cfg.fBlocks[i].fNodes[0].fKind) {
case BasicBlock::Node::kStatement_Kind:
p = cfg.fBlocks[i].fNodes[0].fStatement->fPosition;
p = (*cfg.fBlocks[i].fNodes[0].statement())->fPosition;
break;
case BasicBlock::Node::kExpression_Kind:
p = (*cfg.fBlocks[i].fNodes[0].fExpression)->fPosition;
p = (*cfg.fBlocks[i].fNodes[0].expression())->fPosition;
break;
}
this->error(p, String("unreachable"));
@ -357,33 +638,34 @@ void Compiler::scanCFG(const FunctionDefinition& f) {
return;
}
// check for undefined variables, perform constant propagation
// check for dead code & undefined variables, perform constant propagation
std::unordered_set<const Variable*> undefinedVariables;
bool updated;
bool needsRescan = false;
do {
if (needsRescan) {
cfg = CFGGenerator().getCFG(f);
this->computeDataFlow(&cfg);
needsRescan = false;
}
updated = false;
for (BasicBlock& b : cfg.fBlocks) {
DefinitionMap definitions = b.fBefore;
for (BasicBlock::Node& n : b.fNodes) {
if (n.fKind == BasicBlock::Node::kExpression_Kind) {
ASSERT(n.fExpression);
Expression* expr = n.fExpression->get();
if (n.fConstantPropagation) {
std::unique_ptr<Expression> optimized = expr->constantPropagate(*fIRGenerator,
definitions);
if (optimized) {
n.fExpression->reset(optimized.release());
expr = n.fExpression->get();
}
}
if (expr->fKind == Expression::kVariableReference_Kind) {
const Variable& var = ((VariableReference*) expr)->fVariable;
if (var.fStorage == Variable::kLocal_Storage &&
!definitions[&var]) {
this->error(expr->fPosition,
"'" + var.fName + "' has not been assigned");
}
}
}
this->addDefinitions(n, &definitions);
for (auto iter = b.fNodes.begin(); iter != b.fNodes.end() && !needsRescan; ++iter) {
if (iter->fKind == BasicBlock::Node::kExpression_Kind) {
this->simplifyExpression(definitions, b, &iter, &undefinedVariables, &updated,
&needsRescan);
} else {
this->simplifyStatement(definitions, b, &iter, &undefinedVariables, &updated,
&needsRescan);
}
this->addDefinitions(*iter, &definitions);
}
}
} while (updated);
ASSERT(!needsRescan);
// check for missing return
if (f.fDeclaration.fReturnType != *fContext.fVoid_Type) {

View File

@ -9,6 +9,7 @@
#define SKSL_COMPILER
#include <set>
#include <unordered_set>
#include <vector>
#include "ir/SkSLProgram.h"
#include "ir/SkSLSymbolTable.h"
@ -71,7 +72,31 @@ private:
void scanCFG(CFG* cfg, BlockId block, std::set<BlockId>* workList);
void scanCFG(const FunctionDefinition& f);
void computeDataFlow(CFG* cfg);
/**
* Simplifies the expression pointed to by iter (in both the IR and CFG structures), if
* possible.
*/
void simplifyExpression(DefinitionMap& definitions,
BasicBlock& b,
std::vector<BasicBlock::Node>::iterator* iter,
std::unordered_set<const Variable*>* undefinedVariables,
bool* outUpdated,
bool* outNeedsRescan);
/**
* Simplifies the statement pointed to by iter (in both the IR and CFG structures), if
* possible.
*/
void simplifyStatement(DefinitionMap& definitions,
BasicBlock& b,
std::vector<BasicBlock::Node>::iterator* iter,
std::unordered_set<const Variable*>* undefinedVariables,
bool* outUpdated,
bool* outNeedsRescan);
void scanCFG(FunctionDefinition& f);
void internalConvertProgram(String text,
Modifiers::Flag* defaultPrecision,

View File

@ -284,6 +284,10 @@ private:
return String("<defined>");
}
bool hasSideEffects() const override {
return false;
}
typedef Expression INHERITED;
};
};

View File

@ -14,6 +14,7 @@
#include "ir/SkSLExtension.h"
#include "ir/SkSLIndexExpression.h"
#include "ir/SkSLModifiersDeclaration.h"
#include "ir/SkSLNop.h"
#include "ir/SkSLVariableReference.h"
namespace SkSL {
@ -509,10 +510,7 @@ void GLSLCodeGenerator::writeFunction(const FunctionDefinition& f) {
StringStream buffer;
fOut = &buffer;
fIndentation++;
for (const auto& s : f.fBody->fStatements) {
this->writeStatement(*s);
this->writeLine();
}
this->writeStatements(((Block&) *f.fBody).fStatements);
fIndentation--;
this->writeLine("}");
@ -620,26 +618,26 @@ void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
ASSERT(decl.fVars.size() > 0);
this->writeModifiers(decl.fVars[0].fVar->fModifiers, global);
this->writeModifiers(decl.fVars[0]->fVar->fModifiers, global);
this->writeType(decl.fBaseType);
String separator(" ");
for (const auto& var : decl.fVars) {
ASSERT(var.fVar->fModifiers == decl.fVars[0].fVar->fModifiers);
ASSERT(var->fVar->fModifiers == decl.fVars[0]->fVar->fModifiers);
this->write(separator);
separator = String(", ");
this->write(var.fVar->fName);
for (const auto& size : var.fSizes) {
this->write(var->fVar->fName);
for (const auto& size : var->fSizes) {
this->write("[");
if (size) {
this->writeExpression(*size, kTopLevel_Precedence);
}
this->write("]");
}
if (var.fValue) {
if (var->fValue) {
this->write(" = ");
this->writeExpression(*var.fValue, kTopLevel_Precedence);
this->writeExpression(*var->fValue, kTopLevel_Precedence);
}
if (!fFoundImageDecl && var.fVar->fType == *fContext.fImage2D_Type) {
if (!fFoundImageDecl && var->fVar->fType == *fContext.fImage2D_Type) {
if (fProgram.fSettings.fCaps->imageLoadStoreExtensionString()) {
fHeader.writeText("#extension ");
fHeader.writeText(fProgram.fSettings.fCaps->imageLoadStoreExtensionString());
@ -690,18 +688,27 @@ void GLSLCodeGenerator::writeStatement(const Statement& s) {
case Statement::kDiscard_Kind:
this->write("discard;");
break;
case Statement::kNop_Kind:
this->write(";");
break;
default:
ABORT("unsupported statement: %s", s.description().c_str());
}
}
void GLSLCodeGenerator::writeStatements(const std::vector<std::unique_ptr<Statement>>& statements) {
for (const auto& s : statements) {
if (!s->isEmpty()) {
this->writeStatement(*s);
this->writeLine();
}
}
}
void GLSLCodeGenerator::writeBlock(const Block& b) {
this->writeLine("{");
fIndentation++;
for (const auto& s : b.fStatements) {
this->writeStatement(*s);
this->writeLine();
}
this->writeStatements(b.fStatements);
fIndentation--;
this->write("}");
}
@ -821,7 +828,7 @@ bool GLSLCodeGenerator::generateCode() {
case ProgramElement::kVar_Kind: {
VarDeclarations& decl = (VarDeclarations&) *e;
if (decl.fVars.size() > 0) {
int builtin = decl.fVars[0].fVar->fModifiers.fLayout.fBuiltin;
int builtin = decl.fVars[0]->fVar->fModifiers.fLayout.fBuiltin;
if (builtin == -1) {
// normal var
this->writeVarDeclarations(decl, true);

View File

@ -145,6 +145,8 @@ private:
void writeStatement(const Statement& s);
void writeStatements(const std::vector<std::unique_ptr<Statement>>& statements);
void writeBlock(const Block& b);
void writeIfStatement(const IfStatement& stmt);

View File

@ -210,7 +210,7 @@ std::unique_ptr<Statement> IRGenerator::convertVarDeclarationStatement(
std::unique_ptr<VarDeclarations> IRGenerator::convertVarDeclarations(const ASTVarDeclarations& decl,
Variable::Storage storage) {
std::vector<VarDeclaration> variables;
std::vector<std::unique_ptr<VarDeclaration>> variables;
const Type* baseType = this->convertType(*decl.fType);
if (!baseType) {
return nullptr;
@ -254,6 +254,7 @@ std::unique_ptr<VarDeclarations> IRGenerator::convertVarDeclarations(const ASTVa
return nullptr;
}
value = this->coerce(std::move(value), *type);
var->fWriteCount = 1;
}
if (storage == Variable::kGlobal_Storage && varDecl.fName == String("sk_FragColor") &&
(*fSymbolTable)[varDecl.fName]) {
@ -265,7 +266,8 @@ std::unique_ptr<VarDeclarations> IRGenerator::convertVarDeclarations(const ASTVa
Variable* old = (Variable*) (*fSymbolTable)[varDecl.fName];
old->fModifiers = var->fModifiers;
} else {
variables.emplace_back(var.get(), std::move(sizes), std::move(value));
variables.emplace_back(new VarDeclaration(var.get(), std::move(sizes),
std::move(value)));
fSymbolTable->add(varDecl.fName, std::move(var));
}
}
@ -542,7 +544,8 @@ std::unique_ptr<FunctionDefinition> IRGenerator::convertFunction(const ASTFuncti
}
if (match) {
if (*returnType != other->fReturnType) {
FunctionDeclaration newDecl(f.fPosition, f.fName, parameters, *returnType);
FunctionDeclaration newDecl(f.fPosition, f.fModifiers, f.fName, parameters,
*returnType);
fErrors.error(f.fPosition, "functions '" + newDecl.description() +
"' and '" + other->description() +
"' differ only in return type");
@ -570,6 +573,7 @@ std::unique_ptr<FunctionDefinition> IRGenerator::convertFunction(const ASTFuncti
if (!decl) {
// couldn't find an existing declaration
auto newDecl = std::unique_ptr<FunctionDeclaration>(new FunctionDeclaration(f.fPosition,
f.fModifiers,
f.fName,
parameters,
*returnType));
@ -590,6 +594,8 @@ std::unique_ptr<FunctionDefinition> IRGenerator::convertFunction(const ASTFuncti
if (!body) {
return nullptr;
}
// conservatively assume all user-defined functions have side effects
((Modifiers&) decl->fModifiers).fFlags |= Modifiers::kHasSideEffects_Flag;
return std::unique_ptr<FunctionDefinition>(new FunctionDefinition(f.fPosition, *decl,
std::move(body)));
}
@ -608,13 +614,13 @@ std::unique_ptr<InterfaceBlock> IRGenerator::convertInterfaceBlock(const ASTInte
return nullptr;
}
for (const auto& var : decl->fVars) {
fields.push_back(Type::Field(var.fVar->fModifiers, var.fVar->fName,
&var.fVar->fType));
if (var.fValue) {
fields.push_back(Type::Field(var->fVar->fModifiers, var->fVar->fName,
&var->fVar->fType));
if (var->fValue) {
fErrors.error(decl->fPosition,
"initializers are not permitted on interface block fields");
}
if (var.fVar->fModifiers.fFlags & (Modifiers::kIn_Flag |
if (var->fVar->fModifiers.fFlags & (Modifiers::kIn_Flag |
Modifiers::kOut_Flag |
Modifiers::kUniform_Flag |
Modifiers::kConst_Flag)) {
@ -944,6 +950,9 @@ static bool determine_binary_type(const Context& context,
std::unique_ptr<Expression> IRGenerator::constantFold(const Expression& left,
Token::Kind op,
const Expression& right) const {
if (!left.isConstant() || !right.isConstant()) {
return nullptr;
}
// 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
@ -1019,6 +1028,28 @@ std::unique_ptr<Expression> IRGenerator::constantFold(const Expression& left,
default: return nullptr;
}
}
if (left.fType.kind() == Type::kVector_Kind &&
left.fType.componentType() == *fContext.fFloat_Type &&
left.fType == right.fType) {
ASSERT(left.fKind == Expression::kConstructor_Kind);
ASSERT(right.fKind == Expression::kConstructor_Kind);
std::vector<std::unique_ptr<Expression>> args;
#define RETURN_VEC_COMPONENTWISE_RESULT(op) \
for (int i = 0; i < left.fType.columns(); i++) { \
float value = ((Constructor&) left).getFVecComponent(i) op \
((Constructor&) right).getFVecComponent(i); \
args.emplace_back(new FloatLiteral(fContext, Position(), value)); \
} \
return std::unique_ptr<Expression>(new Constructor(Position(), left.fType, \
std::move(args)));
switch (op) {
case Token::PLUS: RETURN_VEC_COMPONENTWISE_RESULT(+);
case Token::MINUS: RETURN_VEC_COMPONENTWISE_RESULT(-);
case Token::STAR: RETURN_VEC_COMPONENTWISE_RESULT(*);
case Token::SLASH: RETURN_VEC_COMPONENTWISE_RESULT(/);
default: return nullptr;
}
}
#undef RESULT
return nullptr;
}
@ -1177,7 +1208,8 @@ std::unique_ptr<Expression> IRGenerator::call(Position position,
return nullptr;
}
if (arguments[i] && (function.fParameters[i]->fModifiers.fFlags & Modifiers::kOut_Flag)) {
this->markWrittenTo(*arguments[i], true);
this->markWrittenTo(*arguments[i],
function.fParameters[i]->fModifiers.fFlags & Modifiers::kIn_Flag);
}
}
if (function.fBuiltin && function.fName == "texture" &&

View File

@ -309,7 +309,7 @@ std::unique_ptr<ASTDeclaration> Parser::declaration() {
if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
return nullptr;
}
if (!modifiers.fFlags && this->peek().fKind == Token::LPAREN) {
if (this->peek().fKind == Token::LPAREN) {
this->nextToken();
std::vector<std::unique_ptr<ASTParameter>> parameters;
while (this->peek().fKind != Token::RPAREN) {
@ -334,7 +334,9 @@ std::unique_ptr<ASTDeclaration> Parser::declaration() {
return nullptr;
}
}
return std::unique_ptr<ASTDeclaration>(new ASTFunction(name.fPosition, std::move(type),
return std::unique_ptr<ASTDeclaration>(new ASTFunction(name.fPosition,
modifiers,
std::move(type),
std::move(name.fText),
std::move(parameters),
std::move(body)));
@ -721,6 +723,10 @@ Modifiers Parser::modifiers() {
this->nextToken();
flags |= Modifiers::kRestrict_Flag;
break;
case Token::HASSIDEEFFECTS:
this->nextToken();
flags |= Modifiers::kHasSideEffects_Flag;
break;
default:
return Modifiers(layout, flags);
}

View File

@ -2461,7 +2461,7 @@ SpvId SPIRVCodeGenerator::writeFunction(const FunctionDefinition& f, OutputStrea
write_stringstream(fGlobalInitializersBuffer, out);
}
StringStream bodyBuffer;
this->writeBlock(*f.fBody, bodyBuffer);
this->writeBlock((Block&) *f.fBody, bodyBuffer);
write_stringstream(fVariableBuffer, out);
write_stringstream(bodyBuffer, out);
if (fCurrentBlock) {
@ -2558,7 +2558,7 @@ SpvId SPIRVCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
void SPIRVCodeGenerator::writeGlobalVars(Program::Kind kind, const VarDeclarations& decl,
OutputStream& out) {
for (size_t i = 0; i < decl.fVars.size(); i++) {
const VarDeclaration& varDecl = decl.fVars[i];
const VarDeclaration& varDecl = *decl.fVars[i];
const Variable* var = varDecl.fVar;
// These haven't been implemented in our SPIR-V generator yet and we only currently use them
// in the OpenGL backend.
@ -2620,7 +2620,7 @@ void SPIRVCodeGenerator::writeGlobalVars(Program::Kind kind, const VarDeclaratio
void SPIRVCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, OutputStream& out) {
for (const auto& varDecl : decl.fVars) {
const Variable* var = varDecl.fVar;
const Variable* var = varDecl->fVar;
// These haven't been implemented in our SPIR-V generator yet and we only currently use them
// in the OpenGL backend.
ASSERT(!(var->fModifiers.fFlags & (Modifiers::kReadOnly_Flag |
@ -2633,8 +2633,8 @@ void SPIRVCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, Outpu
SpvId type = this->getPointerType(var->fType, SpvStorageClassFunction);
this->writeInstruction(SpvOpVariable, type, id, SpvStorageClassFunction, fVariableBuffer);
this->writeInstruction(SpvOpName, id, var->fName.c_str(), fNameBuffer);
if (varDecl.fValue) {
SpvId value = this->writeExpression(*varDecl.fValue, out);
if (varDecl->fValue) {
SpvId value = this->writeExpression(*varDecl->fValue, out);
this->writeInstruction(SpvOpStore, id, value, out);
}
}
@ -2642,6 +2642,8 @@ void SPIRVCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, Outpu
void SPIRVCodeGenerator::writeStatement(const Statement& s, OutputStream& out) {
switch (s.fKind) {
case Statement::kNop_Kind:
break;
case Statement::kBlock_Kind:
this->writeBlock((Block&) s, out);
break;

View File

@ -104,6 +104,7 @@ struct Token {
COHERENT,
VOLATILE,
RESTRICT,
HASSIDEEFFECTS,
STRUCT,
LAYOUT,
DIRECTIVE,

View File

@ -19,10 +19,11 @@ namespace SkSL {
* A function declaration or definition. The fBody field will be null for declarations.
*/
struct ASTFunction : public ASTDeclaration {
ASTFunction(Position position, std::unique_ptr<ASTType> returnType, String name,
std::vector<std::unique_ptr<ASTParameter>> parameters,
ASTFunction(Position position, Modifiers modifiers, std::unique_ptr<ASTType> returnType,
String name, std::vector<std::unique_ptr<ASTParameter>> parameters,
std::unique_ptr<ASTBlock> body)
: INHERITED(position, kFunction_Kind)
, fModifiers(modifiers)
, fReturnType(std::move(returnType))
, fName(std::move(name))
, fParameters(std::move(parameters))
@ -44,6 +45,7 @@ struct ASTFunction : public ASTDeclaration {
return result;
}
const Modifiers fModifiers;
const std::unique_ptr<ASTType> fReturnType;
const String fName;
const std::vector<std::unique_ptr<ASTParameter>> fParameters;

View File

@ -26,15 +26,19 @@ struct BinaryExpression : public Expression {
, fOperator(op)
, fRight(std::move(right)) {}
virtual std::unique_ptr<Expression> constantPropagate(
const IRGenerator& irGenerator,
std::unique_ptr<Expression> constantPropagate(const IRGenerator& irGenerator,
const DefinitionMap& definitions) override {
return irGenerator.constantFold(*fLeft,
fOperator,
*fRight);
}
virtual String description() const override {
bool hasSideEffects() const override {
return Token::IsAssignment(fOperator) || fLeft->hasSideEffects() ||
fRight->hasSideEffects();
}
String description() const override {
return "(" + fLeft->description() + " " + Token::OperatorName(fOperator) + " " +
fRight->description() + ")";
}

View File

@ -23,6 +23,15 @@ struct Block : public Statement {
, fSymbols(std::move(symbols))
, fStatements(std::move(statements)) {}
bool isEmpty() const override {
for (const auto& s : fStatements) {
if (!s->isEmpty()) {
return false;
}
}
return true;
}
String description() const override {
String result("{");
for (size_t i = 0; i < fStatements.size(); i++) {
@ -36,7 +45,7 @@ struct Block : public Statement {
// it's important to keep fStatements defined after (and thus destroyed before) fSymbols,
// because destroying statements can modify reference counts in symbols
const std::shared_ptr<SymbolTable> fSymbols;
const std::vector<std::unique_ptr<Statement>> fStatements;
std::vector<std::unique_ptr<Statement>> fStatements;
typedef Statement INHERITED;
};

View File

@ -25,6 +25,10 @@ struct BoolLiteral : public Expression {
return String(fValue ? "true" : "false");
}
bool hasSideEffects() const override {
return false;
}
bool isConstant() const override {
return true;
}

View File

@ -30,20 +30,36 @@ struct Constructor : public Expression {
: INHERITED(position, kConstructor_Kind, type)
, fArguments(std::move(arguments)) {}
virtual std::unique_ptr<Expression> constantPropagate(
const IRGenerator& irGenerator,
std::unique_ptr<Expression> constantPropagate(const IRGenerator& irGenerator,
const DefinitionMap& definitions) override {
if (fArguments.size() == 1 && fArguments[0]->fKind == Expression::kIntLiteral_Kind &&
if (fArguments.size() == 1 && fArguments[0]->fKind == Expression::kIntLiteral_Kind) {
if (fType == *irGenerator.fContext.fFloat_Type) {
// promote float(1) to 1.0
fType == *irGenerator.fContext.fFloat_Type) {
int64_t intValue = ((IntLiteral&) *fArguments[0]).fValue;
return std::unique_ptr<Expression>(new FloatLiteral(irGenerator.fContext,
fPosition,
intValue));
} else if (fType == *irGenerator.fContext.fUInt_Type) {
// promote uint(1) to 1u
int64_t intValue = ((IntLiteral&) *fArguments[0]).fValue;
return std::unique_ptr<Expression>(new IntLiteral(irGenerator.fContext,
fPosition,
intValue,
&fType));
}
}
return nullptr;
}
bool hasSideEffects() const override {
for (const auto& arg : fArguments) {
if (arg->hasSideEffects()) {
return true;
}
}
return false;
}
String description() const override {
String result = fType.description() + "(";
String separator;
@ -65,6 +81,43 @@ struct Constructor : public Expression {
return true;
}
const Expression& getVecComponent(int index) const {
ASSERT(fType.kind() == Type::kVector_Kind);
if (fArguments.size() == 1 && fArguments[0]->fType.kind() == Type::kScalar_Kind) {
return *fArguments[0];
}
int current = 0;
for (const auto& arg : fArguments) {
ASSERT(current <= index);
if (arg->fType.kind() == Type::kScalar_Kind) {
if (index == current) {
return *arg;
}
current++;
} else {
ASSERT(arg->fType.kind() == Type::kVector_Kind);
ASSERT(arg->fKind == Expression::kConstructor_Kind);
if (current + arg->fType.columns() > index) {
return ((const Constructor&) *arg).getVecComponent(index - current);
}
current += arg->fType.columns();
}
}
ABORT("failed to find vector component %d in %s\n", index, description().c_str());
}
double getFVecComponent(int index) const {
const Expression& c = this->getVecComponent(index);
ASSERT(c.fKind == Expression::kFloatLiteral_Kind);
return ((FloatLiteral&) c).fValue;
}
int64_t getIVecComponent(int index) const {
const Expression& c = this->getVecComponent(index);
ASSERT(c.fKind == Expression::kIntLiteral_Kind);
return ((IntLiteral&) c).fValue;
}
std::vector<std::unique_ptr<Expression>> fArguments;
typedef Expression INHERITED;

View File

@ -27,7 +27,7 @@ struct DoStatement : public Statement {
return "do " + fStatement->description() + " while (" + fTest->description() + ");";
}
const std::unique_ptr<Statement> fStatement;
std::unique_ptr<Statement> fStatement;
std::unique_ptr<Expression> fTest;
typedef Statement INHERITED;

View File

@ -52,6 +52,13 @@ struct Expression : public IRNode {
return false;
}
/**
* Returns true if evaluating the expression potentially has side effects. Expressions may never
* return false if they actually have side effects, but it is legal (though suboptimal) to
* return true if there are not actually any side effects.
*/
virtual bool hasSideEffects() const = 0;
/**
* Given a map of known constant variable values, substitute them in for references to those
* variables occurring in this expression and its subexpressions. Similar simplifications, such

View File

@ -31,7 +31,11 @@ struct FieldAccess : public Expression {
, fFieldIndex(fieldIndex)
, fOwnerKind(ownerKind) {}
virtual String description() const override {
bool hasSideEffects() const override {
return fBase->hasSideEffects();
}
String description() const override {
return fBase->description() + "." + fBase->fType.fields()[fFieldIndex].fName;
}

View File

@ -17,14 +17,19 @@ namespace SkSL {
* A literal floating point number.
*/
struct FloatLiteral : public Expression {
FloatLiteral(const Context& context, Position position, double value)
: INHERITED(position, kFloatLiteral_Kind, *context.fFloat_Type)
FloatLiteral(const Context& context, Position position, double value,
const Type* type = nullptr)
: INHERITED(position, kFloatLiteral_Kind, type ? *type : *context.fFloat_Type)
, fValue(value) {}
virtual String description() const override {
String description() const override {
return to_string(fValue);
}
bool hasSideEffects() const override {
return false;
}
bool isConstant() const override {
return true;
}

View File

@ -48,10 +48,10 @@ struct ForStatement : public Statement {
// it's important to keep fSymbols defined first (and thus destroyed last) because destroying
// the other fields can update symbol reference counts
const std::shared_ptr<SymbolTable> fSymbols;
const std::unique_ptr<Statement> fInitializer;
std::unique_ptr<Statement> fInitializer;
std::unique_ptr<Expression> fTest;
std::unique_ptr<Expression> fNext;
const std::unique_ptr<Statement> fStatement;
std::unique_ptr<Statement> fStatement;
typedef Statement INHERITED;
};

View File

@ -23,6 +23,15 @@ struct FunctionCall : public Expression {
, fFunction(std::move(function))
, fArguments(std::move(arguments)) {}
bool hasSideEffects() const override {
for (const auto& arg : fArguments) {
if (arg->hasSideEffects()) {
return true;
}
}
return fFunction.fModifiers.fFlags & Modifiers::kHasSideEffects_Flag;
}
String description() const override {
String result = fFunction.fName + "(";
String separator;

View File

@ -21,11 +21,12 @@ namespace SkSL {
* A function declaration (not a definition -- does not contain a body).
*/
struct FunctionDeclaration : public Symbol {
FunctionDeclaration(Position position, String name,
FunctionDeclaration(Position position, Modifiers modifiers, String name,
std::vector<const Variable*> parameters, const Type& returnType)
: INHERITED(position, kFunctionDeclaration_Kind, std::move(name))
, fDefined(false)
, fBuiltin(false)
, fModifiers(modifiers)
, fParameters(std::move(parameters))
, fReturnType(returnType) {}
@ -102,6 +103,7 @@ struct FunctionDeclaration : public Symbol {
mutable bool fDefined;
bool fBuiltin;
Modifiers fModifiers;
const std::vector<const Variable*> fParameters;
const Type& fReturnType;

View File

@ -19,7 +19,7 @@ namespace SkSL {
*/
struct FunctionDefinition : public ProgramElement {
FunctionDefinition(Position position, const FunctionDeclaration& declaration,
std::unique_ptr<Block> body)
std::unique_ptr<Statement> body)
: INHERITED(position, kFunction_Kind)
, fDeclaration(declaration)
, fBody(std::move(body)) {}
@ -29,7 +29,7 @@ struct FunctionDefinition : public ProgramElement {
}
const FunctionDeclaration& fDeclaration;
const std::unique_ptr<Block> fBody;
std::unique_ptr<Statement> fBody;
typedef ProgramElement INHERITED;
};

View File

@ -24,7 +24,11 @@ struct FunctionReference : public Expression {
: INHERITED(position, kFunctionReference_Kind, *context.fInvalid_Type)
, fFunctions(function) {}
virtual String description() const override {
bool hasSideEffects() const override {
return false;
}
String description() const override {
ASSERT(false);
return String("<function>");
}

View File

@ -33,8 +33,9 @@ struct IfStatement : public Statement {
}
std::unique_ptr<Expression> fTest;
const std::unique_ptr<Statement> fIfTrue;
const std::unique_ptr<Statement> fIfFalse;
std::unique_ptr<Statement> fIfTrue;
// may be null
std::unique_ptr<Statement> fIfFalse;
typedef Statement INHERITED;
};

View File

@ -51,6 +51,10 @@ struct IndexExpression : public Expression {
ASSERT(fIndex->fType == *context.fInt_Type || fIndex->fType == *context.fUInt_Type);
}
bool hasSideEffects() const override {
return fBase->hasSideEffects() || fIndex->hasSideEffects();
}
String description() const override {
return fBase->description() + "[" + fIndex->description() + "]";
}

View File

@ -23,10 +23,14 @@ struct IntLiteral : public Expression {
: INHERITED(position, kIntLiteral_Kind, type ? *type : *context.fInt_Type)
, fValue(value) {}
virtual String description() const override {
String description() const override {
return to_string(fValue);
}
bool hasSideEffects() const override {
return false;
}
bool isConstant() const override {
return true;
}

View File

@ -18,20 +18,21 @@ namespace SkSL {
struct Modifiers {
enum Flag {
kNo_Flag = 0,
kConst_Flag = 1,
kIn_Flag = 2,
kOut_Flag = 4,
kLowp_Flag = 8,
kMediump_Flag = 16,
kHighp_Flag = 32,
kUniform_Flag = 64,
kFlat_Flag = 128,
kNoPerspective_Flag = 256,
kReadOnly_Flag = 512,
kWriteOnly_Flag = 1024,
kCoherent_Flag = 2048,
kVolatile_Flag = 4096,
kRestrict_Flag = 8192
kConst_Flag = 1 << 0,
kIn_Flag = 1 << 1,
kOut_Flag = 1 << 2,
kLowp_Flag = 1 << 3,
kMediump_Flag = 1 << 4,
kHighp_Flag = 1 << 5,
kUniform_Flag = 1 << 6,
kFlat_Flag = 1 << 7,
kNoPerspective_Flag = 1 << 8,
kReadOnly_Flag = 1 << 9,
kWriteOnly_Flag = 1 << 10,
kCoherent_Flag = 1 << 11,
kVolatile_Flag = 1 << 12,
kRestrict_Flag = 1 << 13,
kHasSideEffects_Flag = 1 << 14
};
Modifiers()
@ -80,6 +81,9 @@ struct Modifiers {
if (fFlags & kRestrict_Flag) {
result += "restrict ";
}
if (fFlags & kHasSideEffects_Flag) {
result += "sk_has_side_effects ";
}
if ((fFlags & kIn_Flag) && (fFlags & kOut_Flag)) {
result += "inout ";

36
src/sksl/ir/SkSLNop.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_NOP
#define SKSL_NOP
#include "SkSLStatement.h"
#include "SkSLSymbolTable.h"
namespace SkSL {
/**
* A no-op statement that does nothing.
*/
struct Nop : public Statement {
Nop()
: INHERITED(Position(), kNop_Kind) {}
virtual bool isEmpty() const override {
return true;
}
String description() const override {
return String(";");
}
typedef Statement INHERITED;
};
} // namespace
#endif

View File

@ -22,7 +22,11 @@ struct PostfixExpression : public Expression {
, fOperand(std::move(operand))
, fOperator(op) {}
virtual String description() const override {
bool hasSideEffects() const override {
return true;
}
String description() const override {
return fOperand->description() + Token::OperatorName(fOperator);
}

View File

@ -22,7 +22,29 @@ struct PrefixExpression : public Expression {
, fOperand(std::move(operand))
, fOperator(op) {}
virtual String description() const override {
bool isConstant() const override {
return fOperator == Token::MINUS && fOperand->isConstant();
}
bool hasSideEffects() const override {
return fOperator == Token::PLUSPLUS || fOperator == Token::MINUSMINUS ||
fOperand->hasSideEffects();
}
virtual std::unique_ptr<Expression> constantPropagate(
const IRGenerator& irGenerator,
const DefinitionMap& definitions) override {
if (fOperand->fKind == Expression::kFloatLiteral_Kind) {
return std::unique_ptr<Expression>(new FloatLiteral(
irGenerator.fContext,
Position(),
-((FloatLiteral&) *fOperand).fValue));
}
return nullptr;
}
String description() const override {
return Token::OperatorName(fOperator) + fOperand->description();
}

View File

@ -25,7 +25,9 @@ struct Statement : public IRNode {
kDo_Kind,
kExpression_Kind,
kFor_Kind,
kGroup_Kind,
kIf_Kind,
kNop_Kind,
kReturn_Kind,
kSwitch_Kind,
kVarDeclarations_Kind,
@ -36,6 +38,10 @@ struct Statement : public IRNode {
: INHERITED(position)
, fKind(kind) {}
virtual bool isEmpty() const {
return false;
}
const Kind fKind;
typedef IRNode INHERITED;

View File

@ -69,6 +69,34 @@ struct Swizzle : public Expression {
ASSERT(fComponents.size() >= 1 && fComponents.size() <= 4);
}
virtual std::unique_ptr<Expression> constantPropagate(
const IRGenerator& irGenerator,
const DefinitionMap& definitions) override {
if (fBase->fKind == Expression::kConstructor_Kind && fBase->isConstant()) {
// we're swizzling a constant vector, e.g. vec4(1).x. Simplify it.
ASSERT(fBase->fKind == Expression::kConstructor_Kind);
if (fType == *irGenerator.fContext.fInt_Type) {
ASSERT(fComponents.size() == 1);
int64_t value = ((Constructor&) *fBase).getIVecComponent(fComponents[0]);
return std::unique_ptr<Expression>(new IntLiteral(irGenerator.fContext,
Position(),
value));
} else if (fType == *irGenerator.fContext.fFloat_Type) {
ASSERT(fComponents.size() == 1);
double value = ((Constructor&) *fBase).getFVecComponent(fComponents[0]);
return std::unique_ptr<Expression>(new FloatLiteral(irGenerator.fContext,
Position(),
value));
}
}
return nullptr;
}
bool hasSideEffects() const override {
return fBase->hasSideEffects();
}
String description() const override {
String result = fBase->description() + ".";
for (int x : fComponents) {

View File

@ -26,6 +26,10 @@ struct TernaryExpression : public Expression {
ASSERT(fIfTrue->fType == fIfFalse->fType);
}
bool hasSideEffects() const override {
return fTest->hasSideEffects() || fIfTrue->hasSideEffects() || fIfFalse->hasSideEffects();
}
String description() const override {
return "(" + fTest->description() + " ? " + fIfTrue->description() + " : " +
fIfFalse->description() + ")";

View File

@ -22,6 +22,10 @@ struct TypeReference : public Expression {
: INHERITED(position, kTypeReference_Kind, *context.fInvalid_Type)
, fValue(type) {}
bool hasSideEffects() const override {
return false;
}
String description() const override {
return fValue.name();
}

View File

@ -26,7 +26,7 @@ struct UnresolvedFunction : public Symbol {
#endif
}
virtual String description() const override {
String description() const override {
return fName;
}

View File

@ -53,7 +53,7 @@ struct VarDeclaration {
*/
struct VarDeclarations : public ProgramElement {
VarDeclarations(Position position, const Type* baseType,
std::vector<VarDeclaration> vars)
std::vector<std::unique_ptr<VarDeclaration>> vars)
: INHERITED(position, kVar_Kind)
, fBaseType(*baseType)
, fVars(std::move(vars)) {}
@ -62,18 +62,18 @@ struct VarDeclarations : public ProgramElement {
if (!fVars.size()) {
return String();
}
String result = fVars[0].fVar->fModifiers.description() + fBaseType.description() + " ";
String result = fVars[0]->fVar->fModifiers.description() + fBaseType.description() + " ";
String separator;
for (const auto& var : fVars) {
result += separator;
separator = ", ";
result += var.description();
result += var->description();
}
return result;
}
const Type& fBaseType;
std::vector<VarDeclaration> fVars;
std::vector<std::unique_ptr<VarDeclaration>> fVars;
typedef ProgramElement INHERITED;
};

View File

@ -40,6 +40,10 @@ struct Variable : public Symbol {
return fModifiers.description() + fType.fName + " " + fName;
}
bool dead() const {
return !fWriteCount || (!fReadCount && !(fModifiers.fFlags & Modifiers::kOut_Flag));
}
mutable Modifiers fModifiers;
const Type& fType;
const Storage fStorage;

View File

@ -41,7 +41,7 @@ struct VariableReference : public Expression {
}
}
virtual ~VariableReference() override {
~VariableReference() override {
if (fRefKind != kWrite_RefKind) {
fVariable.fReadCount--;
}
@ -67,16 +67,17 @@ struct VariableReference : public Expression {
fRefKind = refKind;
}
bool hasSideEffects() const override {
return false;
}
String description() const override {
return fVariable.fName;
}
virtual std::unique_ptr<Expression> constantPropagate(
const IRGenerator& irGenerator,
const DefinitionMap& definitions) override {
auto exprIter = definitions.find(&fVariable);
if (exprIter != definitions.end() && exprIter->second) {
const Expression* expr = exprIter->second->get();
static std::unique_ptr<Expression> copy_constant(const IRGenerator& irGenerator,
const Expression* expr) {
ASSERT(expr->isConstant());
switch (expr->fKind) {
case Expression::kIntLiteral_Kind:
return std::unique_ptr<Expression>(new IntLiteral(
@ -88,18 +89,41 @@ struct VariableReference : public Expression {
irGenerator.fContext,
Position(),
((FloatLiteral*) expr)->fValue));
default:
break;
case Expression::kBoolLiteral_Kind:
return std::unique_ptr<Expression>(new BoolLiteral(irGenerator.fContext,
Position(),
((BoolLiteral*) expr)->fValue));
case Expression::kConstructor_Kind: {
const Constructor* c = (const Constructor*) expr;
std::vector<std::unique_ptr<Expression>> args;
for (const auto& arg : c->fArguments) {
args.push_back(copy_constant(irGenerator, arg.get()));
}
return std::unique_ptr<Expression>(new Constructor(Position(), c->fType,
std::move(args)));
}
default:
ABORT("unsupported constant\n");
}
}
std::unique_ptr<Expression> constantPropagate(const IRGenerator& irGenerator,
const DefinitionMap& definitions) override {
if (fRefKind != kRead_RefKind) {
return nullptr;
}
auto exprIter = definitions.find(&fVariable);
if (exprIter != definitions.end() && exprIter->second &&
(*exprIter->second)->isConstant()) {
return copy_constant(irGenerator, exprIter->second->get());
}
return nullptr;
}
const Variable& fVariable;
private:
RefKind fRefKind;
private:
typedef Expression INHERITED;
};

View File

@ -28,7 +28,7 @@ struct WhileStatement : public Statement {
}
std::unique_ptr<Expression> fTest;
const std::unique_ptr<Statement> fStatement;
std::unique_ptr<Statement> fStatement;
typedef Statement INHERITED;
};

View File

@ -424,8 +424,8 @@ static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner );
yyg->yy_hold_char = *yy_cp; \
*yy_cp = '\0'; \
yyg->yy_c_buf_p = yy_cp;
#define YY_NUM_RULES 92
#define YY_END_OF_BUFFER 93
#define YY_NUM_RULES 93
#define YY_END_OF_BUFFER 94
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
@ -433,36 +433,37 @@ struct yy_trans_info
flex_int32_t yy_verify;
flex_int32_t yy_nxt;
};
static const flex_int16_t yy_accept[253] =
static const flex_int16_t yy_accept[271] =
{ 0,
0, 0, 93, 91, 90, 90, 64, 91, 38, 54,
59, 40, 41, 52, 50, 47, 51, 46, 53, 4,
4, 66, 87, 71, 67, 70, 65, 44, 45, 58,
38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
38, 38, 38, 38, 38, 38, 38, 38, 42, 57,
43, 60, 90, 69, 39, 38, 78, 63, 83, 76,
48, 74, 49, 75, 1, 0, 88, 77, 2, 4,
0, 0, 55, 73, 68, 72, 56, 82, 62, 38,
38, 38, 38, 38, 12, 38, 38, 38, 38, 38,
8, 20, 38, 38, 38, 38, 38, 38, 38, 38,
0, 0, 94, 92, 91, 91, 65, 92, 39, 55,
60, 41, 42, 53, 51, 48, 52, 47, 54, 4,
4, 67, 88, 72, 68, 71, 66, 45, 46, 59,
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
39, 39, 39, 39, 39, 39, 39, 39, 43, 58,
44, 61, 91, 70, 40, 39, 79, 64, 84, 77,
49, 75, 50, 76, 1, 0, 89, 78, 2, 4,
0, 0, 56, 74, 69, 73, 57, 83, 63, 39,
39, 39, 39, 39, 12, 39, 39, 39, 39, 39,
8, 20, 39, 39, 39, 39, 39, 39, 39, 39,
38, 38, 38, 38, 38, 38, 81, 61, 39, 86,
0, 0, 0, 88, 1, 0, 0, 3, 5, 79,
80, 85, 38, 38, 38, 38, 38, 38, 38, 38,
38, 10, 38, 38, 38, 38, 38, 38, 21, 38,
38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
84, 0, 1, 89, 0, 0, 2, 38, 14, 38,
38, 38, 38, 38, 9, 38, 28, 38, 38, 38,
25, 38, 38, 38, 38, 38, 38, 38, 38, 6,
38, 38, 38, 38, 0, 1, 16, 38, 24, 38,
38, 38, 7, 27, 22, 38, 38, 38, 38, 38,
39, 39, 39, 39, 39, 39, 39, 82, 62, 40,
87, 0, 0, 0, 89, 1, 0, 0, 3, 5,
80, 81, 86, 39, 39, 39, 39, 39, 39, 39,
39, 39, 10, 39, 39, 39, 39, 39, 39, 21,
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
39, 39, 85, 0, 1, 90, 0, 0, 2, 39,
14, 39, 39, 39, 39, 39, 9, 39, 28, 39,
39, 39, 25, 39, 39, 39, 39, 39, 39, 39,
39, 39, 6, 39, 39, 39, 39, 0, 1, 16,
39, 24, 39, 39, 39, 7, 27, 22, 39, 39,
38, 38, 38, 38, 38, 38, 11, 38, 38, 38,
38, 38, 36, 38, 38, 38, 38, 38, 19, 35,
13, 38, 38, 38, 38, 38, 15, 18, 26, 38,
38, 38, 38, 23, 38, 38, 32, 17, 38, 38,
30, 34, 33, 38, 38, 37, 31, 38, 38, 38,
29, 0
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
11, 39, 39, 39, 39, 39, 37, 39, 39, 39,
39, 39, 19, 39, 36, 13, 39, 39, 39, 39,
39, 15, 18, 26, 39, 39, 39, 39, 39, 23,
39, 39, 32, 17, 39, 39, 30, 34, 39, 33,
39, 39, 38, 39, 31, 39, 39, 39, 39, 39,
39, 29, 39, 39, 39, 39, 39, 39, 35, 0
} ;
static const YY_CHAR yy_ec[256] =
@ -476,11 +477,11 @@ static const YY_CHAR yy_ec[256] =
22, 23, 24, 1, 25, 25, 25, 25, 26, 25,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
27, 1, 28, 29, 6, 1, 30, 31, 32, 33,
27, 1, 28, 29, 30, 1, 31, 32, 33, 34,
34, 35, 36, 37, 38, 6, 39, 40, 41, 42,
43, 44, 6, 45, 46, 47, 48, 49, 50, 51,
52, 6, 53, 54, 55, 56, 1, 1, 1, 1,
35, 36, 37, 38, 39, 6, 40, 41, 42, 43,
44, 45, 6, 46, 47, 48, 49, 50, 51, 52,
53, 6, 54, 55, 56, 57, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -497,178 +498,188 @@ static const YY_CHAR yy_ec[256] =
1, 1, 1, 1, 1
} ;
static const YY_CHAR yy_meta[57] =
static const YY_CHAR yy_meta[58] =
{ 0,
1, 1, 2, 1, 1, 3, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 4, 4, 1, 1,
1, 1, 1, 1, 5, 5, 1, 1, 1, 5,
5, 5, 5, 5, 5, 3, 3, 3, 3, 3,
1, 1, 1, 1, 5, 5, 1, 1, 1, 3,
5, 5, 5, 5, 5, 5, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 1, 1, 1, 1
3, 3, 3, 1, 1, 1, 1
} ;
static const flex_int16_t yy_base[259] =
static const flex_int16_t yy_base[277] =
{ 0,
0, 0, 320, 321, 55, 57, 297, 0, 0, 296,
53, 321, 321, 295, 50, 321, 49, 47, 57, 52,
59, 321, 321, 59, 294, 60, 321, 321, 321, 62,
270, 57, 54, 274, 59, 275, 59, 65, 278, 268,
262, 264, 274, 57, 262, 264, 262, 53, 321, 74,
321, 321, 103, 321, 0, 0, 321, 282, 321, 321,
321, 321, 321, 321, 92, 292, 0, 321, 95, 99,
118, 0, 280, 321, 321, 321, 279, 321, 278, 265,
252, 78, 262, 250, 0, 249, 254, 263, 247, 255,
0, 247, 237, 238, 254, 242, 238, 250, 92, 238,
0, 0, 339, 340, 56, 58, 316, 0, 0, 315,
54, 340, 340, 314, 51, 340, 50, 48, 58, 53,
60, 340, 340, 60, 313, 61, 340, 340, 340, 63,
288, 56, 54, 292, 60, 293, 54, 63, 296, 286,
280, 282, 292, 62, 280, 282, 280, 65, 340, 74,
340, 340, 106, 340, 0, 0, 340, 301, 340, 340,
340, 340, 340, 340, 97, 311, 0, 340, 99, 104,
119, 0, 299, 340, 340, 340, 298, 340, 297, 283,
270, 97, 280, 268, 0, 267, 272, 281, 265, 273,
0, 265, 255, 256, 272, 260, 256, 268, 95, 272,
244, 233, 242, 239, 240, 239, 321, 254, 0, 321,
128, 264, 258, 0, 126, 136, 106, 138, 0, 321,
321, 321, 243, 238, 237, 111, 240, 237, 234, 221,
219, 0, 228, 216, 220, 218, 223, 226, 0, 227,
225, 210, 208, 207, 207, 219, 217, 221, 210, 202,
321, 144, 146, 321, 153, 151, 155, 209, 0, 202,
199, 207, 196, 213, 0, 208, 0, 197, 193, 191,
0, 190, 192, 198, 192, 189, 188, 200, 199, 0,
187, 182, 194, 193, 157, 159, 0, 192, 0, 183,
184, 178, 0, 0, 0, 175, 180, 174, 173, 176,
255, 261, 250, 259, 256, 257, 256, 340, 272, 0,
340, 132, 282, 276, 0, 130, 140, 110, 142, 0,
340, 340, 340, 260, 255, 254, 114, 257, 254, 251,
238, 236, 0, 245, 233, 237, 235, 240, 243, 0,
244, 242, 227, 225, 235, 223, 223, 235, 233, 237,
226, 218, 340, 146, 149, 340, 156, 154, 158, 225,
0, 218, 215, 223, 212, 229, 0, 224, 0, 213,
209, 207, 0, 206, 208, 214, 208, 205, 204, 218,
215, 214, 0, 202, 197, 209, 208, 160, 162, 0,
207, 0, 198, 199, 193, 0, 0, 0, 190, 195,
179, 174, 168, 177, 168, 174, 0, 168, 168, 161,
161, 174, 0, 162, 161, 166, 163, 170, 0, 0,
0, 160, 160, 157, 146, 145, 0, 0, 0, 132,
116, 99, 102, 0, 113, 101, 0, 0, 105, 92,
0, 0, 0, 79, 80, 0, 0, 81, 62, 32,
0, 321, 175, 178, 181, 186, 191, 193
189, 188, 191, 194, 189, 184, 182, 191, 182, 188,
0, 182, 182, 175, 175, 188, 0, 176, 175, 180,
177, 184, 0, 186, 0, 0, 173, 173, 170, 164,
176, 0, 0, 0, 175, 165, 155, 159, 159, 0,
170, 163, 0, 0, 170, 159, 0, 0, 157, 0,
129, 121, 0, 121, 0, 114, 116, 95, 111, 103,
89, 0, 84, 82, 77, 73, 51, 20, 0, 340,
178, 181, 184, 189, 194, 196
} ;
static const flex_int16_t yy_def[259] =
static const flex_int16_t yy_def[277] =
{ 0,
252, 1, 252, 252, 252, 252, 252, 253, 254, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 252, 252,
252, 252, 252, 252, 255, 254, 252, 252, 252, 252,
252, 252, 252, 252, 252, 256, 257, 252, 252, 252,
252, 258, 252, 252, 252, 252, 252, 252, 252, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
270, 1, 270, 270, 270, 270, 270, 271, 272, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 270, 270,
270, 270, 270, 270, 273, 272, 270, 270, 270, 270,
270, 270, 270, 270, 270, 274, 275, 270, 270, 270,
270, 276, 270, 270, 270, 270, 270, 270, 270, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
254, 254, 254, 254, 254, 254, 252, 252, 255, 252,
252, 256, 256, 257, 252, 252, 252, 252, 258, 252,
252, 252, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
252, 252, 252, 252, 252, 252, 252, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 252, 252, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
272, 272, 272, 272, 272, 272, 272, 270, 270, 273,
270, 270, 274, 274, 275, 270, 270, 270, 270, 276,
270, 270, 270, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 270, 270, 270, 270, 270, 270, 270, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 270, 270, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 254, 254, 254, 254, 254, 254,
254, 0, 252, 252, 252, 252, 252, 252
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
272, 272, 272, 272, 272, 272, 272, 272, 272, 0,
270, 270, 270, 270, 270, 270
} ;
static const flex_int16_t yy_nxt[378] =
static const flex_int16_t yy_nxt[398] =
{ 0,
4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 9, 9, 28, 29, 30, 9,
31, 32, 33, 34, 35, 9, 36, 37, 9, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
9, 9, 49, 50, 51, 52, 53, 53, 53, 53,
58, 61, 63, 65, 65, 251, 69, 66, 70, 70,
64, 62, 67, 69, 59, 70, 70, 71, 68, 73,
74, 76, 77, 78, 71, 71, 81, 83, 87, 105,
79, 84, 71, 91, 93, 107, 85, 106, 88, 82,
9, 31, 32, 33, 34, 35, 9, 36, 37, 9,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 9, 9, 49, 50, 51, 52, 53, 53, 53,
53, 58, 61, 63, 65, 65, 269, 69, 66, 70,
70, 64, 62, 67, 69, 59, 70, 70, 71, 68,
73, 74, 76, 77, 78, 71, 81, 71, 83, 91,
87, 79, 84, 93, 71, 108, 92, 85, 268, 82,
92, 89, 72, 100, 53, 53, 101, 94, 65, 65,
250, 115, 115, 69, 125, 70, 70, 111, 249, 126,
116, 141, 118, 118, 71, 111, 248, 108, 116, 117,
247, 117, 71, 246, 118, 118, 245, 142, 143, 152,
244, 152, 115, 115, 153, 153, 243, 156, 242, 156,
241, 155, 157, 157, 118, 118, 161, 162, 240, 155,
153, 153, 153, 153, 185, 239, 185, 157, 157, 186,
186, 157, 157, 186, 186, 186, 186, 55, 238, 55,
56, 56, 56, 109, 109, 109, 112, 112, 112, 112,
112, 114, 237, 114, 114, 114, 119, 119, 236, 235,
88, 100, 106, 89, 72, 267, 94, 53, 53, 101,
107, 266, 102, 65, 65, 116, 116, 265, 69, 264,
70, 70, 112, 263, 117, 142, 119, 119, 109, 71,
118, 112, 118, 117, 126, 119, 119, 262, 71, 127,
261, 143, 144, 154, 260, 154, 116, 116, 155, 155,
259, 158, 258, 158, 257, 157, 159, 159, 119, 119,
163, 164, 155, 155, 157, 155, 155, 188, 256, 188,
159, 159, 189, 189, 159, 159, 189, 189, 189, 189,
55, 255, 55, 56, 56, 56, 110, 110, 110, 113,
113, 113, 113, 113, 115, 254, 115, 115, 115, 120,
120, 253, 252, 251, 250, 249, 248, 247, 246, 245,
244, 243, 242, 241, 240, 239, 238, 237, 236, 235,
234, 233, 232, 231, 230, 229, 228, 227, 226, 225,
224, 223, 222, 221, 220, 219, 218, 217, 216, 215,
214, 213, 212, 211, 210, 209, 208, 207, 206, 205,
204, 203, 202, 201, 200, 199, 198, 197, 196, 195,
194, 193, 192, 191, 190, 189, 188, 187, 184, 183,
194, 193, 192, 191, 190, 187, 186, 185, 184, 183,
182, 181, 180, 179, 178, 177, 176, 175, 174, 173,
172, 171, 170, 169, 168, 167, 166, 165, 164, 163,
160, 159, 158, 154, 113, 151, 150, 149, 148, 147,
146, 145, 144, 140, 139, 138, 137, 136, 135, 134,
133, 132, 131, 130, 129, 128, 127, 124, 123, 122,
172, 171, 170, 169, 168, 167, 166, 165, 162, 161,
160, 156, 114, 153, 152, 151, 150, 149, 148, 147,
146, 145, 141, 140, 139, 138, 137, 136, 135, 134,
133, 132, 131, 130, 129, 128, 125, 124, 123, 122,
121, 114, 111, 105, 104, 103, 99, 98, 97, 96,
95, 90, 86, 80, 75, 60, 57, 54, 270, 3,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270
121, 120, 113, 110, 104, 103, 102, 99, 98, 97,
96, 95, 90, 86, 80, 75, 60, 57, 54, 252,
3, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252
} ;
static const flex_int16_t yy_chk[378] =
static const flex_int16_t yy_chk[398] =
{ 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 5, 5, 6, 6,
11, 15, 17, 18, 18, 250, 20, 19, 20, 20,
17, 15, 19, 21, 11, 21, 21, 20, 19, 24,
24, 26, 26, 30, 21, 20, 32, 33, 35, 48,
30, 33, 21, 37, 38, 50, 33, 48, 35, 32,
1, 1, 1, 1, 1, 1, 1, 5, 5, 6,
6, 11, 15, 17, 18, 18, 268, 20, 19, 20,
20, 17, 15, 19, 21, 11, 21, 21, 20, 19,
24, 24, 26, 26, 30, 21, 32, 20, 33, 37,
35, 30, 33, 38, 21, 50, 37, 33, 267, 32,
37, 35, 20, 44, 53, 53, 44, 38, 65, 65,
249, 69, 69, 70, 82, 70, 70, 65, 248, 82,
69, 99, 117, 117, 70, 65, 245, 50, 69, 71,
244, 71, 70, 240, 71, 71, 239, 99, 99, 111,
236, 111, 115, 115, 111, 111, 235, 116, 233, 116,
232, 115, 116, 116, 118, 118, 126, 126, 231, 115,
152, 152, 153, 153, 155, 230, 155, 156, 156, 155,
155, 157, 157, 185, 185, 186, 186, 253, 226, 253,
254, 254, 254, 255, 255, 255, 256, 256, 256, 256,
256, 257, 225, 257, 257, 257, 258, 258, 224, 223,
35, 44, 48, 35, 20, 266, 38, 53, 53, 44,
48, 265, 44, 65, 65, 69, 69, 264, 70, 263,
70, 70, 65, 261, 69, 99, 118, 118, 50, 70,
71, 65, 71, 69, 82, 71, 71, 260, 70, 82,
259, 99, 99, 112, 258, 112, 116, 116, 112, 112,
257, 117, 256, 117, 254, 116, 117, 117, 119, 119,
127, 127, 154, 154, 116, 155, 155, 157, 252, 157,
158, 158, 157, 157, 159, 159, 188, 188, 189, 189,
271, 251, 271, 272, 272, 272, 273, 273, 273, 274,
274, 274, 274, 274, 275, 249, 275, 275, 275, 276,
222, 218, 217, 216, 215, 214, 212, 211, 210, 209,
208, 206, 205, 204, 203, 202, 201, 200, 199, 198,
197, 196, 192, 191, 190, 188, 184, 183, 182, 181,
179, 178, 177, 176, 175, 174, 173, 172, 170, 169,
168, 166, 164, 163, 162, 161, 160, 158, 150, 149,
148, 147, 146, 145, 144, 143, 142, 141, 140, 138,
137, 136, 135, 134, 133, 131, 130, 129, 128, 127,
125, 124, 123, 113, 112, 108, 106, 105, 104, 103,
102, 101, 100, 98, 97, 96, 95, 94, 93, 92,
90, 89, 88, 87, 86, 84, 83, 81, 80, 79,
276, 246, 245, 242, 241, 239, 238, 237, 236, 235,
231, 230, 229, 228, 227, 224, 222, 221, 220, 219,
218, 216, 215, 214, 213, 212, 210, 209, 208, 207,
206, 205, 204, 203, 202, 201, 200, 199, 195, 194,
193, 191, 187, 186, 185, 184, 182, 181, 180, 179,
178, 177, 176, 175, 174, 172, 171, 170, 168, 166,
165, 164, 163, 162, 160, 152, 151, 150, 149, 148,
147, 146, 145, 144, 143, 142, 141, 139, 138, 137,
136, 135, 134, 132, 131, 130, 129, 128, 126, 125,
124, 114, 113, 109, 107, 106, 105, 104, 103, 102,
101, 100, 98, 97, 96, 95, 94, 93, 92, 90,
89, 88, 87, 86, 84, 83, 81, 80, 79, 77,
73, 66, 58, 47, 46, 45, 43, 42, 41, 40,
39, 36, 34, 31, 25, 14, 10, 7, 3, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270, 270, 270, 270,
270, 270, 270, 270, 270, 270, 270
77, 73, 66, 58, 47, 46, 45, 43, 42, 41,
40, 39, 36, 34, 31, 25, 14, 10, 7, 3,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252, 252, 252, 252,
252, 252, 252, 252, 252, 252, 252
} ;
/* Table of booleans, true if rule could match eol. */
static const flex_int32_t yy_rule_can_match_eol[93] =
static const flex_int32_t yy_rule_can_match_eol[94] =
{ 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, };
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, };
/* The intent behind this definition is that it'll catch
* any uses of REJECT which flex missed.
@ -697,7 +708,7 @@ static const flex_int32_t yy_rule_can_match_eol[93] =
*/
#define YY_NO_UNISTD_H 1
#line 694 "lex.sksl.c"
#line 705 "lex.sksl.c"
#define INITIAL 0
@ -960,7 +971,7 @@ YY_DECL
#line 30 "sksl.flex"
#line 957 "lex.sksl.c"
#line 968 "lex.sksl.c"
while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */
{
@ -987,13 +998,13 @@ yy_match:
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 253 )
if ( yy_current_state >= 271 )
yy_c = yy_meta[yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
++yy_cp;
}
while ( yy_current_state != 252 );
while ( yy_current_state != 270 );
yy_cp = yyg->yy_last_accepting_cpos;
yy_current_state = yyg->yy_last_accepting_state;
@ -1198,296 +1209,301 @@ YY_RULE_SETUP
case 35:
YY_RULE_SETUP
#line 100 "sksl.flex"
{ return SkSL::Token::STRUCT; }
{ return SkSL::Token::HASSIDEEFFECTS; }
YY_BREAK
case 36:
YY_RULE_SETUP
#line 102 "sksl.flex"
{ return SkSL::Token::LAYOUT; }
{ return SkSL::Token::STRUCT; }
YY_BREAK
case 37:
YY_RULE_SETUP
#line 104 "sksl.flex"
{ return SkSL::Token::PRECISION; }
{ return SkSL::Token::LAYOUT; }
YY_BREAK
case 38:
YY_RULE_SETUP
#line 106 "sksl.flex"
{ return SkSL::Token::IDENTIFIER; }
{ return SkSL::Token::PRECISION; }
YY_BREAK
case 39:
YY_RULE_SETUP
#line 108 "sksl.flex"
{ return SkSL::Token::DIRECTIVE; }
{ return SkSL::Token::IDENTIFIER; }
YY_BREAK
case 40:
YY_RULE_SETUP
#line 110 "sksl.flex"
{ return SkSL::Token::LPAREN; }
{ return SkSL::Token::DIRECTIVE; }
YY_BREAK
case 41:
YY_RULE_SETUP
#line 112 "sksl.flex"
{ return SkSL::Token::RPAREN; }
{ return SkSL::Token::LPAREN; }
YY_BREAK
case 42:
YY_RULE_SETUP
#line 114 "sksl.flex"
{ return SkSL::Token::LBRACE; }
{ return SkSL::Token::RPAREN; }
YY_BREAK
case 43:
YY_RULE_SETUP
#line 116 "sksl.flex"
{ return SkSL::Token::RBRACE; }
{ return SkSL::Token::LBRACE; }
YY_BREAK
case 44:
YY_RULE_SETUP
#line 118 "sksl.flex"
{ return SkSL::Token::LBRACKET; }
{ return SkSL::Token::RBRACE; }
YY_BREAK
case 45:
YY_RULE_SETUP
#line 120 "sksl.flex"
{ return SkSL::Token::RBRACKET; }
{ return SkSL::Token::LBRACKET; }
YY_BREAK
case 46:
YY_RULE_SETUP
#line 122 "sksl.flex"
{ return SkSL::Token::DOT; }
{ return SkSL::Token::RBRACKET; }
YY_BREAK
case 47:
YY_RULE_SETUP
#line 124 "sksl.flex"
{ return SkSL::Token::COMMA; }
{ return SkSL::Token::DOT; }
YY_BREAK
case 48:
YY_RULE_SETUP
#line 126 "sksl.flex"
{ return SkSL::Token::PLUSPLUS; }
{ return SkSL::Token::COMMA; }
YY_BREAK
case 49:
YY_RULE_SETUP
#line 128 "sksl.flex"
{ return SkSL::Token::MINUSMINUS; }
{ return SkSL::Token::PLUSPLUS; }
YY_BREAK
case 50:
YY_RULE_SETUP
#line 130 "sksl.flex"
{ return SkSL::Token::PLUS; }
{ return SkSL::Token::MINUSMINUS; }
YY_BREAK
case 51:
YY_RULE_SETUP
#line 132 "sksl.flex"
{ return SkSL::Token::MINUS; }
{ return SkSL::Token::PLUS; }
YY_BREAK
case 52:
YY_RULE_SETUP
#line 134 "sksl.flex"
{ return SkSL::Token::STAR; }
{ return SkSL::Token::MINUS; }
YY_BREAK
case 53:
YY_RULE_SETUP
#line 136 "sksl.flex"
{ return SkSL::Token::SLASH; }
{ return SkSL::Token::STAR; }
YY_BREAK
case 54:
YY_RULE_SETUP
#line 138 "sksl.flex"
{ return SkSL::Token::PERCENT; }
{ return SkSL::Token::SLASH; }
YY_BREAK
case 55:
YY_RULE_SETUP
#line 140 "sksl.flex"
{ return SkSL::Token::SHL; }
{ return SkSL::Token::PERCENT; }
YY_BREAK
case 56:
YY_RULE_SETUP
#line 142 "sksl.flex"
{ return SkSL::Token::SHR; }
{ return SkSL::Token::SHL; }
YY_BREAK
case 57:
YY_RULE_SETUP
#line 144 "sksl.flex"
{ return SkSL::Token::BITWISEOR; }
{ return SkSL::Token::SHR; }
YY_BREAK
case 58:
YY_RULE_SETUP
#line 146 "sksl.flex"
{ return SkSL::Token::BITWISEXOR; }
{ return SkSL::Token::BITWISEOR; }
YY_BREAK
case 59:
YY_RULE_SETUP
#line 148 "sksl.flex"
{ return SkSL::Token::BITWISEAND; }
{ return SkSL::Token::BITWISEXOR; }
YY_BREAK
case 60:
YY_RULE_SETUP
#line 150 "sksl.flex"
{ return SkSL::Token::BITWISENOT; }
{ return SkSL::Token::BITWISEAND; }
YY_BREAK
case 61:
YY_RULE_SETUP
#line 152 "sksl.flex"
{ return SkSL::Token::LOGICALOR; }
{ return SkSL::Token::BITWISENOT; }
YY_BREAK
case 62:
YY_RULE_SETUP
#line 154 "sksl.flex"
{ return SkSL::Token::LOGICALXOR; }
{ return SkSL::Token::LOGICALOR; }
YY_BREAK
case 63:
YY_RULE_SETUP
#line 156 "sksl.flex"
{ return SkSL::Token::LOGICALAND; }
{ return SkSL::Token::LOGICALXOR; }
YY_BREAK
case 64:
YY_RULE_SETUP
#line 158 "sksl.flex"
{ return SkSL::Token::LOGICALNOT; }
{ return SkSL::Token::LOGICALAND; }
YY_BREAK
case 65:
YY_RULE_SETUP
#line 160 "sksl.flex"
{ return SkSL::Token::QUESTION; }
{ return SkSL::Token::LOGICALNOT; }
YY_BREAK
case 66:
YY_RULE_SETUP
#line 162 "sksl.flex"
{ return SkSL::Token::COLON; }
{ return SkSL::Token::QUESTION; }
YY_BREAK
case 67:
YY_RULE_SETUP
#line 164 "sksl.flex"
{ return SkSL::Token::EQ; }
{ return SkSL::Token::COLON; }
YY_BREAK
case 68:
YY_RULE_SETUP
#line 166 "sksl.flex"
{ return SkSL::Token::EQEQ; }
{ return SkSL::Token::EQ; }
YY_BREAK
case 69:
YY_RULE_SETUP
#line 168 "sksl.flex"
{ return SkSL::Token::NEQ; }
{ return SkSL::Token::EQEQ; }
YY_BREAK
case 70:
YY_RULE_SETUP
#line 170 "sksl.flex"
{ return SkSL::Token::GT; }
{ return SkSL::Token::NEQ; }
YY_BREAK
case 71:
YY_RULE_SETUP
#line 172 "sksl.flex"
{ return SkSL::Token::LT; }
{ return SkSL::Token::GT; }
YY_BREAK
case 72:
YY_RULE_SETUP
#line 174 "sksl.flex"
{ return SkSL::Token::GTEQ; }
{ return SkSL::Token::LT; }
YY_BREAK
case 73:
YY_RULE_SETUP
#line 176 "sksl.flex"
{ return SkSL::Token::LTEQ; }
{ return SkSL::Token::GTEQ; }
YY_BREAK
case 74:
YY_RULE_SETUP
#line 178 "sksl.flex"
{ return SkSL::Token::PLUSEQ; }
{ return SkSL::Token::LTEQ; }
YY_BREAK
case 75:
YY_RULE_SETUP
#line 180 "sksl.flex"
{ return SkSL::Token::MINUSEQ; }
{ return SkSL::Token::PLUSEQ; }
YY_BREAK
case 76:
YY_RULE_SETUP
#line 182 "sksl.flex"
{ return SkSL::Token::STAREQ; }
{ return SkSL::Token::MINUSEQ; }
YY_BREAK
case 77:
YY_RULE_SETUP
#line 184 "sksl.flex"
{ return SkSL::Token::SLASHEQ; }
{ return SkSL::Token::STAREQ; }
YY_BREAK
case 78:
YY_RULE_SETUP
#line 186 "sksl.flex"
{ return SkSL::Token::PERCENTEQ; }
{ return SkSL::Token::SLASHEQ; }
YY_BREAK
case 79:
YY_RULE_SETUP
#line 188 "sksl.flex"
{ return SkSL::Token::SHLEQ; }
{ return SkSL::Token::PERCENTEQ; }
YY_BREAK
case 80:
YY_RULE_SETUP
#line 190 "sksl.flex"
{ return SkSL::Token::SHREQ; }
{ return SkSL::Token::SHLEQ; }
YY_BREAK
case 81:
YY_RULE_SETUP
#line 192 "sksl.flex"
{ return SkSL::Token::BITWISEOREQ; }
{ return SkSL::Token::SHREQ; }
YY_BREAK
case 82:
YY_RULE_SETUP
#line 194 "sksl.flex"
{ return SkSL::Token::BITWISEXOREQ; }
{ return SkSL::Token::BITWISEOREQ; }
YY_BREAK
case 83:
YY_RULE_SETUP
#line 196 "sksl.flex"
{ return SkSL::Token::BITWISEANDEQ; }
{ return SkSL::Token::BITWISEXOREQ; }
YY_BREAK
case 84:
YY_RULE_SETUP
#line 198 "sksl.flex"
{ return SkSL::Token::LOGICALOREQ; }
{ return SkSL::Token::BITWISEANDEQ; }
YY_BREAK
case 85:
YY_RULE_SETUP
#line 200 "sksl.flex"
{ return SkSL::Token::LOGICALXOREQ; }
{ return SkSL::Token::LOGICALOREQ; }
YY_BREAK
case 86:
YY_RULE_SETUP
#line 202 "sksl.flex"
{ return SkSL::Token::LOGICALANDEQ; }
{ return SkSL::Token::LOGICALXOREQ; }
YY_BREAK
case 87:
YY_RULE_SETUP
#line 204 "sksl.flex"
{ return SkSL::Token::SEMICOLON; }
{ return SkSL::Token::LOGICALANDEQ; }
YY_BREAK
case 88:
YY_RULE_SETUP
#line 206 "sksl.flex"
/* line comment */
{ return SkSL::Token::SEMICOLON; }
YY_BREAK
case 89:
/* rule 89 can match eol */
YY_RULE_SETUP
#line 208 "sksl.flex"
/* block comment */
/* line comment */
YY_BREAK
case 90:
/* rule 90 can match eol */
YY_RULE_SETUP
#line 210 "sksl.flex"
/* whitespace */
/* block comment */
YY_BREAK
case 91:
/* rule 91 can match eol */
YY_RULE_SETUP
#line 212 "sksl.flex"
{ return SkSL::Token::INVALID_TOKEN; }
/* whitespace */
YY_BREAK
case 92:
YY_RULE_SETUP
#line 214 "sksl.flex"
{ return SkSL::Token::INVALID_TOKEN; }
YY_BREAK
case 93:
YY_RULE_SETUP
#line 216 "sksl.flex"
ECHO;
YY_BREAK
#line 1484 "lex.sksl.c"
#line 1500 "lex.sksl.c"
case YY_STATE_EOF(INITIAL):
yyterminate();
@ -1783,7 +1799,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 253 )
if ( yy_current_state >= 271 )
yy_c = yy_meta[yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
@ -1812,11 +1828,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
{
yy_current_state = (int) yy_def[yy_current_state];
if ( yy_current_state >= 253 )
if ( yy_current_state >= 271 )
yy_c = yy_meta[yy_c];
}
yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
yy_is_jam = (yy_current_state == 252);
yy_is_jam = (yy_current_state == 270);
(void)yyg;
return yy_is_jam ? 0 : yy_current_state;
@ -2664,7 +2680,7 @@ void skslfree (void * ptr , yyscan_t yyscanner)
#define YYTABLES_NAME "yytables"
#line 214 "sksl.flex"
#line 216 "sksl.flex"
int skslwrap(yyscan_t scanner) {

View File

@ -97,6 +97,8 @@ volatile { return SkSL::Token::VOLATILE; }
restrict { return SkSL::Token::RESTRICT; }
sk_has_side_effects { return SkSL::Token::HASSIDEEFFECTS; }
struct { return SkSL::Token::STRUCT; }
layout { return SkSL::Token::LAYOUT; }

View File

@ -14,7 +14,7 @@ layout(builtin=9999) vec4 gl_LastFragColor;
layout(builtin=9999) vec4 gl_LastFragColorARM;
layout(builtin=9999) int gl_SampleMaskIn[1];
layout(builtin=9999) out int gl_SampleMask[1];
layout(builtin=9999) vec4 gl_SecondaryFragColorEXT;
layout(builtin=9999) out vec4 gl_SecondaryFragColorEXT;
layout(location=0,index=0,builtin=10001) out vec4 sk_FragColor;

View File

@ -16,9 +16,9 @@ out sk_PerVertex {
layout(builtin=8) int sk_InvocationID;
void EmitStreamVertex(int stream);
void EndStreamPrimitive(int stream);
void EmitVertex();
void EndPrimitive();
sk_has_side_effects void EmitStreamVertex(int stream);
sk_has_side_effects void EndStreamPrimitive(int stream);
sk_has_side_effects void EmitVertex();
sk_has_side_effects void EndPrimitive();
)

View File

@ -11,6 +11,13 @@
#if SK_SUPPORT_GPU
// Note that the optimizer will aggressively kill dead code and substitute constants in place of
// variables, so we have to jump through a few hoops to ensure that the code in these tests has the
// necessary side-effects to remain live. In some cases we rely on the optimizer not (yet) being
// smart enough to optimize around certain constructs; as the optimizer gets smarter it will
// undoubtedly end up breaking some of these tests. That is a good thing, as long as the new code is
// equivalent!
static void test(skiatest::Reporter* r, const char* src, const SkSL::Program::Settings& settings,
const char* expected, SkSL::Program::Inputs* inputs,
SkSL::Program::Kind kind = SkSL::Program::kFragment_Kind) {
@ -57,7 +64,7 @@ DEF_TEST(SkSLControl, r) {
"void main() {"
"if (sqrt(2) > 5) { sk_FragColor = vec4(0.75); } else { discard; }"
"int i = 0;"
"while (i < 10) sk_FragColor *= 0.5;"
"while (i < 10) { sk_FragColor *= 0.5; i++; }"
"do { sk_FragColor += 0.01; } while (sk_FragColor.x < 0.75);"
"for (int i = 0; i < 10; i++) {"
"if (i % 2 == 1) break; else continue;"
@ -74,7 +81,10 @@ DEF_TEST(SkSLControl, r) {
" discard;\n"
" }\n"
" int i = 0;\n"
" while (true) sk_FragColor *= 0.5;\n"
" while (i < 10) {\n"
" sk_FragColor *= 0.5;\n"
" i++;\n"
" }\n"
" do {\n"
" sk_FragColor += 0.01;\n"
" } while (sk_FragColor.x < 0.75);\n"
@ -105,8 +115,8 @@ DEF_TEST(SkSLFunctions, r) {
"}\n"
"void main() {\n"
" float x = 10.0;\n"
" bar(10.0);\n"
" sk_FragColor = vec4(10.0);\n"
" bar(x);\n"
" sk_FragColor = vec4(x);\n"
"}\n");
}
@ -165,16 +175,16 @@ DEF_TEST(SkSLMatrices, r) {
"mat3x4 z = x * y;"
"vec3 v1 = mat3(1) * vec3(1);"
"vec3 v2 = vec3(1) * mat3(1);"
"sk_FragColor = vec4(z[0].x, v1 + v2);"
"}",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" mat2x4 x = mat2x4(1.0);\n"
" mat3x2 y = mat3x2(1.0, 0.0, 0.0, 1.0, vec2(2.0, 2.0));\n"
" mat3x4 z = x * y;\n"
" mat3x4 z = mat2x4(1.0) * mat3x2(1.0, 0.0, 0.0, 1.0, vec2(2.0, 2.0));\n"
" vec3 v1 = mat3(1.0) * vec3(1.0);\n"
" vec3 v2 = vec3(1.0) * mat3(1.0);\n"
" sk_FragColor = vec4(z[0].x, v1 + v2);\n"
"}\n");
}
@ -289,16 +299,21 @@ DEF_TEST(SkSLVersion, r) {
DEF_TEST(SkSLUsesPrecisionModifiers, r) {
test(r,
"void main() { float x = 0.75; highp float y = 1; }",
"void main() { float x = 0.75; highp float y = 1; x++; y++;"
"sk_FragColor.rg = vec2(x, y); }",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float x = 0.75;\n"
" float y = 1.0;\n"
" x++;\n"
" y++;\n"
" sk_FragColor.xy = vec2(x, y);\n"
"}\n");
test(r,
"void main() { float x = 0.75; highp float y = 1; }",
"void main() { float x = 0.75; highp float y = 1; x++; y++;"
"sk_FragColor.rg = vec2(x, y); }",
*SkSL::ShaderCapsFactory::UsesPrecisionModifiers(),
"#version 400\n"
"precision highp float;\n"
@ -306,6 +321,9 @@ DEF_TEST(SkSLUsesPrecisionModifiers, r) {
"void main() {\n"
" float x = 0.75;\n"
" highp float y = 1.0;\n"
" x++;\n"
" y++;\n"
" sk_FragColor.xy = vec2(x, y);\n"
"}\n");
}
@ -313,20 +331,19 @@ DEF_TEST(SkSLMinAbs, r) {
test(r,
"void main() {"
"float x = -5;"
"x = min(abs(x), 6);"
"sk_FragColor.r = min(abs(x), 6);"
"}",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float x = -5.0;\n"
" x = min(abs(-5.0), 6.0);\n"
" sk_FragColor.x = min(abs(-5.0), 6.0);\n"
"}\n");
test(r,
"void main() {"
"float x = -5.0;"
"x = min(abs(x), 6.0);"
"sk_FragColor.r = min(abs(x), 6.0);"
"}",
*SkSL::ShaderCapsFactory::CannotUseMinAndAbsTogether(),
"#version 400\n"
@ -334,30 +351,29 @@ DEF_TEST(SkSLMinAbs, r) {
"void main() {\n"
" float minAbsHackVar0;\n"
" float minAbsHackVar1;\n"
" float x = -5.0;\n"
" x = ((minAbsHackVar0 = abs(-5.0)) < (minAbsHackVar1 = 6.0) ? minAbsHackVar0 : "
"minAbsHackVar1);\n"
" sk_FragColor.x = ((minAbsHackVar0 = abs(-5.0)) < (minAbsHackVar1 = 6.0) ? "
"minAbsHackVar0 : minAbsHackVar1);\n"
"}\n");
}
DEF_TEST(SkSLNegatedAtan, r) {
test(r,
"void main() { vec2 x = vec2(1, 2); float y = atan(x.x, -(2 * x.y)); }",
"void main() { vec2 x = vec2(sqrt(2)); sk_FragColor.r = atan(x.x, -x.y); }",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" vec2 x = vec2(1.0, 2.0);\n"
" float y = atan(x.x, -(2.0 * x.y));\n"
" vec2 x = vec2(sqrt(2.0));\n"
" sk_FragColor.x = atan(x.x, -x.y);\n"
"}\n");
test(r,
"void main() { vec2 x = vec2(1, 2); float y = atan(x.x, -(2 * x.y)); }",
"void main() { vec2 x = vec2(sqrt(2)); sk_FragColor.r = atan(x.x, -x.y); }",
*SkSL::ShaderCapsFactory::MustForceNegatedAtanParamToFloat(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" vec2 x = vec2(1.0, 2.0);\n"
" float y = atan(x.x, -1.0 * (2.0 * x.y));\n"
" vec2 x = vec2(sqrt(2.0));\n"
" sk_FragColor.x = atan(x.x, -1.0 * x.y);\n"
"}\n");
}
@ -377,28 +393,46 @@ DEF_TEST(SkSLHex, r) {
test(r,
"void main() {"
"int i1 = 0x0;"
"i1++;"
"int i2 = 0x1234abcd;"
"i2++;"
"int i3 = 0x7fffffff;"
"i3++;"
"int i4 = 0xffffffff;"
"i4++;"
"int i5 = -0xbeef;"
"i5++;"
"uint u1 = 0x0;"
"u1++;"
"uint u2 = 0x1234abcd;"
"u2++;"
"uint u3 = 0x7fffffff;"
"u3++;"
"uint u4 = 0xffffffff;"
"u4++;"
"}",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" int i1 = 0;\n"
" i1++;\n"
" int i2 = 305441741;\n"
" i2++;\n"
" int i3 = 2147483647;\n"
" i3++;\n"
" int i4 = -1;\n"
" i4++;\n"
" int i5 = -48879;\n"
" i5++;\n"
" uint u1 = 0u;\n"
" u1++;\n"
" uint u2 = 305441741u;\n"
" u2++;\n"
" uint u3 = 2147483647u;\n"
" u3++;\n"
" uint u4 = 4294967295u;\n"
" u4++;\n"
"}\n");
}
@ -438,29 +472,29 @@ DEF_TEST(SkSLArrayConstructors, r) {
DEF_TEST(SkSLDerivatives, r) {
test(r,
"void main() { float x = dFdx(1); }",
"void main() { sk_FragColor.r = dFdx(1); }",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float x = dFdx(1.0);\n"
" sk_FragColor.x = dFdx(1.0);\n"
"}\n");
test(r,
"void main() { float x = 1; }",
"void main() { sk_FragColor.r = 1; }",
*SkSL::ShaderCapsFactory::ShaderDerivativeExtensionString(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float x = 1.0;\n"
" sk_FragColor.x = 1.0;\n"
"}\n");
test(r,
"void main() { float x = dFdx(1); }",
"void main() { sk_FragColor.r = dFdx(1); }",
*SkSL::ShaderCapsFactory::ShaderDerivativeExtensionString(),
"#version 400\n"
"#extension GL_OES_standard_derivatives : require\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float x = dFdx(1.0);\n"
" sk_FragColor.x = dFdx(1.0);\n"
"}\n");
}
@ -468,76 +502,121 @@ DEF_TEST(SkSLConstantFolding, r) {
test(r,
"void main() {"
"float f_add = 32 + 2;"
"sk_FragColor.r = f_add;"
"float f_sub = 32 - 2;"
"sk_FragColor.r = f_sub;"
"float f_mul = 32 * 2;"
"sk_FragColor.r = f_mul;"
"float f_div = 32 / 2;"
"sk_FragColor.r = f_div;"
"float mixed = (12 > 2.0) ? (10 * 2 / 5 + 18 - 3) : 0;"
"sk_FragColor.r = mixed;"
"int i_add = 32 + 2;"
"sk_FragColor.r = i_add;"
"int i_sub = 32 - 2;"
"sk_FragColor.r = i_sub;"
"int i_mul = 32 * 2;"
"sk_FragColor.r = i_mul;"
"int i_div = 32 / 2;"
"sk_FragColor.r = i_div;"
"int i_or = 12 | 6;"
"sk_FragColor.r = i_or;"
"int i_and = 254 & 7;"
"sk_FragColor.r = i_and;"
"int i_xor = 2 ^ 7;"
"sk_FragColor.r = i_xor;"
"int i_shl = 1 << 4;"
"sk_FragColor.r = i_shl;"
"int i_shr = 128 >> 2;"
"sk_FragColor.r = i_shr;"
"bool gt_it = 6 > 5;"
"sk_FragColor.r = true ? 1 : 0;"
"bool gt_if = 6 > 6;"
"sk_FragColor.r = gt_if ? 1 : 0;"
"bool gt_ft = 6.0 > 5.0;"
"sk_FragColor.r = gt_ft ? 1 : 0;"
"bool gt_ff = 6.0 > 6.0;"
"sk_FragColor.r = gt_ff ? 1 : 0;"
"bool gte_it = 6 >= 6;"
"sk_FragColor.r = gte_it ? 1 : 0;"
"bool gte_if = 6 >= 7;"
"sk_FragColor.r = gte_if ? 1 : 0;"
"bool gte_ft = 6.0 >= 6.0;"
"sk_FragColor.r = gte_ft ? 1 : 0;"
"bool gte_ff = 6.0 >= 7.0;"
"sk_FragColor.r = gte_ff ? 1 : 0;"
"bool lte_it = 6 <= 6;"
"sk_FragColor.r = lte_it ? 1 : 0;"
"bool lte_if = 6 <= 5;"
"sk_FragColor.r = lte_if ? 1 : 0;"
"bool lte_ft = 6.0 <= 6.0;"
"sk_FragColor.r = lte_ft ? 1 : 0;"
"bool lte_ff = 6.0 <= 5.0;"
"sk_FragColor.r = lte_ff ? 1 : 0;"
"bool or_t = 1 == 1 || 2 == 8;"
"sk_FragColor.r = or_t ? 1 : 0;"
"bool or_f = 1 > 1 || 2 == 8;"
"sk_FragColor.r = or_f ? 1 : 0;"
"bool and_t = 1 == 1 && 2 <= 8;"
"sk_FragColor.r = and_t ? 1 : 0;"
"bool and_f = 1 == 2 && 2 == 8;"
"sk_FragColor.r = and_f ? 1 : 0;"
"bool xor_t = 1 == 1 ^^ 1 != 1;"
"sk_FragColor.r = xor_t ? 1 : 0;"
"bool xor_f = 1 == 1 ^^ 1 == 1;"
"sk_FragColor.r = xor_f ? 1 : 0;"
"int ternary = 10 > 5 ? 10 : 5;"
"sk_FragColor.r = ternary;"
"sk_FragColor.r = vec4(0.5, 1, 1, 1).x;"
"sk_FragColor = vec4(vec2(1), vec2(2, 3)) + vec4(5, 6, 7, 8);"
"sk_FragColor = vec4(8, vec3(10)) - vec4(1);"
"sk_FragColor = vec4(2) * vec4(1, 2, 3, 4);"
"sk_FragColor = vec4(12) / vec4(1, 2, 3, 4);"
"sk_FragColor.r = (vec4(12) / vec4(1, 2, 3, 4)).y;"
"}",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float f_add = 34.0;\n"
" float f_sub = 30.0;\n"
" float f_mul = 64.0;\n"
" float f_div = 16.0;\n"
" float mixed = 19.0;\n"
" int i_add = 34;\n"
" int i_sub = 30;\n"
" int i_mul = 64;\n"
" int i_div = 16;\n"
" int i_or = 14;\n"
" int i_and = 6;\n"
" int i_xor = 5;\n"
" int i_shl = 16;\n"
" int i_shr = 32;\n"
" bool gt_it = true;\n"
" bool gt_if = false;\n"
" bool gt_ft = true;\n"
" bool gt_ff = false;\n"
" bool gte_it = true;\n"
" bool gte_if = false;\n"
" bool gte_ft = true;\n"
" bool gte_ff = false;\n"
" bool lte_it = true;\n"
" bool lte_if = false;\n"
" bool lte_ft = true;\n"
" bool lte_ff = false;\n"
" bool or_t = true;\n"
" bool or_f = false;\n"
" bool and_t = true;\n"
" bool and_f = false;\n"
" bool xor_t = true;\n"
" bool xor_f = false;\n"
" int ternary = 10;\n"
" sk_FragColor.x = 34.0;\n"
" sk_FragColor.x = 30.0;\n"
" sk_FragColor.x = 64.0;\n"
" sk_FragColor.x = 16.0;\n"
" sk_FragColor.x = 19.0;\n"
" sk_FragColor.x = 34.0;\n"
" sk_FragColor.x = 30.0;\n"
" sk_FragColor.x = 64.0;\n"
" sk_FragColor.x = 16.0;\n"
" sk_FragColor.x = 14.0;\n"
" sk_FragColor.x = 6.0;\n"
" sk_FragColor.x = 5.0;\n"
" sk_FragColor.x = 16.0;\n"
" sk_FragColor.x = 32.0;\n"
" sk_FragColor.x = 1.0;\n"
" sk_FragColor.x = 0.0;\n"
" sk_FragColor.x = 1.0;\n"
" sk_FragColor.x = 0.0;\n"
" sk_FragColor.x = 1.0;\n"
" sk_FragColor.x = 0.0;\n"
" sk_FragColor.x = 1.0;\n"
" sk_FragColor.x = 0.0;\n"
" sk_FragColor.x = 1.0;\n"
" sk_FragColor.x = 0.0;\n"
" sk_FragColor.x = 1.0;\n"
" sk_FragColor.x = 0.0;\n"
" sk_FragColor.x = 1.0;\n"
" sk_FragColor.x = 0.0;\n"
" sk_FragColor.x = 1.0;\n"
" sk_FragColor.x = 0.0;\n"
" sk_FragColor.x = 1.0;\n"
" sk_FragColor.x = 0.0;\n"
" sk_FragColor.x = 10.0;\n"
" sk_FragColor.x = 0.5;\n"
" sk_FragColor = vec4(6.0, 7.0, 9.0, 11.0);\n"
" sk_FragColor = vec4(7.0, 9.0, 9.0, 9.0);\n"
" sk_FragColor = vec4(2.0, 4.0, 6.0, 8.0);\n"
" sk_FragColor = vec4(12.0, 6.0, 4.0, 3.0);\n"
" sk_FragColor.x = 6.0;\n"
"}\n");
}
@ -549,40 +628,34 @@ DEF_TEST(SkSLStaticIf, r) {
"if (2 > 1) x = 2; else x = 3;"
"if (1 > 2) x = 4; else x = 5;"
"if (false) x = 6;"
"sk_FragColor.r = x;"
"}",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" int x;\n"
" x = 1;\n"
" x = 2;\n"
" x = 5;\n"
" {\n"
" }\n"
" sk_FragColor.x = 5.0;\n"
"}\n");
}
DEF_TEST(SkSLCaps, r) {
test(r,
"void main() {"
"int x;"
"int x = 0;"
"int y = 0;"
"int z = 0;"
"int w = 0;"
"if (sk_Caps.externalTextureSupport) x = 1;"
"if (sk_Caps.fbFetchSupport) x = 2;"
"if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.texelFetchSupport) x = 3;"
"if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.canUseAnyFunctionInShader) x = 4;"
"if (sk_Caps.fbFetchSupport) y = 1;"
"if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.texelFetchSupport) z = 1;"
"if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.canUseAnyFunctionInShader) w = 1;"
"sk_FragColor = vec4(x, y, z, w);"
"}",
*SkSL::ShaderCapsFactory::VariousCaps(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" int x;\n"
" x = 1;\n"
" {\n"
" }\n"
" x = 3;\n"
" {\n"
" }\n"
" sk_FragColor = vec4(1.0, 0.0, 1.0, 0.0);\n"
"}\n");
}
@ -595,6 +668,7 @@ DEF_TEST(SkSLTexture, r) {
"vec4 b = texture(two, vec2(0));"
"vec4 c = texture(one, vec2(0));"
"vec4 d = texture(two, vec3(0));"
"sk_FragColor = vec4(a.x, b.x, c.x, d.x);"
"}",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
@ -606,6 +680,7 @@ DEF_TEST(SkSLTexture, r) {
" vec4 b = texture(two, vec2(0.0));\n"
" vec4 c = textureProj(one, vec2(0.0));\n"
" vec4 d = textureProj(two, vec3(0.0));\n"
" sk_FragColor = vec4(a.x, b.x, c.x, d.x);\n"
"}\n");
test(r,
"uniform sampler1D one;"
@ -615,6 +690,7 @@ DEF_TEST(SkSLTexture, r) {
"vec4 b = texture(two, vec2(0));"
"vec4 c = texture(one, vec2(0));"
"vec4 d = texture(two, vec3(0));"
"sk_FragColor = vec4(a.x, b.x, c.x, d.x);"
"}",
*SkSL::ShaderCapsFactory::Version110(),
"#version 110\n"
@ -625,6 +701,7 @@ DEF_TEST(SkSLTexture, r) {
" vec4 b = texture2D(two, vec2(0.0));\n"
" vec4 c = texture1DProj(one, vec2(0.0));\n"
" vec4 d = texture2DProj(two, vec3(0.0));\n"
" gl_FragColor = vec4(a.x, b.x, c.x, d.x);\n"
"}\n");
}
@ -740,13 +817,14 @@ DEF_TEST(SkSLClipDistance, r) {
DEF_TEST(SkSLArrayTypes, r) {
test(r,
"void main() { vec2 x[2] = vec2[2](vec2(1), vec2(2));"
"vec2[2] y = vec2[2](vec2(3), vec2(4)); }",
"vec2[2] y = vec2[2](vec2(3), vec2(4));"
"sk_FragColor = vec4(x[0], y[1]); }",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" vec2 x[2] = vec2[2](vec2(1.0), vec2(2.0));\n"
" vec2[2] y = vec2[2](vec2(3.0), vec2(4.0));\n"
" sk_FragColor = vec4(vec2[2](vec2(1.0), vec2(2.0))[0], "
"vec2[2](vec2(3.0), vec2(4.0))[1]);\n"
"}\n");
}
@ -827,14 +905,13 @@ DEF_TEST(SkSLSwitch, r) {
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float x;\n"
" switch (2) {\n"
" case 0:\n"
" x = 0.0;\n"
" ;\n"
" case 1:\n"
" x = 1.0;\n"
" ;\n"
" default:\n"
" x = 2.0;\n"
" ;\n"
" }\n"
" sk_FragColor = vec4(2.0);\n"
"}\n");
@ -903,4 +980,26 @@ DEF_TEST(SkSLRectangleTexture, r) {
"}\n");
}
DEF_TEST(SkSLUnusedVars, r) {
test(r,
"void main() {"
"float a = 1, b = 2, c = 3;"
"float d = c;"
"float e = d;"
"b++;"
"d++;"
"sk_FragColor = vec4(b, b, d, d);"
"}",
*SkSL::ShaderCapsFactory::Default(),
"#version 400\n"
"out vec4 sk_FragColor;\n"
"void main() {\n"
" float b = 2.0;\n"
" float d = 3.0;\n"
" b++;\n"
" d++;\n"
" sk_FragColor = vec4(b, b, d, d);\n"
"}\n");
}
#endif