1 /*
2  * Copyright 2016 Google Inc.
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 "src/sksl/SkSLCompiler.h"
9 
10 #include <memory>
11 #include <unordered_set>
12 
13 #include "src/core/SkScopeExit.h"
14 #include "src/core/SkTraceEvent.h"
15 #include "src/sksl/SkSLAnalysis.h"
16 #include "src/sksl/SkSLConstantFolder.h"
17 #include "src/sksl/SkSLIRGenerator.h"
18 #include "src/sksl/SkSLOperators.h"
19 #include "src/sksl/SkSLProgramSettings.h"
20 #include "src/sksl/SkSLRehydrator.h"
21 #include "src/sksl/codegen/SkSLCPPCodeGenerator.h"
22 #include "src/sksl/codegen/SkSLDSLCPPCodeGenerator.h"
23 #include "src/sksl/codegen/SkSLGLSLCodeGenerator.h"
24 #include "src/sksl/codegen/SkSLHCodeGenerator.h"
25 #include "src/sksl/codegen/SkSLMetalCodeGenerator.h"
26 #include "src/sksl/codegen/SkSLSPIRVCodeGenerator.h"
27 #include "src/sksl/codegen/SkSLSPIRVtoHLSL.h"
28 #include "src/sksl/ir/SkSLEnum.h"
29 #include "src/sksl/ir/SkSLExpression.h"
30 #include "src/sksl/ir/SkSLExpressionStatement.h"
31 #include "src/sksl/ir/SkSLFunctionCall.h"
32 #include "src/sksl/ir/SkSLIntLiteral.h"
33 #include "src/sksl/ir/SkSLModifiersDeclaration.h"
34 #include "src/sksl/ir/SkSLNop.h"
35 #include "src/sksl/ir/SkSLSymbolTable.h"
36 #include "src/sksl/ir/SkSLTernaryExpression.h"
37 #include "src/sksl/ir/SkSLUnresolvedFunction.h"
38 #include "src/sksl/ir/SkSLVarDeclarations.h"
39 #include "src/utils/SkBitSet.h"
40 
41 #include <fstream>
42 
43 #if !defined(SKSL_STANDALONE) & SK_SUPPORT_GPU
44 #include "include/gpu/GrContextOptions.h"
45 #include "src/gpu/GrShaderCaps.h"
46 #endif
47 
48 #ifdef SK_ENABLE_SPIRV_VALIDATION
49 #include "spirv-tools/libspirv.hpp"
50 #endif
51 
52 #if defined(SKSL_STANDALONE)
53 
54 // In standalone mode, we load the textual sksl source files. GN generates or copies these files
55 // to the skslc executable directory. The "data" in this mode is just the filename.
56 #define MODULE_DATA(name) MakeModulePath("sksl_" #name ".sksl")
57 
58 #else
59 
60 // At runtime, we load the dehydrated sksl data files. The data is a (pointer, size) pair.
61 #include "src/sksl/generated/sksl_fp.dehydrated.sksl"
62 #include "src/sksl/generated/sksl_frag.dehydrated.sksl"
63 #include "src/sksl/generated/sksl_geom.dehydrated.sksl"
64 #include "src/sksl/generated/sksl_gpu.dehydrated.sksl"
65 #include "src/sksl/generated/sksl_public.dehydrated.sksl"
66 #include "src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl"
67 #include "src/sksl/generated/sksl_rt_shader.dehydrated.sksl"
68 #include "src/sksl/generated/sksl_vert.dehydrated.sksl"
69 
70 #define MODULE_DATA(name) MakeModuleData(SKSL_INCLUDE_sksl_##name,\
71                                          SKSL_INCLUDE_sksl_##name##_LENGTH)
72 
73 #endif
74 
75 namespace SkSL {
76 
77 // These flags allow tools like Viewer or Nanobench to override the compiler's ProgramSettings.
78 Compiler::OverrideFlag Compiler::sOptimizer = OverrideFlag::kDefault;
79 Compiler::OverrideFlag Compiler::sInliner = OverrideFlag::kDefault;
80 
81 using RefKind = VariableReference::RefKind;
82 
83 class AutoSource {
84 public:
AutoSource(Compiler * compiler,const String * source)85     AutoSource(Compiler* compiler, const String* source)
86             : fCompiler(compiler) {
87         SkASSERT(!fCompiler->fSource);
88         fCompiler->fSource = source;
89     }
90 
~AutoSource()91     ~AutoSource() {
92         fCompiler->fSource = nullptr;
93     }
94 
95     Compiler* fCompiler;
96 };
97 
98 class AutoProgramConfig {
99 public:
AutoProgramConfig(std::shared_ptr<Context> & context,ProgramConfig * config)100     AutoProgramConfig(std::shared_ptr<Context>& context, ProgramConfig* config)
101             : fContext(context.get()) {
102         SkASSERT(!fContext->fConfig);
103         fContext->fConfig = config;
104     }
105 
~AutoProgramConfig()106     ~AutoProgramConfig() {
107         fContext->fConfig = nullptr;
108     }
109 
110     Context* fContext;
111 };
112 
113 class AutoModifiersPool {
114 public:
AutoModifiersPool(std::shared_ptr<Context> & context,ModifiersPool * modifiersPool)115     AutoModifiersPool(std::shared_ptr<Context>& context, ModifiersPool* modifiersPool)
116             : fContext(context.get()) {
117         SkASSERT(!fContext->fModifiersPool);
118         fContext->fModifiersPool = modifiersPool;
119     }
120 
~AutoModifiersPool()121     ~AutoModifiersPool() {
122         fContext->fModifiersPool = nullptr;
123     }
124 
125     Context* fContext;
126 };
127 
Compiler(const ShaderCapsClass * caps)128 Compiler::Compiler(const ShaderCapsClass* caps)
129         : fContext(std::make_shared<Context>(/*errors=*/*this, *caps))
130         , fInliner(fContext.get()) {
131     SkASSERT(caps);
132     fRootSymbolTable = std::make_shared<SymbolTable>(this, /*builtin=*/true);
133     fPrivateSymbolTable = std::make_shared<SymbolTable>(fRootSymbolTable, /*builtin=*/true);
134     fIRGenerator = std::make_unique<IRGenerator>(fContext.get());
135 
136 #define TYPE(t) fContext->fTypes.f ## t .get()
137 
138     const SkSL::Symbol* rootTypes[] = {
139         TYPE(Void),
140 
141         TYPE( Float), TYPE( Float2), TYPE( Float3), TYPE( Float4),
142         TYPE(  Half), TYPE(  Half2), TYPE(  Half3), TYPE(  Half4),
143         TYPE(   Int), TYPE(   Int2), TYPE(   Int3), TYPE(   Int4),
144         TYPE(  Bool), TYPE(  Bool2), TYPE(  Bool3), TYPE(  Bool4),
145 
146         TYPE(Float2x2), TYPE(Float3x3), TYPE(Float4x4),
147         TYPE( Half2x2), TYPE( Half3x3), TYPE(Half4x4),
148 
149         TYPE(SquareMat), TYPE(SquareHMat),
150 
151         TYPE(GenType), TYPE(GenHType), TYPE(GenIType), TYPE(GenBType),
152         TYPE(Vec),     TYPE(HVec),     TYPE(IVec),     TYPE(BVec),
153 
154         TYPE(ColorFilter),
155         TYPE(Shader),
156     };
157 
158     const SkSL::Symbol* privateTypes[] = {
159         TYPE(  UInt), TYPE(  UInt2), TYPE(  UInt3), TYPE(  UInt4),
160         TYPE( Short), TYPE( Short2), TYPE( Short3), TYPE( Short4),
161         TYPE(UShort), TYPE(UShort2), TYPE(UShort3), TYPE(UShort4),
162 
163         TYPE(GenUType), TYPE(UVec),
164         TYPE(SVec), TYPE(USVec),
165 
166         TYPE(Float2x3), TYPE(Float2x4),
167         TYPE(Float3x2), TYPE(Float3x4),
168         TYPE(Float4x2), TYPE(Float4x3),
169 
170         TYPE(Half2x3),  TYPE(Half2x4),
171         TYPE(Half3x2),  TYPE(Half3x4),
172         TYPE(Half4x2),  TYPE(Half4x3),
173 
174         TYPE(Mat), TYPE(HMat),
175 
176         TYPE(Sampler1D), TYPE(Sampler2D), TYPE(Sampler3D),
177         TYPE(SamplerExternalOES),
178         TYPE(Sampler2DRect),
179 
180         TYPE(ISampler2D),
181         TYPE(SubpassInput), TYPE(SubpassInputMS),
182 
183         TYPE(Sampler),
184         TYPE(Texture2D),
185 
186         TYPE(FragmentProcessor),
187     };
188 
189     for (const SkSL::Symbol* type : rootTypes) {
190         fRootSymbolTable->addWithoutOwnership(type);
191     }
192     for (const SkSL::Symbol* type : privateTypes) {
193         fPrivateSymbolTable->addWithoutOwnership(type);
194     }
195 
196 #undef TYPE
197 
198     // sk_Caps is "builtin", but all references to it are resolved to Settings, so we don't need to
199     // treat it as builtin (ie, no need to clone it into the Program).
200     fPrivateSymbolTable->add(std::make_unique<Variable>(/*offset=*/-1,
201                                                         fCoreModifiers.add(Modifiers{}),
202                                                         "sk_Caps",
203                                                         fContext->fTypes.fSkCaps.get(),
204                                                         /*builtin=*/false,
205                                                         Variable::Storage::kGlobal));
206 
207     fRootModule = {fRootSymbolTable, /*fIntrinsics=*/nullptr};
208     fPrivateModule = {fPrivateSymbolTable, /*fIntrinsics=*/nullptr};
209 }
210 
~Compiler()211 Compiler::~Compiler() {}
212 
loadGPUModule()213 const ParsedModule& Compiler::loadGPUModule() {
214     if (!fGPUModule.fSymbols) {
215         fGPUModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(gpu), fPrivateModule);
216     }
217     return fGPUModule;
218 }
219 
loadFragmentModule()220 const ParsedModule& Compiler::loadFragmentModule() {
221     if (!fFragmentModule.fSymbols) {
222         fFragmentModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(frag),
223                                             this->loadGPUModule());
224     }
225     return fFragmentModule;
226 }
227 
loadVertexModule()228 const ParsedModule& Compiler::loadVertexModule() {
229     if (!fVertexModule.fSymbols) {
230         fVertexModule = this->parseModule(ProgramKind::kVertex, MODULE_DATA(vert),
231                                           this->loadGPUModule());
232     }
233     return fVertexModule;
234 }
235 
loadGeometryModule()236 const ParsedModule& Compiler::loadGeometryModule() {
237     if (!fGeometryModule.fSymbols) {
238         fGeometryModule = this->parseModule(ProgramKind::kGeometry, MODULE_DATA(geom),
239                                             this->loadGPUModule());
240     }
241     return fGeometryModule;
242 }
243 
loadFPModule()244 const ParsedModule& Compiler::loadFPModule() {
245     if (!fFPModule.fSymbols) {
246         fFPModule = this->parseModule(ProgramKind::kFragmentProcessor, MODULE_DATA(fp),
247                                       this->loadGPUModule());
248     }
249     return fFPModule;
250 }
251 
loadPublicModule()252 const ParsedModule& Compiler::loadPublicModule() {
253     if (!fPublicModule.fSymbols) {
254         fPublicModule = this->parseModule(ProgramKind::kGeneric, MODULE_DATA(public), fRootModule);
255     }
256     return fPublicModule;
257 }
258 
add_glsl_type_aliases(SkSL::SymbolTable * symbols,const SkSL::BuiltinTypes & types)259 static void add_glsl_type_aliases(SkSL::SymbolTable* symbols, const SkSL::BuiltinTypes& types) {
260     // Add some aliases to the runtime effect modules so that it's friendlier, and more like GLSL
261     symbols->addAlias("vec2", types.fFloat2.get());
262     symbols->addAlias("vec3", types.fFloat3.get());
263     symbols->addAlias("vec4", types.fFloat4.get());
264 
265     symbols->addAlias("ivec2", types.fInt2.get());
266     symbols->addAlias("ivec3", types.fInt3.get());
267     symbols->addAlias("ivec4", types.fInt4.get());
268 
269     symbols->addAlias("bvec2", types.fBool2.get());
270     symbols->addAlias("bvec3", types.fBool3.get());
271     symbols->addAlias("bvec4", types.fBool4.get());
272 
273     symbols->addAlias("mat2", types.fFloat2x2.get());
274     symbols->addAlias("mat3", types.fFloat3x3.get());
275     symbols->addAlias("mat4", types.fFloat4x4.get());
276 }
277 
loadRuntimeColorFilterModule()278 const ParsedModule& Compiler::loadRuntimeColorFilterModule() {
279     if (!fRuntimeColorFilterModule.fSymbols) {
280         fRuntimeColorFilterModule = this->parseModule(ProgramKind::kRuntimeColorFilter,
281                                                       MODULE_DATA(rt_colorfilter),
282                                                       this->loadPublicModule());
283         add_glsl_type_aliases(fRuntimeColorFilterModule.fSymbols.get(), fContext->fTypes);
284     }
285     return fRuntimeColorFilterModule;
286 }
287 
loadRuntimeShaderModule()288 const ParsedModule& Compiler::loadRuntimeShaderModule() {
289     if (!fRuntimeShaderModule.fSymbols) {
290         fRuntimeShaderModule = this->parseModule(
291                 ProgramKind::kRuntimeShader, MODULE_DATA(rt_shader), this->loadPublicModule());
292         add_glsl_type_aliases(fRuntimeShaderModule.fSymbols.get(), fContext->fTypes);
293     }
294     return fRuntimeShaderModule;
295 }
296 
moduleForProgramKind(ProgramKind kind)297 const ParsedModule& Compiler::moduleForProgramKind(ProgramKind kind) {
298     switch (kind) {
299         case ProgramKind::kVertex:             return this->loadVertexModule();             break;
300         case ProgramKind::kFragment:           return this->loadFragmentModule();           break;
301         case ProgramKind::kGeometry:           return this->loadGeometryModule();           break;
302         case ProgramKind::kFragmentProcessor:  return this->loadFPModule();                 break;
303         case ProgramKind::kRuntimeColorFilter: return this->loadRuntimeColorFilterModule(); break;
304         case ProgramKind::kRuntimeShader:      return this->loadRuntimeShaderModule();      break;
305         case ProgramKind::kGeneric:            return this->loadPublicModule();             break;
306     }
307     SkUNREACHABLE;
308 }
309 
loadModule(ProgramKind kind,ModuleData data,std::shared_ptr<SymbolTable> base,bool dehydrate)310 LoadedModule Compiler::loadModule(ProgramKind kind,
311                                   ModuleData data,
312                                   std::shared_ptr<SymbolTable> base,
313                                   bool dehydrate) {
314     if (dehydrate) {
315         // NOTE: This is a workaround. When dehydrating includes, skslc doesn't know which module
316         // it's preparing, nor what the correct base module is. We can't use 'Root', because many
317         // GPU intrinsics reference private types, like samplers or textures. Today, 'Private' does
318         // contain the union of all known types, so this is safe. If we ever have types that only
319         // exist in 'Public' (for example), this logic needs to be smarter (by choosing the correct
320         // base for the module we're compiling).
321         base = fPrivateSymbolTable;
322     }
323     SkASSERT(base);
324 
325     // Put the core-module modifier pool into the context.
326     AutoModifiersPool autoPool(fContext, &fCoreModifiers);
327 
328     // Built-in modules always use default program settings.
329     ProgramConfig config;
330     config.fKind = kind;
331     config.fSettings.fReplaceSettings = !dehydrate;
332     AutoProgramConfig autoConfig(fContext, &config);
333 
334 #if defined(SKSL_STANDALONE)
335     SkASSERT(data.fPath);
336     std::ifstream in(data.fPath);
337     String text{std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()};
338     if (in.rdstate()) {
339         printf("error reading %s\n", data.fPath);
340         abort();
341     }
342     const String* source = fRootSymbolTable->takeOwnershipOfString(std::move(text));
343     AutoSource as(this, source);
344 
345     ParsedModule baseModule = {base, /*fIntrinsics=*/nullptr};
346     IRGenerator::IRBundle ir = fIRGenerator->convertProgram(baseModule, /*isBuiltinCode=*/true,
347                                                             source->c_str(), source->length(),
348                                                             /*externalFunctions=*/nullptr);
349     SkASSERT(ir.fSharedElements.empty());
350     LoadedModule module = { kind, std::move(ir.fSymbolTable), std::move(ir.fElements) };
351     if (this->fErrorCount) {
352         printf("Unexpected errors: %s\n", this->fErrorText.c_str());
353         SkDEBUGFAILF("%s %s\n", data.fPath, this->fErrorText.c_str());
354     }
355 #else
356     SkASSERT(data.fData && (data.fSize != 0));
357     Rehydrator rehydrator(fContext.get(), base, data.fData, data.fSize);
358     LoadedModule module = { kind, rehydrator.symbolTable(), rehydrator.elements() };
359 #endif
360 
361     return module;
362 }
363 
parseModule(ProgramKind kind,ModuleData data,const ParsedModule & base)364 ParsedModule Compiler::parseModule(ProgramKind kind, ModuleData data, const ParsedModule& base) {
365     LoadedModule module = this->loadModule(kind, data, base.fSymbols, /*dehydrate=*/false);
366     this->optimize(module);
367 
368     // For modules that just declare (but don't define) intrinsic functions, there will be no new
369     // program elements. In that case, we can share our parent's intrinsic map:
370     if (module.fElements.empty()) {
371         return ParsedModule{module.fSymbols, base.fIntrinsics};
372     }
373 
374     auto intrinsics = std::make_shared<IRIntrinsicMap>(base.fIntrinsics.get());
375 
376     // Now, transfer all of the program elements to an intrinsic map. This maps certain types of
377     // global objects to the declaring ProgramElement.
378     for (std::unique_ptr<ProgramElement>& element : module.fElements) {
379         switch (element->kind()) {
380             case ProgramElement::Kind::kFunction: {
381                 const FunctionDefinition& f = element->as<FunctionDefinition>();
382                 SkASSERT(f.declaration().isBuiltin());
383                 intrinsics->insertOrDie(f.declaration().description(), std::move(element));
384                 break;
385             }
386             case ProgramElement::Kind::kFunctionPrototype: {
387                 // These are already in the symbol table.
388                 break;
389             }
390             case ProgramElement::Kind::kEnum: {
391                 const Enum& e = element->as<Enum>();
392                 SkASSERT(e.isBuiltin());
393                 intrinsics->insertOrDie(e.typeName(), std::move(element));
394                 break;
395             }
396             case ProgramElement::Kind::kGlobalVar: {
397                 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
398                 const Variable& var = global.declaration()->as<VarDeclaration>().var();
399                 SkASSERT(var.isBuiltin());
400                 intrinsics->insertOrDie(var.name(), std::move(element));
401                 break;
402             }
403             case ProgramElement::Kind::kInterfaceBlock: {
404                 const Variable& var = element->as<InterfaceBlock>().variable();
405                 SkASSERT(var.isBuiltin());
406                 intrinsics->insertOrDie(var.name(), std::move(element));
407                 break;
408             }
409             default:
410                 printf("Unsupported element: %s\n", element->description().c_str());
411                 SkASSERT(false);
412                 break;
413         }
414     }
415 
416     return ParsedModule{module.fSymbols, std::move(intrinsics)};
417 }
418 
convertProgram(ProgramKind kind,String text,const Program::Settings & settings,const std::vector<std::unique_ptr<ExternalFunction>> * externalFunctions)419 std::unique_ptr<Program> Compiler::convertProgram(
420         ProgramKind kind,
421         String text,
422         const Program::Settings& settings,
423         const std::vector<std::unique_ptr<ExternalFunction>>* externalFunctions) {
424     TRACE_EVENT0("skia.shaders", "SkSL::Compiler::convertProgram");
425 
426     SkASSERT(!externalFunctions || (kind == ProgramKind::kGeneric));
427 
428     // Loading and optimizing our base module might reset the inliner, so do that first,
429     // *then* configure the inliner with the settings for this program.
430     const ParsedModule& baseModule = this->moduleForProgramKind(kind);
431 
432     // Put a fresh modifier pool into the context.
433     auto modifiersPool = std::make_unique<ModifiersPool>();
434     AutoModifiersPool autoPool(fContext, modifiersPool.get());
435 
436     // Update our context to point to the program configuration for the duration of compilation.
437     auto config = std::make_unique<ProgramConfig>(ProgramConfig{kind, settings});
438     AutoProgramConfig autoConfig(fContext, config.get());
439 
440     // Honor our optimization-override flags.
441     switch (sOptimizer) {
442         case OverrideFlag::kDefault:
443             break;
444         case OverrideFlag::kOff:
445             config->fSettings.fOptimize = false;
446             break;
447         case OverrideFlag::kOn:
448             config->fSettings.fOptimize = true;
449             break;
450     }
451 
452     switch (sInliner) {
453         case OverrideFlag::kDefault:
454             break;
455         case OverrideFlag::kOff:
456             config->fSettings.fInlineThreshold = 0;
457             break;
458         case OverrideFlag::kOn:
459             if (config->fSettings.fInlineThreshold == 0) {
460                 config->fSettings.fInlineThreshold = kDefaultInlineThreshold;
461             }
462             break;
463     }
464 
465     // Disable optimization settings that depend on a parent setting which has been disabled.
466     config->fSettings.fInlineThreshold *= (int)config->fSettings.fOptimize;
467     config->fSettings.fRemoveDeadFunctions &= config->fSettings.fOptimize;
468     config->fSettings.fRemoveDeadVariables &= config->fSettings.fOptimize;
469 
470     fErrorText = "";
471     fErrorCount = 0;
472     fInliner.reset();
473 
474     auto textPtr = std::make_unique<String>(std::move(text));
475     AutoSource as(this, textPtr.get());
476 
477     // Enable node pooling while converting and optimizing the program for a performance boost.
478     // The Program will take ownership of the pool.
479     std::unique_ptr<Pool> pool;
480     if (fContext->fCaps.useNodePools()) {
481         pool = Pool::Create();
482         pool->attachToThread();
483     }
484     IRGenerator::IRBundle ir = fIRGenerator->convertProgram(baseModule, /*isBuiltinCode=*/false,
485                                                             textPtr->c_str(), textPtr->size(),
486                                                             externalFunctions);
487     auto program = std::make_unique<Program>(std::move(textPtr),
488                                              std::move(config),
489                                              fContext,
490                                              std::move(ir.fElements),
491                                              std::move(ir.fSharedElements),
492                                              std::move(modifiersPool),
493                                              std::move(ir.fSymbolTable),
494                                              std::move(pool),
495                                              ir.fInputs);
496     bool success = false;
497     if (fErrorCount) {
498         // Do not return programs that failed to compile.
499     } else if (!this->optimize(*program)) {
500         // Do not return programs that failed to optimize.
501     } else {
502         // We have a successful program!
503         success = true;
504     }
505 
506     if (program->fPool) {
507         program->fPool->detachFromThread();
508     }
509     return success ? std::move(program) : nullptr;
510 }
511 
verifyStaticTests(const Program & program)512 void Compiler::verifyStaticTests(const Program& program) {
513     class StaticTestVerifier : public ProgramVisitor {
514     public:
515         StaticTestVerifier(ErrorReporter* r) : fReporter(r) {}
516 
517         using ProgramVisitor::visitProgramElement;
518 
519         bool visitStatement(const Statement& stmt) override {
520             switch (stmt.kind()) {
521                 case Statement::Kind::kIf:
522                     if (stmt.as<IfStatement>().isStatic()) {
523                         fReporter->error(stmt.fOffset, "static if has non-static test");
524                     }
525                     break;
526 
527                 case Statement::Kind::kSwitch:
528                     if (stmt.as<SwitchStatement>().isStatic()) {
529                         fReporter->error(stmt.fOffset, "static switch has non-static test");
530                     }
531                     break;
532 
533                 default:
534                     break;
535             }
536             return INHERITED::visitStatement(stmt);
537         }
538 
539         bool visitExpression(const Expression&) override {
540             // We aren't looking for anything inside an Expression, so skip them entirely.
541             return false;
542         }
543 
544     private:
545         using INHERITED = ProgramVisitor;
546         ErrorReporter* fReporter;
547     };
548 
549     // If invalid static tests are permitted, we don't need to check anything.
550     if (fContext->fConfig->fSettings.fPermitInvalidStaticTests) {
551         return;
552     }
553 
554     // Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
555     StaticTestVerifier visitor{this};
556     for (const std::unique_ptr<ProgramElement>& element : program.ownedElements()) {
557         if (element->is<FunctionDefinition>()) {
558             visitor.visitProgramElement(*element);
559         }
560     }
561 }
562 
optimize(LoadedModule & module)563 bool Compiler::optimize(LoadedModule& module) {
564     SkASSERT(!fErrorCount);
565 
566     // Create a temporary program configuration with default settings.
567     ProgramConfig config;
568     config.fKind = module.fKind;
569     AutoProgramConfig autoConfig(fContext, &config);
570 
571     // Reset the Inliner.
572     fInliner.reset();
573 
574     std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
575 
576     while (fErrorCount == 0) {
577         // Perform inline-candidate analysis and inline any functions deemed suitable.
578         if (!fInliner.analyze(module.fElements, module.fSymbols, usage.get())) {
579             break;
580         }
581     }
582     return fErrorCount == 0;
583 }
584 
removeDeadFunctions(Program & program,ProgramUsage * usage)585 bool Compiler::removeDeadFunctions(Program& program, ProgramUsage* usage) {
586     bool madeChanges = false;
587 
588     if (program.fConfig->fSettings.fRemoveDeadFunctions) {
589         auto isDeadFunction = [&](const ProgramElement* element) {
590             if (!element->is<FunctionDefinition>()) {
591                 return false;
592             }
593             const FunctionDefinition& fn = element->as<FunctionDefinition>();
594             if (fn.declaration().isMain() || usage->get(fn.declaration()) > 0) {
595                 return false;
596             }
597             usage->remove(*element);
598             madeChanges = true;
599             return true;
600         };
601 
602         program.fElements.erase(std::remove_if(program.fElements.begin(),
603                                                program.fElements.end(),
604                                                [&](const std::unique_ptr<ProgramElement>& element) {
605                                                    return isDeadFunction(element.get());
606                                                }),
607                                 program.fElements.end());
608         program.fSharedElements.erase(std::remove_if(program.fSharedElements.begin(),
609                                                      program.fSharedElements.end(),
610                                                      isDeadFunction),
611                                       program.fSharedElements.end());
612     }
613     return madeChanges;
614 }
615 
removeDeadGlobalVariables(Program & program,ProgramUsage * usage)616 bool Compiler::removeDeadGlobalVariables(Program& program, ProgramUsage* usage) {
617     bool madeChanges = false;
618 
619     if (program.fConfig->fSettings.fRemoveDeadVariables) {
620         auto isDeadVariable = [&](const ProgramElement* element) {
621             if (!element->is<GlobalVarDeclaration>()) {
622                 return false;
623             }
624             const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
625             const VarDeclaration& varDecl = global.declaration()->as<VarDeclaration>();
626             if (!usage->isDead(varDecl.var())) {
627                 return false;
628             }
629             madeChanges = true;
630             return true;
631         };
632 
633         program.fElements.erase(std::remove_if(program.fElements.begin(),
634                                                program.fElements.end(),
635                                                [&](const std::unique_ptr<ProgramElement>& element) {
636                                                    return isDeadVariable(element.get());
637                                                }),
638                                 program.fElements.end());
639         program.fSharedElements.erase(std::remove_if(program.fSharedElements.begin(),
640                                                      program.fSharedElements.end(),
641                                                      isDeadVariable),
642                                       program.fSharedElements.end());
643     }
644     return madeChanges;
645 }
646 
removeDeadLocalVariables(Program & program,ProgramUsage * usage)647 bool Compiler::removeDeadLocalVariables(Program& program, ProgramUsage* usage) {
648     class DeadLocalVariableEliminator : public ProgramWriter {
649     public:
650         DeadLocalVariableEliminator(const Context& context, ProgramUsage* usage)
651                 : fContext(context)
652                 , fUsage(usage) {}
653 
654         using ProgramWriter::visitProgramElement;
655 
656         bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
657             // We don't need to look inside expressions at all.
658             return false;
659         }
660 
661         bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
662             if (stmt->is<VarDeclaration>()) {
663                 VarDeclaration& varDecl = stmt->as<VarDeclaration>();
664                 const Variable* var = &varDecl.var();
665                 ProgramUsage::VariableCounts* counts = fUsage->fVariableCounts.find(var);
666                 SkASSERT(counts);
667                 SkASSERT(counts->fDeclared);
668                 if (CanEliminate(var, *counts)) {
669                     if (var->initialValue()) {
670                         // The variable has an initial-value expression, which might have side
671                         // effects. ExpressionStatement::Make will preserve side effects, but
672                         // replaces pure expressions with Nop.
673                         fUsage->remove(stmt.get());
674                         stmt = ExpressionStatement::Make(fContext, std::move(varDecl.value()));
675                         fUsage->add(stmt.get());
676                     } else {
677                         // The variable has no initial-value and can be cleanly eliminated.
678                         fUsage->remove(stmt.get());
679                         stmt = std::make_unique<Nop>();
680                     }
681                     fMadeChanges = true;
682                 }
683                 return false;
684             }
685             return INHERITED::visitStatementPtr(stmt);
686         }
687 
688         static bool CanEliminate(const Variable* var, const ProgramUsage::VariableCounts& counts) {
689             if (!counts.fDeclared || counts.fRead || var->storage() != VariableStorage::kLocal) {
690                 return false;
691             }
692             if (var->initialValue()) {
693                 SkASSERT(counts.fWrite >= 1);
694                 return counts.fWrite == 1;
695             } else {
696                 return counts.fWrite == 0;
697             }
698         }
699 
700         bool fMadeChanges = false;
701         const Context& fContext;
702         ProgramUsage* fUsage;
703 
704         using INHERITED = ProgramWriter;
705     };
706 
707     DeadLocalVariableEliminator visitor{*fContext, usage};
708 
709     if (program.fConfig->fSettings.fRemoveDeadVariables) {
710         for (auto& [var, counts] : usage->fVariableCounts) {
711             if (DeadLocalVariableEliminator::CanEliminate(var, counts)) {
712                 // This program contains at least one dead local variable.
713                 // Scan the program for any dead local variables and eliminate them all.
714                 for (std::unique_ptr<ProgramElement>& pe : program.ownedElements()) {
715                     if (pe->is<FunctionDefinition>()) {
716                         visitor.visitProgramElement(*pe);
717                     }
718                 }
719                 break;
720             }
721         }
722     }
723 
724     return visitor.fMadeChanges;
725 }
726 
optimize(Program & program)727 bool Compiler::optimize(Program& program) {
728     // The optimizer only needs to run when it is enabled.
729     if (!program.fConfig->fSettings.fOptimize) {
730         return true;
731     }
732 
733     SkASSERT(!fErrorCount);
734     ProgramUsage* usage = program.fUsage.get();
735 
736     if (fErrorCount == 0) {
737         // Run the inliner only once; it is expensive! Multiple passes can occasionally shake out
738         // more wins, but it's diminishing returns.
739         fInliner.analyze(program.ownedElements(), program.fSymbols, usage);
740 
741         while (this->removeDeadFunctions(program, usage)) {
742             // Removing dead functions may cause more functions to become unreferenced. Try again.
743         }
744         while (this->removeDeadLocalVariables(program, usage)) {
745             // Removing dead variables may cause more variables to become unreferenced. Try again.
746         }
747         if (program.fConfig->fKind != ProgramKind::kFragmentProcessor) {
748             this->removeDeadGlobalVariables(program, usage);
749         }
750     }
751 
752     if (fErrorCount == 0) {
753         this->verifyStaticTests(program);
754     }
755 
756     return fErrorCount == 0;
757 }
758 
759 #if defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
760 
toSPIRV(Program & program,OutputStream & out)761 bool Compiler::toSPIRV(Program& program, OutputStream& out) {
762     TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toSPIRV");
763     AutoSource as(this, program.fSource.get());
764 #ifdef SK_ENABLE_SPIRV_VALIDATION
765     StringStream buffer;
766     SPIRVCodeGenerator cg(fContext.get(), &program, this, &buffer);
767     bool result = cg.generateCode();
768     if (result && program.fConfig->fSettings.fValidateSPIRV) {
769         spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
770         const String& data = buffer.str();
771         SkASSERT(0 == data.size() % 4);
772         String errors;
773         auto dumpmsg = [&errors](spv_message_level_t, const char*, const spv_position_t&,
774                                  const char* m) {
775             errors.appendf("SPIR-V validation error: %s\n", m);
776         };
777         tools.SetMessageConsumer(dumpmsg);
778 
779         // Verify that the SPIR-V we produced is valid. At runtime, we will abort() with a message
780         // explaining the error. In standalone mode (skslc), we will send the message, plus the
781         // entire disassembled SPIR-V (for easier context & debugging) as *our* error message.
782         result = tools.Validate((const uint32_t*) data.c_str(), data.size() / 4);
783 
784         if (!result) {
785 #if defined(SKSL_STANDALONE)
786             // Convert the string-stream to a SPIR-V disassembly.
787             std::string disassembly;
788             if (tools.Disassemble((const uint32_t*)data.data(), data.size() / 4, &disassembly)) {
789                 errors.append(disassembly);
790             }
791             this->error(-1, errors);
792 #else
793             SkDEBUGFAILF("%s", errors.c_str());
794 #endif
795         }
796         out.write(data.c_str(), data.size());
797     }
798 #else
799     SPIRVCodeGenerator cg(fContext.get(), &program, this, &out);
800     bool result = cg.generateCode();
801 #endif
802     return result;
803 }
804 
toSPIRV(Program & program,String * out)805 bool Compiler::toSPIRV(Program& program, String* out) {
806     StringStream buffer;
807     bool result = this->toSPIRV(program, buffer);
808     if (result) {
809         *out = buffer.str();
810     }
811     return result;
812 }
813 
toGLSL(Program & program,OutputStream & out)814 bool Compiler::toGLSL(Program& program, OutputStream& out) {
815     TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toGLSL");
816     AutoSource as(this, program.fSource.get());
817     GLSLCodeGenerator cg(fContext.get(), &program, this, &out);
818     bool result = cg.generateCode();
819     return result;
820 }
821 
toGLSL(Program & program,String * out)822 bool Compiler::toGLSL(Program& program, String* out) {
823     StringStream buffer;
824     bool result = this->toGLSL(program, buffer);
825     if (result) {
826         *out = buffer.str();
827     }
828     return result;
829 }
830 
toHLSL(Program & program,String * out)831 bool Compiler::toHLSL(Program& program, String* out) {
832     String spirv;
833     if (!this->toSPIRV(program, &spirv)) {
834         return false;
835     }
836 
837     return SPIRVtoHLSL(spirv, out);
838 }
839 
toMetal(Program & program,OutputStream & out)840 bool Compiler::toMetal(Program& program, OutputStream& out) {
841     TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toMetal");
842     AutoSource as(this, program.fSource.get());
843     MetalCodeGenerator cg(fContext.get(), &program, this, &out);
844     bool result = cg.generateCode();
845     return result;
846 }
847 
toMetal(Program & program,String * out)848 bool Compiler::toMetal(Program& program, String* out) {
849     StringStream buffer;
850     bool result = this->toMetal(program, buffer);
851     if (result) {
852         *out = buffer.str();
853     }
854     return result;
855 }
856 
857 #if defined(SKSL_STANDALONE) || GR_TEST_UTILS
toCPP(Program & program,String name,OutputStream & out)858 bool Compiler::toCPP(Program& program, String name, OutputStream& out) {
859     AutoSource as(this, program.fSource.get());
860     CPPCodeGenerator cg(fContext.get(), &program, this, name, &out);
861     bool result = cg.generateCode();
862     return result;
863 }
864 
toDSLCPP(Program & program,String name,OutputStream & out)865 bool Compiler::toDSLCPP(Program& program, String name, OutputStream& out) {
866     AutoSource as(this, program.fSource.get());
867     DSLCPPCodeGenerator cg(fContext.get(), &program, this, name, &out);
868     bool result = cg.generateCode();
869     return result;
870 }
871 
toH(Program & program,String name,OutputStream & out)872 bool Compiler::toH(Program& program, String name, OutputStream& out) {
873     AutoSource as(this, program.fSource.get());
874     HCodeGenerator cg(fContext.get(), &program, this, name, &out);
875     bool result = cg.generateCode();
876     return result;
877 }
878 #endif // defined(SKSL_STANDALONE) || GR_TEST_UTILS
879 
880 #endif // defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
881 
position(int offset)882 Position Compiler::position(int offset) {
883     if (fSource && offset >= 0) {
884         int line = 1;
885         int column = 1;
886         for (int i = 0; i < offset; i++) {
887             if ((*fSource)[i] == '\n') {
888                 ++line;
889                 column = 1;
890             }
891             else {
892                 ++column;
893             }
894         }
895         return Position(line, column);
896     } else {
897         return Position(-1, -1);
898     }
899 }
900 
error(int offset,String msg)901 void Compiler::error(int offset, String msg) {
902     fErrorCount++;
903     Position pos = this->position(offset);
904     fErrorTextLength.push_back(fErrorText.length());
905     fErrorText += "error: " + (pos.fLine >= 1 ? to_string(pos.fLine) + ": " : "") + msg + "\n";
906 }
907 
setErrorCount(int c)908 void Compiler::setErrorCount(int c) {
909     if (c < fErrorCount) {
910         fErrorText.resize(fErrorTextLength[c]);
911         fErrorTextLength.resize(c);
912         fErrorCount = c;
913     }
914 }
915 
errorText(bool showCount)916 String Compiler::errorText(bool showCount) {
917     if (showCount) {
918         this->writeErrorCount();
919     }
920     fErrorCount = 0;
921     String result = fErrorText;
922     fErrorText = "";
923     return result;
924 }
925 
writeErrorCount()926 void Compiler::writeErrorCount() {
927     if (fErrorCount) {
928         fErrorText += to_string(fErrorCount) + " error";
929         if (fErrorCount > 1) {
930             fErrorText += "s";
931         }
932         fErrorText += "\n";
933     }
934 }
935 
936 }  // namespace SkSL
937