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