Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Functions
SkSL::Transform Namespace Reference

Functions

void FindAndDeclareBuiltinVariables (Program &program)
 
ModifierFlags AddConstToVarModifiers (const Variable &var, const Expression *initialValue, const ProgramUsage *usage)
 
std::unique_ptr< ExpressionRewriteIndexedSwizzle (const Context &context, const IndexExpression &swizzle)
 
void FindAndDeclareBuiltinFunctions (Program &program)
 
void FindAndDeclareBuiltinStructs (Program &program)
 
void EliminateUnreachableCode (Module &module, ProgramUsage *usage)
 
void EliminateUnreachableCode (Program &program)
 
void EliminateEmptyStatements (Module &module)
 
void EliminateUnnecessaryBraces (Module &module)
 
bool EliminateDeadFunctions (const Context &context, Module &module, ProgramUsage *usage)
 
bool EliminateDeadFunctions (Program &program)
 
bool EliminateDeadLocalVariables (const Context &context, Module &module, ProgramUsage *usage)
 
bool EliminateDeadLocalVariables (Program &program)
 
bool EliminateDeadGlobalVariables (const Context &context, Module &module, ProgramUsage *usage, bool onlyPrivateGlobals)
 
bool EliminateDeadGlobalVariables (Program &program)
 
void RenamePrivateSymbols (Context &context, Module &module, ProgramUsage *usage, ProgramKind kind)
 
void ReplaceConstVarsWithLiterals (Module &module, ProgramUsage *usage)
 
std::unique_ptr< StatementHoistSwitchVarDeclarationsAtTopLevel (const Context &, std::unique_ptr< SwitchStatement >)
 

Function Documentation

◆ AddConstToVarModifiers()

ModifierFlags SkSL::Transform::AddConstToVarModifiers ( const Variable var,
const Expression initialValue,
const ProgramUsage usage 
)

Checks to see if it would be safe to add const to the modifier flags of a variable. If so, returns the modifiers with const applied; if not, returns the existing modifiers as-is. Adding const allows the inliner to fold away more values and generate tighter code.

Definition at line 19 of file SkSLAddConstToVarModifiers.cpp.

21 {
22 // If the variable is already marked as `const`, keep our existing modifiers.
24 if (flags.isConst()) {
25 return flags;
26 }
27 // If the variable doesn't have a compile-time-constant initial value, we can't `const` it.
28 if (!initialValue || !Analysis::IsCompileTimeConstant(*initialValue)) {
29 return flags;
30 }
31 // This only works for variables that are written-to a single time.
32 ProgramUsage::VariableCounts counts = usage->get(var);
33 if (counts.fWrite != 1) {
34 return flags;
35 }
36 // Add `const` to our variable's modifier flags, making it eligible for constant-folding.
37 return flags | ModifierFlag::kConst;
38}
ModifierFlags modifierFlags() const
FlutterSemanticsFlag flags
static void usage(char *argv0)

◆ EliminateDeadFunctions() [1/2]

bool SkSL::Transform::EliminateDeadFunctions ( const Context context,
Module module,
ProgramUsage usage 
)

Eliminates functions in a program which are never called. Returns true if any changes were made.

Definition at line 63 of file SkSLEliminateDeadFunctions.cpp.

65 {
66 size_t numElements = module.fElements.size();
67
69 module.fElements.erase(std::remove_if(module.fElements.begin(),
70 module.fElements.end(),
71 [&](const std::unique_ptr<ProgramElement>& pe) {
72 return dead_function_predicate(pe.get(), usage);
73 }),
74 module.fElements.end());
75 }
76 return module.fElements.size() < numElements;
77}
ProgramConfig * fConfig
Definition SkSLContext.h:33
std::vector< std::unique_ptr< ProgramElement > > fElements
ProgramSettings fSettings

◆ EliminateDeadFunctions() [2/2]

bool SkSL::Transform::EliminateDeadFunctions ( Program program)

Definition at line 38 of file SkSLEliminateDeadFunctions.cpp.

38 {
39 ProgramUsage* usage = program.fUsage.get();
40
41 size_t numOwnedElements = program.fOwnedElements.size();
42 size_t numSharedElements = program.fSharedElements.size();
43
44 if (program.fConfig->fSettings.fRemoveDeadFunctions) {
45 program.fOwnedElements.erase(std::remove_if(program.fOwnedElements.begin(),
46 program.fOwnedElements.end(),
47 [&](const std::unique_ptr<ProgramElement>& pe) {
48 return dead_function_predicate(pe.get(),
49 usage);
50 }),
51 program.fOwnedElements.end());
52 program.fSharedElements.erase(std::remove_if(program.fSharedElements.begin(),
53 program.fSharedElements.end(),
54 [&](const ProgramElement* pe) {
55 return dead_function_predicate(pe, usage);
56 }),
57 program.fSharedElements.end());
58 }
59 return program.fOwnedElements.size() < numOwnedElements ||
60 program.fSharedElements.size() < numSharedElements;
61}
std::vector< std::unique_ptr< ProgramElement > > fOwnedElements
std::unique_ptr< ProgramUsage > fUsage
std::vector< const ProgramElement * > fSharedElements
std::unique_ptr< ProgramConfig > fConfig

◆ EliminateDeadGlobalVariables() [1/2]

bool SkSL::Transform::EliminateDeadGlobalVariables ( const Context context,
Module module,
ProgramUsage usage,
bool  onlyPrivateGlobals 
)

Definition at line 45 of file SkSLEliminateDeadGlobalVariables.cpp.

48 {
49 auto isDeadVariable = [&](const ProgramElement& element) {
50 return is_dead_variable(element, usage, onlyPrivateGlobals);
51 };
52
53 size_t numElements = module.fElements.size();
55 module.fElements.erase(std::remove_if(module.fElements.begin(),
56 module.fElements.end(),
57 [&](const std::unique_ptr<ProgramElement>& pe) {
58 return isDeadVariable(*pe);
59 }),
60 module.fElements.end());
61 }
62 return module.fElements.size() < numElements;
63}
static bool is_dead_variable(const ProgramElement &element, ProgramUsage *usage, bool onlyPrivateGlobals)

◆ EliminateDeadGlobalVariables() [2/2]

bool SkSL::Transform::EliminateDeadGlobalVariables ( Program program)

Definition at line 65 of file SkSLEliminateDeadGlobalVariables.cpp.

65 {
66 auto isDeadVariable = [&](const ProgramElement& element) {
67 return is_dead_variable(element, program.fUsage.get(), /*onlyPrivateGlobals=*/false);
68 };
69
70 size_t numOwnedElements = program.fOwnedElements.size();
71 size_t numSharedElements = program.fSharedElements.size();
72 if (program.fConfig->fSettings.fRemoveDeadVariables) {
73 program.fOwnedElements.erase(std::remove_if(program.fOwnedElements.begin(),
74 program.fOwnedElements.end(),
75 [&](const std::unique_ptr<ProgramElement>& pe) {
76 return isDeadVariable(*pe);
77 }),
78 program.fOwnedElements.end());
79 program.fSharedElements.erase(std::remove_if(program.fSharedElements.begin(),
80 program.fSharedElements.end(),
81 [&](const ProgramElement* pe) {
82 return isDeadVariable(*pe);
83 }),
84 program.fSharedElements.end());
85 }
86 return program.fOwnedElements.size() < numOwnedElements ||
87 program.fSharedElements.size() < numSharedElements;
88}

◆ EliminateDeadLocalVariables() [1/2]

bool SkSL::Transform::EliminateDeadLocalVariables ( const Context context,
Module module,
ProgramUsage usage 
)

Eliminates variables in a program which are never read or written (past their initializer). Preserves side effects from initializers, if any. Returns true if any changes were made.

Definition at line 158 of file SkSLEliminateDeadLocalVariables.cpp.

160 {
161 return eliminate_dead_local_variables(context, SkSpan(module.fElements), usage);
162}
static bool eliminate_dead_local_variables(const Context &context, SkSpan< std::unique_ptr< ProgramElement > > elements, ProgramUsage *usage)

◆ EliminateDeadLocalVariables() [2/2]

bool SkSL::Transform::EliminateDeadLocalVariables ( Program program)

Definition at line 164 of file SkSLEliminateDeadLocalVariables.cpp.

164 {
165 return program.fConfig->fSettings.fRemoveDeadVariables
167 SkSpan(program.fOwnedElements),
168 program.fUsage.get())
169 : false;
170}
std::shared_ptr< Context > fContext

◆ EliminateEmptyStatements()

void SkSL::Transform::EliminateEmptyStatements ( Module module)

Eliminates empty statements in a module (Nops, or blocks holding only Nops). Not implemented for Programs because Nops are harmless, but they waste space in long-lived module IR.

Definition at line 63 of file SkSLEliminateEmptyStatements.cpp.

63 {
65}
static void eliminate_empty_statements(SkSpan< std::unique_ptr< ProgramElement > > elements)

◆ EliminateUnnecessaryBraces()

void SkSL::Transform::EliminateUnnecessaryBraces ( Module module)

Eliminates unnecessary braces in a module (e.g., single-statement child blocks). Not implemented for Programs because extra braces are harmless, but they waste space in long-lived module IR.

Definition at line 107 of file SkSLEliminateUnnecessaryBraces.cpp.

107 {
109}
static void eliminate_unnecessary_braces(SkSpan< std::unique_ptr< ProgramElement > > elements)

◆ EliminateUnreachableCode() [1/2]

void SkSL::Transform::EliminateUnreachableCode ( Module module,
ProgramUsage usage 
)

Eliminates statements in a block which cannot be reached; for example, a statement immediately after a return or continue can safely be eliminated.

Definition at line 208 of file SkSLEliminateUnreachableCode.cpp.

208 {
210}
static void eliminate_unreachable_code(SkSpan< std::unique_ptr< ProgramElement > > elements, ProgramUsage *usage)

◆ EliminateUnreachableCode() [2/2]

void SkSL::Transform::EliminateUnreachableCode ( Program program)

Definition at line 212 of file SkSLEliminateUnreachableCode.cpp.

212 {
213 return eliminate_unreachable_code(SkSpan(program.fOwnedElements), program.fUsage.get());
214}

◆ FindAndDeclareBuiltinFunctions()

void SkSL::Transform::FindAndDeclareBuiltinFunctions ( Program program)

Copies built-in functions from modules into the program. Relies on ProgramUsage to determine which functions are necessary.

Definition at line 31 of file SkSLFindAndDeclareBuiltinFunctions.cpp.

31 {
32 ProgramUsage* usage = program.fUsage.get();
33 Context& context = *program.fContext;
34
35 std::vector<const FunctionDefinition*> addedBuiltins;
36 for (;;) {
37 // Find all the built-ins referenced by the program but not yet included in the code.
38 size_t numBuiltinsAtStart = addedBuiltins.size();
39 for (const auto& [symbol, count] : usage->fCallCounts) {
40 const FunctionDeclaration& fn = symbol->as<FunctionDeclaration>();
41 if (!fn.isBuiltin() || count == 0) {
42 // Not a built-in; skip it.
43 continue;
44 }
45 if (fn.intrinsicKind() == k_dFdy_IntrinsicKind) {
46 // Programs that invoke the `dFdy` intrinsic will need the RTFlip input.
47 if (!context.fConfig->fSettings.fForceNoRTFlip) {
48 program.fInterface.fRTFlipUniform |= Program::Interface::kRTFlip_Derivative;
49 }
50 }
51 if (const FunctionDefinition* builtinDef = fn.definition()) {
52 // Make sure we only add a built-in function once. We rarely add more than a handful
53 // of builtin functions, so linear search here is good enough.
54 if (std::find(addedBuiltins.begin(), addedBuiltins.end(), builtinDef) ==
55 addedBuiltins.end()) {
56 addedBuiltins.push_back(builtinDef);
57 }
58 }
59 }
60
61 if (addedBuiltins.size() == numBuiltinsAtStart) {
62 // If we didn't reference any more built-in functions than before, we're done.
63 break;
64 }
65
66 // Sort the referenced builtin functions into a consistent order; otherwise our output will
67 // become non-deterministic. The exact order isn't particularly important; we sort backwards
68 // because we add elements to the shared-elements in reverse order at the end.
69 std::sort(addedBuiltins.begin() + numBuiltinsAtStart,
70 addedBuiltins.end(),
71 [](const FunctionDefinition* aDefinition, const FunctionDefinition* bDefinition) {
72 const FunctionDeclaration& a = aDefinition->declaration();
73 const FunctionDeclaration& b = bDefinition->declaration();
74 if (a.name() != b.name()) {
75 return a.name() > b.name();
76 }
77 return a.description() > b.description();
78 });
79
80 // Update the ProgramUsage to track all these newly discovered functions.
81 int usageCallCounts = usage->fCallCounts.count();
82
83 for (size_t index = numBuiltinsAtStart; index < addedBuiltins.size(); ++index) {
84 usage->add(*addedBuiltins[index]);
85 }
86
87 if (usage->fCallCounts.count() == usageCallCounts) {
88 // If we aren't making any more unique function calls than before, we're done.
89 break;
90 }
91 }
92
93 // Insert the new functions into the program's shared elements, right at the front.
94 // They are added in reverse so that the deepest dependencies are added to the top.
95 program.fSharedElements.insert(program.fSharedElements.begin(),
96 addedBuiltins.rbegin(), addedBuiltins.rend());
97}
int count
const FunctionDefinition * definition() const
IntrinsicKind intrinsicKind() const
const T & as() const
Definition SkSLIRNode.h:133
static bool b
struct MyStruct a[10]
ProgramInterface fInterface

◆ FindAndDeclareBuiltinStructs()

void SkSL::Transform::FindAndDeclareBuiltinStructs ( Program program)

Copies built-in structs from modules into the program. Relies on ProgramUsage to determine which structs are necessary.

Definition at line 60 of file SkSLFindAndDeclareBuiltinStructs.cpp.

60 {
61 // Check if the program references any builtin structs at all.
62 if (contains_builtin_struct(*program.fUsage)) {
63 // Visit all of our modules to find struct definitions that were referenced by ProgramUsage.
64 std::vector<const ProgramElement*> addedStructDefs;
65 get_struct_definitions_from_module(program, *program.fContext->fModule, &addedStructDefs);
66
67 // Copy the struct definitions into our shared elements, and update ProgramUsage to match.
68 program.fSharedElements.insert(program.fSharedElements.begin(),
69 addedStructDefs.begin(), addedStructDefs.end());
70
71 for (const ProgramElement* element : addedStructDefs) {
72 program.fUsage->add(*element);
73 }
74 }
75}
static bool contains_builtin_struct(const ProgramUsage &usage)
static void get_struct_definitions_from_module(Program &program, const Module &module, std::vector< const ProgramElement * > *addedStructDefs)

◆ FindAndDeclareBuiltinVariables()

void SkSL::Transform::FindAndDeclareBuiltinVariables ( Program program)

Scans the finished program for built-in variables like sk_FragColor and adds them to the program's shared elements.

Definition at line 127 of file SkSLFindAndDeclareBuiltinVariables.cpp.

127 {
128 using Interface = Program::Interface;
129 const Context& context = *program.fContext;
130 const SymbolTable& symbols = *program.fSymbols;
131 BuiltinVariableScanner scanner(context, symbols);
132
133 if (ProgramConfig::IsFragment(program.fConfig->fKind)) {
134 // Find main() in the program and check its return type.
135 // If it's half4, we treat that as an implicit write to sk_FragColor and add a reference.
136 scanner.addImplicitFragColorWrite(program.fOwnedElements);
137 }
138
139 // Scan all the variables used by the program and declare any built-ins.
140 for (const auto& [var, counts] : program.fUsage->fVariableCounts) {
141 if (var->isBuiltin()) {
142 scanner.addDeclaringElement(var);
143
144 switch (var->layout().fBuiltin) {
145 // Set the RTFlip program input if we find sk_FragCoord or sk_Clockwise.
147 if (!context.fConfig->fSettings.fForceNoRTFlip) {
148 program.fInterface.fRTFlipUniform |= Interface::kRTFlip_FragCoord;
149 }
150 break;
151
153 if (!context.fConfig->fSettings.fForceNoRTFlip) {
154 program.fInterface.fRTFlipUniform |= Interface::kRTFlip_Clockwise;
155 }
156 break;
157
158 // Set the UseLastFragColor program input if we find sk_LastFragColor.
159 // Metal and Dawn define this as a program input, rather than a global variable.
161 program.fInterface.fUseLastFragColor = true;
162 break;
163
164 // Set secondary color output if we find sk_SecondaryFragColor.
166 program.fInterface.fOutputSecondaryColor = true;
167 break;
168 }
169 }
170 }
171
172 // Sort the referenced builtin functions into a consistent order; otherwise our output will
173 // become non-deterministic. The exact order isn't particularly important.
174 scanner.sortNewElements();
175
176 // Add all the newly-declared elements to the program, and update ProgramUsage to match.
177 program.fSharedElements.insert(program.fSharedElements.begin(),
178 scanner.fNewElements.begin(),
179 scanner.fNewElements.end());
180
181 for (const ProgramElement* element : scanner.fNewElements) {
182 program.fUsage->add(*element);
183 }
184}
constexpr int SK_CLOCKWISE_BUILTIN
constexpr int SK_LASTFRAGCOLOR_BUILTIN
constexpr int SK_SECONDARYFRAGCOLOR_BUILTIN
constexpr int SK_FRAGCOORD_BUILTIN
std::vector< const ProgramElement * > fNewElements
std::unique_ptr< SymbolTable > fSymbols

◆ HoistSwitchVarDeclarationsAtTopLevel()

std::unique_ptr< Statement > SkSL::Transform::HoistSwitchVarDeclarationsAtTopLevel ( const Context context,
std::unique_ptr< SwitchStatement stmt 
)

Looks for variables inside of the top-level of a switch body, such as:

switch (x) { case 1: int i; // i is at top-level case 2: float f = 5.0; // f is at top-level, and has an initial-value assignment case 3: { bool b; } // b is not at top-level; it has an additional scope }

If any top-level variables are found, a scoped block is created around the switch, and the variable declarations are moved out of the switch body and into the outer scope. (Variables with additional scoping are left as-is.) Then, we replace the declarations with assignment statements:

{ int i; float f; switch (a) { case 1: // i is declared above and does not need initialization case 2: f = 5.0; // f is declared above and initialized here case 3: { bool b; } // b is left as-is because it has a block-scope } }

This doesn't change the meaning or correctness of the code. If the switch needs to be rewriten (e.g. due to the restrictions of ES2 or WGSL), this transformation prevents scoping issues with variables falling out of scope between switch-cases when we fall through.

If there are no variables at the top-level, the switch statement is returned as-is.

Definition at line 36 of file SkSLHoistSwitchVarDeclarationsAtTopLevel.cpp.

38 {
39 struct HoistSwitchVarDeclsVisitor : public ProgramWriter {
40 HoistSwitchVarDeclsVisitor(const Context& c) : fContext(c) {}
41
42 bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
43 // We don't need to recurse into expressions.
44 return false;
45 }
46
47 bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
48 switch (stmt->kind()) {
50 // Recurse inward from the switch and its inner switch-cases.
51 return INHERITED::visitStatementPtr(stmt);
52
54 if (!stmt->as<Block>().isScope()) {
55 // Recurse inward from unscoped blocks.
56 return INHERITED::visitStatementPtr(stmt);
57 }
58 break;
59
61 // Keep track of variable declarations.
62 fVarDeclarations.push_back(&stmt);
63 break;
64
65 default:
66 break;
67 }
68
69 // We don't need to recurse into other statement types; we're only interested in the top
70 // level of the switch statement.
71 return false;
72 }
73
74 const Context& fContext;
75 TArray<std::unique_ptr<Statement>*> fVarDeclarations;
76
77 using INHERITED = ProgramWriter;
78 };
79
80 // Visit every switch-case in the switch, looking for hoistable var-declarations.
81 HoistSwitchVarDeclsVisitor visitor(context);
82 for (std::unique_ptr<Statement>& sc : stmt->as<SwitchStatement>().cases()) {
83 visitor.visitStatementPtr(sc);
84 }
85
86 // If no declarations were found, return the switch as-is.
87 if (visitor.fVarDeclarations.empty()) {
88 return stmt;
89 }
90
91 // Move all of the var-declaration statements into a separate block.
92 SymbolTable* switchSymbols = stmt->caseBlock()->as<Block>().symbolTable();
93 std::unique_ptr<SymbolTable> blockSymbols = switchSymbols->insertNewParent();
94
95 StatementArray blockStmts;
96 blockStmts.reserve_exact(visitor.fVarDeclarations.size() + 1);
97 for (std::unique_ptr<Statement>* innerDeclaration : visitor.fVarDeclarations) {
98 VarDeclaration& decl = (*innerDeclaration)->as<VarDeclaration>();
99 Variable* var = decl.var();
100 bool isConst = var->modifierFlags().isConst();
101
102 std::unique_ptr<Statement> replacementStmt;
103 if (decl.value() && !isConst) {
104 // The inner variable-declaration has an initial-value; we must replace the declaration
105 // with an assignment to the variable. This also has the helpful effect of stripping off
106 // the initial-value from the declaration.
107 struct AssignmentHelper : public IRHelpers {
108 using IRHelpers::IRHelpers;
109
110 std::unique_ptr<Statement> makeAssignmentStmt(VarDeclaration& decl) const {
111 return Assign(Ref(decl.var()), std::move(decl.value()));
112 }
113 };
114
115 AssignmentHelper helper(context);
116 replacementStmt = helper.makeAssignmentStmt(decl);
117 } else {
118 // The inner variable-declaration has no initial-value, or it's const and has a constant
119 // value; we can move it upwards as-is and replace its statement with a no-op.
120 SkASSERT(!isConst || Analysis::IsConstantExpression(*decl.value()));
121
122 replacementStmt = Nop::Make();
123 }
124
125 // Move the var-declaration above the switch, and replace the existing statement with either
126 // an assignment (if there was an initial-value) or a no-op (if there wasn't one).
127 blockStmts.push_back(std::move(*innerDeclaration));
128 *innerDeclaration = std::move(replacementStmt);
129
130 // Hoist the variable's symbol outside of the switch's symbol table, and into the enclosing
131 // block's symbol table.
132 switchSymbols->moveSymbolTo(blockSymbols.get(), var, context);
133 }
134
135 // Return a scoped Block holding the switch.
136 Position pos = stmt->fPosition;
137 blockStmts.push_back(std::move(stmt));
138 return Block::MakeBlock(pos, std::move(blockStmts), Block::Kind::kBracedScope,
139 std::move(blockSymbols));
140}
SkPoint pos
#define SkASSERT(cond)
Definition SkAssert.h:116
#define INHERITED(method,...)
const Context & fContext
bool visitExpressionPtr(std::unique_ptr< Expression > &e) override
bool visitStatementPtr(std::unique_ptr< Statement > &s) override
void reserve_exact(int n)
Definition SkTArray.h:176
skia_private::STArray< 2, std::unique_ptr< Statement > > StatementArray
Definition SkSLDefines.h:34
RefPtr< T > Ref(T *ptr)
Definition ref_ptr.h:237

◆ RenamePrivateSymbols()

void SkSL::Transform::RenamePrivateSymbols ( Context context,
Module module,
ProgramUsage usage,
ProgramKind  kind 
)

Renames private functions and function-local variables to minimize code size.

Definition at line 58 of file SkSLRenamePrivateSymbols.cpp.

61 {
62 class SymbolRenamer : public ProgramWriter {
63 public:
64 SymbolRenamer(Context& context,
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
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}
#define SkDEBUGFAILF(fmt,...)
Definition SkAssert.h:119
#define SkASSERTF(cond, fmt,...)
Definition SkAssert.h:117
virtual bool visitProgramElement(typename T::ProgramElement &programElement)
const char * name
Definition fuchsia.cc:50
static void strip_export_flag(Context &context, const FunctionDeclaration *funcDecl, SymbolTable *symbols)
constexpr bool starts_with(std::string_view str, std::string_view prefix)
std::unique_ptr< SymbolTable > fSymbols
static bool IsRuntimeEffect(ProgramKind kind)

◆ ReplaceConstVarsWithLiterals()

void SkSL::Transform::ReplaceConstVarsWithLiterals ( Module module,
ProgramUsage usage 
)

Replaces constant variables in a program with their equivalent values.

Definition at line 32 of file SkSLReplaceConstVarsWithLiterals.cpp.

32 {
33 class ConstVarReplacer : public ProgramWriter {
34 public:
35 ConstVarReplacer(ProgramUsage* usage) : fUsage(usage) {}
36
37 using ProgramWriter::visitProgramElement;
38
39 bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
40 // If this is a variable...
41 if (expr->is<VariableReference>()) {
43 // ... and it's a candidate for size reduction...
44 if (fCandidates.contains(var.variable())) {
45 // ... get its constant value...
46 if (const Expression* value = ConstantFolder::GetConstantValueOrNull(var)) {
47 // ... and replace it with that value.
48 fUsage->remove(expr.get());
49 expr = value->clone();
50 fUsage->add(expr.get());
51 return false;
52 }
53 }
54 }
55 return INHERITED::visitExpressionPtr(expr);
56 }
57
58 ProgramUsage* fUsage;
59 THashSet<const Variable*> fCandidates;
60
61 using INHERITED = ProgramWriter;
62 };
63
64 ConstVarReplacer visitor{usage};
65
66 for (const auto& [var, count] : usage->fVariableCounts) {
67 // We can only replace const variables that still exist, and that have initial values.
68 if (!count.fVarExists || count.fWrite != 1) {
69 continue;
70 }
71 if (!var->modifierFlags().isConst()) {
72 continue;
73 }
74 if (!var->initialValue()) {
75 continue;
76 }
77 // The current size is:
78 // strlen("const type varname=initialvalue;`") + count*strlen("varname").
79 size_t initialvalueSize = ConstantFolder::GetConstantValueForVariable(*var->initialValue())
80 ->description()
81 .size();
82 size_t totalOldSize = var->description().size() + // const type varname
83 1 + // =
84 initialvalueSize + // initialvalue
85 1 + // ;
86 count.fRead * var->name().size(); // count * varname
87 // If we replace varname with initialvalue everywhere, the new size would be:
88 // count*strlen("initialvalue")
89 size_t totalNewSize = count.fRead * initialvalueSize; // count * initialvalue
90
91 if (totalNewSize <= totalOldSize) {
92 visitor.fCandidates.add(var);
93 }
94 }
95
96 if (!visitor.fCandidates.empty()) {
97 for (std::unique_ptr<ProgramElement>& pe : module.fElements) {
98 if (pe->is<FunctionDefinition>()) {
99 visitor.visitProgramElement(*pe);
100 }
101 }
102 }
103}
const Variable * variable() const
std::string description(OperatorPrecedence) const override
uint8_t value

◆ RewriteIndexedSwizzle()

std::unique_ptr< Expression > SkSL::Transform::RewriteIndexedSwizzle ( const Context context,
const IndexExpression swizzle 
)

Rewrites indexed swizzles of the form myVec.zyx[i] by replacing the swizzle with a lookup into a constant vector. e.g., the above expression would be rewritten as myVec[vec3(2, 1, 0)[i]]. This roughly matches glslang's handling of the code.

Definition at line 24 of file SkSLRewriteIndexedSwizzle.cpp.

25 {
26 // The index expression _must_ have a swizzle base for this transformation to be valid.
27 if (!indexExpr.base()->is<Swizzle>()) {
28 return nullptr;
29 }
30 const Swizzle& swizzle = indexExpr.base()->as<Swizzle>();
31
32 // Convert the swizzle components to a literal array.
33 double vecArray[4];
34 for (int index = 0; index < swizzle.components().size(); ++index) {
35 vecArray[index] = swizzle.components()[index];
36 }
37
38 // Make a compound constructor with the literal array.
39 const Type& vecType =
40 context.fTypes.fInt->toCompound(context, swizzle.components().size(), /*rows=*/1);
41 std::unique_ptr<Expression> vec =
42 ConstructorCompound::MakeFromConstants(context, indexExpr.fPosition, vecType, vecArray);
43
44 // Create a rewritten inner-expression corresponding to `vec(1,2,3)[originalIndex]`.
45 std::unique_ptr<Expression> innerExpr = IndexExpression::Make(
46 context, indexExpr.fPosition, std::move(vec), indexExpr.index()->clone());
47
48 // Return a rewritten outer-expression corresponding to `base[vec(1,2,3)[originalIndex]]`.
49 return IndexExpression::Make(
50 context, indexExpr.fPosition, swizzle.base()->clone(), std::move(innerExpr));
51}
const std::unique_ptr< Type > fInt
const BuiltinTypes & fTypes
Definition SkSLContext.h:30
std::unique_ptr< Expression > & base()
std::unique_ptr< Expression > & base()
Definition SkSLSwizzle.h:82
const ComponentArray & components() const
Definition SkSLSwizzle.h:90
int size() const
Definition SkTArray.h:416