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 #ifndef SKSL_TRANSFORM 9 #define SKSL_TRANSFORM 10 11 #include "src/sksl/SkSLDefines.h" 12 #include "src/sksl/ir/SkSLModifierFlags.h" 13 14 #include <cstdint> 15 #include <memory> 16 17 namespace SkSL { 18 19 class Block; 20 class Context; 21 class Expression; 22 class IndexExpression; 23 class Position; 24 class ProgramUsage; 25 class SymbolTable; 26 class Variable; 27 enum class ProgramKind : int8_t; 28 struct Module; 29 struct Program; 30 31 namespace Transform { 32 33 /** 34 * Checks to see if it would be safe to add `const` to the modifier flags of a variable. If so, 35 * returns the modifiers with `const` applied; if not, returns the existing modifiers as-is. Adding 36 * `const` allows the inliner to fold away more values and generate tighter code. 37 */ 38 ModifierFlags AddConstToVarModifiers(const Variable& var, 39 const Expression* initialValue, 40 const ProgramUsage* usage); 41 42 /** 43 * Rewrites indexed swizzles of the form `myVec.zyx[i]` by replacing the swizzle with a lookup into 44 * a constant vector. e.g., the above expression would be rewritten as `myVec[vec3(2, 1, 0)[i]]`. 45 * This roughly matches glslang's handling of the code. 46 */ 47 std::unique_ptr<Expression> RewriteIndexedSwizzle(const Context& context, 48 const IndexExpression& swizzle); 49 50 /** 51 * Copies built-in functions from modules into the program. Relies on ProgramUsage to determine 52 * which functions are necessary. 53 */ 54 void FindAndDeclareBuiltinFunctions(Program& program); 55 56 /** 57 * Copies built-in structs from modules into the program. Relies on ProgramUsage to determine 58 * which structs are necessary. 59 */ 60 void FindAndDeclareBuiltinStructs(Program& program); 61 62 /** 63 * Scans the finished program for built-in variables like `sk_FragColor` and adds them to the 64 * program's shared elements. 65 */ 66 void FindAndDeclareBuiltinVariables(Program& program); 67 68 /** 69 * Eliminates statements in a block which cannot be reached; for example, a statement 70 * immediately after a `return` or `continue` can safely be eliminated. 71 */ 72 void EliminateUnreachableCode(Module& module, ProgramUsage* usage); 73 void EliminateUnreachableCode(Program& program); 74 75 /** 76 * Eliminates empty statements in a module (Nops, or blocks holding only Nops). Not implemented for 77 * Programs because Nops are harmless, but they waste space in long-lived module IR. 78 */ 79 void EliminateEmptyStatements(Module& module); 80 81 /** 82 * Eliminates unnecessary braces in a module (e.g., single-statement child blocks). Not implemented 83 * for Programs because extra braces are harmless, but they waste space in long-lived module IR. 84 */ 85 void EliminateUnnecessaryBraces(const Context& context, Module& module); 86 87 /** 88 * Replaces splat-casts like `float4(myFloat)` with `myFloat.xxxx`. This should be slightly smaller 89 * in textual form, and will be optimized back to the splat-cast at load time. 90 */ 91 void ReplaceSplatCastsWithSwizzles(const Context& context, Module& module); 92 93 /** 94 * Eliminates functions in a program which are never called. Returns true if any changes were made. 95 */ 96 bool EliminateDeadFunctions(const Context& context, Module& module, ProgramUsage* usage); 97 bool EliminateDeadFunctions(Program& program); 98 99 /** 100 * Eliminates variables in a program which are never read or written (past their initializer). 101 * Preserves side effects from initializers, if any. Returns true if any changes were made. 102 */ 103 bool EliminateDeadLocalVariables(const Context& context, 104 Module& module, 105 ProgramUsage* usage); 106 bool EliminateDeadLocalVariables(Program& program); 107 bool EliminateDeadGlobalVariables(const Context& context, 108 Module& module, 109 ProgramUsage* usage, 110 bool onlyPrivateGlobals); 111 bool EliminateDeadGlobalVariables(Program& program); 112 113 /** Renames private functions and function-local variables to minimize code size. */ 114 void RenamePrivateSymbols(Context& context, Module& module, ProgramUsage* usage, ProgramKind kind); 115 116 /** Replaces constant variables in a program with their equivalent values. */ 117 void ReplaceConstVarsWithLiterals(Module& module, ProgramUsage* usage); 118 119 /** 120 * Looks for variables inside of the top-level of switch-cases, such as: 121 * 122 * case 1: int i; // `i` is at top-level 123 * case 2: float f = 5.0; // `f` is at top-level, and has an initial-value assignment 124 * case 3: { bool b; } // `b` is not at top-level; it has an additional scope 125 * 126 * If any top-level variables are found, a scoped block is created and returned which holds the 127 * variable declarations from the switch-cases and into the outer scope. (Variables with additional 128 * scoping are left as-is.) Then, we replace the declarations with nops or assignment statements. 129 * That is, we would return a Block like this: 130 * 131 * { 132 * int i; 133 * float f; 134 * } 135 * 136 * And we would also mutate the passed-in case statements to eliminate the variable decarations: 137 * 138 * case 1: Nop; // `i` is declared in the returned block and needs no initialization 139 * case 2: f = 5.0; // `f` is declared in the returned block and initialized here 140 * case 3: { bool b; } // `b` is left as-is because it has a block-scope 141 * 142 * This doesn't change the meaning or correctness of the code. If the switch needs to be rewriten 143 * (e.g. due to the restrictions of ES2 or WGSL), this transformation prevents scoping issues with 144 * variables falling out of scope between switch-cases when we fall through. 145 * 146 * If there are no variables at the top-level, null is returned. 147 */ 148 std::unique_ptr<Block> HoistSwitchVarDeclarationsAtTopLevel(const Context&, StatementArray& cases, 149 SymbolTable& symbolTable, Position pos); 150 151 } // namespace Transform 152 } // namespace SkSL 153 154 #endif 155