• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/sksl/DSLCore.h"
14 #include "src/core/SkTraceEvent.h"
15 #include "src/sksl/SkSLConstantFolder.h"
16 #include "src/sksl/SkSLDSLParser.h"
17 #include "src/sksl/SkSLIntrinsicMap.h"
18 #include "src/sksl/SkSLOperators.h"
19 #include "src/sksl/SkSLProgramSettings.h"
20 #include "src/sksl/SkSLRehydrator.h"
21 #include "src/sksl/SkSLThreadContext.h"
22 #include "src/sksl/codegen/SkSLGLSLCodeGenerator.h"
23 #include "src/sksl/codegen/SkSLMetalCodeGenerator.h"
24 #include "src/sksl/codegen/SkSLSPIRVCodeGenerator.h"
25 #include "src/sksl/codegen/SkSLSPIRVtoHLSL.h"
26 #include "src/sksl/dsl/priv/DSLWriter.h"
27 #include "src/sksl/dsl/priv/DSL_priv.h"
28 #include "src/sksl/ir/SkSLExpression.h"
29 #include "src/sksl/ir/SkSLExpressionStatement.h"
30 #include "src/sksl/ir/SkSLExternalFunctionReference.h"
31 #include "src/sksl/ir/SkSLField.h"
32 #include "src/sksl/ir/SkSLFieldAccess.h"
33 #include "src/sksl/ir/SkSLFunctionCall.h"
34 #include "src/sksl/ir/SkSLFunctionDefinition.h"
35 #include "src/sksl/ir/SkSLFunctionReference.h"
36 #include "src/sksl/ir/SkSLInterfaceBlock.h"
37 #include "src/sksl/ir/SkSLLiteral.h"
38 #include "src/sksl/ir/SkSLModifiersDeclaration.h"
39 #include "src/sksl/ir/SkSLNop.h"
40 #include "src/sksl/ir/SkSLSymbolTable.h"
41 #include "src/sksl/ir/SkSLTernaryExpression.h"
42 #include "src/sksl/ir/SkSLTypeReference.h"
43 #include "src/sksl/ir/SkSLUnresolvedFunction.h"
44 #include "src/sksl/ir/SkSLVarDeclarations.h"
45 #include "src/sksl/transform/SkSLProgramWriter.h"
46 #include "src/sksl/transform/SkSLTransform.h"
47 #include "src/utils/SkBitSet.h"
48 
49 #include <fstream>
50 
51 #if !defined(SKSL_STANDALONE) & SK_SUPPORT_GPU
52 #include "include/gpu/GrContextOptions.h"
53 #include "src/gpu/GrShaderCaps.h"
54 #endif
55 
56 #ifdef SK_ENABLE_SPIRV_VALIDATION
57 #include "spirv-tools/libspirv.hpp"
58 #endif
59 
60 #if defined(SKSL_STANDALONE)
61 
62 // In standalone mode, we load the textual sksl source files. GN generates or copies these files
63 // to the skslc executable directory. The "data" in this mode is just the filename.
64 #define MODULE_DATA(name) MakeModulePath("sksl_" #name ".sksl")
65 
66 #else
67 
68 // At runtime, we load the dehydrated sksl data files. The data is a (pointer, size) pair.
69 #include "src/sksl/generated/sksl_frag.dehydrated.sksl"
70 #include "src/sksl/generated/sksl_gpu.dehydrated.sksl"
71 #include "src/sksl/generated/sksl_public.dehydrated.sksl"
72 #include "src/sksl/generated/sksl_rt_shader.dehydrated.sksl"
73 #include "src/sksl/generated/sksl_vert.dehydrated.sksl"
74 
75 #define MODULE_DATA(name) MakeModuleData(SKSL_INCLUDE_sksl_##name,\
76                                          SKSL_INCLUDE_sksl_##name##_LENGTH)
77 
78 #endif
79 
80 namespace SkSL {
81 
82 // These flags allow tools like Viewer or Nanobench to override the compiler's ProgramSettings.
83 Compiler::OverrideFlag Compiler::sOptimizer = OverrideFlag::kDefault;
84 Compiler::OverrideFlag Compiler::sInliner = OverrideFlag::kDefault;
85 
86 using RefKind = VariableReference::RefKind;
87 
88 class AutoSource {
89 public:
AutoSource(Compiler * compiler,const char * source)90     AutoSource(Compiler* compiler, const char* source)
91             : fCompiler(compiler) {
92         SkASSERT(!fCompiler->errorReporter().source());
93         fCompiler->errorReporter().setSource(source);
94     }
95 
~AutoSource()96     ~AutoSource() {
97         fCompiler->errorReporter().setSource(nullptr);
98     }
99 
100     Compiler* fCompiler;
101 };
102 
103 class AutoProgramConfig {
104 public:
AutoProgramConfig(std::shared_ptr<Context> & context,ProgramConfig * config)105     AutoProgramConfig(std::shared_ptr<Context>& context, ProgramConfig* config)
106             : fContext(context.get())
107             , fOldConfig(fContext->fConfig) {
108         fContext->fConfig = config;
109     }
110 
~AutoProgramConfig()111     ~AutoProgramConfig() {
112         fContext->fConfig = fOldConfig;
113     }
114 
115     Context* fContext;
116     ProgramConfig* fOldConfig;
117 };
118 
119 class AutoModifiersPool {
120 public:
AutoModifiersPool(std::shared_ptr<Context> & context,ModifiersPool * modifiersPool)121     AutoModifiersPool(std::shared_ptr<Context>& context, ModifiersPool* modifiersPool)
122             : fContext(context.get()) {
123         SkASSERT(!fContext->fModifiersPool);
124         fContext->fModifiersPool = modifiersPool;
125     }
126 
~AutoModifiersPool()127     ~AutoModifiersPool() {
128         fContext->fModifiersPool = nullptr;
129     }
130 
131     Context* fContext;
132 };
133 
Compiler(const ShaderCapsClass * caps)134 Compiler::Compiler(const ShaderCapsClass* caps)
135         : fErrorReporter(this)
136         , fContext(std::make_shared<Context>(fErrorReporter, *caps, fMangler))
137         , fInliner(fContext.get()) {
138     SkASSERT(caps);
139     fRootModule.fSymbols = this->makeRootSymbolTable();
140     fPrivateModule.fSymbols = this->makePrivateSymbolTable(fRootModule.fSymbols);
141 }
142 
~Compiler()143 Compiler::~Compiler() {}
144 
145 #define TYPE(t) &BuiltinTypes::f ## t
146 
147 using BuiltinTypePtr = const std::unique_ptr<Type> BuiltinTypes::*;
148 
149 inline static constexpr BuiltinTypePtr kRootTypes[] = {
150     TYPE(Void),
151 
152     TYPE( Float), TYPE( Float2), TYPE( Float3), TYPE( Float4),
153     TYPE(  Half), TYPE(  Half2), TYPE(  Half3), TYPE(  Half4),
154     TYPE(   Int), TYPE(   Int2), TYPE(   Int3), TYPE(   Int4),
155     TYPE(  UInt), TYPE(  UInt2), TYPE(  UInt3), TYPE(  UInt4),
156     TYPE( Short), TYPE( Short2), TYPE( Short3), TYPE( Short4),
157     TYPE(UShort), TYPE(UShort2), TYPE(UShort3), TYPE(UShort4),
158     TYPE(  Bool), TYPE(  Bool2), TYPE(  Bool3), TYPE(  Bool4),
159 
160     TYPE(Float2x2), TYPE(Float2x3), TYPE(Float2x4),
161     TYPE(Float3x2), TYPE(Float3x3), TYPE(Float3x4),
162     TYPE(Float4x2), TYPE(Float4x3), TYPE(Float4x4),
163 
164     TYPE(Half2x2),  TYPE(Half2x3),  TYPE(Half2x4),
165     TYPE(Half3x2),  TYPE(Half3x3),  TYPE(Half3x4),
166     TYPE(Half4x2),  TYPE(Half4x3),  TYPE(Half4x4),
167 
168     TYPE(SquareMat), TYPE(SquareHMat),
169     TYPE(Mat),       TYPE(HMat),
170 
171     // TODO(skia:12349): generic short/ushort
172     TYPE(GenType),   TYPE(GenIType), TYPE(GenUType),
173     TYPE(GenHType),   /* (GenSType)      (GenUSType) */
174     TYPE(GenBType),
175 
176     TYPE(Vec),     TYPE(IVec),     TYPE(UVec),
177     TYPE(HVec),    TYPE(SVec),     TYPE(USVec),
178     TYPE(BVec),
179 
180     TYPE(ColorFilter),
181     TYPE(Shader),
182     TYPE(Blender),
183 };
184 
185 inline static constexpr BuiltinTypePtr kPrivateTypes[] = {
186     TYPE(Sampler1D), TYPE(Sampler2D), TYPE(Sampler3D),
187     TYPE(SamplerExternalOES),
188     TYPE(Sampler2DRect),
189 
190     TYPE(ISampler2D),
191     TYPE(SubpassInput), TYPE(SubpassInputMS),
192 
193     TYPE(Sampler),
194     TYPE(Texture2D),
195 };
196 
197 #undef TYPE
198 
makeRootSymbolTable()199 std::shared_ptr<SymbolTable> Compiler::makeRootSymbolTable() {
200     auto rootSymbolTable = std::make_shared<SymbolTable>(*fContext, /*builtin=*/true);
201 
202     for (BuiltinTypePtr rootType : kRootTypes) {
203         rootSymbolTable->addWithoutOwnership((fContext->fTypes.*rootType).get());
204     }
205 
206     return rootSymbolTable;
207 }
208 
makePrivateSymbolTable(std::shared_ptr<SymbolTable> parent)209 std::shared_ptr<SymbolTable> Compiler::makePrivateSymbolTable(std::shared_ptr<SymbolTable> parent) {
210     auto privateSymbolTable = std::make_shared<SymbolTable>(parent, /*builtin=*/true);
211 
212     for (BuiltinTypePtr privateType : kPrivateTypes) {
213         privateSymbolTable->addWithoutOwnership((fContext->fTypes.*privateType).get());
214     }
215 
216     // sk_Caps is "builtin", but all references to it are resolved to Settings, so we don't need to
217     // treat it as builtin (ie, no need to clone it into the Program).
218     privateSymbolTable->add(std::make_unique<Variable>(/*line=*/-1,
219                                                        fCoreModifiers.add(Modifiers{}),
220                                                        "sk_Caps",
221                                                        fContext->fTypes.fSkCaps.get(),
222                                                        /*builtin=*/false,
223                                                        Variable::Storage::kGlobal));
224     return privateSymbolTable;
225 }
226 
loadGPUModule()227 const ParsedModule& Compiler::loadGPUModule() {
228     if (!fGPUModule.fSymbols) {
229         fGPUModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(gpu), fPrivateModule);
230     }
231     return fGPUModule;
232 }
233 
loadFragmentModule()234 const ParsedModule& Compiler::loadFragmentModule() {
235     if (!fFragmentModule.fSymbols) {
236         fFragmentModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(frag),
237                                             this->loadGPUModule());
238     }
239     return fFragmentModule;
240 }
241 
loadVertexModule()242 const ParsedModule& Compiler::loadVertexModule() {
243     if (!fVertexModule.fSymbols) {
244         fVertexModule = this->parseModule(ProgramKind::kVertex, MODULE_DATA(vert),
245                                           this->loadGPUModule());
246     }
247     return fVertexModule;
248 }
249 
add_glsl_type_aliases(SkSL::SymbolTable * symbols,const SkSL::BuiltinTypes & types)250 static void add_glsl_type_aliases(SkSL::SymbolTable* symbols, const SkSL::BuiltinTypes& types) {
251     // Add some aliases to the runtime effect modules so that it's friendlier, and more like GLSL.
252     symbols->addAlias("vec2", types.fFloat2.get());
253     symbols->addAlias("vec3", types.fFloat3.get());
254     symbols->addAlias("vec4", types.fFloat4.get());
255 
256     symbols->addAlias("ivec2", types.fInt2.get());
257     symbols->addAlias("ivec3", types.fInt3.get());
258     symbols->addAlias("ivec4", types.fInt4.get());
259 
260     symbols->addAlias("bvec2", types.fBool2.get());
261     symbols->addAlias("bvec3", types.fBool3.get());
262     symbols->addAlias("bvec4", types.fBool4.get());
263 
264     symbols->addAlias("mat2", types.fFloat2x2.get());
265     symbols->addAlias("mat3", types.fFloat3x3.get());
266     symbols->addAlias("mat4", types.fFloat4x4.get());
267 
268     // Alias every private type to "invalid". This will prevent code from using built-in names like
269     // `sampler2D` as variable names.
270     for (BuiltinTypePtr privateType : kPrivateTypes) {
271         symbols->addAlias((types.*privateType)->name(), types.fInvalid.get());
272     }
273 }
274 
loadPublicModule()275 const ParsedModule& Compiler::loadPublicModule() {
276     if (!fPublicModule.fSymbols) {
277         fPublicModule = this->parseModule(ProgramKind::kGeneric, MODULE_DATA(public), fRootModule);
278         add_glsl_type_aliases(fPublicModule.fSymbols.get(), fContext->fTypes);
279     }
280     return fPublicModule;
281 }
282 
loadRuntimeShaderModule()283 const ParsedModule& Compiler::loadRuntimeShaderModule() {
284     if (!fRuntimeShaderModule.fSymbols) {
285         fRuntimeShaderModule = this->parseModule(
286                 ProgramKind::kRuntimeShader, MODULE_DATA(rt_shader), this->loadPublicModule());
287     }
288     return fRuntimeShaderModule;
289 }
290 
moduleForProgramKind(ProgramKind kind)291 const ParsedModule& Compiler::moduleForProgramKind(ProgramKind kind) {
292     switch (kind) {
293         case ProgramKind::kVertex:             return this->loadVertexModule();        break;
294         case ProgramKind::kFragment:           return this->loadFragmentModule();      break;
295         case ProgramKind::kRuntimeColorFilter: return this->loadPublicModule();        break;
296         case ProgramKind::kRuntimeShader:      return this->loadRuntimeShaderModule(); break;
297         case ProgramKind::kRuntimeBlender:     return this->loadPublicModule();        break;
298         case ProgramKind::kGeneric:            return this->loadPublicModule();        break;
299     }
300     SkUNREACHABLE;
301 }
302 
loadModule(ProgramKind kind,ModuleData data,std::shared_ptr<SymbolTable> base,bool dehydrate)303 LoadedModule Compiler::loadModule(ProgramKind kind,
304                                   ModuleData data,
305                                   std::shared_ptr<SymbolTable> base,
306                                   bool dehydrate) {
307     if (dehydrate) {
308         // NOTE: This is a workaround. When dehydrating includes, skslc doesn't know which module
309         // it's preparing, nor what the correct base module is. We can't use 'Root', because many
310         // GPU intrinsics reference private types, like samplers or textures. Today, 'Private' does
311         // contain the union of all known types, so this is safe. If we ever have types that only
312         // exist in 'Public' (for example), this logic needs to be smarter (by choosing the correct
313         // base for the module we're compiling).
314         base = fPrivateModule.fSymbols;
315     }
316     SkASSERT(base);
317 
318     // Put the core-module modifier pool into the context.
319     AutoModifiersPool autoPool(fContext, &fCoreModifiers);
320 
321     // Built-in modules always use default program settings.
322     Program::Settings settings;
323     settings.fReplaceSettings = !dehydrate;
324 
325 #if defined(SKSL_STANDALONE)
326     SkASSERT(this->errorCount() == 0);
327     SkASSERT(data.fPath);
328     std::ifstream in(data.fPath);
329     String text{std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()};
330     if (in.rdstate()) {
331         printf("error reading %s\n", data.fPath);
332         abort();
333     }
334     ParsedModule baseModule = {base, /*fIntrinsics=*/nullptr};
335     LoadedModule result = DSLParser(this, settings, kind,
336             std::move(text)).moduleInheritingFrom(std::move(baseModule));
337     if (this->errorCount()) {
338         printf("Unexpected errors: %s\n", this->fErrorText.c_str());
339         SkDEBUGFAILF("%s %s\n", data.fPath, this->fErrorText.c_str());
340     }
341 #else
342     ProgramConfig config;
343     config.fIsBuiltinCode = true;
344     config.fKind = kind;
345     config.fSettings = settings;
346     AutoProgramConfig autoConfig(fContext, &config);
347     SkASSERT(data.fData && (data.fSize != 0));
348     Rehydrator rehydrator(fContext.get(), base, data.fData, data.fSize);
349     LoadedModule result = { kind, rehydrator.symbolTable(), rehydrator.elements() };
350 #endif
351 
352     return result;
353 }
354 
parseModule(ProgramKind kind,ModuleData data,const ParsedModule & base)355 ParsedModule Compiler::parseModule(ProgramKind kind, ModuleData data, const ParsedModule& base) {
356     LoadedModule module = this->loadModule(kind, data, base.fSymbols, /*dehydrate=*/false);
357     this->optimize(module);
358 
359     // For modules that just declare (but don't define) intrinsic functions, there will be no new
360     // program elements. In that case, we can share our parent's intrinsic map:
361     if (module.fElements.empty()) {
362         return ParsedModule{module.fSymbols, base.fIntrinsics};
363     }
364 
365     auto intrinsics = std::make_shared<IntrinsicMap>(base.fIntrinsics.get());
366 
367     // Now, transfer all of the program elements to an intrinsic map. This maps certain types of
368     // global objects to the declaring ProgramElement.
369     for (std::unique_ptr<ProgramElement>& element : module.fElements) {
370         switch (element->kind()) {
371             case ProgramElement::Kind::kFunction: {
372                 const FunctionDefinition& f = element->as<FunctionDefinition>();
373                 SkASSERT(f.declaration().isBuiltin());
374                 intrinsics->insertOrDie(f.declaration().description(), std::move(element));
375                 break;
376             }
377             case ProgramElement::Kind::kFunctionPrototype: {
378                 // These are already in the symbol table.
379                 break;
380             }
381             case ProgramElement::Kind::kGlobalVar: {
382                 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
383                 const Variable& var = global.declaration()->as<VarDeclaration>().var();
384                 SkASSERT(var.isBuiltin());
385                 intrinsics->insertOrDie(String(var.name()), std::move(element));
386                 break;
387             }
388             case ProgramElement::Kind::kInterfaceBlock: {
389                 const Variable& var = element->as<InterfaceBlock>().variable();
390                 SkASSERT(var.isBuiltin());
391                 intrinsics->insertOrDie(String(var.name()), std::move(element));
392                 break;
393             }
394             default:
395                 printf("Unsupported element: %s\n", element->description().c_str());
396                 SkASSERT(false);
397                 break;
398         }
399     }
400 
401     return ParsedModule{module.fSymbols, std::move(intrinsics)};
402 }
403 
convertProgram(ProgramKind kind,String text,Program::Settings settings)404 std::unique_ptr<Program> Compiler::convertProgram(ProgramKind kind,
405                                                   String text,
406                                                   Program::Settings settings) {
407     TRACE_EVENT0("skia.shaders", "SkSL::Compiler::convertProgram");
408 
409     SkASSERT(!settings.fExternalFunctions || (kind == ProgramKind::kGeneric));
410 
411     // Honor our optimization-override flags.
412     switch (sOptimizer) {
413         case OverrideFlag::kDefault:
414             break;
415         case OverrideFlag::kOff:
416             settings.fOptimize = false;
417             break;
418         case OverrideFlag::kOn:
419             settings.fOptimize = true;
420             break;
421     }
422 
423     switch (sInliner) {
424         case OverrideFlag::kDefault:
425             break;
426         case OverrideFlag::kOff:
427             settings.fInlineThreshold = 0;
428             break;
429         case OverrideFlag::kOn:
430             if (settings.fInlineThreshold == 0) {
431                 settings.fInlineThreshold = kDefaultInlineThreshold;
432             }
433             break;
434     }
435 
436     // Disable optimization settings that depend on a parent setting which has been disabled.
437     settings.fInlineThreshold *= (int)settings.fOptimize;
438     settings.fRemoveDeadFunctions &= settings.fOptimize;
439     settings.fRemoveDeadVariables &= settings.fOptimize;
440 
441     // Runtime effects always allow narrowing conversions.
442     if (ProgramConfig::IsRuntimeEffect(kind)) {
443         settings.fAllowNarrowingConversions = true;
444     }
445 
446     this->resetErrors();
447     fInliner.reset();
448 
449     settings.fDSLMangling = false;
450     return DSLParser(this, settings, kind, std::move(text)).program();
451 }
452 
convertIdentifier(int line,skstd::string_view name)453 std::unique_ptr<Expression> Compiler::convertIdentifier(int line, skstd::string_view name) {
454     const Symbol* result = (*fSymbolTable)[name];
455     if (!result) {
456         this->errorReporter().error(line, "unknown identifier '" + name + "'");
457         return nullptr;
458     }
459     switch (result->kind()) {
460         case Symbol::Kind::kFunctionDeclaration: {
461             std::vector<const FunctionDeclaration*> f = {
462                 &result->as<FunctionDeclaration>()
463             };
464             return std::make_unique<FunctionReference>(*fContext, line, f);
465         }
466         case Symbol::Kind::kUnresolvedFunction: {
467             const UnresolvedFunction* f = &result->as<UnresolvedFunction>();
468             return std::make_unique<FunctionReference>(*fContext, line, f->functions());
469         }
470         case Symbol::Kind::kVariable: {
471             const Variable* var = &result->as<Variable>();
472             const Modifiers& modifiers = var->modifiers();
473             switch (modifiers.fLayout.fBuiltin) {
474                 case SK_FRAGCOORD_BUILTIN:
475                     if (fContext->fCaps.canUseFragCoord()) {
476                         ThreadContext::Inputs().fUseFlipRTUniform = true;
477                     }
478                     break;
479                 case SK_CLOCKWISE_BUILTIN:
480                     ThreadContext::Inputs().fUseFlipRTUniform = true;
481                     break;
482             }
483             // default to kRead_RefKind; this will be corrected later if the variable is written to
484             return VariableReference::Make(line, var, VariableReference::RefKind::kRead);
485         }
486         case Symbol::Kind::kField: {
487             const Field* field = &result->as<Field>();
488             auto base = VariableReference::Make(line, &field->owner(),
489                                                 VariableReference::RefKind::kRead);
490             return FieldAccess::Make(*fContext, std::move(base), field->fieldIndex(),
491                                      FieldAccess::OwnerKind::kAnonymousInterfaceBlock);
492         }
493         case Symbol::Kind::kType: {
494             return TypeReference::Convert(*fContext, line, &result->as<Type>());
495         }
496         case Symbol::Kind::kExternal: {
497             const ExternalFunction* r = &result->as<ExternalFunction>();
498             return std::make_unique<ExternalFunctionReference>(line, r);
499         }
500         default:
501             SK_ABORT("unsupported symbol type %d\n", (int) result->kind());
502     }
503 }
504 
optimize(LoadedModule & module)505 bool Compiler::optimize(LoadedModule& module) {
506     SkASSERT(!this->errorCount());
507 
508     // Create a temporary program configuration with default settings.
509     ProgramConfig config;
510     config.fIsBuiltinCode = true;
511     config.fKind = module.fKind;
512     AutoProgramConfig autoConfig(fContext, &config);
513     AutoModifiersPool autoPool(fContext, &fCoreModifiers);
514 
515     // Reset the Inliner.
516     fInliner.reset();
517 
518     std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
519 
520     while (this->errorCount() == 0) {
521         // Perform inline-candidate analysis and inline any functions deemed suitable.
522         if (!this->runInliner(module.fElements, module.fSymbols, usage.get())) {
523             break;
524         }
525     }
526     return this->errorCount() == 0;
527 }
528 
optimize(Program & program)529 bool Compiler::optimize(Program& program) {
530     // The optimizer only needs to run when it is enabled.
531     if (!program.fConfig->fSettings.fOptimize) {
532         return true;
533     }
534 
535     SkASSERT(!this->errorCount());
536     ProgramUsage* usage = program.fUsage.get();
537 
538     if (this->errorCount() == 0) {
539         // Run the inliner only once; it is expensive! Multiple passes can occasionally shake out
540         // more wins, but it's diminishing returns.
541         this->runInliner(program.fOwnedElements, program.fSymbols, usage);
542 
543         // Unreachable code can confuse some drivers, so it's worth removing. (skia:12012)
544         Transform::EliminateUnreachableCode(program, usage);
545 
546         while (Transform::EliminateDeadFunctions(program, usage)) {
547             // Removing dead functions may cause more functions to become unreferenced. Try again.
548         }
549         while (Transform::EliminateDeadLocalVariables(program, usage)) {
550             // Removing dead variables may cause more variables to become unreferenced. Try again.
551         }
552 
553         Transform::EliminateDeadGlobalVariables(program, usage);
554     }
555 
556     return this->errorCount() == 0;
557 }
558 
runInliner(const std::vector<std::unique_ptr<ProgramElement>> & elements,std::shared_ptr<SymbolTable> symbols,ProgramUsage * usage)559 bool Compiler::runInliner(const std::vector<std::unique_ptr<ProgramElement>>& elements,
560                           std::shared_ptr<SymbolTable> symbols,
561                           ProgramUsage* usage) {
562     // The program's SymbolTable was taken out of fSymbolTable when the program was bundled, but
563     // the inliner relies (indirectly) on having a valid SymbolTable.
564     // In particular, inlining can turn a non-optimizable expression like `normalize(myVec)` into
565     // `normalize(vec2(7))`, which is now optimizable. The optimizer can use DSL to simplify this
566     // expression--e.g., in the case of normalize, using DSL's Length(). The DSL relies on
567     // convertIdentifier() to look up `length`. convertIdentifier() needs a valid symbol table to
568     // find the declaration of `length`. To allow this chain of events to succeed, we re-insert the
569     // program's symbol table temporarily.
570     SkASSERT(!fSymbolTable);
571     fSymbolTable = symbols;
572 
573     bool result = fInliner.analyze(elements, symbols, usage);
574 
575     fSymbolTable = nullptr;
576     return result;
577 }
578 
finalize(Program & program)579 bool Compiler::finalize(Program& program) {
580     // Do a pass looking for @if/@switch statements that didn't optimize away, or dangling
581     // FunctionReference or TypeReference expressions. Report these as errors.
582     Analysis::VerifyStaticTestsAndExpressions(program);
583 
584     // Verify that the program conforms to ES2 limitations.
585     if (fContext->fConfig->strictES2Mode() && this->errorCount() == 0) {
586         // Enforce Appendix A, Section 5 of the GLSL ES 1.00 spec -- Indexing. This logic assumes
587         // that all loops meet the criteria of Section 4, and if they don't, could crash.
588         for (const auto& pe : program.fOwnedElements) {
589             Analysis::ValidateIndexingForES2(*pe, this->errorReporter());
590         }
591         // Verify that the program size is reasonable after unrolling and inlining. This also
592         // issues errors for static recursion and overly-deep function-call chains.
593         Analysis::CheckProgramUnrolledSize(program);
594     }
595 
596     return this->errorCount() == 0;
597 }
598 
599 #if defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
600 
toSPIRV(Program & program,OutputStream & out)601 bool Compiler::toSPIRV(Program& program, OutputStream& out) {
602     TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toSPIRV");
603     AutoSource as(this, program.fSource->c_str());
604     ProgramSettings settings;
605     settings.fDSLUseMemoryPool = false;
606     dsl::Start(this, program.fConfig->fKind, settings);
607     dsl::SetErrorReporter(&fErrorReporter);
608     fSymbolTable = program.fSymbols;
609 #ifdef SK_ENABLE_SPIRV_VALIDATION
610     StringStream buffer;
611     SPIRVCodeGenerator cg(fContext.get(), &program, &buffer);
612     bool result = cg.generateCode();
613     if (result && program.fConfig->fSettings.fValidateSPIRV) {
614         spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
615         const String& data = buffer.str();
616         SkASSERT(0 == data.size() % 4);
617         String errors;
618         auto dumpmsg = [&errors](spv_message_level_t, const char*, const spv_position_t&,
619                                  const char* m) {
620             errors.appendf("SPIR-V validation error: %s\n", m);
621         };
622         tools.SetMessageConsumer(dumpmsg);
623 
624         // Verify that the SPIR-V we produced is valid. At runtime, we will abort() with a message
625         // explaining the error. In standalone mode (skslc), we will send the message, plus the
626         // entire disassembled SPIR-V (for easier context & debugging) as *our* error message.
627         result = tools.Validate((const uint32_t*) data.c_str(), data.size() / 4);
628 
629         if (!result) {
630 #if defined(SKSL_STANDALONE)
631             // Convert the string-stream to a SPIR-V disassembly.
632             std::string disassembly;
633             if (tools.Disassemble((const uint32_t*)data.data(), data.size() / 4, &disassembly)) {
634                 errors.append(disassembly);
635             }
636             this->errorReporter().error(-1, errors);
637             this->errorReporter().reportPendingErrors(PositionInfo());
638 #else
639             SkDEBUGFAILF("%s", errors.c_str());
640 #endif
641         }
642         out.write(data.c_str(), data.size());
643     }
644 #else
645     SPIRVCodeGenerator cg(fContext.get(), &program, &out);
646     bool result = cg.generateCode();
647 #endif
648     dsl::End();
649     return result;
650 }
651 
toSPIRV(Program & program,String * out)652 bool Compiler::toSPIRV(Program& program, String* out) {
653     StringStream buffer;
654     bool result = this->toSPIRV(program, buffer);
655     if (result) {
656         *out = buffer.str();
657     }
658     return result;
659 }
660 
toGLSL(Program & program,OutputStream & out)661 bool Compiler::toGLSL(Program& program, OutputStream& out) {
662     TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toGLSL");
663     AutoSource as(this, program.fSource->c_str());
664     GLSLCodeGenerator cg(fContext.get(), &program, &out);
665     bool result = cg.generateCode();
666     return result;
667 }
668 
toGLSL(Program & program,String * out)669 bool Compiler::toGLSL(Program& program, String* out) {
670     StringStream buffer;
671     bool result = this->toGLSL(program, buffer);
672     if (result) {
673         *out = buffer.str();
674     }
675     return result;
676 }
677 
toHLSL(Program & program,String * out)678 bool Compiler::toHLSL(Program& program, String* out) {
679     String spirv;
680     if (!this->toSPIRV(program, &spirv)) {
681         return false;
682     }
683 
684     return SPIRVtoHLSL(spirv, out);
685 }
686 
toMetal(Program & program,OutputStream & out)687 bool Compiler::toMetal(Program& program, OutputStream& out) {
688     TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toMetal");
689     AutoSource as(this, program.fSource->c_str());
690     MetalCodeGenerator cg(fContext.get(), &program, &out);
691     bool result = cg.generateCode();
692     return result;
693 }
694 
toMetal(Program & program,String * out)695 bool Compiler::toMetal(Program& program, String* out) {
696     StringStream buffer;
697     bool result = this->toMetal(program, buffer);
698     if (result) {
699         *out = buffer.str();
700     }
701     return result;
702 }
703 
704 #endif // defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
705 
handleError(skstd::string_view msg,PositionInfo pos)706 void Compiler::handleError(skstd::string_view msg, PositionInfo pos) {
707     fErrorText += "error: " + (pos.line() >= 1 ? to_string(pos.line()) + ": " : "") + msg + "\n";
708 }
709 
errorText(bool showCount)710 String Compiler::errorText(bool showCount) {
711     if (showCount) {
712         this->writeErrorCount();
713     }
714     String result = fErrorText;
715     this->resetErrors();
716     return result;
717 }
718 
writeErrorCount()719 void Compiler::writeErrorCount() {
720     int count = this->errorCount();
721     if (count) {
722         fErrorText += to_string(count) + " error";
723         if (count > 1) {
724             fErrorText += "s";
725         }
726         fErrorText += "\n";
727     }
728 }
729 
730 }  // namespace SkSL
731