1 //
2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 #include "compiler/translator/ValidateGlobalInitializer.h"
8 
9 #include "compiler/translator/tree_util/IntermTraverse.h"
10 
11 namespace sh
12 {
13 
14 namespace
15 {
16 
17 const int kMaxAllowedTraversalDepth = 256;
18 
19 class ValidateGlobalInitializerTraverser : public TIntermTraverser
20 {
21   public:
22     ValidateGlobalInitializerTraverser(int shaderVersion,
23                                        bool isWebGL,
24                                        bool hasExtNonConstGlobalInitializers);
25 
26     void visitSymbol(TIntermSymbol *node) override;
27     void visitConstantUnion(TIntermConstantUnion *node) override;
28     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
29     bool visitBinary(Visit visit, TIntermBinary *node) override;
30     bool visitUnary(Visit visit, TIntermUnary *node) override;
31 
isValid() const32     bool isValid() const { return mIsValid && mMaxDepth < mMaxAllowedDepth; }
issueWarning() const33     bool issueWarning() const { return mIssueWarning; }
34 
35   private:
onNonConstInitializerVisit(bool accept)36     ANGLE_INLINE void onNonConstInitializerVisit(bool accept)
37     {
38         if (accept)
39         {
40             if (!mExtNonConstGlobalInitializers)
41             {
42                 mIssueWarning = true;
43             }
44         }
45         else
46         {
47             mIsValid = false;
48         }
49     }
50 
51     int mShaderVersion;
52     bool mIsWebGL;
53     bool mExtNonConstGlobalInitializers;
54     bool mIsValid;
55     bool mIssueWarning;
56 };
57 
visitSymbol(TIntermSymbol * node)58 void ValidateGlobalInitializerTraverser::visitSymbol(TIntermSymbol *node)
59 {
60     // ESSL 1.00 section 4.3 (or ESSL 3.00 section 4.3):
61     // Global initializers must be constant expressions.
62     switch (node->getType().getQualifier())
63     {
64         case EvqConst:
65             break;
66         case EvqGlobal:
67         case EvqTemporary:
68         case EvqUniform:
69             // We allow these cases to be compatible with legacy ESSL 1.00 content.
70             // Implement stricter rules for ESSL 3.00 since there's no legacy content to deal
71             // with.
72             onNonConstInitializerVisit(mExtNonConstGlobalInitializers ||
73                                        ((mShaderVersion < 300) && mIsWebGL));
74             break;
75         default:
76             mIsValid = false;
77     }
78 }
79 
visitConstantUnion(TIntermConstantUnion * node)80 void ValidateGlobalInitializerTraverser::visitConstantUnion(TIntermConstantUnion *node)
81 {
82     // Constant unions that are not constant expressions may result from folding a ternary
83     // expression.
84     switch (node->getType().getQualifier())
85     {
86         case EvqConst:
87             break;
88         case EvqTemporary:
89             onNonConstInitializerVisit(mExtNonConstGlobalInitializers ||
90                                        ((mShaderVersion < 300) && mIsWebGL));
91             break;
92         default:
93             UNREACHABLE();
94     }
95 }
96 
visitAggregate(Visit visit,TIntermAggregate * node)97 bool ValidateGlobalInitializerTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
98 {
99     // Disallow calls to user-defined functions and texture lookup functions in global variable
100     // initializers.
101     // This is done simply by disabling all function calls - built-in math functions don't use
102     // the function call ops.
103     if (node->isFunctionCall())
104     {
105         onNonConstInitializerVisit(mExtNonConstGlobalInitializers);
106     }
107     return true;
108 }
109 
visitBinary(Visit visit,TIntermBinary * node)110 bool ValidateGlobalInitializerTraverser::visitBinary(Visit visit, TIntermBinary *node)
111 {
112     if (node->isAssignment())
113     {
114         onNonConstInitializerVisit(mExtNonConstGlobalInitializers);
115     }
116     return true;
117 }
118 
visitUnary(Visit visit,TIntermUnary * node)119 bool ValidateGlobalInitializerTraverser::visitUnary(Visit visit, TIntermUnary *node)
120 {
121     if (node->isAssignment())
122     {
123         onNonConstInitializerVisit(mExtNonConstGlobalInitializers);
124     }
125     return true;
126 }
127 
ValidateGlobalInitializerTraverser(int shaderVersion,bool isWebGL,bool hasExtNonConstGlobalInitializers)128 ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(
129     int shaderVersion,
130     bool isWebGL,
131     bool hasExtNonConstGlobalInitializers)
132     : TIntermTraverser(true, false, false, nullptr),
133       mShaderVersion(shaderVersion),
134       mIsWebGL(isWebGL),
135       mExtNonConstGlobalInitializers(hasExtNonConstGlobalInitializers),
136       mIsValid(true),
137       mIssueWarning(false)
138 {
139     setMaxAllowedDepth(kMaxAllowedTraversalDepth);
140 }
141 
142 }  // namespace
143 
ValidateGlobalInitializer(TIntermTyped * initializer,int shaderVersion,bool isWebGL,bool hasExtNonConstGlobalInitializers,bool * warning)144 bool ValidateGlobalInitializer(TIntermTyped *initializer,
145                                int shaderVersion,
146                                bool isWebGL,
147                                bool hasExtNonConstGlobalInitializers,
148                                bool *warning)
149 {
150     ValidateGlobalInitializerTraverser validate(shaderVersion, isWebGL,
151                                                 hasExtNonConstGlobalInitializers);
152     initializer->traverse(&validate);
153     ASSERT(warning != nullptr);
154     *warning = validate.issueWarning();
155     return validate.isValid();
156 }
157 
158 }  // namespace sh
159