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/SkSLVariable.h"
9
10 #include "include/private/SkSLIRNode.h"
11 #include "include/private/SkSLLayout.h"
12 #include "include/sksl/SkSLErrorReporter.h"
13 #include "src/base/SkStringView.h"
14 #include "src/sksl/SkSLCompiler.h"
15 #include "src/sksl/SkSLContext.h"
16 #include "src/sksl/SkSLMangler.h"
17 #include "src/sksl/SkSLModifiersPool.h"
18 #include "src/sksl/SkSLProgramSettings.h"
19 #include "src/sksl/SkSLThreadContext.h"
20 #include "src/sksl/ir/SkSLExpression.h"
21 #include "src/sksl/ir/SkSLInterfaceBlock.h"
22 #include "src/sksl/ir/SkSLSymbolTable.h"
23 #include "src/sksl/ir/SkSLVarDeclarations.h"
24
25 #include <type_traits>
26 #include <utility>
27
28 namespace SkSL {
29
~Variable()30 Variable::~Variable() {
31 // Unhook this Variable from its associated VarDeclaration, since we're being deleted.
32 if (VarDeclaration* declaration = this->varDeclaration()) {
33 declaration->detachDeadVariable();
34 }
35 }
36
~InterfaceBlockVariable()37 InterfaceBlockVariable::~InterfaceBlockVariable() {
38 // Unhook this Variable from its associated InterfaceBlock, since we're being deleted.
39 if (fInterfaceBlockElement) {
40 fInterfaceBlockElement->detachDeadVariable();
41 }
42 }
43
initialValue() const44 const Expression* Variable::initialValue() const {
45 VarDeclaration* declaration = this->varDeclaration();
46 return declaration ? declaration->value().get() : nullptr;
47 }
48
varDeclaration() const49 VarDeclaration* Variable::varDeclaration() const {
50 if (!fDeclaringElement) {
51 return nullptr;
52 }
53 SkASSERT(fDeclaringElement->is<VarDeclaration>() ||
54 fDeclaringElement->is<GlobalVarDeclaration>());
55 return fDeclaringElement->is<GlobalVarDeclaration>()
56 ? &fDeclaringElement->as<GlobalVarDeclaration>().varDeclaration()
57 : &fDeclaringElement->as<VarDeclaration>();
58 }
59
globalVarDeclaration() const60 GlobalVarDeclaration* Variable::globalVarDeclaration() const {
61 if (!fDeclaringElement) {
62 return nullptr;
63 }
64 SkASSERT(fDeclaringElement->is<VarDeclaration>() ||
65 fDeclaringElement->is<GlobalVarDeclaration>());
66 return fDeclaringElement->is<GlobalVarDeclaration>()
67 ? &fDeclaringElement->as<GlobalVarDeclaration>()
68 : nullptr;
69 }
70
setVarDeclaration(VarDeclaration * declaration)71 void Variable::setVarDeclaration(VarDeclaration* declaration) {
72 SkASSERT(!fDeclaringElement || this == declaration->var());
73 if (!fDeclaringElement) {
74 fDeclaringElement = declaration;
75 }
76 }
77
setGlobalVarDeclaration(GlobalVarDeclaration * global)78 void Variable::setGlobalVarDeclaration(GlobalVarDeclaration* global) {
79 SkASSERT(!fDeclaringElement || this == global->varDeclaration().var());
80 fDeclaringElement = global;
81 }
82
mangledName() const83 std::string Variable::mangledName() const {
84 // Only private variables need to use name mangling.
85 std::string_view name = this->name();
86 if (!skstd::starts_with(name, '$')) {
87 return std::string(name);
88 }
89
90 // The $ prefix will fail to compile in GLSL, so replace it with `sk_Priv`.
91 name.remove_prefix(1);
92 return "sk_Priv" + std::string(name);
93 }
94
Convert(const Context & context,Position pos,Position modifiersPos,const Modifiers & modifiers,const Type * baseType,Position namePos,std::string_view name,bool isArray,std::unique_ptr<Expression> arraySize,Variable::Storage storage)95 std::unique_ptr<Variable> Variable::Convert(const Context& context,
96 Position pos,
97 Position modifiersPos,
98 const Modifiers& modifiers,
99 const Type* baseType,
100 Position namePos,
101 std::string_view name,
102 bool isArray,
103 std::unique_ptr<Expression> arraySize,
104 Variable::Storage storage) {
105 if (modifiers.fLayout.fLocation == 0 && modifiers.fLayout.fIndex == 0 &&
106 (modifiers.fFlags & Modifiers::kOut_Flag) &&
107 ProgramConfig::IsFragment(context.fConfig->fKind) && name != Compiler::FRAGCOLOR_NAME) {
108 context.fErrors->error(modifiersPos,
109 "out location=0, index=0 is reserved for sk_FragColor");
110 }
111 if (baseType->isUnsizedArray() && storage != Variable::Storage::kInterfaceBlock) {
112 context.fErrors->error(pos, "unsized arrays are not permitted here");
113 }
114 if (ProgramConfig::IsCompute(ThreadContext::Context().fConfig->fKind) &&
115 modifiers.fLayout.fBuiltin == -1) {
116 if (storage == Variable::Storage::kGlobal) {
117 if (modifiers.fFlags & Modifiers::kIn_Flag) {
118 context.fErrors->error(pos, "pipeline inputs not permitted in compute shaders");
119 } else if (modifiers.fFlags & Modifiers::kOut_Flag) {
120 context.fErrors->error(pos, "pipeline outputs not permitted in compute shaders");
121 }
122 }
123 }
124
125 return Make(context, pos, modifiersPos, modifiers, baseType, name, isArray,
126 std::move(arraySize), storage);
127 }
128
Make(const Context & context,Position pos,Position modifiersPos,const Modifiers & modifiers,const Type * baseType,std::string_view name,bool isArray,std::unique_ptr<Expression> arraySize,Variable::Storage storage)129 std::unique_ptr<Variable> Variable::Make(const Context& context,
130 Position pos,
131 Position modifiersPos,
132 const Modifiers& modifiers,
133 const Type* baseType,
134 std::string_view name,
135 bool isArray,
136 std::unique_ptr<Expression> arraySize,
137 Variable::Storage storage) {
138 const Type* type = baseType;
139 int arraySizeValue = 0;
140 if (isArray) {
141 SkASSERT(arraySize);
142 arraySizeValue = type->convertArraySize(context, pos, std::move(arraySize));
143 if (!arraySizeValue) {
144 return nullptr;
145 }
146 type = ThreadContext::SymbolTable()->addArrayDimension(type, arraySizeValue);
147 }
148 if (type->componentType().isInterfaceBlock()) {
149 return std::make_unique<InterfaceBlockVariable>(pos,
150 modifiersPos,
151 context.fModifiersPool->add(modifiers),
152 name,
153 type,
154 context.fConfig->fIsBuiltinCode,
155 storage);
156 } else {
157 return std::make_unique<Variable>(pos,
158 modifiersPos,
159 context.fModifiersPool->add(modifiers),
160 name,
161 type,
162 context.fConfig->fIsBuiltinCode,
163 storage);
164 }
165 }
166
MakeScratchVariable(const Context & context,Mangler & mangler,std::string_view baseName,const Type * type,const Modifiers & modifiers,SymbolTable * symbolTable,std::unique_ptr<Expression> initialValue)167 Variable::ScratchVariable Variable::MakeScratchVariable(const Context& context,
168 Mangler& mangler,
169 std::string_view baseName,
170 const Type* type,
171 const Modifiers& modifiers,
172 SymbolTable* symbolTable,
173 std::unique_ptr<Expression> initialValue) {
174 // $floatLiteral or $intLiteral aren't real types that we can use for scratch variables, so
175 // replace them if they ever appear here. If this happens, we likely forgot to coerce a type
176 // somewhere during compilation.
177 if (type->isLiteral()) {
178 SkDEBUGFAIL("found a $literal type in MakeScratchVariable");
179 type = &type->scalarTypeForLiteral();
180 }
181
182 // Out-parameters aren't supported.
183 SkASSERT(!(modifiers.fFlags & Modifiers::kOut_Flag));
184
185 // Provide our new variable with a unique name, and add it to our symbol table.
186 const std::string* name =
187 symbolTable->takeOwnershipOfString(mangler.uniqueName(baseName, symbolTable));
188
189 // Create our new variable and add it to the symbol table.
190 ScratchVariable result;
191 auto var = std::make_unique<Variable>(initialValue ? initialValue->fPosition : Position(),
192 /*modifiersPosition=*/Position(),
193 context.fModifiersPool->add(Modifiers{}),
194 name->c_str(),
195 type,
196 symbolTable->isBuiltin(),
197 Variable::Storage::kLocal);
198
199 // If we are creating an array type, reduce it to base type plus array-size.
200 int arraySize = 0;
201 if (type->isArray()) {
202 arraySize = type->columns();
203 type = &type->componentType();
204 }
205 // Create our variable declaration.
206 result.fVarDecl = VarDeclaration::Make(context, var.get(), type, arraySize,
207 std::move(initialValue));
208 result.fVarSymbol = symbolTable->add(std::move(var));
209 return result;
210 }
211
212 } // namespace SkSL
213