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 "src/core/SkScopeExit.h"
14 #include "src/core/SkTraceEvent.h"
15 #include "src/sksl/SkSLAnalysis.h"
16 #include "src/sksl/SkSLConstantFolder.h"
17 #include "src/sksl/SkSLIRGenerator.h"
18 #include "src/sksl/SkSLOperators.h"
19 #include "src/sksl/SkSLProgramSettings.h"
20 #include "src/sksl/SkSLRehydrator.h"
21 #include "src/sksl/codegen/SkSLCPPCodeGenerator.h"
22 #include "src/sksl/codegen/SkSLDSLCPPCodeGenerator.h"
23 #include "src/sksl/codegen/SkSLGLSLCodeGenerator.h"
24 #include "src/sksl/codegen/SkSLHCodeGenerator.h"
25 #include "src/sksl/codegen/SkSLMetalCodeGenerator.h"
26 #include "src/sksl/codegen/SkSLSPIRVCodeGenerator.h"
27 #include "src/sksl/codegen/SkSLSPIRVtoHLSL.h"
28 #include "src/sksl/ir/SkSLEnum.h"
29 #include "src/sksl/ir/SkSLExpression.h"
30 #include "src/sksl/ir/SkSLExpressionStatement.h"
31 #include "src/sksl/ir/SkSLFunctionCall.h"
32 #include "src/sksl/ir/SkSLIntLiteral.h"
33 #include "src/sksl/ir/SkSLModifiersDeclaration.h"
34 #include "src/sksl/ir/SkSLNop.h"
35 #include "src/sksl/ir/SkSLSymbolTable.h"
36 #include "src/sksl/ir/SkSLTernaryExpression.h"
37 #include "src/sksl/ir/SkSLUnresolvedFunction.h"
38 #include "src/sksl/ir/SkSLVarDeclarations.h"
39 #include "src/utils/SkBitSet.h"
40
41 #include <fstream>
42
43 #if !defined(SKSL_STANDALONE) & SK_SUPPORT_GPU
44 #include "include/gpu/GrContextOptions.h"
45 #include "src/gpu/GrShaderCaps.h"
46 #endif
47
48 #ifdef SK_ENABLE_SPIRV_VALIDATION
49 #include "spirv-tools/libspirv.hpp"
50 #endif
51
52 #if defined(SKSL_STANDALONE)
53
54 // In standalone mode, we load the textual sksl source files. GN generates or copies these files
55 // to the skslc executable directory. The "data" in this mode is just the filename.
56 #define MODULE_DATA(name) MakeModulePath("sksl_" #name ".sksl")
57
58 #else
59
60 // At runtime, we load the dehydrated sksl data files. The data is a (pointer, size) pair.
61 #include "src/sksl/generated/sksl_fp.dehydrated.sksl"
62 #include "src/sksl/generated/sksl_frag.dehydrated.sksl"
63 #include "src/sksl/generated/sksl_geom.dehydrated.sksl"
64 #include "src/sksl/generated/sksl_gpu.dehydrated.sksl"
65 #include "src/sksl/generated/sksl_public.dehydrated.sksl"
66 #include "src/sksl/generated/sksl_rt_colorfilter.dehydrated.sksl"
67 #include "src/sksl/generated/sksl_rt_shader.dehydrated.sksl"
68 #include "src/sksl/generated/sksl_vert.dehydrated.sksl"
69
70 #define MODULE_DATA(name) MakeModuleData(SKSL_INCLUDE_sksl_##name,\
71 SKSL_INCLUDE_sksl_##name##_LENGTH)
72
73 #endif
74
75 namespace SkSL {
76
77 // These flags allow tools like Viewer or Nanobench to override the compiler's ProgramSettings.
78 Compiler::OverrideFlag Compiler::sOptimizer = OverrideFlag::kDefault;
79 Compiler::OverrideFlag Compiler::sInliner = OverrideFlag::kDefault;
80
81 using RefKind = VariableReference::RefKind;
82
83 class AutoSource {
84 public:
AutoSource(Compiler * compiler,const String * source)85 AutoSource(Compiler* compiler, const String* source)
86 : fCompiler(compiler) {
87 SkASSERT(!fCompiler->fSource);
88 fCompiler->fSource = source;
89 }
90
~AutoSource()91 ~AutoSource() {
92 fCompiler->fSource = nullptr;
93 }
94
95 Compiler* fCompiler;
96 };
97
98 class AutoProgramConfig {
99 public:
AutoProgramConfig(std::shared_ptr<Context> & context,ProgramConfig * config)100 AutoProgramConfig(std::shared_ptr<Context>& context, ProgramConfig* config)
101 : fContext(context.get()) {
102 SkASSERT(!fContext->fConfig);
103 fContext->fConfig = config;
104 }
105
~AutoProgramConfig()106 ~AutoProgramConfig() {
107 fContext->fConfig = nullptr;
108 }
109
110 Context* fContext;
111 };
112
113 class AutoModifiersPool {
114 public:
AutoModifiersPool(std::shared_ptr<Context> & context,ModifiersPool * modifiersPool)115 AutoModifiersPool(std::shared_ptr<Context>& context, ModifiersPool* modifiersPool)
116 : fContext(context.get()) {
117 SkASSERT(!fContext->fModifiersPool);
118 fContext->fModifiersPool = modifiersPool;
119 }
120
~AutoModifiersPool()121 ~AutoModifiersPool() {
122 fContext->fModifiersPool = nullptr;
123 }
124
125 Context* fContext;
126 };
127
Compiler(const ShaderCapsClass * caps)128 Compiler::Compiler(const ShaderCapsClass* caps)
129 : fContext(std::make_shared<Context>(/*errors=*/*this, *caps))
130 , fInliner(fContext.get()) {
131 SkASSERT(caps);
132 fRootSymbolTable = std::make_shared<SymbolTable>(this, /*builtin=*/true);
133 fPrivateSymbolTable = std::make_shared<SymbolTable>(fRootSymbolTable, /*builtin=*/true);
134 fIRGenerator = std::make_unique<IRGenerator>(fContext.get());
135
136 #define TYPE(t) fContext->fTypes.f ## t .get()
137
138 const SkSL::Symbol* rootTypes[] = {
139 TYPE(Void),
140
141 TYPE( Float), TYPE( Float2), TYPE( Float3), TYPE( Float4),
142 TYPE( Half), TYPE( Half2), TYPE( Half3), TYPE( Half4),
143 TYPE( Int), TYPE( Int2), TYPE( Int3), TYPE( Int4),
144 TYPE( Bool), TYPE( Bool2), TYPE( Bool3), TYPE( Bool4),
145
146 TYPE(Float2x2), TYPE(Float3x3), TYPE(Float4x4),
147 TYPE( Half2x2), TYPE( Half3x3), TYPE(Half4x4),
148
149 TYPE(SquareMat), TYPE(SquareHMat),
150
151 TYPE(GenType), TYPE(GenHType), TYPE(GenIType), TYPE(GenBType),
152 TYPE(Vec), TYPE(HVec), TYPE(IVec), TYPE(BVec),
153
154 TYPE(ColorFilter),
155 TYPE(Shader),
156 };
157
158 const SkSL::Symbol* privateTypes[] = {
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
163 TYPE(GenUType), TYPE(UVec),
164 TYPE(SVec), TYPE(USVec),
165
166 TYPE(Float2x3), TYPE(Float2x4),
167 TYPE(Float3x2), TYPE(Float3x4),
168 TYPE(Float4x2), TYPE(Float4x3),
169
170 TYPE(Half2x3), TYPE(Half2x4),
171 TYPE(Half3x2), TYPE(Half3x4),
172 TYPE(Half4x2), TYPE(Half4x3),
173
174 TYPE(Mat), TYPE(HMat),
175
176 TYPE(Sampler1D), TYPE(Sampler2D), TYPE(Sampler3D),
177 TYPE(SamplerExternalOES),
178 TYPE(Sampler2DRect),
179
180 TYPE(ISampler2D),
181 TYPE(SubpassInput), TYPE(SubpassInputMS),
182
183 TYPE(Sampler),
184 TYPE(Texture2D),
185
186 TYPE(FragmentProcessor),
187 };
188
189 for (const SkSL::Symbol* type : rootTypes) {
190 fRootSymbolTable->addWithoutOwnership(type);
191 }
192 for (const SkSL::Symbol* type : privateTypes) {
193 fPrivateSymbolTable->addWithoutOwnership(type);
194 }
195
196 #undef TYPE
197
198 // sk_Caps is "builtin", but all references to it are resolved to Settings, so we don't need to
199 // treat it as builtin (ie, no need to clone it into the Program).
200 fPrivateSymbolTable->add(std::make_unique<Variable>(/*offset=*/-1,
201 fCoreModifiers.add(Modifiers{}),
202 "sk_Caps",
203 fContext->fTypes.fSkCaps.get(),
204 /*builtin=*/false,
205 Variable::Storage::kGlobal));
206
207 fRootModule = {fRootSymbolTable, /*fIntrinsics=*/nullptr};
208 fPrivateModule = {fPrivateSymbolTable, /*fIntrinsics=*/nullptr};
209 }
210
~Compiler()211 Compiler::~Compiler() {}
212
loadGPUModule()213 const ParsedModule& Compiler::loadGPUModule() {
214 if (!fGPUModule.fSymbols) {
215 fGPUModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(gpu), fPrivateModule);
216 }
217 return fGPUModule;
218 }
219
loadFragmentModule()220 const ParsedModule& Compiler::loadFragmentModule() {
221 if (!fFragmentModule.fSymbols) {
222 fFragmentModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(frag),
223 this->loadGPUModule());
224 }
225 return fFragmentModule;
226 }
227
loadVertexModule()228 const ParsedModule& Compiler::loadVertexModule() {
229 if (!fVertexModule.fSymbols) {
230 fVertexModule = this->parseModule(ProgramKind::kVertex, MODULE_DATA(vert),
231 this->loadGPUModule());
232 }
233 return fVertexModule;
234 }
235
loadGeometryModule()236 const ParsedModule& Compiler::loadGeometryModule() {
237 if (!fGeometryModule.fSymbols) {
238 fGeometryModule = this->parseModule(ProgramKind::kGeometry, MODULE_DATA(geom),
239 this->loadGPUModule());
240 }
241 return fGeometryModule;
242 }
243
loadFPModule()244 const ParsedModule& Compiler::loadFPModule() {
245 if (!fFPModule.fSymbols) {
246 fFPModule = this->parseModule(ProgramKind::kFragmentProcessor, MODULE_DATA(fp),
247 this->loadGPUModule());
248 }
249 return fFPModule;
250 }
251
loadPublicModule()252 const ParsedModule& Compiler::loadPublicModule() {
253 if (!fPublicModule.fSymbols) {
254 fPublicModule = this->parseModule(ProgramKind::kGeneric, MODULE_DATA(public), fRootModule);
255 }
256 return fPublicModule;
257 }
258
add_glsl_type_aliases(SkSL::SymbolTable * symbols,const SkSL::BuiltinTypes & types)259 static void add_glsl_type_aliases(SkSL::SymbolTable* symbols, const SkSL::BuiltinTypes& types) {
260 // Add some aliases to the runtime effect modules so that it's friendlier, and more like GLSL
261 symbols->addAlias("vec2", types.fFloat2.get());
262 symbols->addAlias("vec3", types.fFloat3.get());
263 symbols->addAlias("vec4", types.fFloat4.get());
264
265 symbols->addAlias("ivec2", types.fInt2.get());
266 symbols->addAlias("ivec3", types.fInt3.get());
267 symbols->addAlias("ivec4", types.fInt4.get());
268
269 symbols->addAlias("bvec2", types.fBool2.get());
270 symbols->addAlias("bvec3", types.fBool3.get());
271 symbols->addAlias("bvec4", types.fBool4.get());
272
273 symbols->addAlias("mat2", types.fFloat2x2.get());
274 symbols->addAlias("mat3", types.fFloat3x3.get());
275 symbols->addAlias("mat4", types.fFloat4x4.get());
276 }
277
loadRuntimeColorFilterModule()278 const ParsedModule& Compiler::loadRuntimeColorFilterModule() {
279 if (!fRuntimeColorFilterModule.fSymbols) {
280 fRuntimeColorFilterModule = this->parseModule(ProgramKind::kRuntimeColorFilter,
281 MODULE_DATA(rt_colorfilter),
282 this->loadPublicModule());
283 add_glsl_type_aliases(fRuntimeColorFilterModule.fSymbols.get(), fContext->fTypes);
284 }
285 return fRuntimeColorFilterModule;
286 }
287
loadRuntimeShaderModule()288 const ParsedModule& Compiler::loadRuntimeShaderModule() {
289 if (!fRuntimeShaderModule.fSymbols) {
290 fRuntimeShaderModule = this->parseModule(
291 ProgramKind::kRuntimeShader, MODULE_DATA(rt_shader), this->loadPublicModule());
292 add_glsl_type_aliases(fRuntimeShaderModule.fSymbols.get(), fContext->fTypes);
293 }
294 return fRuntimeShaderModule;
295 }
296
moduleForProgramKind(ProgramKind kind)297 const ParsedModule& Compiler::moduleForProgramKind(ProgramKind kind) {
298 switch (kind) {
299 case ProgramKind::kVertex: return this->loadVertexModule(); break;
300 case ProgramKind::kFragment: return this->loadFragmentModule(); break;
301 case ProgramKind::kGeometry: return this->loadGeometryModule(); break;
302 case ProgramKind::kFragmentProcessor: return this->loadFPModule(); break;
303 case ProgramKind::kRuntimeColorFilter: return this->loadRuntimeColorFilterModule(); break;
304 case ProgramKind::kRuntimeShader: return this->loadRuntimeShaderModule(); break;
305 case ProgramKind::kGeneric: return this->loadPublicModule(); break;
306 }
307 SkUNREACHABLE;
308 }
309
loadModule(ProgramKind kind,ModuleData data,std::shared_ptr<SymbolTable> base,bool dehydrate)310 LoadedModule Compiler::loadModule(ProgramKind kind,
311 ModuleData data,
312 std::shared_ptr<SymbolTable> base,
313 bool dehydrate) {
314 if (dehydrate) {
315 // NOTE: This is a workaround. When dehydrating includes, skslc doesn't know which module
316 // it's preparing, nor what the correct base module is. We can't use 'Root', because many
317 // GPU intrinsics reference private types, like samplers or textures. Today, 'Private' does
318 // contain the union of all known types, so this is safe. If we ever have types that only
319 // exist in 'Public' (for example), this logic needs to be smarter (by choosing the correct
320 // base for the module we're compiling).
321 base = fPrivateSymbolTable;
322 }
323 SkASSERT(base);
324
325 // Put the core-module modifier pool into the context.
326 AutoModifiersPool autoPool(fContext, &fCoreModifiers);
327
328 // Built-in modules always use default program settings.
329 ProgramConfig config;
330 config.fKind = kind;
331 config.fSettings.fReplaceSettings = !dehydrate;
332 AutoProgramConfig autoConfig(fContext, &config);
333
334 #if defined(SKSL_STANDALONE)
335 SkASSERT(data.fPath);
336 std::ifstream in(data.fPath);
337 String text{std::istreambuf_iterator<char>(in), std::istreambuf_iterator<char>()};
338 if (in.rdstate()) {
339 printf("error reading %s\n", data.fPath);
340 abort();
341 }
342 const String* source = fRootSymbolTable->takeOwnershipOfString(std::move(text));
343 AutoSource as(this, source);
344
345 ParsedModule baseModule = {base, /*fIntrinsics=*/nullptr};
346 IRGenerator::IRBundle ir = fIRGenerator->convertProgram(baseModule, /*isBuiltinCode=*/true,
347 source->c_str(), source->length(),
348 /*externalFunctions=*/nullptr);
349 SkASSERT(ir.fSharedElements.empty());
350 LoadedModule module = { kind, std::move(ir.fSymbolTable), std::move(ir.fElements) };
351 if (this->fErrorCount) {
352 printf("Unexpected errors: %s\n", this->fErrorText.c_str());
353 SkDEBUGFAILF("%s %s\n", data.fPath, this->fErrorText.c_str());
354 }
355 #else
356 SkASSERT(data.fData && (data.fSize != 0));
357 Rehydrator rehydrator(fContext.get(), base, data.fData, data.fSize);
358 LoadedModule module = { kind, rehydrator.symbolTable(), rehydrator.elements() };
359 #endif
360
361 return module;
362 }
363
parseModule(ProgramKind kind,ModuleData data,const ParsedModule & base)364 ParsedModule Compiler::parseModule(ProgramKind kind, ModuleData data, const ParsedModule& base) {
365 LoadedModule module = this->loadModule(kind, data, base.fSymbols, /*dehydrate=*/false);
366 this->optimize(module);
367
368 // For modules that just declare (but don't define) intrinsic functions, there will be no new
369 // program elements. In that case, we can share our parent's intrinsic map:
370 if (module.fElements.empty()) {
371 return ParsedModule{module.fSymbols, base.fIntrinsics};
372 }
373
374 auto intrinsics = std::make_shared<IRIntrinsicMap>(base.fIntrinsics.get());
375
376 // Now, transfer all of the program elements to an intrinsic map. This maps certain types of
377 // global objects to the declaring ProgramElement.
378 for (std::unique_ptr<ProgramElement>& element : module.fElements) {
379 switch (element->kind()) {
380 case ProgramElement::Kind::kFunction: {
381 const FunctionDefinition& f = element->as<FunctionDefinition>();
382 SkASSERT(f.declaration().isBuiltin());
383 intrinsics->insertOrDie(f.declaration().description(), std::move(element));
384 break;
385 }
386 case ProgramElement::Kind::kFunctionPrototype: {
387 // These are already in the symbol table.
388 break;
389 }
390 case ProgramElement::Kind::kEnum: {
391 const Enum& e = element->as<Enum>();
392 SkASSERT(e.isBuiltin());
393 intrinsics->insertOrDie(e.typeName(), std::move(element));
394 break;
395 }
396 case ProgramElement::Kind::kGlobalVar: {
397 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
398 const Variable& var = global.declaration()->as<VarDeclaration>().var();
399 SkASSERT(var.isBuiltin());
400 intrinsics->insertOrDie(var.name(), std::move(element));
401 break;
402 }
403 case ProgramElement::Kind::kInterfaceBlock: {
404 const Variable& var = element->as<InterfaceBlock>().variable();
405 SkASSERT(var.isBuiltin());
406 intrinsics->insertOrDie(var.name(), std::move(element));
407 break;
408 }
409 default:
410 printf("Unsupported element: %s\n", element->description().c_str());
411 SkASSERT(false);
412 break;
413 }
414 }
415
416 return ParsedModule{module.fSymbols, std::move(intrinsics)};
417 }
418
convertProgram(ProgramKind kind,String text,const Program::Settings & settings,const std::vector<std::unique_ptr<ExternalFunction>> * externalFunctions)419 std::unique_ptr<Program> Compiler::convertProgram(
420 ProgramKind kind,
421 String text,
422 const Program::Settings& settings,
423 const std::vector<std::unique_ptr<ExternalFunction>>* externalFunctions) {
424 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::convertProgram");
425
426 SkASSERT(!externalFunctions || (kind == ProgramKind::kGeneric));
427
428 // Loading and optimizing our base module might reset the inliner, so do that first,
429 // *then* configure the inliner with the settings for this program.
430 const ParsedModule& baseModule = this->moduleForProgramKind(kind);
431
432 // Put a fresh modifier pool into the context.
433 auto modifiersPool = std::make_unique<ModifiersPool>();
434 AutoModifiersPool autoPool(fContext, modifiersPool.get());
435
436 // Update our context to point to the program configuration for the duration of compilation.
437 auto config = std::make_unique<ProgramConfig>(ProgramConfig{kind, settings});
438 AutoProgramConfig autoConfig(fContext, config.get());
439
440 // Honor our optimization-override flags.
441 switch (sOptimizer) {
442 case OverrideFlag::kDefault:
443 break;
444 case OverrideFlag::kOff:
445 config->fSettings.fOptimize = false;
446 break;
447 case OverrideFlag::kOn:
448 config->fSettings.fOptimize = true;
449 break;
450 }
451
452 switch (sInliner) {
453 case OverrideFlag::kDefault:
454 break;
455 case OverrideFlag::kOff:
456 config->fSettings.fInlineThreshold = 0;
457 break;
458 case OverrideFlag::kOn:
459 if (config->fSettings.fInlineThreshold == 0) {
460 config->fSettings.fInlineThreshold = kDefaultInlineThreshold;
461 }
462 break;
463 }
464
465 // Disable optimization settings that depend on a parent setting which has been disabled.
466 config->fSettings.fInlineThreshold *= (int)config->fSettings.fOptimize;
467 config->fSettings.fRemoveDeadFunctions &= config->fSettings.fOptimize;
468 config->fSettings.fRemoveDeadVariables &= config->fSettings.fOptimize;
469
470 fErrorText = "";
471 fErrorCount = 0;
472 fInliner.reset();
473
474 auto textPtr = std::make_unique<String>(std::move(text));
475 AutoSource as(this, textPtr.get());
476
477 // Enable node pooling while converting and optimizing the program for a performance boost.
478 // The Program will take ownership of the pool.
479 std::unique_ptr<Pool> pool;
480 if (fContext->fCaps.useNodePools()) {
481 pool = Pool::Create();
482 pool->attachToThread();
483 }
484 IRGenerator::IRBundle ir = fIRGenerator->convertProgram(baseModule, /*isBuiltinCode=*/false,
485 textPtr->c_str(), textPtr->size(),
486 externalFunctions);
487 auto program = std::make_unique<Program>(std::move(textPtr),
488 std::move(config),
489 fContext,
490 std::move(ir.fElements),
491 std::move(ir.fSharedElements),
492 std::move(modifiersPool),
493 std::move(ir.fSymbolTable),
494 std::move(pool),
495 ir.fInputs);
496 bool success = false;
497 if (fErrorCount) {
498 // Do not return programs that failed to compile.
499 } else if (!this->optimize(*program)) {
500 // Do not return programs that failed to optimize.
501 } else {
502 // We have a successful program!
503 success = true;
504 }
505
506 if (program->fPool) {
507 program->fPool->detachFromThread();
508 }
509 return success ? std::move(program) : nullptr;
510 }
511
verifyStaticTests(const Program & program)512 void Compiler::verifyStaticTests(const Program& program) {
513 class StaticTestVerifier : public ProgramVisitor {
514 public:
515 StaticTestVerifier(ErrorReporter* r) : fReporter(r) {}
516
517 using ProgramVisitor::visitProgramElement;
518
519 bool visitStatement(const Statement& stmt) override {
520 switch (stmt.kind()) {
521 case Statement::Kind::kIf:
522 if (stmt.as<IfStatement>().isStatic()) {
523 fReporter->error(stmt.fOffset, "static if has non-static test");
524 }
525 break;
526
527 case Statement::Kind::kSwitch:
528 if (stmt.as<SwitchStatement>().isStatic()) {
529 fReporter->error(stmt.fOffset, "static switch has non-static test");
530 }
531 break;
532
533 default:
534 break;
535 }
536 return INHERITED::visitStatement(stmt);
537 }
538
539 bool visitExpression(const Expression&) override {
540 // We aren't looking for anything inside an Expression, so skip them entirely.
541 return false;
542 }
543
544 private:
545 using INHERITED = ProgramVisitor;
546 ErrorReporter* fReporter;
547 };
548
549 // If invalid static tests are permitted, we don't need to check anything.
550 if (fContext->fConfig->fSettings.fPermitInvalidStaticTests) {
551 return;
552 }
553
554 // Check all of the program's owned elements. (Built-in elements are assumed to be valid.)
555 StaticTestVerifier visitor{this};
556 for (const std::unique_ptr<ProgramElement>& element : program.ownedElements()) {
557 if (element->is<FunctionDefinition>()) {
558 visitor.visitProgramElement(*element);
559 }
560 }
561 }
562
optimize(LoadedModule & module)563 bool Compiler::optimize(LoadedModule& module) {
564 SkASSERT(!fErrorCount);
565
566 // Create a temporary program configuration with default settings.
567 ProgramConfig config;
568 config.fKind = module.fKind;
569 AutoProgramConfig autoConfig(fContext, &config);
570
571 // Reset the Inliner.
572 fInliner.reset();
573
574 std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
575
576 while (fErrorCount == 0) {
577 // Perform inline-candidate analysis and inline any functions deemed suitable.
578 if (!fInliner.analyze(module.fElements, module.fSymbols, usage.get())) {
579 break;
580 }
581 }
582 return fErrorCount == 0;
583 }
584
removeDeadFunctions(Program & program,ProgramUsage * usage)585 bool Compiler::removeDeadFunctions(Program& program, ProgramUsage* usage) {
586 bool madeChanges = false;
587
588 if (program.fConfig->fSettings.fRemoveDeadFunctions) {
589 auto isDeadFunction = [&](const ProgramElement* element) {
590 if (!element->is<FunctionDefinition>()) {
591 return false;
592 }
593 const FunctionDefinition& fn = element->as<FunctionDefinition>();
594 if (fn.declaration().isMain() || usage->get(fn.declaration()) > 0) {
595 return false;
596 }
597 usage->remove(*element);
598 madeChanges = true;
599 return true;
600 };
601
602 program.fElements.erase(std::remove_if(program.fElements.begin(),
603 program.fElements.end(),
604 [&](const std::unique_ptr<ProgramElement>& element) {
605 return isDeadFunction(element.get());
606 }),
607 program.fElements.end());
608 program.fSharedElements.erase(std::remove_if(program.fSharedElements.begin(),
609 program.fSharedElements.end(),
610 isDeadFunction),
611 program.fSharedElements.end());
612 }
613 return madeChanges;
614 }
615
removeDeadGlobalVariables(Program & program,ProgramUsage * usage)616 bool Compiler::removeDeadGlobalVariables(Program& program, ProgramUsage* usage) {
617 bool madeChanges = false;
618
619 if (program.fConfig->fSettings.fRemoveDeadVariables) {
620 auto isDeadVariable = [&](const ProgramElement* element) {
621 if (!element->is<GlobalVarDeclaration>()) {
622 return false;
623 }
624 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
625 const VarDeclaration& varDecl = global.declaration()->as<VarDeclaration>();
626 if (!usage->isDead(varDecl.var())) {
627 return false;
628 }
629 madeChanges = true;
630 return true;
631 };
632
633 program.fElements.erase(std::remove_if(program.fElements.begin(),
634 program.fElements.end(),
635 [&](const std::unique_ptr<ProgramElement>& element) {
636 return isDeadVariable(element.get());
637 }),
638 program.fElements.end());
639 program.fSharedElements.erase(std::remove_if(program.fSharedElements.begin(),
640 program.fSharedElements.end(),
641 isDeadVariable),
642 program.fSharedElements.end());
643 }
644 return madeChanges;
645 }
646
removeDeadLocalVariables(Program & program,ProgramUsage * usage)647 bool Compiler::removeDeadLocalVariables(Program& program, ProgramUsage* usage) {
648 class DeadLocalVariableEliminator : public ProgramWriter {
649 public:
650 DeadLocalVariableEliminator(const Context& context, ProgramUsage* usage)
651 : fContext(context)
652 , fUsage(usage) {}
653
654 using ProgramWriter::visitProgramElement;
655
656 bool visitExpressionPtr(std::unique_ptr<Expression>& expr) override {
657 // We don't need to look inside expressions at all.
658 return false;
659 }
660
661 bool visitStatementPtr(std::unique_ptr<Statement>& stmt) override {
662 if (stmt->is<VarDeclaration>()) {
663 VarDeclaration& varDecl = stmt->as<VarDeclaration>();
664 const Variable* var = &varDecl.var();
665 ProgramUsage::VariableCounts* counts = fUsage->fVariableCounts.find(var);
666 SkASSERT(counts);
667 SkASSERT(counts->fDeclared);
668 if (CanEliminate(var, *counts)) {
669 if (var->initialValue()) {
670 // The variable has an initial-value expression, which might have side
671 // effects. ExpressionStatement::Make will preserve side effects, but
672 // replaces pure expressions with Nop.
673 fUsage->remove(stmt.get());
674 stmt = ExpressionStatement::Make(fContext, std::move(varDecl.value()));
675 fUsage->add(stmt.get());
676 } else {
677 // The variable has no initial-value and can be cleanly eliminated.
678 fUsage->remove(stmt.get());
679 stmt = std::make_unique<Nop>();
680 }
681 fMadeChanges = true;
682 }
683 return false;
684 }
685 return INHERITED::visitStatementPtr(stmt);
686 }
687
688 static bool CanEliminate(const Variable* var, const ProgramUsage::VariableCounts& counts) {
689 if (!counts.fDeclared || counts.fRead || var->storage() != VariableStorage::kLocal) {
690 return false;
691 }
692 if (var->initialValue()) {
693 SkASSERT(counts.fWrite >= 1);
694 return counts.fWrite == 1;
695 } else {
696 return counts.fWrite == 0;
697 }
698 }
699
700 bool fMadeChanges = false;
701 const Context& fContext;
702 ProgramUsage* fUsage;
703
704 using INHERITED = ProgramWriter;
705 };
706
707 DeadLocalVariableEliminator visitor{*fContext, usage};
708
709 if (program.fConfig->fSettings.fRemoveDeadVariables) {
710 for (auto& [var, counts] : usage->fVariableCounts) {
711 if (DeadLocalVariableEliminator::CanEliminate(var, counts)) {
712 // This program contains at least one dead local variable.
713 // Scan the program for any dead local variables and eliminate them all.
714 for (std::unique_ptr<ProgramElement>& pe : program.ownedElements()) {
715 if (pe->is<FunctionDefinition>()) {
716 visitor.visitProgramElement(*pe);
717 }
718 }
719 break;
720 }
721 }
722 }
723
724 return visitor.fMadeChanges;
725 }
726
optimize(Program & program)727 bool Compiler::optimize(Program& program) {
728 // The optimizer only needs to run when it is enabled.
729 if (!program.fConfig->fSettings.fOptimize) {
730 return true;
731 }
732
733 SkASSERT(!fErrorCount);
734 ProgramUsage* usage = program.fUsage.get();
735
736 if (fErrorCount == 0) {
737 // Run the inliner only once; it is expensive! Multiple passes can occasionally shake out
738 // more wins, but it's diminishing returns.
739 fInliner.analyze(program.ownedElements(), program.fSymbols, usage);
740
741 while (this->removeDeadFunctions(program, usage)) {
742 // Removing dead functions may cause more functions to become unreferenced. Try again.
743 }
744 while (this->removeDeadLocalVariables(program, usage)) {
745 // Removing dead variables may cause more variables to become unreferenced. Try again.
746 }
747 if (program.fConfig->fKind != ProgramKind::kFragmentProcessor) {
748 this->removeDeadGlobalVariables(program, usage);
749 }
750 }
751
752 if (fErrorCount == 0) {
753 this->verifyStaticTests(program);
754 }
755
756 return fErrorCount == 0;
757 }
758
759 #if defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
760
toSPIRV(Program & program,OutputStream & out)761 bool Compiler::toSPIRV(Program& program, OutputStream& out) {
762 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toSPIRV");
763 AutoSource as(this, program.fSource.get());
764 #ifdef SK_ENABLE_SPIRV_VALIDATION
765 StringStream buffer;
766 SPIRVCodeGenerator cg(fContext.get(), &program, this, &buffer);
767 bool result = cg.generateCode();
768 if (result && program.fConfig->fSettings.fValidateSPIRV) {
769 spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
770 const String& data = buffer.str();
771 SkASSERT(0 == data.size() % 4);
772 String errors;
773 auto dumpmsg = [&errors](spv_message_level_t, const char*, const spv_position_t&,
774 const char* m) {
775 errors.appendf("SPIR-V validation error: %s\n", m);
776 };
777 tools.SetMessageConsumer(dumpmsg);
778
779 // Verify that the SPIR-V we produced is valid. At runtime, we will abort() with a message
780 // explaining the error. In standalone mode (skslc), we will send the message, plus the
781 // entire disassembled SPIR-V (for easier context & debugging) as *our* error message.
782 result = tools.Validate((const uint32_t*) data.c_str(), data.size() / 4);
783
784 if (!result) {
785 #if defined(SKSL_STANDALONE)
786 // Convert the string-stream to a SPIR-V disassembly.
787 std::string disassembly;
788 if (tools.Disassemble((const uint32_t*)data.data(), data.size() / 4, &disassembly)) {
789 errors.append(disassembly);
790 }
791 this->error(-1, errors);
792 #else
793 SkDEBUGFAILF("%s", errors.c_str());
794 #endif
795 }
796 out.write(data.c_str(), data.size());
797 }
798 #else
799 SPIRVCodeGenerator cg(fContext.get(), &program, this, &out);
800 bool result = cg.generateCode();
801 #endif
802 return result;
803 }
804
toSPIRV(Program & program,String * out)805 bool Compiler::toSPIRV(Program& program, String* out) {
806 StringStream buffer;
807 bool result = this->toSPIRV(program, buffer);
808 if (result) {
809 *out = buffer.str();
810 }
811 return result;
812 }
813
toGLSL(Program & program,OutputStream & out)814 bool Compiler::toGLSL(Program& program, OutputStream& out) {
815 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toGLSL");
816 AutoSource as(this, program.fSource.get());
817 GLSLCodeGenerator cg(fContext.get(), &program, this, &out);
818 bool result = cg.generateCode();
819 return result;
820 }
821
toGLSL(Program & program,String * out)822 bool Compiler::toGLSL(Program& program, String* out) {
823 StringStream buffer;
824 bool result = this->toGLSL(program, buffer);
825 if (result) {
826 *out = buffer.str();
827 }
828 return result;
829 }
830
toHLSL(Program & program,String * out)831 bool Compiler::toHLSL(Program& program, String* out) {
832 String spirv;
833 if (!this->toSPIRV(program, &spirv)) {
834 return false;
835 }
836
837 return SPIRVtoHLSL(spirv, out);
838 }
839
toMetal(Program & program,OutputStream & out)840 bool Compiler::toMetal(Program& program, OutputStream& out) {
841 TRACE_EVENT0("skia.shaders", "SkSL::Compiler::toMetal");
842 AutoSource as(this, program.fSource.get());
843 MetalCodeGenerator cg(fContext.get(), &program, this, &out);
844 bool result = cg.generateCode();
845 return result;
846 }
847
toMetal(Program & program,String * out)848 bool Compiler::toMetal(Program& program, String* out) {
849 StringStream buffer;
850 bool result = this->toMetal(program, buffer);
851 if (result) {
852 *out = buffer.str();
853 }
854 return result;
855 }
856
857 #if defined(SKSL_STANDALONE) || GR_TEST_UTILS
toCPP(Program & program,String name,OutputStream & out)858 bool Compiler::toCPP(Program& program, String name, OutputStream& out) {
859 AutoSource as(this, program.fSource.get());
860 CPPCodeGenerator cg(fContext.get(), &program, this, name, &out);
861 bool result = cg.generateCode();
862 return result;
863 }
864
toDSLCPP(Program & program,String name,OutputStream & out)865 bool Compiler::toDSLCPP(Program& program, String name, OutputStream& out) {
866 AutoSource as(this, program.fSource.get());
867 DSLCPPCodeGenerator cg(fContext.get(), &program, this, name, &out);
868 bool result = cg.generateCode();
869 return result;
870 }
871
toH(Program & program,String name,OutputStream & out)872 bool Compiler::toH(Program& program, String name, OutputStream& out) {
873 AutoSource as(this, program.fSource.get());
874 HCodeGenerator cg(fContext.get(), &program, this, name, &out);
875 bool result = cg.generateCode();
876 return result;
877 }
878 #endif // defined(SKSL_STANDALONE) || GR_TEST_UTILS
879
880 #endif // defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
881
position(int offset)882 Position Compiler::position(int offset) {
883 if (fSource && offset >= 0) {
884 int line = 1;
885 int column = 1;
886 for (int i = 0; i < offset; i++) {
887 if ((*fSource)[i] == '\n') {
888 ++line;
889 column = 1;
890 }
891 else {
892 ++column;
893 }
894 }
895 return Position(line, column);
896 } else {
897 return Position(-1, -1);
898 }
899 }
900
error(int offset,String msg)901 void Compiler::error(int offset, String msg) {
902 fErrorCount++;
903 Position pos = this->position(offset);
904 fErrorTextLength.push_back(fErrorText.length());
905 fErrorText += "error: " + (pos.fLine >= 1 ? to_string(pos.fLine) + ": " : "") + msg + "\n";
906 }
907
setErrorCount(int c)908 void Compiler::setErrorCount(int c) {
909 if (c < fErrorCount) {
910 fErrorText.resize(fErrorTextLength[c]);
911 fErrorTextLength.resize(c);
912 fErrorCount = c;
913 }
914 }
915
errorText(bool showCount)916 String Compiler::errorText(bool showCount) {
917 if (showCount) {
918 this->writeErrorCount();
919 }
920 fErrorCount = 0;
921 String result = fErrorText;
922 fErrorText = "";
923 return result;
924 }
925
writeErrorCount()926 void Compiler::writeErrorCount() {
927 if (fErrorCount) {
928 fErrorText += to_string(fErrorCount) + " error";
929 if (fErrorCount > 1) {
930 fErrorText += "s";
931 }
932 fErrorText += "\n";
933 }
934 }
935
936 } // namespace SkSL
937