• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/sksl/ir/SkSLVarDeclarations.h"
9 
10 #include "include/sksl/SkSLErrorReporter.h"
11 #include "src/sksl/SkSLAnalysis.h"
12 #include "src/sksl/SkSLCompiler.h"
13 #include "src/sksl/SkSLContext.h"
14 #include "src/sksl/SkSLProgramSettings.h"
15 #include "src/sksl/SkSLThreadContext.h"
16 
17 namespace SkSL {
18 
19 
clone() const20 std::unique_ptr<Statement> VarDeclaration::clone() const {
21     // Cloning a VarDeclaration is inherently problematic, as we normally expect a one-to-one
22     // mapping between Variables and VarDeclarations and a straightforward clone would violate this
23     // assumption. We could of course theoretically clone the Variable as well, but that would
24     // require additional context and tracking, since for the whole process to work we would also
25     // have to fixup any subsequent VariableReference clones to point to the newly cloned Variables
26     // instead of the originals.
27     //
28     // Since the only reason we ever clone VarDeclarations is to support tests of clone() and we do
29     // not expect to ever need to do so otherwise, a full solution to this issue is unnecessary at
30     // the moment. We instead just keep track of whether a VarDeclaration is a clone so we can
31     // handle its cleanup properly. This allows clone() to work in the simple case that a
32     // VarDeclaration's clone does not outlive the original, which is adequate for testing. Since
33     // this leaves a sharp  edge in place - destroying the original could cause a use-after-free in
34     // some circumstances - we also disable cloning altogether unless the
35     // fAllowVarDeclarationCloneForTesting ProgramSetting is enabled.
36     if (ThreadContext::Settings().fAllowVarDeclarationCloneForTesting) {
37         return std::make_unique<VarDeclaration>(&this->var(),
38                                                 &this->baseType(),
39                                                 fArraySize,
40                                                 this->value() ? this->value()->clone() : nullptr,
41                                                 /*isClone=*/true);
42     } else {
43         SkDEBUGFAIL("VarDeclaration::clone() is unsupported");
44         return nullptr;
45     }
46 }
47 
description() const48 std::string VarDeclaration::description() const {
49     std::string result = this->var().modifiers().description() + this->baseType().description() +
50                          " " + std::string(this->var().name());
51     if (this->arraySize() > 0) {
52         String::appendf(&result, "[%d]", this->arraySize());
53     }
54     if (this->value()) {
55         result += " = " + this->value()->description();
56     }
57     result += ";";
58     return result;
59 }
60 
ErrorCheck(const Context & context,int line,const Modifiers & modifiers,const Type * baseType,Variable::Storage storage)61 void VarDeclaration::ErrorCheck(const Context& context,
62                                 int line,
63                                 const Modifiers& modifiers,
64                                 const Type* baseType,
65                                 Variable::Storage storage) {
66     if (baseType->matches(*context.fTypes.fInvalid)) {
67         context.fErrors->error(line, "invalid type");
68         return;
69     }
70     if (baseType->isVoid()) {
71         context.fErrors->error(line, "variables of type 'void' are not allowed");
72         return;
73     }
74     if (context.fConfig->strictES2Mode() && baseType->isArray()) {
75         context.fErrors->error(line, "array size must appear after variable name");
76     }
77 
78     if (baseType->componentType().isOpaque() && storage != Variable::Storage::kGlobal) {
79         context.fErrors->error(line,
80                 "variables of type '" + baseType->displayName() + "' must be global");
81     }
82     if ((modifiers.fFlags & Modifiers::kIn_Flag) && baseType->isMatrix()) {
83         context.fErrors->error(line, "'in' variables may not have matrix type");
84     }
85     if ((modifiers.fFlags & Modifiers::kIn_Flag) && (modifiers.fFlags & Modifiers::kUniform_Flag)) {
86         context.fErrors->error(line, "'in uniform' variables not permitted");
87     }
88     if (ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
89         if (modifiers.fFlags & Modifiers::kIn_Flag) {
90             context.fErrors->error(line, "'in' variables not permitted in runtime effects");
91         }
92     }
93     if (baseType->isEffectChild() && !(modifiers.fFlags & Modifiers::kUniform_Flag)) {
94         context.fErrors->error(line,
95                 "variables of type '" + baseType->displayName() + "' must be uniform");
96     }
97     if (modifiers.fFlags & SkSL::Modifiers::kUniform_Flag &&
98         (context.fConfig->fKind == ProgramKind::kCustomMeshVertex ||
99          context.fConfig->fKind == ProgramKind::kCustomMeshFragment)) {
100         context.fErrors->error(line, "uniforms are not permitted in custom mesh shaders");
101     }
102     if (modifiers.fLayout.fFlags & Layout::kColor_Flag) {
103         if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
104             context.fErrors->error(line, "'layout(color)' is only permitted in runtime effects");
105         }
106         if (!(modifiers.fFlags & Modifiers::kUniform_Flag)) {
107             context.fErrors->error(line,
108                                    "'layout(color)' is only permitted on 'uniform' variables");
109         }
110         auto validColorXformType = [](const Type& t) {
111             return t.isVector() && t.componentType().isFloat() &&
112                    (t.columns() == 3 || t.columns() == 4);
113         };
114         if (!validColorXformType(*baseType) && !(baseType->isArray() &&
115                                                  validColorXformType(baseType->componentType()))) {
116             context.fErrors->error(line,
117                                    "'layout(color)' is not permitted on variables of type '" +
118                                            baseType->displayName() + "'");
119         }
120     }
121     int permitted = Modifiers::kConst_Flag | Modifiers::kHighp_Flag | Modifiers::kMediump_Flag |
122                     Modifiers::kLowp_Flag;
123     if (storage == Variable::Storage::kGlobal) {
124         permitted |= Modifiers::kIn_Flag | Modifiers::kOut_Flag | Modifiers::kUniform_Flag |
125                      Modifiers::kFlat_Flag | Modifiers::kNoPerspective_Flag;
126     }
127     // TODO(skbug.com/11301): Migrate above checks into building a mask of permitted layout flags
128 
129     int permittedLayoutFlags = ~0;
130     // We don't allow 'binding' or 'set' on normal uniform variables, only on textures, samplers,
131     // and interface blocks (holding uniform variables). They're also only allowed at global scope,
132     // not on interface block fields (or locals/parameters).
133     bool permitBindingAndSet = baseType->typeKind() == Type::TypeKind::kSampler ||
134                                baseType->typeKind() == Type::TypeKind::kSeparateSampler ||
135                                baseType->typeKind() == Type::TypeKind::kTexture ||
136                                baseType->isInterfaceBlock();
137     if (storage != Variable::Storage::kGlobal ||
138         ((modifiers.fFlags & Modifiers::kUniform_Flag) && !permitBindingAndSet)) {
139         permittedLayoutFlags &= ~Layout::kBinding_Flag;
140         permittedLayoutFlags &= ~Layout::kSet_Flag;
141     }
142     modifiers.checkPermitted(context, line, permitted, permittedLayoutFlags);
143 }
144 
ErrorCheckAndCoerce(const Context & context,const Variable & var,std::unique_ptr<Expression> & value)145 bool VarDeclaration::ErrorCheckAndCoerce(const Context& context, const Variable& var,
146         std::unique_ptr<Expression>& value) {
147     const Type* baseType = &var.type();
148     if (baseType->isArray()) {
149         baseType = &baseType->componentType();
150     }
151     ErrorCheck(context, var.fLine, var.modifiers(), baseType, var.storage());
152     if (value) {
153         if (var.type().isOpaque()) {
154             context.fErrors->error(value->fLine, "opaque type '" + var.type().displayName() +
155                                                  "' cannot use initializer expressions");
156             return false;
157         }
158         if (var.modifiers().fFlags & Modifiers::kIn_Flag) {
159             context.fErrors->error(value->fLine,
160                                    "'in' variables cannot use initializer expressions");
161             return false;
162         }
163         if (var.modifiers().fFlags & Modifiers::kUniform_Flag) {
164             context.fErrors->error(value->fLine,
165                                    "'uniform' variables cannot use initializer expressions");
166             return false;
167         }
168         if (var.storage() == Variable::Storage::kInterfaceBlock) {
169             context.fErrors->error(value->fLine,
170                                    "initializers are not permitted on interface block fields");
171             return false;
172         }
173         value = var.type().coerceExpression(std::move(value), context);
174         if (!value) {
175             return false;
176         }
177     }
178     if (var.modifiers().fFlags & Modifiers::kConst_Flag) {
179         if (!value) {
180             context.fErrors->error(var.fLine, "'const' variables must be initialized");
181             return false;
182         }
183         if (!Analysis::IsConstantExpression(*value)) {
184             context.fErrors->error(value->fLine,
185                                    "'const' variable initializer must be a constant expression");
186             return false;
187         }
188     }
189     if (var.storage() == Variable::Storage::kInterfaceBlock) {
190         if (var.type().isOpaque()) {
191             context.fErrors->error(var.fLine, "opaque type '" + var.type().displayName() +
192                                               "' is not permitted in an interface block");
193             return false;
194         }
195     }
196     if (var.storage() == Variable::Storage::kGlobal) {
197         if (value && !Analysis::IsConstantExpression(*value)) {
198             context.fErrors->error(value->fLine,
199                                    "global variable initializer must be a constant expression");
200             return false;
201         }
202     }
203     return true;
204 }
205 
Convert(const Context & context,std::unique_ptr<Variable> var,std::unique_ptr<Expression> value,bool addToSymbolTable)206 std::unique_ptr<Statement> VarDeclaration::Convert(const Context& context,
207         std::unique_ptr<Variable> var, std::unique_ptr<Expression> value, bool addToSymbolTable) {
208     if (!ErrorCheckAndCoerce(context, *var, value)) {
209         return nullptr;
210     }
211     const Type* baseType = &var->type();
212     int arraySize = 0;
213     if (baseType->isArray()) {
214         arraySize = baseType->columns();
215         baseType = &baseType->componentType();
216     }
217     std::unique_ptr<Statement> varDecl = VarDeclaration::Make(context, var.get(), baseType,
218             arraySize, std::move(value));
219     if (!varDecl) {
220         return nullptr;
221     }
222 
223     // Detect the declaration of magical variables.
224     if ((var->storage() == Variable::Storage::kGlobal) && var->name() == Compiler::FRAGCOLOR_NAME) {
225         // Silently ignore duplicate definitions of `sk_FragColor`.
226         const Symbol* symbol = (*ThreadContext::SymbolTable())[var->name()];
227         if (symbol) {
228             return nullptr;
229         }
230     } else if ((var->storage() == Variable::Storage::kGlobal ||
231                 var->storage() == Variable::Storage::kInterfaceBlock) &&
232                var->name() == Compiler::RTADJUST_NAME) {
233         // `sk_RTAdjust` is special, and makes the IR generator emit position-fixup expressions.
234         if (ThreadContext::RTAdjustState().fVar || ThreadContext::RTAdjustState().fInterfaceBlock) {
235             context.fErrors->error(var->fLine, "duplicate definition of 'sk_RTAdjust'");
236             return nullptr;
237         }
238         if (!var->type().matches(*context.fTypes.fFloat4)) {
239             context.fErrors->error(var->fLine, "sk_RTAdjust must have type 'float4'");
240             return nullptr;
241         }
242         ThreadContext::RTAdjustState().fVar = var.get();
243     }
244 
245     if (addToSymbolTable) {
246         ThreadContext::SymbolTable()->add(std::move(var));
247     } else {
248         ThreadContext::SymbolTable()->takeOwnershipOfSymbol(std::move(var));
249     }
250     return varDecl;
251 }
252 
Make(const Context & context,Variable * var,const Type * baseType,int arraySize,std::unique_ptr<Expression> value)253 std::unique_ptr<Statement> VarDeclaration::Make(const Context& context, Variable* var,
254         const Type* baseType, int arraySize, std::unique_ptr<Expression> value) {
255     SkASSERT(!baseType->isArray());
256     // function parameters cannot have variable declarations
257     SkASSERT(var->storage() != Variable::Storage::kParameter);
258     // 'const' variables must be initialized
259     SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) || value);
260     // 'const' variable initializer must be a constant expression
261     SkASSERT(!(var->modifiers().fFlags & Modifiers::kConst_Flag) ||
262              Analysis::IsConstantExpression(*value));
263     // global variable initializer must be a constant expression
264     SkASSERT(!(value && var->storage() == Variable::Storage::kGlobal &&
265                !Analysis::IsConstantExpression(*value)));
266     // opaque type not permitted on an interface block
267     SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && var->type().isOpaque()));
268     // initializers are not permitted on interface block fields
269     SkASSERT(!(var->storage() == Variable::Storage::kInterfaceBlock && value));
270     // opaque type cannot use initializer expressions
271     SkASSERT(!(value && var->type().isOpaque()));
272     // 'in' variables cannot use initializer expressions
273     SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kIn_Flag)));
274     // 'uniform' variables cannot use initializer expressions
275     SkASSERT(!(value && (var->modifiers().fFlags & Modifiers::kUniform_Flag)));
276 
277     auto result = std::make_unique<VarDeclaration>(var, baseType, arraySize, std::move(value));
278     var->setDeclaration(result.get());
279     return std::move(result);
280 }
281 
282 }  // namespace SkSL
283