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