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