Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
angle
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
angle
Commits
da8ea022
Commit
da8ea022
authored
May 29, 2013
by
shannonwoods@chromium.org
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add expression complexity and call stack depth limits.
git-svn-id:
https://angleproject.googlecode.com/svn/branches/dx11proto@2254
736b8ea6-26fd-11df-bfd4-992fa37f6226
parent
b0f1b486
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
703 additions
and
50 deletions
+703
-50
common.gypi
build/common.gypi
+4
-0
ShaderLang.h
include/GLSLANG/ShaderLang.h
+13
-1
build_angle.gypi
src/build_angle.gypi
+2
-2
Compiler.cpp
src/compiler/Compiler.cpp
+43
-9
DetectCallDepth.cpp
src/compiler/DetectCallDepth.cpp
+85
-23
DetectCallDepth.h
src/compiler/DetectCallDepth.h
+26
-6
ShHandle.h
src/compiler/ShHandle.h
+6
-2
intermediate.h
src/compiler/intermediate.h
+6
-2
translator_common.vcxproj
src/compiler/translator_common.vcxproj
+2
-2
translator_common.vcxproj.filters
src/compiler/translator_common.vcxproj.filters
+2
-2
build_tests.gyp
tests/build_tests.gyp
+2
-1
ExpressionLimit_test.cpp
tests/compiler_tests/ExpressionLimit_test.cpp
+512
-0
No files found.
build/common.gypi
View file @
da8ea022
...
...
@@ -110,6 +110,10 @@
'LinkIncremental': '2',
},
},
'xcode_settings': {
'COPY_PHASE_STRIP': 'NO',
'GCC_OPTIMIZATION_LEVEL': '0',
},
}, # Debug
'Release': {
'inherit_from': ['Common'],
...
...
include/GLSLANG/ShaderLang.h
View file @
da8ea022
...
...
@@ -162,7 +162,13 @@ typedef enum {
// vec234, or mat234 type. The ShArrayIndexClampingStrategy enum,
// specified in the ShBuiltInResources when constructing the
// compiler, selects the strategy for the clamping implementation.
SH_CLAMP_INDIRECT_ARRAY_BOUNDS
=
0x1000
SH_CLAMP_INDIRECT_ARRAY_BOUNDS
=
0x1000
,
// This flag limits the complexity of an expression.
SH_LIMIT_EXPRESSION_COMPLEXITY
=
0x2000
,
// This flag limits the depth of the call stack.
SH_LIMIT_CALL_STACK_DEPTH
=
0x4000
,
}
ShCompileOptions
;
// Defines alternate strategies for implementing array index clamping.
...
...
@@ -225,6 +231,12 @@ typedef struct
// Selects a strategy to use when implementing array index clamping.
// Default is SH_CLAMP_WITH_CLAMP_INTRINSIC.
ShArrayIndexClampingStrategy
ArrayIndexClampingStrategy
;
// The maximum complexity an expression can be.
int
MaxExpressionComplexity
;
// The maximum depth a call stack can be.
int
MaxCallStackDepth
;
}
ShBuiltInResources
;
//
...
...
src/build_angle.gypi
View file @
da8ea022
...
...
@@ -72,8 +72,8 @@
'compiler/ConstantUnion.h',
'compiler/debug.cpp',
'compiler/debug.h',
'compiler/Detect
Recursion
.cpp',
'compiler/Detect
Recursion
.h',
'compiler/Detect
CallDepth
.cpp',
'compiler/Detect
CallDepth
.h',
'compiler/Diagnostics.h',
'compiler/Diagnostics.cpp',
'compiler/DirectiveHandler.h',
...
...
src/compiler/Compiler.cpp
View file @
da8ea022
...
...
@@ -5,7 +5,7 @@
//
#include "compiler/BuiltInFunctionEmulator.h"
#include "compiler/Detect
Recursion
.h"
#include "compiler/Detect
CallDepth
.h"
#include "compiler/ForLoopUnroll.h"
#include "compiler/Initialize.h"
#include "compiler/InitializeParseContext.h"
...
...
@@ -104,6 +104,9 @@ TShHandleBase::~TShHandleBase() {
TCompiler
::
TCompiler
(
ShShaderType
type
,
ShShaderSpec
spec
)
:
shaderType
(
type
),
shaderSpec
(
spec
),
maxUniformVectors
(
0
),
maxExpressionComplexity
(
0
),
maxCallStackDepth
(
0
),
fragmentPrecisionHigh
(
false
),
clampingStrategy
(
SH_CLAMP_WITH_CLAMP_INTRINSIC
),
builtInFunctionEmulator
(
type
)
...
...
@@ -122,6 +125,8 @@ bool TCompiler::Init(const ShBuiltInResources& resources)
maxUniformVectors
=
(
shaderType
==
SH_VERTEX_SHADER
)
?
resources
.
MaxVertexUniformVectors
:
resources
.
MaxFragmentUniformVectors
;
maxExpressionComplexity
=
resources
.
MaxExpressionComplexity
;
maxCallStackDepth
=
resources
.
MaxCallStackDepth
;
TScopedPoolAllocator
scopedAlloc
(
&
allocator
,
false
);
// Generate built-in symbol table.
...
...
@@ -185,7 +190,7 @@ bool TCompiler::compile(const char* const shaderStrings[],
success
=
intermediate
.
postProcess
(
root
);
if
(
success
)
success
=
detect
Recursion
(
root
);
success
=
detect
CallDepth
(
root
,
infoSink
,
(
compileOptions
&
SH_LIMIT_CALL_STACK_DEPTH
)
!=
0
);
if
(
success
&&
(
compileOptions
&
SH_VALIDATE_LOOP_INDEXING
))
success
=
validateLimitations
(
root
);
...
...
@@ -208,6 +213,10 @@ bool TCompiler::compile(const char* const shaderStrings[],
if
(
success
&&
(
compileOptions
&
SH_CLAMP_INDIRECT_ARRAY_BOUNDS
))
arrayBoundsClamper
.
MarkIndirectArrayBoundsForClamping
(
root
);
// Disallow expressions deemed too complex.
if
(
success
&&
(
compileOptions
&
SH_LIMIT_EXPRESSION_COMPLEXITY
))
success
=
limitExpressionComplexity
(
root
);
// Call mapLongVariableNames() before collectAttribsUniforms() so in
// collectAttribsUniforms() we already have the mapped symbol names and
// we could composite mapped and original variable names.
...
...
@@ -268,24 +277,27 @@ void TCompiler::clearResults()
nameMap
.
clear
();
}
bool
TCompiler
::
detect
Recursion
(
TIntermNode
*
root
)
bool
TCompiler
::
detect
CallDepth
(
TIntermNode
*
root
,
TInfoSink
&
infoSink
,
bool
limitCallStackDepth
)
{
Detect
Recursion
detect
;
Detect
CallDepth
detect
(
infoSink
,
limitCallStackDepth
,
maxCallStackDepth
)
;
root
->
traverse
(
&
detect
);
switch
(
detect
.
detect
Recursion
())
{
case
Detect
Recursion
:
:
kErrorNone
:
switch
(
detect
.
detect
CallDepth
())
{
case
Detect
CallDepth
:
:
kErrorNone
:
return
true
;
case
Detect
Recursion
:
:
kErrorMissingMain
:
case
Detect
CallDepth
:
:
kErrorMissingMain
:
infoSink
.
info
.
prefix
(
EPrefixError
);
infoSink
.
info
<<
"Missing main()"
;
return
false
;
case
Detect
Recursion
:
:
kErrorRecursion
:
case
Detect
CallDepth
:
:
kErrorRecursion
:
infoSink
.
info
.
prefix
(
EPrefixError
);
infoSink
.
info
<<
"Function recursion detected"
;
return
false
;
case
DetectCallDepth
:
:
kErrorMaxDepthExceeded
:
infoSink
.
info
.
prefix
(
EPrefixError
);
infoSink
.
info
<<
"Function call stack too deep"
;
return
false
;
default
:
UNREACHABLE
();
return
false
;
}
}
...
...
@@ -327,6 +339,28 @@ bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
}
}
bool
TCompiler
::
limitExpressionComplexity
(
TIntermNode
*
root
)
{
TIntermTraverser
traverser
;
root
->
traverse
(
&
traverser
);
TDependencyGraph
graph
(
root
);
for
(
TFunctionCallVector
::
const_iterator
iter
=
graph
.
beginUserDefinedFunctionCalls
();
iter
!=
graph
.
endUserDefinedFunctionCalls
();
++
iter
)
{
TGraphFunctionCall
*
samplerSymbol
=
*
iter
;
TDependencyGraphTraverser
graphTraverser
;
samplerSymbol
->
traverse
(
&
graphTraverser
);
}
if
(
traverser
.
getMaxDepth
()
>
maxExpressionComplexity
)
{
infoSink
.
info
<<
"Expression too complex."
;
return
false
;
}
return
true
;
}
bool
TCompiler
::
enforceFragmentShaderTimingRestrictions
(
const
TDependencyGraph
&
graph
)
{
RestrictFragmentShaderTiming
restrictor
(
infoSink
.
info
);
...
...
src/compiler/Detect
Recursion
.cpp
→
src/compiler/Detect
CallDepth
.cpp
View file @
da8ea022
...
...
@@ -4,21 +4,24 @@
// found in the LICENSE file.
//
#include "compiler/DetectRecursion.h"
#include "compiler/DetectCallDepth.h"
#include "compiler/InfoSink.h"
DetectRecursion
::
FunctionNode
::
FunctionNode
(
const
TString
&
fname
)
const
int
DetectCallDepth
::
FunctionNode
::
kInfiniteCallDepth
;
DetectCallDepth
::
FunctionNode
::
FunctionNode
(
const
TString
&
fname
)
:
name
(
fname
),
visit
(
PreVisit
)
{
}
const
TString
&
Detect
Recursion
::
FunctionNode
::
getName
()
const
const
TString
&
Detect
CallDepth
::
FunctionNode
::
getName
()
const
{
return
name
;
}
void
Detect
Recursion
::
FunctionNode
::
addCallee
(
Detect
Recursion
::
FunctionNode
*
callee
)
void
Detect
CallDepth
::
FunctionNode
::
addCallee
(
Detect
CallDepth
::
FunctionNode
*
callee
)
{
for
(
size_t
i
=
0
;
i
<
callees
.
size
();
++
i
)
{
if
(
callees
[
i
]
==
callee
)
...
...
@@ -27,21 +30,31 @@ void DetectRecursion::FunctionNode::addCallee(
callees
.
push_back
(
callee
);
}
bool
DetectRecursion
::
FunctionNode
::
detectRecursion
(
)
int
DetectCallDepth
::
FunctionNode
::
detectCallDepth
(
DetectCallDepth
*
detectCallDepth
,
int
depth
)
{
ASSERT
(
visit
==
PreVisit
);
ASSERT
(
detectCallDepth
);
int
maxDepth
=
depth
;
visit
=
InVisit
;
for
(
size_t
i
=
0
;
i
<
callees
.
size
();
++
i
)
{
switch
(
callees
[
i
]
->
visit
)
{
case
InVisit
:
// cycle detected, i.e., recursion detected.
return
true
;
return
kInfiniteCallDepth
;
case
PostVisit
:
break
;
case
PreVisit
:
{
bool
recursion
=
callees
[
i
]
->
detectRecursion
();
if
(
recursion
)
return
true
;
// Check before we recurse so we don't go too depth
if
(
detectCallDepth
->
checkExceedsMaxDepth
(
depth
))
return
depth
;
int
callDepth
=
callees
[
i
]
->
detectCallDepth
(
detectCallDepth
,
depth
+
1
);
// Check after we recurse so we can exit immediately and provide info.
if
(
detectCallDepth
->
checkExceedsMaxDepth
(
callDepth
))
{
detectCallDepth
->
getInfoSink
().
info
<<
"<-"
<<
callees
[
i
]
->
getName
();
return
callDepth
;
}
maxDepth
=
std
::
max
(
callDepth
,
maxDepth
);
break
;
}
default
:
...
...
@@ -50,21 +63,29 @@ bool DetectRecursion::FunctionNode::detectRecursion()
}
}
visit
=
PostVisit
;
return
false
;
return
maxDepth
;
}
DetectRecursion
::
DetectRecursion
()
:
currentFunction
(
NULL
)
void
DetectCallDepth
::
FunctionNode
::
reset
()
{
visit
=
PreVisit
;
}
DetectRecursion
::~
DetectRecursion
()
DetectCallDepth
::
DetectCallDepth
(
TInfoSink
&
infoSink
,
bool
limitCallStackDepth
,
int
maxCallStackDepth
)
:
TIntermTraverser
(
true
,
false
,
true
,
false
),
currentFunction
(
NULL
),
infoSink
(
infoSink
),
maxDepth
(
limitCallStackDepth
?
maxCallStackDepth
:
FunctionNode
::
kInfiniteCallDepth
)
{
}
DetectCallDepth
::~
DetectCallDepth
()
{
for
(
size_t
i
=
0
;
i
<
functions
.
size
();
++
i
)
delete
functions
[
i
];
}
bool
Detect
Recursion
::
visitAggregate
(
Visit
visit
,
TIntermAggregate
*
node
)
bool
Detect
CallDepth
::
visitAggregate
(
Visit
visit
,
TIntermAggregate
*
node
)
{
switch
(
node
->
getOp
())
{
...
...
@@ -81,19 +102,21 @@ bool DetectRecursion::visitAggregate(Visit visit, TIntermAggregate* node)
currentFunction
=
new
FunctionNode
(
node
->
getName
());
functions
.
push_back
(
currentFunction
);
}
}
else
if
(
visit
==
PostVisit
)
{
currentFunction
=
NULL
;
}
break
;
}
case
EOpFunctionCall
:
{
// Function call.
if
(
visit
==
PreVisit
)
{
ASSERT
(
currentFunction
!=
NULL
);
FunctionNode
*
func
=
findFunctionByName
(
node
->
getName
());
if
(
func
==
NULL
)
{
func
=
new
FunctionNode
(
node
->
getName
());
functions
.
push_back
(
func
);
}
currentFunction
->
addCallee
(
func
);
if
(
currentFunction
)
currentFunction
->
addCallee
(
func
);
}
break
;
}
...
...
@@ -103,17 +126,56 @@ bool DetectRecursion::visitAggregate(Visit visit, TIntermAggregate* node)
return
true
;
}
DetectRecursion
::
ErrorCode
DetectRecursion
::
detectRecursion
()
bool
DetectCallDepth
::
checkExceedsMaxDepth
(
int
depth
)
{
return
depth
>=
maxDepth
;
}
void
DetectCallDepth
::
resetFunctionNodes
()
{
for
(
size_t
i
=
0
;
i
<
functions
.
size
();
++
i
)
{
functions
[
i
]
->
reset
();
}
}
DetectCallDepth
::
ErrorCode
DetectCallDepth
::
detectCallDepthForFunction
(
FunctionNode
*
func
)
{
FunctionNode
*
main
=
findFunctionByName
(
"main("
);
if
(
main
==
NULL
)
return
kErrorMissingMain
;
if
(
main
->
detectRecursion
())
currentFunction
=
NULL
;
resetFunctionNodes
();
int
maxCallDepth
=
func
->
detectCallDepth
(
this
,
1
);
if
(
maxCallDepth
==
FunctionNode
::
kInfiniteCallDepth
)
return
kErrorRecursion
;
if
(
maxCallDepth
>=
maxDepth
)
return
kErrorMaxDepthExceeded
;
return
kErrorNone
;
}
DetectCallDepth
::
ErrorCode
DetectCallDepth
::
detectCallDepth
()
{
if
(
maxDepth
!=
FunctionNode
::
kInfiniteCallDepth
)
{
// Check all functions because the driver may fail on them
// TODO: Before detectingRecursion, strip unused functions.
for
(
size_t
i
=
0
;
i
<
functions
.
size
();
++
i
)
{
ErrorCode
error
=
detectCallDepthForFunction
(
functions
[
i
]);
if
(
error
!=
kErrorNone
)
return
error
;
}
}
else
{
FunctionNode
*
main
=
findFunctionByName
(
"main("
);
if
(
main
==
NULL
)
return
kErrorMissingMain
;
return
detectCallDepthForFunction
(
main
);
}
return
kErrorNone
;
}
Detect
Recursion
::
FunctionNode
*
DetectRecursion
::
findFunctionByName
(
Detect
CallDepth
::
FunctionNode
*
DetectCallDepth
::
findFunctionByName
(
const
TString
&
name
)
{
for
(
size_t
i
=
0
;
i
<
functions
.
size
();
++
i
)
{
...
...
src/compiler/Detect
Recursion
.h
→
src/compiler/Detect
CallDepth
.h
View file @
da8ea022
...
...
@@ -9,28 +9,36 @@
#include "GLSLANG/ShaderLang.h"
#include <limits.h>
#include "compiler/intermediate.h"
#include "compiler/VariableInfo.h"
class
TInfoSink
;
// Traverses intermediate tree to detect function recursion.
class
Detect
Recursion
:
public
TIntermTraverser
{
class
Detect
CallDepth
:
public
TIntermTraverser
{
public
:
enum
ErrorCode
{
kErrorMissingMain
,
kErrorRecursion
,
kErrorMaxDepthExceeded
,
kErrorNone
};
Detect
Recursion
(
);
~
Detect
Recursion
();
Detect
CallDepth
(
TInfoSink
&
infoSync
,
bool
limitCallStackDepth
,
int
maxCallStackDepth
);
~
Detect
CallDepth
();
virtual
bool
visitAggregate
(
Visit
,
TIntermAggregate
*
);
ErrorCode
detectRecursion
();
bool
checkExceedsMaxDepth
(
int
depth
);
ErrorCode
detectCallDepth
();
private
:
class
FunctionNode
{
public
:
static
const
int
kInfiniteCallDepth
=
INT_MAX
;
FunctionNode
(
const
TString
&
fname
);
const
TString
&
getName
()
const
;
...
...
@@ -38,8 +46,11 @@ private:
// If a function is already in the callee list, this becomes a no-op.
void
addCallee
(
FunctionNode
*
callee
);
// Return true if recursive function calls are detected.
bool
detectRecursion
();
// Returns kInifinityCallDepth if recursive function calls are detected.
int
detectCallDepth
(
DetectCallDepth
*
detectCallDepth
,
int
depth
);
// Reset state.
void
reset
();
private
:
// mangled function name is unique.
...
...
@@ -51,10 +62,19 @@ private:
Visit
visit
;
};
ErrorCode
detectCallDepthForFunction
(
FunctionNode
*
func
);
FunctionNode
*
findFunctionByName
(
const
TString
&
name
);
void
resetFunctionNodes
();
TInfoSink
&
getInfoSink
()
{
return
infoSink
;
}
TVector
<
FunctionNode
*>
functions
;
FunctionNode
*
currentFunction
;
TInfoSink
&
infoSink
;
int
maxDepth
;
DetectCallDepth
(
const
DetectCallDepth
&
);
void
operator
=
(
const
DetectCallDepth
&
);
};
#endif // COMPILER_DETECT_RECURSION_H_
src/compiler/ShHandle.h
View file @
da8ea022
...
...
@@ -83,8 +83,8 @@ protected:
bool
InitBuiltInSymbolTable
(
const
ShBuiltInResources
&
resources
);
// Clears the results from the previous compilation.
void
clearResults
();
// Return true if function recursion is detected.
bool
detect
Recursion
(
TIntermNode
*
root
);
// Return true if function recursion is detected
or call depth exceeded
.
bool
detect
CallDepth
(
TIntermNode
*
root
,
TInfoSink
&
infoSink
,
bool
limitCallStackDepth
);
// Rewrites a shader's intermediate tree according to the CSS Shaders spec.
void
rewriteCSSShader
(
TIntermNode
*
root
);
// Returns true if the given shader does not exceed the minimum
...
...
@@ -106,6 +106,8 @@ protected:
// Returns true if the shader does not use sampler dependent values to affect control
// flow or in operations whose time can depend on the input values.
bool
enforceFragmentShaderTimingRestrictions
(
const
TDependencyGraph
&
graph
);
// Return true if the maximum expression complexity below the limit.
bool
limitExpressionComplexity
(
TIntermNode
*
root
);
// Get built-in extensions with default behavior.
const
TExtensionBehavior
&
getExtensionBehavior
()
const
;
// Get the resources set by InitBuiltInSymbolTable
...
...
@@ -120,6 +122,8 @@ private:
ShShaderSpec
shaderSpec
;
int
maxUniformVectors
;
int
maxExpressionComplexity
;
int
maxCallStackDepth
;
ShBuiltInResources
compileResources
;
...
...
src/compiler/intermediate.h
View file @
da8ea022
...
...
@@ -18,6 +18,7 @@
#include "GLSLANG/ShaderLang.h"
#include <algorithm>
#include "compiler/Common.h"
#include "compiler/Types.h"
#include "compiler/ConstantUnion.h"
...
...
@@ -546,7 +547,8 @@ public:
inVisit
(
inVisit
),
postVisit
(
postVisit
),
rightToLeft
(
rightToLeft
),
depth
(
0
)
{}
depth
(
0
),
maxDepth
(
0
)
{}
virtual
~
TIntermTraverser
()
{};
virtual
void
visitSymbol
(
TIntermSymbol
*
)
{}
...
...
@@ -558,7 +560,8 @@ public:
virtual
bool
visitLoop
(
Visit
visit
,
TIntermLoop
*
)
{
return
true
;}
virtual
bool
visitBranch
(
Visit
visit
,
TIntermBranch
*
)
{
return
true
;}
void
incrementDepth
()
{
depth
++
;}
int
getMaxDepth
()
const
{
return
maxDepth
;}
void
incrementDepth
()
{
depth
++
;
maxDepth
=
std
::
max
(
maxDepth
,
depth
);
}
void
decrementDepth
()
{
depth
--
;}
// Return the original name if hash function pointer is NULL;
...
...
@@ -572,6 +575,7 @@ public:
protected
:
int
depth
;
int
maxDepth
;
};
#endif // __INTERMEDIATE_H
src/compiler/translator_common.vcxproj
View file @
da8ea022
...
...
@@ -141,7 +141,7 @@
<ClCompile
Include=
"BuiltInFunctionEmulator.cpp"
/>
<ClCompile
Include=
"Compiler.cpp"
/>
<ClCompile
Include=
"debug.cpp"
/>
<ClCompile
Include=
"Detect
Recursion
.cpp"
/>
<ClCompile
Include=
"Detect
CallDepth
.cpp"
/>
<ClCompile
Include=
"Diagnostics.cpp"
/>
<ClCompile
Include=
"DirectiveHandler.cpp"
/>
<ClCompile
Include=
"ForLoopUnroll.cpp"
/>
...
...
@@ -231,7 +231,7 @@
<ClInclude
Include=
"Common.h"
/>
<ClInclude
Include=
"ConstantUnion.h"
/>
<ClInclude
Include=
"debug.h"
/>
<ClInclude
Include=
"Detect
Recursion
.h"
/>
<ClInclude
Include=
"Detect
CallDepth
.h"
/>
<ClInclude
Include=
"Diagnostics.h"
/>
<ClInclude
Include=
"DirectiveHandler.h"
/>
<ClInclude
Include=
"ForLoopUnroll.h"
/>
...
...
src/compiler/translator_common.vcxproj.filters
View file @
da8ea022
...
...
@@ -38,7 +38,7 @@
<ClCompile
Include=
"debug.cpp"
>
<Filter>
Source Files
</Filter>
</ClCompile>
<ClCompile
Include=
"Detect
Recursion
.cpp"
>
<ClCompile
Include=
"Detect
CallDepth
.cpp"
>
<Filter>
Source Files
</Filter>
</ClCompile>
<ClCompile
Include=
"Diagnostics.cpp"
>
...
...
@@ -154,7 +154,7 @@
<ClInclude
Include=
"debug.h"
>
<Filter>
Header Files
</Filter>
</ClInclude>
<ClInclude
Include=
"Detect
Recursion
.h"
>
<ClInclude
Include=
"Detect
CallDepth
.h"
>
<Filter>
Header Files
</Filter>
</ClInclude>
<ClInclude
Include=
"Diagnostics.h"
>
...
...
tests/build_tests.gyp
View file @
da8ea022
...
...
@@ -67,7 +67,7 @@
'target_name': 'compiler_tests',
'type': 'executable',
'dependencies': [
'../src/build_angle.gyp:translator_
common
',
'../src/build_angle.gyp:translator_
glsl
',
'gtest',
'gmock',
],
...
...
@@ -79,6 +79,7 @@
],
'sources': [
'../third_party/googlemock/src/gmock_main.cc',
'compiler_tests/ExpressionLimit_test.cpp',
'compiler_tests/VariablePacker_test.cpp',
],
},
...
...
tests/compiler_tests/ExpressionLimit_test.cpp
0 → 100644
View file @
da8ea022
//
// 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 <sstream>
#include <string>
#include <vector>
#include "GLSLANG/ShaderLang.h"
#include "gtest/gtest.h"
#define SHADER(Src) #Src
class
ExpressionLimitTest
:
public
testing
::
Test
{
protected
:
static
const
int
kMaxExpressionComplexity
=
16
;
static
const
int
kMaxCallStackDepth
=
16
;
static
const
char
*
kExpressionTooComplex
;
static
const
char
*
kCallStackTooDeep
;
static
const
char
*
kHasRecursion
;
virtual
void
SetUp
()
{
memset
(
&
resources
,
0
,
sizeof
(
resources
));
ASSERT_TRUE
(
ShInitialize
()
!=
0
)
<<
"Could not ShInitialize"
;
GenerateResources
(
&
resources
);
}
virtual
void
TearDown
()
{
ASSERT_TRUE
(
ShFinalize
()
!=
0
);
}
// Set up the per compile resources
void
GenerateResources
(
ShBuiltInResources
*
resources
)
{
ShInitBuiltInResources
(
resources
);
resources
->
MaxVertexAttribs
=
8
;
resources
->
MaxVertexUniformVectors
=
128
;
resources
->
MaxVaryingVectors
=
8
;
resources
->
MaxVertexTextureImageUnits
=
0
;
resources
->
MaxCombinedTextureImageUnits
=
8
;
resources
->
MaxTextureImageUnits
=
8
;
resources
->
MaxFragmentUniformVectors
=
16
;
resources
->
MaxDrawBuffers
=
1
;
resources
->
OES_standard_derivatives
=
0
;
resources
->
OES_EGL_image_external
=
0
;
resources
->
MaxExpressionComplexity
=
kMaxExpressionComplexity
;
resources
->
MaxCallStackDepth
=
kMaxCallStackDepth
;
}
void
GenerateLongExpression
(
int
length
,
std
::
stringstream
*
ss
)
{
for
(
int
ii
=
0
;
ii
<
length
;
++
ii
)
{
*
ss
<<
"+ vec4("
<<
ii
<<
")"
;
}
}
std
::
string
GenerateShaderWithLongExpression
(
int
length
)
{
static
const
char
*
shaderStart
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
void
main
()
{
gl_FragColor
=
u_color
);
std
::
stringstream
ss
;
ss
<<
shaderStart
;
GenerateLongExpression
(
length
,
&
ss
);
ss
<<
"; }"
;
return
ss
.
str
();
}
std
::
string
GenerateShaderWithUnusedLongExpression
(
int
length
)
{
static
const
char
*
shaderStart
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
void
main
()
{
gl_FragColor
=
u_color
;
}
vec4
someFunction
()
{
return
u_color
);
std
::
stringstream
ss
;
ss
<<
shaderStart
;
GenerateLongExpression
(
length
,
&
ss
);
ss
<<
"; }"
;
return
ss
.
str
();
}
void
GenerateDeepFunctionStack
(
int
length
,
std
::
stringstream
*
ss
)
{
static
const
char
*
shaderStart
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
function0
()
{
return
u_color
;
}
);
*
ss
<<
shaderStart
;
for
(
int
ii
=
0
;
ii
<
length
;
++
ii
)
{
*
ss
<<
"vec4 function"
<<
(
ii
+
1
)
<<
"() {
\n
"
<<
" return function"
<<
ii
<<
"();
\n
"
<<
"}
\n
"
;
}
}
std
::
string
GenerateShaderWithDeepFunctionStack
(
int
length
)
{
std
::
stringstream
ss
;
GenerateDeepFunctionStack
(
length
,
&
ss
);
ss
<<
"void main() {
\n
"
<<
" gl_FragColor = function"
<<
length
<<
"();
\n
"
<<
"}"
;
return
ss
.
str
();
}
std
::
string
GenerateShaderWithUnusedDeepFunctionStack
(
int
length
)
{
std
::
stringstream
ss
;
GenerateDeepFunctionStack
(
length
,
&
ss
);
ss
<<
"void main() {
\n
"
<<
" gl_FragColor = vec4(0,0,0,0);
\n
"
<<
"}"
;
return
ss
.
str
();
}
// Compiles a shader and if there's an error checks for a specific
// substring in the error log. This way we know the error is specific
// to the issue we are testing.
bool
CheckShaderCompilation
(
ShHandle
compiler
,
const
char
*
source
,
int
compileOptions
,
const
char
*
expected_error
)
{
bool
success
=
ShCompile
(
compiler
,
&
source
,
1
,
compileOptions
);
if
(
success
)
{
success
=
!
expected_error
;
}
else
{
size_t
bufferLen
=
0
;
ShGetInfo
(
compiler
,
SH_INFO_LOG_LENGTH
,
&
bufferLen
);
char
*
buffer
(
new
char
[
bufferLen
]);
ShGetInfoLog
(
compiler
,
buffer
);
std
::
string
log
(
buffer
,
buffer
+
bufferLen
);
delete
[]
buffer
;
if
(
expected_error
)
success
=
log
.
find
(
expected_error
)
!=
std
::
string
::
npos
;
EXPECT_TRUE
(
success
)
<<
log
<<
"
\n
----shader----
\n
"
<<
source
;
}
return
success
;
}
ShBuiltInResources
resources
;
};
const
char
*
ExpressionLimitTest
::
kExpressionTooComplex
=
"Expression too complex"
;
const
char
*
ExpressionLimitTest
::
kCallStackTooDeep
=
"call stack too deep"
;
const
char
*
ExpressionLimitTest
::
kHasRecursion
=
"Function recursion detected"
;
TEST_F
(
ExpressionLimitTest
,
ExpressionComplexity
)
{
ShShaderSpec
spec
=
SH_WEBGL_SPEC
;
ShShaderOutput
output
=
SH_ESSL_OUTPUT
;
ShHandle
vertexCompiler
=
ShConstructCompiler
(
SH_FRAGMENT_SHADER
,
spec
,
output
,
&
resources
);
int
compileOptions
=
SH_LIMIT_EXPRESSION_COMPLEXITY
;
// Test expression under the limit passes.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithLongExpression
(
kMaxExpressionComplexity
-
10
).
c_str
(),
compileOptions
,
NULL
));
// Test expression over the limit fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithLongExpression
(
kMaxExpressionComplexity
+
10
).
c_str
(),
compileOptions
,
kExpressionTooComplex
));
// Test expression over the limit without a limit does not fail.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithLongExpression
(
kMaxExpressionComplexity
+
10
).
c_str
(),
compileOptions
&
~
SH_LIMIT_EXPRESSION_COMPLEXITY
,
NULL
));
}
TEST_F
(
ExpressionLimitTest
,
UnusedExpressionComplexity
)
{
ShShaderSpec
spec
=
SH_WEBGL_SPEC
;
ShShaderOutput
output
=
SH_ESSL_OUTPUT
;
ShHandle
vertexCompiler
=
ShConstructCompiler
(
SH_FRAGMENT_SHADER
,
spec
,
output
,
&
resources
);
int
compileOptions
=
SH_LIMIT_EXPRESSION_COMPLEXITY
;
// Test expression under the limit passes.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithUnusedLongExpression
(
kMaxExpressionComplexity
-
10
).
c_str
(),
compileOptions
,
NULL
));
// Test expression over the limit fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithUnusedLongExpression
(
kMaxExpressionComplexity
+
10
).
c_str
(),
compileOptions
,
kExpressionTooComplex
));
// Test expression over the limit without a limit does not fail.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithUnusedLongExpression
(
kMaxExpressionComplexity
+
10
).
c_str
(),
compileOptions
&
~
SH_LIMIT_EXPRESSION_COMPLEXITY
,
NULL
));
}
TEST_F
(
ExpressionLimitTest
,
CallStackDepth
)
{
ShShaderSpec
spec
=
SH_WEBGL_SPEC
;
ShShaderOutput
output
=
SH_ESSL_OUTPUT
;
ShHandle
vertexCompiler
=
ShConstructCompiler
(
SH_FRAGMENT_SHADER
,
spec
,
output
,
&
resources
);
int
compileOptions
=
SH_LIMIT_CALL_STACK_DEPTH
;
// Test call stack under the limit passes.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithDeepFunctionStack
(
kMaxCallStackDepth
-
10
).
c_str
(),
compileOptions
,
NULL
));
// Test call stack over the limit fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithDeepFunctionStack
(
kMaxCallStackDepth
+
10
).
c_str
(),
compileOptions
,
kCallStackTooDeep
));
// Test call stack over the limit without limit does not fail.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithDeepFunctionStack
(
kMaxCallStackDepth
+
10
).
c_str
(),
compileOptions
&
~
SH_LIMIT_CALL_STACK_DEPTH
,
NULL
));
}
TEST_F
(
ExpressionLimitTest
,
UnusedCallStackDepth
)
{
ShShaderSpec
spec
=
SH_WEBGL_SPEC
;
ShShaderOutput
output
=
SH_ESSL_OUTPUT
;
ShHandle
vertexCompiler
=
ShConstructCompiler
(
SH_FRAGMENT_SHADER
,
spec
,
output
,
&
resources
);
int
compileOptions
=
SH_LIMIT_CALL_STACK_DEPTH
;
// Test call stack under the limit passes.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithUnusedDeepFunctionStack
(
kMaxCallStackDepth
-
10
).
c_str
(),
compileOptions
,
NULL
));
// Test call stack over the limit fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithUnusedDeepFunctionStack
(
kMaxCallStackDepth
+
10
).
c_str
(),
compileOptions
,
kCallStackTooDeep
));
// Test call stack over the limit without limit does not fail.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
GenerateShaderWithUnusedDeepFunctionStack
(
kMaxCallStackDepth
+
10
).
c_str
(),
compileOptions
&
~
SH_LIMIT_CALL_STACK_DEPTH
,
NULL
));
}
TEST_F
(
ExpressionLimitTest
,
Recursion
)
{
ShShaderSpec
spec
=
SH_WEBGL_SPEC
;
ShShaderOutput
output
=
SH_ESSL_OUTPUT
;
ShHandle
vertexCompiler
=
ShConstructCompiler
(
SH_FRAGMENT_SHADER
,
spec
,
output
,
&
resources
);
int
compileOptions
=
0
;
static
const
char
*
shaderWithRecursion0
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
someFunc
()
{
return
someFunc
();
}
void
main
()
{
gl_FragColor
=
u_color
*
someFunc
();
}
);
static
const
char
*
shaderWithRecursion1
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
someFunc
();
vec4
someFunc1
()
{
return
someFunc
();
}
vec4
someFunc
()
{
return
someFunc1
();
}
void
main
()
{
gl_FragColor
=
u_color
*
someFunc
();
}
);
static
const
char
*
shaderWithRecursion2
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
someFunc
()
{
if
(
u_color
.
x
>
0.5
)
{
return
someFunc
();
}
else
{
return
vec4
(
1
);
}
}
void
main
()
{
gl_FragColor
=
someFunc
();
}
);
static
const
char
*
shaderWithRecursion3
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
someFunc
()
{
if
(
u_color
.
x
>
0.5
)
{
return
vec4
(
1
);
}
else
{
return
someFunc
();
}
}
void
main
()
{
gl_FragColor
=
someFunc
();
}
);
static
const
char
*
shaderWithRecursion4
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
someFunc
()
{
return
(
u_color
.
x
>
0.5
)
?
vec4
(
1
)
:
someFunc
();
}
void
main
()
{
gl_FragColor
=
someFunc
();
}
);
static
const
char
*
shaderWithRecursion5
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
someFunc
()
{
return
(
u_color
.
x
>
0.5
)
?
someFunc
()
:
vec4
(
1
);
}
void
main
()
{
gl_FragColor
=
someFunc
();
}
);
static
const
char
*
shaderWithRecursion6
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
someFunc
()
{
return
someFunc
();
}
void
main
()
{
gl_FragColor
=
u_color
;
}
);
static
const
char
*
shaderWithNoRecursion
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec3
rgb
(
int
r
,
int
g
,
int
b
)
{
return
vec3
(
float
(
r
)
/
255.0
,
float
(
g
)
/
255.0
,
float
(
b
)
/
255.0
);
}
// these external calls used to incorrectly trigger
// recursion detection.
vec3
hairColor0
=
rgb
(
151
,
200
,
234
);
vec3
faceColor2
=
rgb
(
183
,
148
,
133
);
void
main
()
{
gl_FragColor
=
u_color
+
vec4
(
hairColor0
+
faceColor2
,
0
);
}
);
static
const
char
*
shaderWithRecursion7
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
function2
()
{
return
u_color
;
}
vec4
function1
()
{
vec4
a
=
function2
();
vec4
b
=
function1
();
return
a
+
b
;
}
void
main
()
{
gl_FragColor
=
function1
();
}
);
static
const
char
*
shaderWithRecursion8
=
SHADER
(
precision
mediump
float
;
uniform
vec4
u_color
;
vec4
function1
();
vec4
function3
()
{
return
function1
();
}
vec4
function2
()
{
return
function3
();
}
vec4
function1
()
{
return
function2
();
}
void
main
()
{
gl_FragColor
=
function1
();
}
);
// Check simple recursions fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion0
,
compileOptions
,
kHasRecursion
));
// Check simple recursions fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion1
,
compileOptions
,
kHasRecursion
));
// Check if recursions fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion2
,
compileOptions
,
kHasRecursion
));
// Check if recursions fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion3
,
compileOptions
,
kHasRecursion
));
// Check ternary recursions fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion4
,
compileOptions
,
kHasRecursion
));
// Check ternary recursions fails.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion5
,
compileOptions
,
kHasRecursion
));
// Check unused recursions passes.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion6
,
compileOptions
,
NULL
));
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion7
,
compileOptions
,
kHasRecursion
));
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion8
,
compileOptions
,
kHasRecursion
));
// Check unused recursions fails if limiting call stack
// since we check all paths.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithRecursion6
,
compileOptions
|
SH_LIMIT_CALL_STACK_DEPTH
,
kHasRecursion
));
// Check unused recursions passes.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithNoRecursion
,
compileOptions
,
NULL
));
// Check unused recursions passes if limiting call stack.
EXPECT_TRUE
(
CheckShaderCompilation
(
vertexCompiler
,
shaderWithNoRecursion
,
compileOptions
|
SH_LIMIT_CALL_STACK_DEPTH
,
NULL
));
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment