1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google LLC.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariable.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEnumBitMask.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkStringView.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLCompiler.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLIntrinsicList.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLMangler.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpression.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIRNode.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLInterfaceBlock.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLayout.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSymbolTable.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVarDeclarations.h"
24*c8dee2aaSAndroid Build Coastguard Worker
25*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
26*c8dee2aaSAndroid Build Coastguard Worker
27*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker static constexpr Layout kDefaultLayout;
30*c8dee2aaSAndroid Build Coastguard Worker
~Variable()31*c8dee2aaSAndroid Build Coastguard Worker Variable::~Variable() {
32*c8dee2aaSAndroid Build Coastguard Worker // Unhook this Variable from its associated VarDeclaration, since we're being deleted.
33*c8dee2aaSAndroid Build Coastguard Worker if (VarDeclaration* declaration = this->varDeclaration()) {
34*c8dee2aaSAndroid Build Coastguard Worker declaration->detachDeadVariable();
35*c8dee2aaSAndroid Build Coastguard Worker }
36*c8dee2aaSAndroid Build Coastguard Worker }
37*c8dee2aaSAndroid Build Coastguard Worker
~ExtendedVariable()38*c8dee2aaSAndroid Build Coastguard Worker ExtendedVariable::~ExtendedVariable() {
39*c8dee2aaSAndroid Build Coastguard Worker // Unhook this Variable from its associated InterfaceBlock, since we're being deleted.
40*c8dee2aaSAndroid Build Coastguard Worker if (fInterfaceBlockElement) {
41*c8dee2aaSAndroid Build Coastguard Worker fInterfaceBlockElement->detachDeadVariable();
42*c8dee2aaSAndroid Build Coastguard Worker }
43*c8dee2aaSAndroid Build Coastguard Worker }
44*c8dee2aaSAndroid Build Coastguard Worker
initialValue() const45*c8dee2aaSAndroid Build Coastguard Worker const Expression* Variable::initialValue() const {
46*c8dee2aaSAndroid Build Coastguard Worker VarDeclaration* declaration = this->varDeclaration();
47*c8dee2aaSAndroid Build Coastguard Worker return declaration ? declaration->value().get() : nullptr;
48*c8dee2aaSAndroid Build Coastguard Worker }
49*c8dee2aaSAndroid Build Coastguard Worker
varDeclaration() const50*c8dee2aaSAndroid Build Coastguard Worker VarDeclaration* Variable::varDeclaration() const {
51*c8dee2aaSAndroid Build Coastguard Worker if (!fDeclaringElement) {
52*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
53*c8dee2aaSAndroid Build Coastguard Worker }
54*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDeclaringElement->is<VarDeclaration>() ||
55*c8dee2aaSAndroid Build Coastguard Worker fDeclaringElement->is<GlobalVarDeclaration>());
56*c8dee2aaSAndroid Build Coastguard Worker return fDeclaringElement->is<GlobalVarDeclaration>()
57*c8dee2aaSAndroid Build Coastguard Worker ? &fDeclaringElement->as<GlobalVarDeclaration>().varDeclaration()
58*c8dee2aaSAndroid Build Coastguard Worker : &fDeclaringElement->as<VarDeclaration>();
59*c8dee2aaSAndroid Build Coastguard Worker }
60*c8dee2aaSAndroid Build Coastguard Worker
globalVarDeclaration() const61*c8dee2aaSAndroid Build Coastguard Worker GlobalVarDeclaration* Variable::globalVarDeclaration() const {
62*c8dee2aaSAndroid Build Coastguard Worker if (!fDeclaringElement) {
63*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
64*c8dee2aaSAndroid Build Coastguard Worker }
65*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fDeclaringElement->is<VarDeclaration>() ||
66*c8dee2aaSAndroid Build Coastguard Worker fDeclaringElement->is<GlobalVarDeclaration>());
67*c8dee2aaSAndroid Build Coastguard Worker return fDeclaringElement->is<GlobalVarDeclaration>()
68*c8dee2aaSAndroid Build Coastguard Worker ? &fDeclaringElement->as<GlobalVarDeclaration>()
69*c8dee2aaSAndroid Build Coastguard Worker : nullptr;
70*c8dee2aaSAndroid Build Coastguard Worker }
71*c8dee2aaSAndroid Build Coastguard Worker
setVarDeclaration(VarDeclaration * declaration)72*c8dee2aaSAndroid Build Coastguard Worker void Variable::setVarDeclaration(VarDeclaration* declaration) {
73*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fDeclaringElement || this == declaration->var());
74*c8dee2aaSAndroid Build Coastguard Worker if (!fDeclaringElement) {
75*c8dee2aaSAndroid Build Coastguard Worker fDeclaringElement = declaration;
76*c8dee2aaSAndroid Build Coastguard Worker }
77*c8dee2aaSAndroid Build Coastguard Worker }
78*c8dee2aaSAndroid Build Coastguard Worker
setGlobalVarDeclaration(GlobalVarDeclaration * global)79*c8dee2aaSAndroid Build Coastguard Worker void Variable::setGlobalVarDeclaration(GlobalVarDeclaration* global) {
80*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fDeclaringElement || this == global->varDeclaration().var());
81*c8dee2aaSAndroid Build Coastguard Worker fDeclaringElement = global;
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker
layout() const84*c8dee2aaSAndroid Build Coastguard Worker const Layout& Variable::layout() const {
85*c8dee2aaSAndroid Build Coastguard Worker return kDefaultLayout;
86*c8dee2aaSAndroid Build Coastguard Worker }
87*c8dee2aaSAndroid Build Coastguard Worker
mangledName() const88*c8dee2aaSAndroid Build Coastguard Worker std::string_view ExtendedVariable::mangledName() const {
89*c8dee2aaSAndroid Build Coastguard Worker return fMangledName.empty() ? this->name() : fMangledName;
90*c8dee2aaSAndroid Build Coastguard Worker }
91*c8dee2aaSAndroid Build Coastguard Worker
Convert(const Context & context,Position pos,Position modifiersPos,const Layout & layout,ModifierFlags flags,const Type * type,Position namePos,std::string_view name,Storage storage)92*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Variable> Variable::Convert(const Context& context,
93*c8dee2aaSAndroid Build Coastguard Worker Position pos,
94*c8dee2aaSAndroid Build Coastguard Worker Position modifiersPos,
95*c8dee2aaSAndroid Build Coastguard Worker const Layout& layout,
96*c8dee2aaSAndroid Build Coastguard Worker ModifierFlags flags,
97*c8dee2aaSAndroid Build Coastguard Worker const Type* type,
98*c8dee2aaSAndroid Build Coastguard Worker Position namePos,
99*c8dee2aaSAndroid Build Coastguard Worker std::string_view name,
100*c8dee2aaSAndroid Build Coastguard Worker Storage storage) {
101*c8dee2aaSAndroid Build Coastguard Worker if (layout.fLocation == 0 &&
102*c8dee2aaSAndroid Build Coastguard Worker layout.fIndex == 0 &&
103*c8dee2aaSAndroid Build Coastguard Worker (flags & ModifierFlag::kOut) &&
104*c8dee2aaSAndroid Build Coastguard Worker ProgramConfig::IsFragment(context.fConfig->fKind) &&
105*c8dee2aaSAndroid Build Coastguard Worker name != Compiler::FRAGCOLOR_NAME) {
106*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(modifiersPos,
107*c8dee2aaSAndroid Build Coastguard Worker "out location=0, index=0 is reserved for sk_FragColor");
108*c8dee2aaSAndroid Build Coastguard Worker }
109*c8dee2aaSAndroid Build Coastguard Worker if (type->isUnsizedArray() && storage != Variable::Storage::kInterfaceBlock
110*c8dee2aaSAndroid Build Coastguard Worker && storage != Variable::Storage::kParameter) {
111*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(pos, "unsized arrays are not permitted here");
112*c8dee2aaSAndroid Build Coastguard Worker }
113*c8dee2aaSAndroid Build Coastguard Worker if (ProgramConfig::IsCompute(context.fConfig->fKind) && layout.fBuiltin == -1) {
114*c8dee2aaSAndroid Build Coastguard Worker if (storage == Variable::Storage::kGlobal) {
115*c8dee2aaSAndroid Build Coastguard Worker if (flags & ModifierFlag::kIn) {
116*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(pos, "pipeline inputs not permitted in compute shaders");
117*c8dee2aaSAndroid Build Coastguard Worker } else if (flags & ModifierFlag::kOut) {
118*c8dee2aaSAndroid Build Coastguard Worker context.fErrors->error(pos, "pipeline outputs not permitted in compute shaders");
119*c8dee2aaSAndroid Build Coastguard Worker }
120*c8dee2aaSAndroid Build Coastguard Worker }
121*c8dee2aaSAndroid Build Coastguard Worker }
122*c8dee2aaSAndroid Build Coastguard Worker if (storage == Variable::Storage::kParameter) {
123*c8dee2aaSAndroid Build Coastguard Worker // The `in` modifier on function parameters is implicit, so we can replace `in float x` with
124*c8dee2aaSAndroid Build Coastguard Worker // `float x`. This prevents any ambiguity when matching a function by its param types.
125*c8dee2aaSAndroid Build Coastguard Worker if ((flags & (ModifierFlag::kOut | ModifierFlag::kIn)) == ModifierFlag::kIn) {
126*c8dee2aaSAndroid Build Coastguard Worker flags &= ~(ModifierFlag::kOut | ModifierFlag::kIn);
127*c8dee2aaSAndroid Build Coastguard Worker }
128*c8dee2aaSAndroid Build Coastguard Worker }
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker // Invent a mangled name for the variable, if it needs one.
131*c8dee2aaSAndroid Build Coastguard Worker std::string mangledName;
132*c8dee2aaSAndroid Build Coastguard Worker if (skstd::starts_with(name, '$')) {
133*c8dee2aaSAndroid Build Coastguard Worker // The $ prefix will fail to compile in GLSL, so replace it with `sk_Priv`.
134*c8dee2aaSAndroid Build Coastguard Worker mangledName = "sk_Priv" + std::string(name.substr(1));
135*c8dee2aaSAndroid Build Coastguard Worker } else if (FindIntrinsicKind(name) != kNotIntrinsic) {
136*c8dee2aaSAndroid Build Coastguard Worker // Having a variable name overlap an intrinsic name will prevent us from calling the
137*c8dee2aaSAndroid Build Coastguard Worker // intrinsic, but it's not illegal for user names to shadow a global symbol.
138*c8dee2aaSAndroid Build Coastguard Worker // Mangle the name to avoid a possible collision.
139*c8dee2aaSAndroid Build Coastguard Worker mangledName = Mangler{}.uniqueName(name, context.fSymbolTable);
140*c8dee2aaSAndroid Build Coastguard Worker }
141*c8dee2aaSAndroid Build Coastguard Worker
142*c8dee2aaSAndroid Build Coastguard Worker return Make(pos, modifiersPos, layout, flags, type, name, std::move(mangledName),
143*c8dee2aaSAndroid Build Coastguard Worker context.fConfig->isBuiltinCode(), storage);
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker
Make(Position pos,Position modifiersPosition,const Layout & layout,ModifierFlags flags,const Type * type,std::string_view name,std::string mangledName,bool builtin,Variable::Storage storage)146*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Variable> Variable::Make(Position pos,
147*c8dee2aaSAndroid Build Coastguard Worker Position modifiersPosition,
148*c8dee2aaSAndroid Build Coastguard Worker const Layout& layout,
149*c8dee2aaSAndroid Build Coastguard Worker ModifierFlags flags,
150*c8dee2aaSAndroid Build Coastguard Worker const Type* type,
151*c8dee2aaSAndroid Build Coastguard Worker std::string_view name,
152*c8dee2aaSAndroid Build Coastguard Worker std::string mangledName,
153*c8dee2aaSAndroid Build Coastguard Worker bool builtin,
154*c8dee2aaSAndroid Build Coastguard Worker Variable::Storage storage) {
155*c8dee2aaSAndroid Build Coastguard Worker // the `in` modifier on function parameters is implicit and should have been removed
156*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!(storage == Variable::Storage::kParameter &&
157*c8dee2aaSAndroid Build Coastguard Worker (flags & (ModifierFlag::kOut | ModifierFlag::kIn)) == ModifierFlag::kIn));
158*c8dee2aaSAndroid Build Coastguard Worker
159*c8dee2aaSAndroid Build Coastguard Worker if (type->componentType().isInterfaceBlock() || !mangledName.empty() ||
160*c8dee2aaSAndroid Build Coastguard Worker layout != kDefaultLayout) {
161*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<ExtendedVariable>(pos,
162*c8dee2aaSAndroid Build Coastguard Worker modifiersPosition,
163*c8dee2aaSAndroid Build Coastguard Worker layout,
164*c8dee2aaSAndroid Build Coastguard Worker flags,
165*c8dee2aaSAndroid Build Coastguard Worker name,
166*c8dee2aaSAndroid Build Coastguard Worker type,
167*c8dee2aaSAndroid Build Coastguard Worker builtin,
168*c8dee2aaSAndroid Build Coastguard Worker storage,
169*c8dee2aaSAndroid Build Coastguard Worker std::move(mangledName));
170*c8dee2aaSAndroid Build Coastguard Worker } else {
171*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<Variable>(pos,
172*c8dee2aaSAndroid Build Coastguard Worker modifiersPosition,
173*c8dee2aaSAndroid Build Coastguard Worker flags,
174*c8dee2aaSAndroid Build Coastguard Worker name,
175*c8dee2aaSAndroid Build Coastguard Worker type,
176*c8dee2aaSAndroid Build Coastguard Worker builtin,
177*c8dee2aaSAndroid Build Coastguard Worker storage);
178*c8dee2aaSAndroid Build Coastguard Worker }
179*c8dee2aaSAndroid Build Coastguard Worker }
180*c8dee2aaSAndroid Build Coastguard Worker
MakeScratchVariable(const Context & context,Mangler & mangler,std::string_view baseName,const Type * type,SymbolTable * symbolTable,std::unique_ptr<Expression> initialValue)181*c8dee2aaSAndroid Build Coastguard Worker Variable::ScratchVariable Variable::MakeScratchVariable(const Context& context,
182*c8dee2aaSAndroid Build Coastguard Worker Mangler& mangler,
183*c8dee2aaSAndroid Build Coastguard Worker std::string_view baseName,
184*c8dee2aaSAndroid Build Coastguard Worker const Type* type,
185*c8dee2aaSAndroid Build Coastguard Worker SymbolTable* symbolTable,
186*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> initialValue) {
187*c8dee2aaSAndroid Build Coastguard Worker // $floatLiteral or $intLiteral aren't real types that we can use for scratch variables, so
188*c8dee2aaSAndroid Build Coastguard Worker // replace them if they ever appear here. If this happens, we likely forgot to coerce a type
189*c8dee2aaSAndroid Build Coastguard Worker // somewhere during compilation.
190*c8dee2aaSAndroid Build Coastguard Worker if (type->isLiteral()) {
191*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGFAIL("found a $literal type in MakeScratchVariable");
192*c8dee2aaSAndroid Build Coastguard Worker type = &type->scalarTypeForLiteral();
193*c8dee2aaSAndroid Build Coastguard Worker }
194*c8dee2aaSAndroid Build Coastguard Worker
195*c8dee2aaSAndroid Build Coastguard Worker // Provide our new variable with a unique name, and add it to our symbol table.
196*c8dee2aaSAndroid Build Coastguard Worker const std::string* name =
197*c8dee2aaSAndroid Build Coastguard Worker symbolTable->takeOwnershipOfString(mangler.uniqueName(baseName, symbolTable));
198*c8dee2aaSAndroid Build Coastguard Worker
199*c8dee2aaSAndroid Build Coastguard Worker // Create our new variable and add it to the symbol table.
200*c8dee2aaSAndroid Build Coastguard Worker ScratchVariable result;
201*c8dee2aaSAndroid Build Coastguard Worker auto var = std::make_unique<Variable>(initialValue ? initialValue->fPosition : Position(),
202*c8dee2aaSAndroid Build Coastguard Worker /*modifiersPosition=*/Position(),
203*c8dee2aaSAndroid Build Coastguard Worker ModifierFlag::kNone,
204*c8dee2aaSAndroid Build Coastguard Worker name->c_str(),
205*c8dee2aaSAndroid Build Coastguard Worker type,
206*c8dee2aaSAndroid Build Coastguard Worker symbolTable->isBuiltin(),
207*c8dee2aaSAndroid Build Coastguard Worker Variable::Storage::kLocal);
208*c8dee2aaSAndroid Build Coastguard Worker
209*c8dee2aaSAndroid Build Coastguard Worker // If we are creating an array type, reduce it to base type plus array-size.
210*c8dee2aaSAndroid Build Coastguard Worker int arraySize = 0;
211*c8dee2aaSAndroid Build Coastguard Worker if (type->isArray()) {
212*c8dee2aaSAndroid Build Coastguard Worker arraySize = type->columns();
213*c8dee2aaSAndroid Build Coastguard Worker type = &type->componentType();
214*c8dee2aaSAndroid Build Coastguard Worker }
215*c8dee2aaSAndroid Build Coastguard Worker // Create our variable declaration.
216*c8dee2aaSAndroid Build Coastguard Worker result.fVarDecl = VarDeclaration::Make(context, var.get(), type, arraySize,
217*c8dee2aaSAndroid Build Coastguard Worker std::move(initialValue));
218*c8dee2aaSAndroid Build Coastguard Worker result.fVarSymbol = symbolTable->add(context, std::move(var));
219*c8dee2aaSAndroid Build Coastguard Worker return result;
220*c8dee2aaSAndroid Build Coastguard Worker }
221*c8dee2aaSAndroid Build Coastguard Worker
222*c8dee2aaSAndroid Build Coastguard Worker } // namespace SkSL
223