Commit 1b896c62 by Corentin Wallez Committed by Commit Bot

translator: remove code related to for-loop unrolling

For loop unrolling is not used and causes the translator fuzzer to find a hang when unrolling tons of nested loops (duh). Also remove MMap.h which was unused. This is essentially a revert of https://codereview.appspot.com/4331048 BUG=chromium:665255 Change-Id: Id6940f7e306d4ed53bc992f751e9ffe733190f17 Reviewed-on: https://chromium-review.googlesource.com/412023 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 5437006c
...@@ -75,12 +75,29 @@ const ShCompileOptions SH_OBJECT_CODE = UINT64_C(1) << 2; ...@@ -75,12 +75,29 @@ const ShCompileOptions SH_OBJECT_CODE = UINT64_C(1) << 2;
const ShCompileOptions SH_VARIABLES = UINT64_C(1) << 3; const ShCompileOptions SH_VARIABLES = UINT64_C(1) << 3;
const ShCompileOptions SH_LINE_DIRECTIVES = UINT64_C(1) << 4; const ShCompileOptions SH_LINE_DIRECTIVES = UINT64_C(1) << 4;
const ShCompileOptions SH_SOURCE_PATH = UINT64_C(1) << 5; const ShCompileOptions SH_SOURCE_PATH = UINT64_C(1) << 5;
const ShCompileOptions SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = UINT64_C(1) << 6;
// If a sampler array index happens to be a loop index, // This flag will keep invariant declaration for input in fragment shader for GLSL >=4.20 on AMD.
// 1) if its type is integer, unroll the loop. // From GLSL >= 4.20, it's optional to add invariant for fragment input, but GPU vendors have
// 2) if its type is float, fail the shader compile. // different implementations about this. Some drivers forbid invariant in fragment for GLSL>= 4.20,
// This is to work around a mac driver bug. // e.g. Linux Mesa, some drivers treat that as optional, e.g. NVIDIA, some drivers require invariant
const ShCompileOptions SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX = UINT64_C(1) << 7; // must match between vertex and fragment shader, e.g. AMD. The behavior on AMD is obviously wrong.
// Remove invariant for input in fragment shader to workaround the restriction on Intel Mesa.
// But don't remove on AMD Linux to avoid triggering the bug on AMD.
const ShCompileOptions SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT = UINT64_C(1) << 6;
// Due to spec difference between GLSL 4.1 or lower and ESSL3, some platforms (for example, Mac OSX
// core profile) require a variable's "invariant"/"centroid" qualifiers to match between vertex and
// fragment shader. A simple solution to allow such shaders to link is to omit the two qualifiers.
// AMD driver in Linux requires invariant qualifier to match between vertex and fragment shaders,
// while ESSL3 disallows invariant qualifier in fragment shader and GLSL >= 4.2 doesn't require
// invariant qualifier to match between shaders. Remove invariant qualifier from vertex shader to
// workaround AMD driver bug.
// Note that the two flags take effect on ESSL3 input shaders translated to GLSL 4.1 or lower and to
// GLSL 4.2 or newer on Linux AMD.
// TODO(zmo): This is not a good long-term solution. Simply dropping these qualifiers may break some
// developers' content. A more complex workaround of dynamically generating, compiling, and
// re-linking shaders that use these qualifiers should be implemented.
const ShCompileOptions SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3 = UINT64_C(1) << 7;
// This flag works around bug in Intel Mac drivers related to abs(i) where // This flag works around bug in Intel Mac drivers related to abs(i) where
// i is an integer. // i is an integer.
...@@ -186,29 +203,6 @@ const ShCompileOptions SH_EMULATE_ISNAN_FLOAT_FUNCTION = UINT64_C(1) << 27; ...@@ -186,29 +203,6 @@ const ShCompileOptions SH_EMULATE_ISNAN_FLOAT_FUNCTION = UINT64_C(1) << 27;
// layout qualifier to be considered active. The uniform block itself is also considered active. // layout qualifier to be considered active. The uniform block itself is also considered active.
const ShCompileOptions SH_USE_UNUSED_STANDARD_SHARED_BLOCKS = UINT64_C(1) << 28; const ShCompileOptions SH_USE_UNUSED_STANDARD_SHARED_BLOCKS = UINT64_C(1) << 28;
// This flag will keep invariant declaration for input in fragment shader for GLSL >=4.20 on AMD.
// From GLSL >= 4.20, it's optional to add invariant for fragment input, but GPU vendors have
// different implementations about this. Some drivers forbid invariant in fragment for GLSL>= 4.20,
// e.g. Linux Mesa, some drivers treat that as optional, e.g. NVIDIA, some drivers require invariant
// must match between vertex and fragment shader, e.g. AMD. The behavior on AMD is obviously wrong.
// Remove invariant for input in fragment shader to workaround the restriction on Intel Mesa.
// But don't remove on AMD Linux to avoid triggering the bug on AMD.
const ShCompileOptions SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT = UINT64_C(1) << 29;
// Due to spec difference between GLSL 4.1 or lower and ESSL3, some platforms (for example, Mac OSX
// core profile) require a variable's "invariant"/"centroid" qualifiers to match between vertex and
// fragment shader. A simple solution to allow such shaders to link is to omit the two qualifiers.
// AMD driver in Linux requires invariant qualifier to match between vertex and fragment shaders,
// while ESSL3 disallows invariant qualifier in fragment shader and GLSL >= 4.2 doesn't require
// invariant qualifier to match between shaders. Remove invariant qualifier from vertex shader to
// workaround AMD driver bug.
// Note that the two flags take effect on ESSL3 input shaders translated to GLSL 4.1 or lower and to
// GLSL 4.2 or newer on Linux AMD.
// TODO(zmo): This is not a good long-term solution. Simply dropping these qualifiers may break some
// developers' content. A more complex workaround of dynamically generating, compiling, and
// re-linking shaders that use these qualifiers should be implemented.
const ShCompileOptions SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3 = UINT64_C(1) << 30;
// Defines alternate strategies for implementing array index clamping. // Defines alternate strategies for implementing array index clamping.
enum ShArrayIndexClampingStrategy enum ShArrayIndexClampingStrategy
{ {
......
...@@ -93,7 +93,6 @@ int main(int argc, char *argv[]) ...@@ -93,7 +93,6 @@ int main(int argc, char *argv[])
case 'i': compileOptions |= SH_INTERMEDIATE_TREE; break; case 'i': compileOptions |= SH_INTERMEDIATE_TREE; break;
case 'o': compileOptions |= SH_OBJECT_CODE; break; case 'o': compileOptions |= SH_OBJECT_CODE; break;
case 'u': compileOptions |= SH_VARIABLES; break; case 'u': compileOptions |= SH_VARIABLES; break;
case 'l': compileOptions |= SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX; break;
case 'p': resources.WEBGL_debug_shader_precision = 1; break; case 'p': resources.WEBGL_debug_shader_precision = 1; break;
case 's': case 's':
if (argv[0][2] == '=') if (argv[0][2] == '=')
...@@ -319,7 +318,6 @@ void usage() ...@@ -319,7 +318,6 @@ void usage()
" -i : print intermediate tree\n" " -i : print intermediate tree\n"
" -o : print translated code\n" " -o : print translated code\n"
" -u : print active attribs, uniforms, varyings and program outputs\n" " -u : print active attribs, uniforms, varyings and program outputs\n"
" -l : unroll for-loops with integer indices\n"
" -p : use precision emulation\n" " -p : use precision emulation\n"
" -s=e2 : use GLES2 spec (this is by default)\n" " -s=e2 : use GLES2 spec (this is by default)\n"
" -s=e3 : use GLES3 spec (in development)\n" " -s=e3 : use GLES3 spec (in development)\n"
......
...@@ -54,8 +54,6 @@ ...@@ -54,8 +54,6 @@
'compiler/translator/ExtensionBehavior.h', 'compiler/translator/ExtensionBehavior.h',
'compiler/translator/FlagStd140Structs.cpp', 'compiler/translator/FlagStd140Structs.cpp',
'compiler/translator/FlagStd140Structs.h', 'compiler/translator/FlagStd140Structs.h',
'compiler/translator/ForLoopUnroll.cpp',
'compiler/translator/ForLoopUnroll.h',
'compiler/translator/HashNames.h', 'compiler/translator/HashNames.h',
'compiler/translator/InfoSink.cpp', 'compiler/translator/InfoSink.cpp',
'compiler/translator/InfoSink.h', 'compiler/translator/InfoSink.h',
...@@ -73,9 +71,6 @@ ...@@ -73,9 +71,6 @@
'compiler/translator/IntermTraverse.cpp', 'compiler/translator/IntermTraverse.cpp',
'compiler/translator/Intermediate.h', 'compiler/translator/Intermediate.h',
'compiler/translator/Intermediate.cpp', 'compiler/translator/Intermediate.cpp',
'compiler/translator/LoopInfo.cpp',
'compiler/translator/LoopInfo.h',
'compiler/translator/MMap.h',
'compiler/translator/NodeSearch.h', 'compiler/translator/NodeSearch.h',
'compiler/translator/Operator.cpp', 'compiler/translator/Operator.cpp',
'compiler/translator/Operator.h', 'compiler/translator/Operator.h',
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include "compiler/translator/DeferGlobalInitializers.h" #include "compiler/translator/DeferGlobalInitializers.h"
#include "compiler/translator/EmulateGLFragColorBroadcast.h" #include "compiler/translator/EmulateGLFragColorBroadcast.h"
#include "compiler/translator/EmulatePrecision.h" #include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/ForLoopUnroll.h"
#include "compiler/translator/Initialize.h" #include "compiler/translator/Initialize.h"
#include "compiler/translator/InitializeParseContext.h" #include "compiler/translator/InitializeParseContext.h"
#include "compiler/translator/InitializeVariables.h" #include "compiler/translator/InitializeVariables.h"
...@@ -370,26 +369,6 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -370,26 +369,6 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
} }
} }
// Unroll for-loop markup needs to happen after validateLimitations pass.
if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
{
ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex,
shouldRunLoopAndIndexingValidation(compileOptions));
root->traverse(&marker);
}
if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX))
{
ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex,
shouldRunLoopAndIndexingValidation(compileOptions));
root->traverse(&marker);
if (marker.samplerArrayIndexIsFloatLoopIndex())
{
infoSink.info.prefix(EPrefixError);
infoSink.info << "sampler array index is float loop index";
success = false;
}
}
// Built-in function emulation needs to happen after validateLimitations pass. // Built-in function emulation needs to happen after validateLimitations pass.
if (success) if (success)
{ {
...@@ -506,17 +485,6 @@ bool TCompiler::compile(const char *const shaderStrings[], ...@@ -506,17 +485,6 @@ bool TCompiler::compile(const char *const shaderStrings[],
compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL; compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL;
} }
ShCompileOptions unrollFlags =
SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX | SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
if ((compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION) != 0 &&
(compileOptions & unrollFlags) != 0)
{
infoSink.info.prefix(EPrefixError);
infoSink.info
<< "Unsupported compile flag combination: unroll & ADD_TRUE_TO_LOOP_CONDITION";
return false;
}
TScopedPoolAllocator scopedAlloc(&allocator); TScopedPoolAllocator scopedAlloc(&allocator);
TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions); TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions);
......
//
// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/ForLoopUnroll.h"
#include "compiler/translator/ValidateLimitations.h"
#include "angle_gl.h"
namespace sh
{
bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node)
{
if (mUnrollCondition != kSamplerArrayIndex)
return true;
// If a sampler array index is also the loop index,
// 1) if the index type is integer, mark the loop for unrolling;
// 2) if the index type if float, set a flag to later fail compile.
switch (node->getOp())
{
case EOpIndexIndirect:
if (node->getLeft() != NULL && node->getRight() != NULL && node->getLeft()->getAsSymbolNode())
{
TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode();
if (IsSampler(symbol->getBasicType()) && symbol->isArray() && !mLoopStack.empty())
{
mVisitSamplerArrayIndexNodeInsideLoop = true;
node->getRight()->traverse(this);
mVisitSamplerArrayIndexNodeInsideLoop = false;
// We have already visited all the children.
return false;
}
}
break;
default:
break;
}
return true;
}
bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node)
{
bool canBeUnrolled = mHasRunLoopValidation;
if (!mHasRunLoopValidation)
{
canBeUnrolled = ValidateLimitations::IsLimitedForLoop(node);
}
if (mUnrollCondition == kIntegerIndex && canBeUnrolled)
{
// Check if loop index type is integer.
// This is called after ValidateLimitations pass, so the loop has the limited form specified
// in ESSL 1.00 appendix A.
TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
TIntermSymbol *symbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
if (symbol->getBasicType() == EbtInt)
node->setUnrollFlag(true);
}
TIntermNode *body = node->getBody();
if (body != nullptr)
{
if (canBeUnrolled)
{
mLoopStack.push(node);
body->traverse(this);
mLoopStack.pop();
}
else
{
body->traverse(this);
}
}
// The loop is fully processed - no need to visit children.
return false;
}
void ForLoopUnrollMarker::visitSymbol(TIntermSymbol* symbol)
{
if (!mVisitSamplerArrayIndexNodeInsideLoop)
return;
TIntermLoop *loop = mLoopStack.findLoop(symbol);
if (loop)
{
switch (symbol->getBasicType())
{
case EbtFloat:
mSamplerArrayIndexIsFloatLoopIndex = true;
break;
case EbtInt:
loop->setUnrollFlag(true);
break;
default:
UNREACHABLE();
}
}
}
} // namespace sh
//
// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
#define COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
#include "compiler/translator/LoopInfo.h"
namespace sh
{
// This class detects for-loops that needs to be unrolled.
// Currently we support two unroll conditions:
// 1) kForLoopWithIntegerIndex: unroll if the index type is integer.
// 2) kForLoopWithSamplerArrayIndex: unroll where a sampler array index
// is also the loop integer index, and reject and fail a compile
// where a sampler array index is also the loop float index.
class ForLoopUnrollMarker : public TIntermTraverser
{
public:
enum UnrollCondition
{
kIntegerIndex,
kSamplerArrayIndex
};
ForLoopUnrollMarker(UnrollCondition condition, bool hasRunLoopValidation)
: TIntermTraverser(true, false, false),
mUnrollCondition(condition),
mSamplerArrayIndexIsFloatLoopIndex(false),
mVisitSamplerArrayIndexNodeInsideLoop(false),
mHasRunLoopValidation(hasRunLoopValidation)
{
}
bool visitBinary(Visit, TIntermBinary *node) override;
bool visitLoop(Visit, TIntermLoop *node) override;
void visitSymbol(TIntermSymbol *node) override;
bool samplerArrayIndexIsFloatLoopIndex() const
{
return mSamplerArrayIndexIsFloatLoopIndex;
}
private:
UnrollCondition mUnrollCondition;
TLoopStack mLoopStack;
bool mSamplerArrayIndexIsFloatLoopIndex;
bool mVisitSamplerArrayIndexNodeInsideLoop;
bool mHasRunLoopValidation;
};
} // namespace sh
#endif // COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
...@@ -201,7 +201,7 @@ class TIntermLoop : public TIntermNode ...@@ -201,7 +201,7 @@ class TIntermLoop : public TIntermNode
TIntermTyped *cond, TIntermTyped *cond,
TIntermTyped *expr, TIntermTyped *expr,
TIntermBlock *body) TIntermBlock *body)
: mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body), mUnrollFlag(false) : mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body)
{ {
} }
...@@ -219,17 +219,12 @@ class TIntermLoop : public TIntermNode ...@@ -219,17 +219,12 @@ class TIntermLoop : public TIntermNode
void setExpression(TIntermTyped *expression) { mExpr = expression; } void setExpression(TIntermTyped *expression) { mExpr = expression; }
void setBody(TIntermBlock *body) { mBody = body; } void setBody(TIntermBlock *body) { mBody = body; }
void setUnrollFlag(bool flag) { mUnrollFlag = flag; }
bool getUnrollFlag() const { return mUnrollFlag; }
protected: protected:
TLoopType mType; TLoopType mType;
TIntermNode *mInit; // for-loop initialization TIntermNode *mInit; // for-loop initialization
TIntermTyped *mCond; // loop exit condition TIntermTyped *mCond; // loop exit condition
TIntermTyped *mExpr; // for-loop expression TIntermTyped *mExpr; // for-loop expression
TIntermBlock *mBody; // loop body TIntermBlock *mBody; // loop body
bool mUnrollFlag; // Whether the loop should be unrolled or not.
}; };
// //
......
//
// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/LoopInfo.h"
namespace sh
{
namespace
{
int EvaluateIntConstant(TIntermConstantUnion *node)
{
ASSERT(node && node->getUnionArrayPointer());
return node->getIConst(0);
}
int GetLoopIntIncrement(TIntermLoop *node)
{
TIntermNode *expr = node->getExpression();
// for expression has one of the following forms:
// loop_index++
// loop_index--
// loop_index += constant_expression
// loop_index -= constant_expression
// ++loop_index
// --loop_index
// The last two forms are not specified in the spec, but I am assuming
// its an oversight.
TIntermUnary *unOp = expr->getAsUnaryNode();
TIntermBinary *binOp = unOp ? NULL : expr->getAsBinaryNode();
TOperator op = EOpNull;
TIntermConstantUnion *incrementNode = NULL;
if (unOp)
{
op = unOp->getOp();
}
else if (binOp)
{
op = binOp->getOp();
ASSERT(binOp->getRight());
incrementNode = binOp->getRight()->getAsConstantUnion();
ASSERT(incrementNode);
}
int increment = 0;
// The operator is one of: ++ -- += -=.
switch (op)
{
case EOpPostIncrement:
case EOpPreIncrement:
ASSERT(unOp && !binOp);
increment = 1;
break;
case EOpPostDecrement:
case EOpPreDecrement:
ASSERT(unOp && !binOp);
increment = -1;
break;
case EOpAddAssign:
ASSERT(!unOp && binOp);
increment = EvaluateIntConstant(incrementNode);
break;
case EOpSubAssign:
ASSERT(!unOp && binOp);
increment = - EvaluateIntConstant(incrementNode);
break;
default:
UNREACHABLE();
}
return increment;
}
} // namespace anonymous
TLoopIndexInfo::TLoopIndexInfo()
: mId(-1),
mType(EbtVoid),
mInitValue(0),
mStopValue(0),
mIncrementValue(0),
mOp(EOpNull),
mCurrentValue(0)
{
}
void TLoopIndexInfo::fillInfo(TIntermLoop *node)
{
if (node == NULL)
return;
// Here we assume all the operations are valid, because the loop node is
// already validated in ValidateLimitations.
TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
mId = symbol->getId();
mType = symbol->getBasicType();
if (mType == EbtInt)
{
TIntermConstantUnion* initNode = declInit->getRight()->getAsConstantUnion();
mInitValue = EvaluateIntConstant(initNode);
mCurrentValue = mInitValue;
mIncrementValue = GetLoopIntIncrement(node);
TIntermBinary* binOp = node->getCondition()->getAsBinaryNode();
mStopValue = EvaluateIntConstant(
binOp->getRight()->getAsConstantUnion());
mOp = binOp->getOp();
}
}
bool TLoopIndexInfo::satisfiesLoopCondition() const
{
// Relational operator is one of: > >= < <= == or !=.
switch (mOp)
{
case EOpEqual:
return (mCurrentValue == mStopValue);
case EOpNotEqual:
return (mCurrentValue != mStopValue);
case EOpLessThan:
return (mCurrentValue < mStopValue);
case EOpGreaterThan:
return (mCurrentValue > mStopValue);
case EOpLessThanEqual:
return (mCurrentValue <= mStopValue);
case EOpGreaterThanEqual:
return (mCurrentValue >= mStopValue);
default:
UNREACHABLE();
return false;
}
}
TLoopInfo::TLoopInfo()
: loop(NULL)
{
}
TLoopInfo::TLoopInfo(TIntermLoop *node)
: loop(node)
{
index.fillInfo(node);
}
TIntermLoop *TLoopStack::findLoop(TIntermSymbol *symbol)
{
if (!symbol)
return NULL;
for (iterator iter = begin(); iter != end(); ++iter)
{
if (iter->index.getId() == symbol->getId())
return iter->loop;
}
return NULL;
}
TLoopIndexInfo *TLoopStack::getIndexInfo(TIntermSymbol *symbol)
{
if (!symbol)
return NULL;
for (iterator iter = begin(); iter != end(); ++iter)
{
if (iter->index.getId() == symbol->getId())
return &(iter->index);
}
return NULL;
}
void TLoopStack::step()
{
ASSERT(!empty());
rbegin()->index.step();
}
bool TLoopStack::satisfiesLoopCondition()
{
ASSERT(!empty());
return rbegin()->index.satisfiesLoopCondition();
}
bool TLoopStack::needsToReplaceSymbolWithValue(TIntermSymbol *symbol)
{
TIntermLoop *loop = findLoop(symbol);
return loop && loop->getUnrollFlag();
}
int TLoopStack::getLoopIndexValue(TIntermSymbol *symbol)
{
TLoopIndexInfo *info = getIndexInfo(symbol);
ASSERT(info);
return info->getCurrentValue();
}
void TLoopStack::push(TIntermLoop *loop)
{
TLoopInfo info(loop);
push_back(info);
}
void TLoopStack::pop()
{
pop_back();
}
} // namespace sh
//
// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_LOOPINFO_H_
#define COMPILER_TRANSLATOR_LOOPINFO_H_
#include "compiler/translator/IntermNode.h"
namespace sh
{
class TLoopIndexInfo
{
public:
TLoopIndexInfo();
// If type is EbtInt, fill all fields of the structure with info
// extracted from a loop node.
// If type is not EbtInt, only fill id and type.
void fillInfo(TIntermLoop *node);
int getId() const { return mId; }
void setId(int id) { mId = id; }
TBasicType getType() const { return mType; }
void setType(TBasicType type) { mType = type; }
int getCurrentValue() const { return mCurrentValue; }
void step() { mCurrentValue += mIncrementValue; }
// Check if the current value satisfies the loop condition.
bool satisfiesLoopCondition() const;
private:
int mId;
TBasicType mType; // Either EbtInt or EbtFloat
// Below fields are only valid if the index's type is int.
int mInitValue;
int mStopValue;
int mIncrementValue;
TOperator mOp;
int mCurrentValue;
};
struct TLoopInfo
{
TLoopIndexInfo index;
TIntermLoop *loop;
TLoopInfo();
TLoopInfo(TIntermLoop *node);
};
class TLoopStack : public TVector<TLoopInfo>
{
public:
// Search loop stack for a loop whose index matches the input symbol.
TIntermLoop *findLoop(TIntermSymbol *symbol);
// Find the loop index info in the loop stack by the input symbol.
TLoopIndexInfo *getIndexInfo(TIntermSymbol *symbol);
// Update the currentValue for the next loop iteration.
void step();
// Return false if loop condition is no longer satisfied.
bool satisfiesLoopCondition();
// Check if the symbol is the index of a loop that's unrolled.
bool needsToReplaceSymbolWithValue(TIntermSymbol *symbol);
// Return the current value of a given loop index symbol.
int getLoopIndexValue(TIntermSymbol *symbol);
void push(TIntermLoop *info);
void pop();
};
} // namespace sh
#endif // COMPILER_TRANSLATOR_LOOPINFO_H_
//
// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_MMAP_H_
#define COMPILER_TRANSLATOR_MMAP_H_
//
// Encapsulate memory mapped files
//
class TMMap {
public:
TMMap(const char* fileName) :
fSize(-1), // -1 is the error value returned by GetFileSize()
fp(NULL),
fBuff(0) // 0 is the error value returned by MapViewOfFile()
{
if ((fp = fopen(fileName, "r")) == NULL)
return;
char c = getc(fp);
fSize = 0;
while (c != EOF) {
fSize++;
c = getc(fp);
}
if (c == EOF)
fSize++;
rewind(fp);
fBuff = (char*)malloc(sizeof(char) * fSize);
int count = 0;
c = getc(fp);
while (c != EOF) {
fBuff[count++] = c;
c = getc(fp);
}
fBuff[count++] = c;
}
char* getData() { return fBuff; }
int getSize() { return fSize; }
~TMMap() {
if (fp != NULL)
fclose(fp);
}
private:
int fSize; // size of file to map in
FILE *fp;
char* fBuff; // the actual data;
};
#endif // COMPILER_TRANSLATOR_MMAP_H_
...@@ -376,10 +376,7 @@ void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type) ...@@ -376,10 +376,7 @@ void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type)
void TOutputGLSLBase::visitSymbol(TIntermSymbol *node) void TOutputGLSLBase::visitSymbol(TIntermSymbol *node)
{ {
TInfoSinkBase &out = objSink(); TInfoSinkBase &out = objSink();
if (mLoopUnrollStack.needsToReplaceSymbolWithValue(node)) out << hashVariableName(node->getName());
out << mLoopUnrollStack.getLoopIndexValue(node);
else
out << hashVariableName(node->getName());
if (mDeclaringVariables && node->getType().isArray()) if (mDeclaringVariables && node->getType().isArray())
out << arrayBrackets(node->getType()); out << arrayBrackets(node->getType());
...@@ -1124,49 +1121,22 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) ...@@ -1124,49 +1121,22 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node)
TLoopType loopType = node->getType(); TLoopType loopType = node->getType();
// Only for loops can be unrolled
ASSERT(!node->getUnrollFlag() || loopType == ELoopFor);
if (loopType == ELoopFor) // for loop if (loopType == ELoopFor) // for loop
{ {
if (!node->getUnrollFlag()) out << "for (";
{ if (node->getInit())
out << "for ("; node->getInit()->traverse(this);
if (node->getInit()) out << "; ";
node->getInit()->traverse(this);
out << "; ";
if (node->getCondition()) if (node->getCondition())
node->getCondition()->traverse(this); node->getCondition()->traverse(this);
out << "; "; out << "; ";
if (node->getExpression()) if (node->getExpression())
node->getExpression()->traverse(this); node->getExpression()->traverse(this);
out << ")\n"; out << ")\n";
visitCodeBlock(node->getBody()); visitCodeBlock(node->getBody());
}
else
{
// Need to put a one-iteration loop here to handle break.
TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
TIntermSymbol *indexSymbol =
(*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
TString name = hashVariableName(indexSymbol->getName());
out << "for (int " << name << " = 0; "
<< name << " < 1; "
<< "++" << name << ")\n";
out << "{\n";
mLoopUnrollStack.push(node);
while (mLoopUnrollStack.satisfiesLoopCondition())
{
visitCodeBlock(node->getBody());
mLoopUnrollStack.step();
}
mLoopUnrollStack.pop();
out << "}\n";
}
} }
else if (loopType == ELoopWhile) // while loop else if (loopType == ELoopWhile) // while loop
{ {
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include <set> #include <set>
#include "compiler/translator/IntermNode.h" #include "compiler/translator/IntermNode.h"
#include "compiler/translator/LoopInfo.h"
#include "compiler/translator/ParseContext.h" #include "compiler/translator/ParseContext.h"
namespace sh namespace sh
...@@ -91,9 +90,6 @@ class TOutputGLSLBase : public TIntermTraverser ...@@ -91,9 +90,6 @@ class TOutputGLSLBase : public TIntermTraverser
// This set contains all the ids of the structs from every scope. // This set contains all the ids of the structs from every scope.
std::set<int> mDeclaredStructs; std::set<int> mDeclaredStructs;
// Stack of loops that need to be unrolled.
TLoopStack mLoopUnrollStack;
ShArrayIndexClampingStrategy mClampingStrategy; ShArrayIndexClampingStrategy mClampingStrategy;
// name hashing. // name hashing.
......
...@@ -16,6 +16,17 @@ namespace sh ...@@ -16,6 +16,17 @@ namespace sh
namespace namespace
{ {
int GetLoopSymbolId(TIntermLoop *loop)
{
// Here we assume all the operations are valid, because the loop node is
// already validated before this call.
TIntermSequence *declSeq = loop->getInit()->getAsDeclarationNode()->getSequence();
TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
return symbol->getId();
}
// Traverses a node to check if it represents a constant index expression. // Traverses a node to check if it represents a constant index expression.
// Definition: // Definition:
// constant-index-expressions are a superset of constant-expressions. // constant-index-expressions are a superset of constant-expressions.
...@@ -28,10 +39,8 @@ namespace ...@@ -28,10 +39,8 @@ namespace
class ValidateConstIndexExpr : public TIntermTraverser class ValidateConstIndexExpr : public TIntermTraverser
{ {
public: public:
ValidateConstIndexExpr(TLoopStack& stack) ValidateConstIndexExpr(const std::vector<int> &loopSymbols)
: TIntermTraverser(true, false, false), : TIntermTraverser(true, false, false), mValid(true), mLoopSymbolIds(loopSymbols)
mValid(true),
mLoopStack(stack)
{ {
} }
...@@ -44,14 +53,15 @@ class ValidateConstIndexExpr : public TIntermTraverser ...@@ -44,14 +53,15 @@ class ValidateConstIndexExpr : public TIntermTraverser
// constant index expression. // constant index expression.
if (mValid) if (mValid)
{ {
mValid = (symbol->getQualifier() == EvqConst) || bool isLoopSymbol = std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(),
(mLoopStack.findLoop(symbol)); symbol->getId()) != mLoopSymbolIds.end();
mValid = (symbol->getQualifier() == EvqConst) || isLoopSymbol;
} }
} }
private: private:
bool mValid; bool mValid;
TLoopStack& mLoopStack; const std::vector<int> mLoopSymbolIds;
}; };
} // namespace anonymous } // namespace anonymous
...@@ -80,9 +90,9 @@ bool ValidateLimitations::IsLimitedForLoop(TIntermLoop *loop) ...@@ -80,9 +90,9 @@ bool ValidateLimitations::IsLimitedForLoop(TIntermLoop *loop)
TIntermNode *body = loop->getBody(); TIntermNode *body = loop->getBody();
if (body != nullptr) if (body != nullptr)
{ {
validate.mLoopStack.push(loop); validate.mLoopSymbolIds.push_back(GetLoopSymbolId(loop));
body->traverse(&validate); body->traverse(&validate);
validate.mLoopStack.pop(); validate.mLoopSymbolIds.pop_back();
} }
return (validate.mNumErrors == 0); return (validate.mNumErrors == 0);
} }
...@@ -140,9 +150,9 @@ bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node) ...@@ -140,9 +150,9 @@ bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node)
TIntermNode *body = node->getBody(); TIntermNode *body = node->getBody();
if (body != NULL) if (body != NULL)
{ {
mLoopStack.push(node); mLoopSymbolIds.push_back(GetLoopSymbolId(node));
body->traverse(this); body->traverse(this);
mLoopStack.pop(); mLoopSymbolIds.pop_back();
} }
// The loop is fully processed - no need to visit children. // The loop is fully processed - no need to visit children.
...@@ -163,12 +173,13 @@ void ValidateLimitations::error(TSourceLoc loc, ...@@ -163,12 +173,13 @@ void ValidateLimitations::error(TSourceLoc loc,
bool ValidateLimitations::withinLoopBody() const bool ValidateLimitations::withinLoopBody() const
{ {
return !mLoopStack.empty(); return !mLoopSymbolIds.empty();
} }
bool ValidateLimitations::isLoopIndex(TIntermSymbol *symbol) bool ValidateLimitations::isLoopIndex(TIntermSymbol *symbol)
{ {
return mLoopStack.findLoop(symbol) != NULL; return std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(), symbol->getId()) !=
mLoopSymbolIds.end();
} }
bool ValidateLimitations::validateLoopType(TIntermLoop *node) bool ValidateLimitations::validateLoopType(TIntermLoop *node)
...@@ -474,7 +485,7 @@ bool ValidateLimitations::isConstIndexExpr(TIntermNode *node) ...@@ -474,7 +485,7 @@ bool ValidateLimitations::isConstIndexExpr(TIntermNode *node)
{ {
ASSERT(node != NULL); ASSERT(node != NULL);
ValidateConstIndexExpr validate(mLoopStack); ValidateConstIndexExpr validate(mLoopSymbolIds);
node->traverse(&validate); node->traverse(&validate);
return validate.isValid(); return validate.isValid();
} }
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#define COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_ #define COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
#include "compiler/translator/IntermNode.h" #include "compiler/translator/IntermNode.h"
#include "compiler/translator/LoopInfo.h"
namespace sh namespace sh
{ {
...@@ -58,7 +57,7 @@ class ValidateLimitations : public TIntermTraverser ...@@ -58,7 +57,7 @@ class ValidateLimitations : public TIntermTraverser
sh::GLenum mShaderType; sh::GLenum mShaderType;
TInfoSinkBase *mSink; TInfoSinkBase *mSink;
int mNumErrors; int mNumErrors;
TLoopStack mLoopStack; std::vector<int> mLoopSymbolIds;
bool mValidateIndexing; bool mValidateIndexing;
bool mValidateInnerLoops; bool mValidateInnerLoops;
}; };
......
...@@ -149,12 +149,6 @@ class MalformedComputeShaderTest : public MalformedShaderTest ...@@ -149,12 +149,6 @@ class MalformedComputeShaderTest : public MalformedShaderTest
} }
}; };
class UnrollForLoopsTest : public MalformedShaderTest
{
public:
UnrollForLoopsTest() { mExtraCompileOptions = SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX; }
};
// This is a test for a bug that used to exist in ANGLE: // This is a test for a bug that used to exist in ANGLE:
// Calling a function with all parameters missing should not succeed. // Calling a function with all parameters missing should not succeed.
TEST_F(MalformedShaderTest, FunctionParameterMismatch) TEST_F(MalformedShaderTest, FunctionParameterMismatch)
...@@ -1445,45 +1439,6 @@ TEST_F(MalformedWebGL1ShaderTest, NonConstantLoopIndex) ...@@ -1445,45 +1439,6 @@ TEST_F(MalformedWebGL1ShaderTest, NonConstantLoopIndex)
} }
} }
// Regression test for an old crash bug in ANGLE.
// ForLoopUnroll used to crash when it encountered a while loop.
TEST_F(UnrollForLoopsTest, WhileLoop)
{
const std::string &shaderString =
"precision mediump float;\n"
"void main()\n"
"{\n"
" while (true) {\n"
" gl_FragColor = vec4(0.0);\n"
" break;\n"
" }\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
// Regression test for an old crash bug in ANGLE.
// ForLoopUnroll used to crash when it encountered a loop that didn't fit the ESSL 1.00
// Appendix A limitations.
TEST_F(UnrollForLoopsTest, UnlimitedForLoop)
{
const std::string &shaderString =
"precision mediump float;\n"
"void main()\n"
"{\n"
" for (;true;) {\n"
" gl_FragColor = vec4(0.0);\n"
" break;\n"
" }\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
// Check that indices that are not integers are rejected. // Check that indices that are not integers are rejected.
// The check should be done even if ESSL 1.00 Appendix A limitations are not applied. // The check should be done even if ESSL 1.00 Appendix A limitations are not applied.
TEST_F(MalformedShaderTest, NonIntegerIndex) TEST_F(MalformedShaderTest, NonIntegerIndex)
...@@ -3155,4 +3110,4 @@ TEST_F(MalformedFragmentShaderGLES31Test, OverqualifyingImageParameter) ...@@ -3155,4 +3110,4 @@ TEST_F(MalformedFragmentShaderGLES31Test, OverqualifyingImageParameter)
{ {
FAIL() << "Shader compilation failed, expecting success " << mInfoLog; FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
} }
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment