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