1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2022 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 "include/core/SkSpan.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
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/SkSLAnalysis.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLModule.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDeclaration.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDefinition.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionPrototype.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIRNode.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifierFlags.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgramElement.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLStatement.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSymbol.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSymbolTable.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVarDeclarations.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariable.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/transform/SkSLProgramWriter.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/transform/SkSLTransform.h"
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
30*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
31*c8dee2aaSAndroid Build Coastguard Worker #include <string>
32*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
33*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
34*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
37*c8dee2aaSAndroid Build Coastguard Worker
38*c8dee2aaSAndroid Build Coastguard Worker class Context;
39*c8dee2aaSAndroid Build Coastguard Worker class ProgramUsage;
40*c8dee2aaSAndroid Build Coastguard Worker enum class ProgramKind : int8_t;
41*c8dee2aaSAndroid Build Coastguard Worker
strip_export_flag(Context & context,const FunctionDeclaration * funcDecl,SymbolTable * symbols)42*c8dee2aaSAndroid Build Coastguard Worker static void strip_export_flag(Context& context,
43*c8dee2aaSAndroid Build Coastguard Worker const FunctionDeclaration* funcDecl,
44*c8dee2aaSAndroid Build Coastguard Worker SymbolTable* symbols) {
45*c8dee2aaSAndroid Build Coastguard Worker // Remove `$export` from every overload of this function.
46*c8dee2aaSAndroid Build Coastguard Worker Symbol* mutableSym = symbols->findMutable(funcDecl->name());
47*c8dee2aaSAndroid Build Coastguard Worker while (mutableSym) {
48*c8dee2aaSAndroid Build Coastguard Worker FunctionDeclaration* mutableDecl = &mutableSym->as<FunctionDeclaration>();
49*c8dee2aaSAndroid Build Coastguard Worker
50*c8dee2aaSAndroid Build Coastguard Worker ModifierFlags flags = mutableDecl->modifierFlags();
51*c8dee2aaSAndroid Build Coastguard Worker flags &= ~ModifierFlag::kExport;
52*c8dee2aaSAndroid Build Coastguard Worker mutableDecl->setModifierFlags(flags);
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker mutableSym = mutableDecl->mutableNextOverload();
55*c8dee2aaSAndroid Build Coastguard Worker }
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker
RenamePrivateSymbols(Context & context,Module & module,ProgramUsage * usage,ProgramKind kind)58*c8dee2aaSAndroid Build Coastguard Worker void Transform::RenamePrivateSymbols(Context& context,
59*c8dee2aaSAndroid Build Coastguard Worker Module& module,
60*c8dee2aaSAndroid Build Coastguard Worker ProgramUsage* usage,
61*c8dee2aaSAndroid Build Coastguard Worker ProgramKind kind) {
62*c8dee2aaSAndroid Build Coastguard Worker class SymbolRenamer : public ProgramWriter {
63*c8dee2aaSAndroid Build Coastguard Worker public:
64*c8dee2aaSAndroid Build Coastguard Worker SymbolRenamer(Context& context,
65*c8dee2aaSAndroid Build Coastguard Worker ProgramUsage* usage,
66*c8dee2aaSAndroid Build Coastguard Worker SymbolTable* symbolBase,
67*c8dee2aaSAndroid Build Coastguard Worker ProgramKind kind)
68*c8dee2aaSAndroid Build Coastguard Worker : fContext(context)
69*c8dee2aaSAndroid Build Coastguard Worker , fUsage(usage)
70*c8dee2aaSAndroid Build Coastguard Worker , fSymbolTableStack({symbolBase})
71*c8dee2aaSAndroid Build Coastguard Worker , fKind(kind) {}
72*c8dee2aaSAndroid Build Coastguard Worker
73*c8dee2aaSAndroid Build Coastguard Worker static std::string FindShortNameForSymbol(const Symbol* sym,
74*c8dee2aaSAndroid Build Coastguard Worker const SymbolTable* symbolTable,
75*c8dee2aaSAndroid Build Coastguard Worker const std::string& namePrefix) {
76*c8dee2aaSAndroid Build Coastguard Worker static constexpr std::string_view kLetters[] = {
77*c8dee2aaSAndroid Build Coastguard Worker "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
78*c8dee2aaSAndroid Build Coastguard Worker "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
79*c8dee2aaSAndroid Build Coastguard Worker "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
80*c8dee2aaSAndroid Build Coastguard Worker "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
81*c8dee2aaSAndroid Build Coastguard Worker
82*c8dee2aaSAndroid Build Coastguard Worker // Try any single-letter option.
83*c8dee2aaSAndroid Build Coastguard Worker for (std::string_view letter : kLetters) {
84*c8dee2aaSAndroid Build Coastguard Worker std::string name = namePrefix + std::string(letter);
85*c8dee2aaSAndroid Build Coastguard Worker if (symbolTable->find(name) == nullptr) {
86*c8dee2aaSAndroid Build Coastguard Worker return name;
87*c8dee2aaSAndroid Build Coastguard Worker }
88*c8dee2aaSAndroid Build Coastguard Worker }
89*c8dee2aaSAndroid Build Coastguard Worker
90*c8dee2aaSAndroid Build Coastguard Worker // Try every two-letter option.
91*c8dee2aaSAndroid Build Coastguard Worker for (std::string_view letterA : kLetters) {
92*c8dee2aaSAndroid Build Coastguard Worker for (std::string_view letterB : kLetters) {
93*c8dee2aaSAndroid Build Coastguard Worker std::string name = namePrefix + std::string(letterA) + std::string(letterB);
94*c8dee2aaSAndroid Build Coastguard Worker if (symbolTable->find(name) == nullptr) {
95*c8dee2aaSAndroid Build Coastguard Worker return name;
96*c8dee2aaSAndroid Build Coastguard Worker }
97*c8dee2aaSAndroid Build Coastguard Worker }
98*c8dee2aaSAndroid Build Coastguard Worker }
99*c8dee2aaSAndroid Build Coastguard Worker
100*c8dee2aaSAndroid Build Coastguard Worker // We struck out. Somehow, all 2700 two-letter names have been claimed.
101*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGFAILF("Unable to find unique name for '%s'", std::string(sym->name()).c_str());
102*c8dee2aaSAndroid Build Coastguard Worker return std::string(sym->name());
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker void minifyVariableName(const Variable* var) {
106*c8dee2aaSAndroid Build Coastguard Worker // Some variables are associated with anonymous parameters--these don't have names and
107*c8dee2aaSAndroid Build Coastguard Worker // aren't present in the symbol table. Their names are already empty so there's no way
108*c8dee2aaSAndroid Build Coastguard Worker // to shrink them further.
109*c8dee2aaSAndroid Build Coastguard Worker if (var->name().empty()) {
110*c8dee2aaSAndroid Build Coastguard Worker return;
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker
113*c8dee2aaSAndroid Build Coastguard Worker // Ensure that this variable is properly set up in the symbol table.
114*c8dee2aaSAndroid Build Coastguard Worker SymbolTable* symbols = fSymbolTableStack.back();
115*c8dee2aaSAndroid Build Coastguard Worker Symbol* mutableSym = symbols->findMutable(var->name());
116*c8dee2aaSAndroid Build Coastguard Worker SkASSERTF(mutableSym != nullptr,
117*c8dee2aaSAndroid Build Coastguard Worker "symbol table missing '%.*s'", (int)var->name().size(), var->name().data());
118*c8dee2aaSAndroid Build Coastguard Worker SkASSERTF(mutableSym == var,
119*c8dee2aaSAndroid Build Coastguard Worker "wrong symbol found for '%.*s'", (int)var->name().size(), var->name().data());
120*c8dee2aaSAndroid Build Coastguard Worker
121*c8dee2aaSAndroid Build Coastguard Worker // Look for a new name for this symbol.
122*c8dee2aaSAndroid Build Coastguard Worker // Note: we always rename _every_ variable, even ones with single-letter names. This is
123*c8dee2aaSAndroid Build Coastguard Worker // a safeguard: if we claimed a name like `i`, and then the program itself contained an
124*c8dee2aaSAndroid Build Coastguard Worker // `i` later on, in a nested SymbolTable, the two names would clash. By always renaming
125*c8dee2aaSAndroid Build Coastguard Worker // everything, we can ignore that problem.
126*c8dee2aaSAndroid Build Coastguard Worker std::string shortName = FindShortNameForSymbol(var, symbols, "");
127*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(symbols->findMutable(shortName) == nullptr);
128*c8dee2aaSAndroid Build Coastguard Worker
129*c8dee2aaSAndroid Build Coastguard Worker // Update the symbol's name.
130*c8dee2aaSAndroid Build Coastguard Worker const std::string* ownedName = symbols->takeOwnershipOfString(std::move(shortName));
131*c8dee2aaSAndroid Build Coastguard Worker symbols->renameSymbol(fContext, mutableSym, *ownedName);
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker
134*c8dee2aaSAndroid Build Coastguard Worker void minifyFunctionName(const FunctionDeclaration* funcDecl) {
135*c8dee2aaSAndroid Build Coastguard Worker // Look for a new name for this function.
136*c8dee2aaSAndroid Build Coastguard Worker std::string namePrefix = ProgramConfig::IsRuntimeEffect(fKind) ? "" : "$";
137*c8dee2aaSAndroid Build Coastguard Worker SymbolTable* symbols = fSymbolTableStack.back();
138*c8dee2aaSAndroid Build Coastguard Worker std::string shortName = FindShortNameForSymbol(funcDecl, symbols,
139*c8dee2aaSAndroid Build Coastguard Worker std::move(namePrefix));
140*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(symbols->findMutable(shortName) == nullptr);
141*c8dee2aaSAndroid Build Coastguard Worker
142*c8dee2aaSAndroid Build Coastguard Worker if (shortName.size() < funcDecl->name().size()) {
143*c8dee2aaSAndroid Build Coastguard Worker // Update the function's name. (If the function has overloads, this will rename all
144*c8dee2aaSAndroid Build Coastguard Worker // of them at once.)
145*c8dee2aaSAndroid Build Coastguard Worker Symbol* mutableSym = symbols->findMutable(funcDecl->name());
146*c8dee2aaSAndroid Build Coastguard Worker const std::string* ownedName = symbols->takeOwnershipOfString(std::move(shortName));
147*c8dee2aaSAndroid Build Coastguard Worker symbols->renameSymbol(fContext, mutableSym, *ownedName);
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker }
150*c8dee2aaSAndroid Build Coastguard Worker
151*c8dee2aaSAndroid Build Coastguard Worker bool functionNameCanBeMinifiedSafely(const FunctionDeclaration& funcDecl) const {
152*c8dee2aaSAndroid Build Coastguard Worker if (ProgramConfig::IsRuntimeEffect(fKind)) {
153*c8dee2aaSAndroid Build Coastguard Worker // The only externally-accessible function in a runtime effect is main().
154*c8dee2aaSAndroid Build Coastguard Worker return !funcDecl.isMain();
155*c8dee2aaSAndroid Build Coastguard Worker } else {
156*c8dee2aaSAndroid Build Coastguard Worker // We will only minify $private_functions, and only ones not marked as $export.
157*c8dee2aaSAndroid Build Coastguard Worker return skstd::starts_with(funcDecl.name(), '$') &&
158*c8dee2aaSAndroid Build Coastguard Worker !funcDecl.modifierFlags().isExport();
159*c8dee2aaSAndroid Build Coastguard Worker }
160*c8dee2aaSAndroid Build Coastguard Worker }
161*c8dee2aaSAndroid Build Coastguard Worker
162*c8dee2aaSAndroid Build Coastguard Worker void minifyFunction(FunctionDefinition& def) {
163*c8dee2aaSAndroid Build Coastguard Worker // If the function is private, minify its name.
164*c8dee2aaSAndroid Build Coastguard Worker const FunctionDeclaration* funcDecl = &def.declaration();
165*c8dee2aaSAndroid Build Coastguard Worker if (this->functionNameCanBeMinifiedSafely(*funcDecl)) {
166*c8dee2aaSAndroid Build Coastguard Worker this->minifyFunctionName(funcDecl);
167*c8dee2aaSAndroid Build Coastguard Worker }
168*c8dee2aaSAndroid Build Coastguard Worker
169*c8dee2aaSAndroid Build Coastguard Worker // Minify the names of each function parameter.
170*c8dee2aaSAndroid Build Coastguard Worker Analysis::SymbolTableStackBuilder symbolTableStackBuilder(def.body().get(),
171*c8dee2aaSAndroid Build Coastguard Worker &fSymbolTableStack);
172*c8dee2aaSAndroid Build Coastguard Worker for (Variable* param : funcDecl->parameters()) {
173*c8dee2aaSAndroid Build Coastguard Worker this->minifyVariableName(param);
174*c8dee2aaSAndroid Build Coastguard Worker }
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker void minifyPrototype(FunctionPrototype& proto) {
178*c8dee2aaSAndroid Build Coastguard Worker const FunctionDeclaration* funcDecl = &proto.declaration();
179*c8dee2aaSAndroid Build Coastguard Worker if (funcDecl->definition()) {
180*c8dee2aaSAndroid Build Coastguard Worker // This function is defined somewhere; this isn't just a loose prototype.
181*c8dee2aaSAndroid Build Coastguard Worker return;
182*c8dee2aaSAndroid Build Coastguard Worker }
183*c8dee2aaSAndroid Build Coastguard Worker
184*c8dee2aaSAndroid Build Coastguard Worker // Eliminate the names of each function parameter.
185*c8dee2aaSAndroid Build Coastguard Worker // The parameter names aren't in the symbol table's name lookup map at all.
186*c8dee2aaSAndroid Build Coastguard Worker // All we need to do is blank out their names.
187*c8dee2aaSAndroid Build Coastguard Worker for (Variable* param : funcDecl->parameters()) {
188*c8dee2aaSAndroid Build Coastguard Worker param->setName("");
189*c8dee2aaSAndroid Build Coastguard Worker }
190*c8dee2aaSAndroid Build Coastguard Worker }
191*c8dee2aaSAndroid Build Coastguard Worker
192*c8dee2aaSAndroid Build Coastguard Worker bool visitProgramElement(ProgramElement& elem) override {
193*c8dee2aaSAndroid Build Coastguard Worker switch (elem.kind()) {
194*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kFunction:
195*c8dee2aaSAndroid Build Coastguard Worker this->minifyFunction(elem.as<FunctionDefinition>());
196*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitProgramElement(elem);
197*c8dee2aaSAndroid Build Coastguard Worker
198*c8dee2aaSAndroid Build Coastguard Worker case ProgramElement::Kind::kFunctionPrototype:
199*c8dee2aaSAndroid Build Coastguard Worker this->minifyPrototype(elem.as<FunctionPrototype>());
200*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitProgramElement(elem);
201*c8dee2aaSAndroid Build Coastguard Worker
202*c8dee2aaSAndroid Build Coastguard Worker default:
203*c8dee2aaSAndroid Build Coastguard Worker return false;
204*c8dee2aaSAndroid Build Coastguard Worker }
205*c8dee2aaSAndroid Build Coastguard Worker }
206*c8dee2aaSAndroid Build Coastguard Worker
207*c8dee2aaSAndroid Build Coastguard Worker bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
208*c8dee2aaSAndroid Build Coastguard Worker Analysis::SymbolTableStackBuilder symbolTableStackBuilder(stmt.get(),
209*c8dee2aaSAndroid Build Coastguard Worker &fSymbolTableStack);
210*c8dee2aaSAndroid Build Coastguard Worker if (stmt->is<VarDeclaration>()) {
211*c8dee2aaSAndroid Build Coastguard Worker // Minify the variable's name.
212*c8dee2aaSAndroid Build Coastguard Worker VarDeclaration& decl = stmt->as<VarDeclaration>();
213*c8dee2aaSAndroid Build Coastguard Worker this->minifyVariableName(decl.var());
214*c8dee2aaSAndroid Build Coastguard Worker }
215*c8dee2aaSAndroid Build Coastguard Worker
216*c8dee2aaSAndroid Build Coastguard Worker return INHERITED::visitStatementPtr(stmt);
217*c8dee2aaSAndroid Build Coastguard Worker }
218*c8dee2aaSAndroid Build Coastguard Worker
219*c8dee2aaSAndroid Build Coastguard Worker Context& fContext;
220*c8dee2aaSAndroid Build Coastguard Worker ProgramUsage* fUsage;
221*c8dee2aaSAndroid Build Coastguard Worker std::vector<SymbolTable*> fSymbolTableStack;
222*c8dee2aaSAndroid Build Coastguard Worker ProgramKind fKind;
223*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = ProgramWriter;
224*c8dee2aaSAndroid Build Coastguard Worker };
225*c8dee2aaSAndroid Build Coastguard Worker
226*c8dee2aaSAndroid Build Coastguard Worker // Rename local variables and private functions.
227*c8dee2aaSAndroid Build Coastguard Worker SymbolRenamer renamer{context, usage, module.fSymbols.get(), kind};
228*c8dee2aaSAndroid Build Coastguard Worker for (std::unique_ptr<ProgramElement>& pe : module.fElements) {
229*c8dee2aaSAndroid Build Coastguard Worker renamer.visitProgramElement(*pe);
230*c8dee2aaSAndroid Build Coastguard Worker }
231*c8dee2aaSAndroid Build Coastguard Worker
232*c8dee2aaSAndroid Build Coastguard Worker // Strip off modifier `$export` from every function. (Only the minifier checks this flag, so we
233*c8dee2aaSAndroid Build Coastguard Worker // can remove it without affecting the meaning of the code.)
234*c8dee2aaSAndroid Build Coastguard Worker for (std::unique_ptr<ProgramElement>& pe : module.fElements) {
235*c8dee2aaSAndroid Build Coastguard Worker if (pe->is<FunctionDefinition>()) {
236*c8dee2aaSAndroid Build Coastguard Worker const FunctionDeclaration* funcDecl = &pe->as<FunctionDefinition>().declaration();
237*c8dee2aaSAndroid Build Coastguard Worker if (funcDecl->modifierFlags().isExport()) {
238*c8dee2aaSAndroid Build Coastguard Worker strip_export_flag(context, funcDecl, module.fSymbols.get());
239*c8dee2aaSAndroid Build Coastguard Worker }
240*c8dee2aaSAndroid Build Coastguard Worker }
241*c8dee2aaSAndroid Build Coastguard Worker }
242*c8dee2aaSAndroid Build Coastguard Worker }
243*c8dee2aaSAndroid Build Coastguard Worker
244*c8dee2aaSAndroid Build Coastguard Worker } // namespace SkSL
245