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. For simplicity, all non-math built-in calls are disallowed.
101 if (node->isFunctionCall() ||
102 (BuiltInGroup::IsBuiltIn(node->getOp()) && !BuiltInGroup::IsMath(node->getOp())))
103 {
104 onNonConstInitializerVisit(mExtNonConstGlobalInitializers);
105 }
106 return true;
107 }
108
visitBinary(Visit visit,TIntermBinary * node)109 bool ValidateGlobalInitializerTraverser::visitBinary(Visit visit, TIntermBinary *node)
110 {
111 if (node->isAssignment())
112 {
113 onNonConstInitializerVisit(mExtNonConstGlobalInitializers);
114 }
115 return true;
116 }
117
visitUnary(Visit visit,TIntermUnary * node)118 bool ValidateGlobalInitializerTraverser::visitUnary(Visit visit, TIntermUnary *node)
119 {
120 if (node->isAssignment())
121 {
122 onNonConstInitializerVisit(mExtNonConstGlobalInitializers);
123 }
124 return true;
125 }
126
ValidateGlobalInitializerTraverser(int shaderVersion,bool isWebGL,bool hasExtNonConstGlobalInitializers)127 ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(
128 int shaderVersion,
129 bool isWebGL,
130 bool hasExtNonConstGlobalInitializers)
131 : TIntermTraverser(true, false, false, nullptr),
132 mShaderVersion(shaderVersion),
133 mIsWebGL(isWebGL),
134 mExtNonConstGlobalInitializers(hasExtNonConstGlobalInitializers),
135 mIsValid(true),
136 mIssueWarning(false)
137 {
138 setMaxAllowedDepth(kMaxAllowedTraversalDepth);
139 }
140
141 } // namespace
142
ValidateGlobalInitializer(TIntermTyped * initializer,int shaderVersion,bool isWebGL,bool hasExtNonConstGlobalInitializers,bool * warning)143 bool ValidateGlobalInitializer(TIntermTyped *initializer,
144 int shaderVersion,
145 bool isWebGL,
146 bool hasExtNonConstGlobalInitializers,
147 bool *warning)
148 {
149 ValidateGlobalInitializerTraverser validate(shaderVersion, isWebGL,
150 hasExtNonConstGlobalInitializers);
151 initializer->traverse(&validate);
152 ASSERT(warning != nullptr);
153 *warning = validate.issueWarning();
154 return validate.isValid();
155 }
156
157 } // namespace sh
158