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