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