xref: /third_party/skia/src/sksl/SkSLCompiler.cpp (revision cb93a386)
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
80namespace SkSL {
81
82// These flags allow tools like Viewer or Nanobench to override the compiler's ProgramSettings.
83Compiler::OverrideFlag Compiler::sOptimizer = OverrideFlag::kDefault;
84Compiler::OverrideFlag Compiler::sInliner = OverrideFlag::kDefault;
85
86using RefKind = VariableReference::RefKind;
87
88class AutoSource {
89public:
90    AutoSource(Compiler* compiler, const char* source)
91            : fCompiler(compiler) {
92        SkASSERT(!fCompiler->errorReporter().source());
93        fCompiler->errorReporter().setSource(source);
94    }
95
96    ~AutoSource() {
97        fCompiler->errorReporter().setSource(nullptr);
98    }
99
100    Compiler* fCompiler;
101};
102
103class AutoProgramConfig {
104public:
105    AutoProgramConfig(std::shared_ptr<Context>& context, ProgramConfig* config)
106            : fContext(context.get())
107            , fOldConfig(fContext->fConfig) {
108        fContext->fConfig = config;
109    }
110
111    ~AutoProgramConfig() {
112        fContext->fConfig = fOldConfig;
113    }
114
115    Context* fContext;
116    ProgramConfig* fOldConfig;
117};
118
119class AutoModifiersPool {
120public:
121    AutoModifiersPool(std::shared_ptr<Context>& context, ModifiersPool* modifiersPool)
122            : fContext(context.get()) {
123        SkASSERT(!fContext->fModifiersPool);
124        fContext->fModifiersPool = modifiersPool;
125    }
126
127    ~AutoModifiersPool() {
128        fContext->fModifiersPool = nullptr;
129    }
130
131    Context* fContext;
132};
133
134Compiler::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
143Compiler::~Compiler() {}
144
145#define TYPE(t) &BuiltinTypes::f ## t
146
147using BuiltinTypePtr = const std::unique_ptr<Type> BuiltinTypes::*;
148
149inline 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
185inline 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
199std::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
209std::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
227const ParsedModule& Compiler::loadGPUModule() {
228    if (!fGPUModule.fSymbols) {
229        fGPUModule = this->parseModule(ProgramKind::kFragment, MODULE_DATA(gpu), fPrivateModule);
230    }
231    return fGPUModule;
232}
233
234const 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
242const 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
250static 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
275const 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
283const 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
291const 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
303LoadedModule 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
355ParsedModule 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
404std::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
453std::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
505bool 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
529bool 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
559bool 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
579bool 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
601bool 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
652bool 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
661bool 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
669bool 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
678bool 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
687bool 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
695bool 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
706void Compiler::handleError(skstd::string_view msg, PositionInfo pos) {
707    fErrorText += "error: " + (pos.line() >= 1 ? to_string(pos.line()) + ": " : "") + msg + "\n";
708}
709
710String Compiler::errorText(bool showCount) {
711    if (showCount) {
712        this->writeErrorCount();
713    }
714    String result = fErrorText;
715    this->resetErrors();
716    return result;
717}
718
719void 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