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