1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2016 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLCompiler.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTraceEvent.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLDefines.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLInliner.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLModule.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLModuleLoader.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLParser.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPool.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramKind.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLProgramUsage.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgram.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgramElement.h" // IWYU pragma: keep
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSymbolTable.h" // IWYU pragma: keep
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/transform/SkSLTransform.h"
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
29*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
30*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
31*c8dee2aaSAndroid Build Coastguard Worker
32*c8dee2aaSAndroid Build Coastguard Worker #if defined(SKSL_STANDALONE)
33*c8dee2aaSAndroid Build Coastguard Worker #include <fstream>
34*c8dee2aaSAndroid Build Coastguard Worker #endif
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
37*c8dee2aaSAndroid Build Coastguard Worker
38*c8dee2aaSAndroid Build Coastguard Worker // These flags allow tools like Viewer or Nanobench to override the compiler's ProgramSettings.
39*c8dee2aaSAndroid Build Coastguard Worker Compiler::OverrideFlag Compiler::sOptimizer = OverrideFlag::kDefault;
40*c8dee2aaSAndroid Build Coastguard Worker Compiler::OverrideFlag Compiler::sInliner = OverrideFlag::kDefault;
41*c8dee2aaSAndroid Build Coastguard Worker
42*c8dee2aaSAndroid Build Coastguard Worker class AutoProgramConfig {
43*c8dee2aaSAndroid Build Coastguard Worker public:
AutoProgramConfig(Context & context,ProgramConfig * config)44*c8dee2aaSAndroid Build Coastguard Worker AutoProgramConfig(Context& context, ProgramConfig* config)
45*c8dee2aaSAndroid Build Coastguard Worker : fContext(context)
46*c8dee2aaSAndroid Build Coastguard Worker , fOldConfig(context.fConfig) {
47*c8dee2aaSAndroid Build Coastguard Worker fContext.fConfig = config;
48*c8dee2aaSAndroid Build Coastguard Worker }
49*c8dee2aaSAndroid Build Coastguard Worker
~AutoProgramConfig()50*c8dee2aaSAndroid Build Coastguard Worker ~AutoProgramConfig() {
51*c8dee2aaSAndroid Build Coastguard Worker fContext.fConfig = fOldConfig;
52*c8dee2aaSAndroid Build Coastguard Worker }
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker Context& fContext;
55*c8dee2aaSAndroid Build Coastguard Worker ProgramConfig* fOldConfig;
56*c8dee2aaSAndroid Build Coastguard Worker };
57*c8dee2aaSAndroid Build Coastguard Worker
Compiler()58*c8dee2aaSAndroid Build Coastguard Worker Compiler::Compiler() : fErrorReporter(this) {
59*c8dee2aaSAndroid Build Coastguard Worker auto moduleLoader = ModuleLoader::Get();
60*c8dee2aaSAndroid Build Coastguard Worker fContext = std::make_shared<Context>(moduleLoader.builtinTypes(), fErrorReporter);
61*c8dee2aaSAndroid Build Coastguard Worker }
62*c8dee2aaSAndroid Build Coastguard Worker
~Compiler()63*c8dee2aaSAndroid Build Coastguard Worker Compiler::~Compiler() {}
64*c8dee2aaSAndroid Build Coastguard Worker
moduleForProgramKind(ProgramKind kind)65*c8dee2aaSAndroid Build Coastguard Worker const Module* Compiler::moduleForProgramKind(ProgramKind kind) {
66*c8dee2aaSAndroid Build Coastguard Worker auto m = ModuleLoader::Get();
67*c8dee2aaSAndroid Build Coastguard Worker switch (kind) {
68*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kFragment: return m.loadFragmentModule(this);
69*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kVertex: return m.loadVertexModule(this);
70*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kCompute: return m.loadComputeModule(this);
71*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kGraphiteFragment: return m.loadGraphiteFragmentModule(this);
72*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kGraphiteVertex: return m.loadGraphiteVertexModule(this);
73*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kGraphiteFragmentES2: return m.loadGraphiteFragmentES2Module(this);
74*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kGraphiteVertexES2: return m.loadGraphiteVertexES2Module(this);
75*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kPrivateRuntimeBlender:
76*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kPrivateRuntimeColorFilter:
77*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kPrivateRuntimeShader: return m.loadPrivateRTShaderModule(this);
78*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kRuntimeColorFilter:
79*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kRuntimeShader:
80*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kRuntimeBlender:
81*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kMeshVertex:
82*c8dee2aaSAndroid Build Coastguard Worker case ProgramKind::kMeshFragment: return m.loadPublicModule(this);
83*c8dee2aaSAndroid Build Coastguard Worker }
84*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
85*c8dee2aaSAndroid Build Coastguard Worker }
86*c8dee2aaSAndroid Build Coastguard Worker
FinalizeSettings(ProgramSettings * settings,ProgramKind kind)87*c8dee2aaSAndroid Build Coastguard Worker void Compiler::FinalizeSettings(ProgramSettings* settings, ProgramKind kind) {
88*c8dee2aaSAndroid Build Coastguard Worker // Honor our optimization-override flags.
89*c8dee2aaSAndroid Build Coastguard Worker switch (sOptimizer) {
90*c8dee2aaSAndroid Build Coastguard Worker case OverrideFlag::kDefault:
91*c8dee2aaSAndroid Build Coastguard Worker break;
92*c8dee2aaSAndroid Build Coastguard Worker case OverrideFlag::kOff:
93*c8dee2aaSAndroid Build Coastguard Worker settings->fOptimize = false;
94*c8dee2aaSAndroid Build Coastguard Worker break;
95*c8dee2aaSAndroid Build Coastguard Worker case OverrideFlag::kOn:
96*c8dee2aaSAndroid Build Coastguard Worker settings->fOptimize = true;
97*c8dee2aaSAndroid Build Coastguard Worker break;
98*c8dee2aaSAndroid Build Coastguard Worker }
99*c8dee2aaSAndroid Build Coastguard Worker
100*c8dee2aaSAndroid Build Coastguard Worker switch (sInliner) {
101*c8dee2aaSAndroid Build Coastguard Worker case OverrideFlag::kDefault:
102*c8dee2aaSAndroid Build Coastguard Worker break;
103*c8dee2aaSAndroid Build Coastguard Worker case OverrideFlag::kOff:
104*c8dee2aaSAndroid Build Coastguard Worker settings->fInlineThreshold = 0;
105*c8dee2aaSAndroid Build Coastguard Worker break;
106*c8dee2aaSAndroid Build Coastguard Worker case OverrideFlag::kOn:
107*c8dee2aaSAndroid Build Coastguard Worker if (settings->fInlineThreshold == 0) {
108*c8dee2aaSAndroid Build Coastguard Worker settings->fInlineThreshold = kDefaultInlineThreshold;
109*c8dee2aaSAndroid Build Coastguard Worker }
110*c8dee2aaSAndroid Build Coastguard Worker break;
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker
113*c8dee2aaSAndroid Build Coastguard Worker // Disable optimization settings that depend on a parent setting which has been disabled.
114*c8dee2aaSAndroid Build Coastguard Worker settings->fInlineThreshold *= (int)settings->fOptimize;
115*c8dee2aaSAndroid Build Coastguard Worker settings->fRemoveDeadFunctions &= settings->fOptimize;
116*c8dee2aaSAndroid Build Coastguard Worker settings->fRemoveDeadVariables &= settings->fOptimize;
117*c8dee2aaSAndroid Build Coastguard Worker
118*c8dee2aaSAndroid Build Coastguard Worker // Runtime effects always allow narrowing conversions.
119*c8dee2aaSAndroid Build Coastguard Worker if (ProgramConfig::IsRuntimeEffect(kind)) {
120*c8dee2aaSAndroid Build Coastguard Worker settings->fAllowNarrowingConversions = true;
121*c8dee2aaSAndroid Build Coastguard Worker }
122*c8dee2aaSAndroid Build Coastguard Worker }
123*c8dee2aaSAndroid Build Coastguard Worker
initializeContext(const SkSL::Module * module,ProgramKind kind,ProgramSettings settings,std::string_view source,ModuleType moduleType)124*c8dee2aaSAndroid Build Coastguard Worker void Compiler::initializeContext(const SkSL::Module* module,
125*c8dee2aaSAndroid Build Coastguard Worker ProgramKind kind,
126*c8dee2aaSAndroid Build Coastguard Worker ProgramSettings settings,
127*c8dee2aaSAndroid Build Coastguard Worker std::string_view source,
128*c8dee2aaSAndroid Build Coastguard Worker ModuleType moduleType) {
129*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fPool);
130*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fConfig);
131*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fContext->fSymbolTable);
132*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fContext->fConfig);
133*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fContext->fModule);
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker // Start the ErrorReporter with a clean slate.
136*c8dee2aaSAndroid Build Coastguard Worker this->resetErrors();
137*c8dee2aaSAndroid Build Coastguard Worker
138*c8dee2aaSAndroid Build Coastguard Worker fConfig = std::make_unique<ProgramConfig>();
139*c8dee2aaSAndroid Build Coastguard Worker fConfig->fModuleType = moduleType;
140*c8dee2aaSAndroid Build Coastguard Worker fConfig->fSettings = settings;
141*c8dee2aaSAndroid Build Coastguard Worker fConfig->fKind = kind;
142*c8dee2aaSAndroid Build Coastguard Worker
143*c8dee2aaSAndroid Build Coastguard Worker // Make sure the passed-in settings are valid.
144*c8dee2aaSAndroid Build Coastguard Worker FinalizeSettings(&fConfig->fSettings, kind);
145*c8dee2aaSAndroid Build Coastguard Worker
146*c8dee2aaSAndroid Build Coastguard Worker if (settings.fUseMemoryPool) {
147*c8dee2aaSAndroid Build Coastguard Worker fPool = Pool::Create();
148*c8dee2aaSAndroid Build Coastguard Worker fPool->attachToThread();
149*c8dee2aaSAndroid Build Coastguard Worker }
150*c8dee2aaSAndroid Build Coastguard Worker
151*c8dee2aaSAndroid Build Coastguard Worker fContext->fConfig = fConfig.get();
152*c8dee2aaSAndroid Build Coastguard Worker fContext->fModule = module;
153*c8dee2aaSAndroid Build Coastguard Worker fContext->fErrors->setSource(source);
154*c8dee2aaSAndroid Build Coastguard Worker
155*c8dee2aaSAndroid Build Coastguard Worker // Set up a clean symbol table atop the parent module's symbols.
156*c8dee2aaSAndroid Build Coastguard Worker fGlobalSymbols = std::make_unique<SymbolTable>(module->fSymbols.get(),
157*c8dee2aaSAndroid Build Coastguard Worker moduleType != ModuleType::program);
158*c8dee2aaSAndroid Build Coastguard Worker fGlobalSymbols->markModuleBoundary();
159*c8dee2aaSAndroid Build Coastguard Worker fContext->fSymbolTable = fGlobalSymbols.get();
160*c8dee2aaSAndroid Build Coastguard Worker }
161*c8dee2aaSAndroid Build Coastguard Worker
cleanupContext()162*c8dee2aaSAndroid Build Coastguard Worker void Compiler::cleanupContext() {
163*c8dee2aaSAndroid Build Coastguard Worker // Clear out the fields we initialized above.
164*c8dee2aaSAndroid Build Coastguard Worker fContext->fConfig = nullptr;
165*c8dee2aaSAndroid Build Coastguard Worker fContext->fModule = nullptr;
166*c8dee2aaSAndroid Build Coastguard Worker fContext->fErrors->setSource(std::string_view());
167*c8dee2aaSAndroid Build Coastguard Worker fContext->fSymbolTable = nullptr;
168*c8dee2aaSAndroid Build Coastguard Worker
169*c8dee2aaSAndroid Build Coastguard Worker fConfig = nullptr;
170*c8dee2aaSAndroid Build Coastguard Worker fGlobalSymbols = nullptr;
171*c8dee2aaSAndroid Build Coastguard Worker
172*c8dee2aaSAndroid Build Coastguard Worker if (fPool) {
173*c8dee2aaSAndroid Build Coastguard Worker fPool->detachFromThread();
174*c8dee2aaSAndroid Build Coastguard Worker fPool = nullptr;
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker }
177*c8dee2aaSAndroid Build Coastguard Worker
compileModule(ProgramKind kind,ModuleType moduleType,std::string moduleSource,const Module * parentModule,bool shouldInline)178*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Module> Compiler::compileModule(ProgramKind kind,
179*c8dee2aaSAndroid Build Coastguard Worker ModuleType moduleType,
180*c8dee2aaSAndroid Build Coastguard Worker std::string moduleSource,
181*c8dee2aaSAndroid Build Coastguard Worker const Module* parentModule,
182*c8dee2aaSAndroid Build Coastguard Worker bool shouldInline) {
183*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(parentModule);
184*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->errorCount() == 0);
185*c8dee2aaSAndroid Build Coastguard Worker
186*c8dee2aaSAndroid Build Coastguard Worker // Wrap the program source in a pointer so it is guaranteed to be stable across moves.
187*c8dee2aaSAndroid Build Coastguard Worker auto sourcePtr = std::make_unique<std::string>(std::move(moduleSource));
188*c8dee2aaSAndroid Build Coastguard Worker
189*c8dee2aaSAndroid Build Coastguard Worker // Compile the module from source, using default program settings (but no memory pooling).
190*c8dee2aaSAndroid Build Coastguard Worker ProgramSettings settings;
191*c8dee2aaSAndroid Build Coastguard Worker settings.fUseMemoryPool = false;
192*c8dee2aaSAndroid Build Coastguard Worker this->initializeContext(parentModule, kind, settings, *sourcePtr, moduleType);
193*c8dee2aaSAndroid Build Coastguard Worker
194*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Module> module = SkSL::Parser(this, settings, kind, std::move(sourcePtr))
195*c8dee2aaSAndroid Build Coastguard Worker .moduleInheritingFrom(parentModule);
196*c8dee2aaSAndroid Build Coastguard Worker
197*c8dee2aaSAndroid Build Coastguard Worker this->cleanupContext();
198*c8dee2aaSAndroid Build Coastguard Worker
199*c8dee2aaSAndroid Build Coastguard Worker if (this->errorCount() != 0) {
200*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Unexpected errors compiling %s:\n\n%s\n",
201*c8dee2aaSAndroid Build Coastguard Worker ModuleTypeToString(moduleType),
202*c8dee2aaSAndroid Build Coastguard Worker this->errorText().c_str());
203*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
204*c8dee2aaSAndroid Build Coastguard Worker }
205*c8dee2aaSAndroid Build Coastguard Worker if (shouldInline) {
206*c8dee2aaSAndroid Build Coastguard Worker this->optimizeModuleAfterLoading(kind, *module);
207*c8dee2aaSAndroid Build Coastguard Worker }
208*c8dee2aaSAndroid Build Coastguard Worker return module;
209*c8dee2aaSAndroid Build Coastguard Worker }
210*c8dee2aaSAndroid Build Coastguard Worker
convertProgram(ProgramKind kind,std::string programSource,const ProgramSettings & settings)211*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Program> Compiler::convertProgram(ProgramKind kind,
212*c8dee2aaSAndroid Build Coastguard Worker std::string programSource,
213*c8dee2aaSAndroid Build Coastguard Worker const ProgramSettings& settings) {
214*c8dee2aaSAndroid Build Coastguard Worker TRACE_EVENT0("skia.shaders", "SkSL::Compiler::convertProgram");
215*c8dee2aaSAndroid Build Coastguard Worker
216*c8dee2aaSAndroid Build Coastguard Worker // Wrap the program source in a pointer so it is guaranteed to be stable across moves.
217*c8dee2aaSAndroid Build Coastguard Worker auto sourcePtr = std::make_unique<std::string>(std::move(programSource));
218*c8dee2aaSAndroid Build Coastguard Worker
219*c8dee2aaSAndroid Build Coastguard Worker // Load the module used by this ProgramKind.
220*c8dee2aaSAndroid Build Coastguard Worker const SkSL::Module* module = this->moduleForProgramKind(kind);
221*c8dee2aaSAndroid Build Coastguard Worker
222*c8dee2aaSAndroid Build Coastguard Worker this->initializeContext(module, kind, settings, *sourcePtr, ModuleType::program);
223*c8dee2aaSAndroid Build Coastguard Worker
224*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Program> program = SkSL::Parser(this, settings, kind, std::move(sourcePtr))
225*c8dee2aaSAndroid Build Coastguard Worker .programInheritingFrom(module);
226*c8dee2aaSAndroid Build Coastguard Worker
227*c8dee2aaSAndroid Build Coastguard Worker this->cleanupContext();
228*c8dee2aaSAndroid Build Coastguard Worker return program;
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker
releaseProgram(std::unique_ptr<std::string> source,std::vector<std::unique_ptr<SkSL::ProgramElement>> programElements)231*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkSL::Program> Compiler::releaseProgram(
232*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<std::string> source,
233*c8dee2aaSAndroid Build Coastguard Worker std::vector<std::unique_ptr<SkSL::ProgramElement>> programElements) {
234*c8dee2aaSAndroid Build Coastguard Worker Pool* pool = fPool.get();
235*c8dee2aaSAndroid Build Coastguard Worker auto result = std::make_unique<SkSL::Program>(std::move(source),
236*c8dee2aaSAndroid Build Coastguard Worker std::move(fConfig),
237*c8dee2aaSAndroid Build Coastguard Worker fContext,
238*c8dee2aaSAndroid Build Coastguard Worker std::move(programElements),
239*c8dee2aaSAndroid Build Coastguard Worker std::move(fGlobalSymbols),
240*c8dee2aaSAndroid Build Coastguard Worker std::move(fPool));
241*c8dee2aaSAndroid Build Coastguard Worker fContext->fSymbolTable = nullptr;
242*c8dee2aaSAndroid Build Coastguard Worker
243*c8dee2aaSAndroid Build Coastguard Worker bool success = this->finalize(*result) &&
244*c8dee2aaSAndroid Build Coastguard Worker this->optimize(*result);
245*c8dee2aaSAndroid Build Coastguard Worker if (pool) {
246*c8dee2aaSAndroid Build Coastguard Worker pool->detachFromThread();
247*c8dee2aaSAndroid Build Coastguard Worker }
248*c8dee2aaSAndroid Build Coastguard Worker return success ? std::move(result) : nullptr;
249*c8dee2aaSAndroid Build Coastguard Worker }
250*c8dee2aaSAndroid Build Coastguard Worker
optimizeModuleBeforeMinifying(ProgramKind kind,Module & module,bool shrinkSymbols)251*c8dee2aaSAndroid Build Coastguard Worker bool Compiler::optimizeModuleBeforeMinifying(ProgramKind kind, Module& module, bool shrinkSymbols) {
252*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->errorCount() == 0);
253*c8dee2aaSAndroid Build Coastguard Worker
254*c8dee2aaSAndroid Build Coastguard Worker auto m = SkSL::ModuleLoader::Get();
255*c8dee2aaSAndroid Build Coastguard Worker
256*c8dee2aaSAndroid Build Coastguard Worker // Create a temporary program configuration with default settings.
257*c8dee2aaSAndroid Build Coastguard Worker ProgramConfig config;
258*c8dee2aaSAndroid Build Coastguard Worker config.fModuleType = module.fModuleType;
259*c8dee2aaSAndroid Build Coastguard Worker config.fKind = kind;
260*c8dee2aaSAndroid Build Coastguard Worker AutoProgramConfig autoConfig(this->context(), &config);
261*c8dee2aaSAndroid Build Coastguard Worker
262*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
263*c8dee2aaSAndroid Build Coastguard Worker
264*c8dee2aaSAndroid Build Coastguard Worker if (shrinkSymbols) {
265*c8dee2aaSAndroid Build Coastguard Worker // Assign shorter names to symbols as long as it won't change the external meaning of the
266*c8dee2aaSAndroid Build Coastguard Worker // code.
267*c8dee2aaSAndroid Build Coastguard Worker Transform::RenamePrivateSymbols(this->context(), module, usage.get(), kind);
268*c8dee2aaSAndroid Build Coastguard Worker
269*c8dee2aaSAndroid Build Coastguard Worker // Replace constant variables with their literal values to save space.
270*c8dee2aaSAndroid Build Coastguard Worker Transform::ReplaceConstVarsWithLiterals(module, usage.get());
271*c8dee2aaSAndroid Build Coastguard Worker }
272*c8dee2aaSAndroid Build Coastguard Worker
273*c8dee2aaSAndroid Build Coastguard Worker // Remove any unreachable code.
274*c8dee2aaSAndroid Build Coastguard Worker Transform::EliminateUnreachableCode(module, usage.get());
275*c8dee2aaSAndroid Build Coastguard Worker
276*c8dee2aaSAndroid Build Coastguard Worker // We can only remove dead functions from runtime shaders, since runtime-effect helper functions
277*c8dee2aaSAndroid Build Coastguard Worker // are isolated from other parts of the program. In a module, an unreferenced function is
278*c8dee2aaSAndroid Build Coastguard Worker // intended to be called by the code that includes the module.
279*c8dee2aaSAndroid Build Coastguard Worker if (kind == ProgramKind::kRuntimeShader) {
280*c8dee2aaSAndroid Build Coastguard Worker while (Transform::EliminateDeadFunctions(this->context(), module, usage.get())) {
281*c8dee2aaSAndroid Build Coastguard Worker // Removing dead functions may cause more functions to become unreferenced. Try again.
282*c8dee2aaSAndroid Build Coastguard Worker }
283*c8dee2aaSAndroid Build Coastguard Worker }
284*c8dee2aaSAndroid Build Coastguard Worker
285*c8dee2aaSAndroid Build Coastguard Worker while (Transform::EliminateDeadLocalVariables(this->context(), module, usage.get())) {
286*c8dee2aaSAndroid Build Coastguard Worker // Removing dead variables may cause more variables to become unreferenced. Try again.
287*c8dee2aaSAndroid Build Coastguard Worker }
288*c8dee2aaSAndroid Build Coastguard Worker
289*c8dee2aaSAndroid Build Coastguard Worker // Runtime shaders are isolated from other parts of the program via name mangling, so we can
290*c8dee2aaSAndroid Build Coastguard Worker // eliminate public globals if they aren't referenced. Otherwise, we only eliminate private
291*c8dee2aaSAndroid Build Coastguard Worker // globals (prefixed with `$`) to avoid changing the meaning of the module code.
292*c8dee2aaSAndroid Build Coastguard Worker bool onlyPrivateGlobals = !ProgramConfig::IsRuntimeEffect(kind);
293*c8dee2aaSAndroid Build Coastguard Worker while (Transform::EliminateDeadGlobalVariables(this->context(), module, usage.get(),
294*c8dee2aaSAndroid Build Coastguard Worker onlyPrivateGlobals)) {
295*c8dee2aaSAndroid Build Coastguard Worker // Repeat until no changes occur.
296*c8dee2aaSAndroid Build Coastguard Worker }
297*c8dee2aaSAndroid Build Coastguard Worker
298*c8dee2aaSAndroid Build Coastguard Worker // We eliminate empty statements to avoid runs of `;;;;;;` caused by the previous passes.
299*c8dee2aaSAndroid Build Coastguard Worker SkSL::Transform::EliminateEmptyStatements(module);
300*c8dee2aaSAndroid Build Coastguard Worker
301*c8dee2aaSAndroid Build Coastguard Worker // We can eliminate `{}` around single-statement blocks.
302*c8dee2aaSAndroid Build Coastguard Worker SkSL::Transform::EliminateUnnecessaryBraces(this->context(), module);
303*c8dee2aaSAndroid Build Coastguard Worker
304*c8dee2aaSAndroid Build Coastguard Worker // We can convert `float4(myFloat)` with `myFloat.xxxx` to save a few characters.
305*c8dee2aaSAndroid Build Coastguard Worker SkSL::Transform::ReplaceSplatCastsWithSwizzles(this->context(), module);
306*c8dee2aaSAndroid Build Coastguard Worker
307*c8dee2aaSAndroid Build Coastguard Worker // Make sure that program usage is still correct after the optimization pass is complete.
308*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*usage == *Analysis::GetUsage(module));
309*c8dee2aaSAndroid Build Coastguard Worker
310*c8dee2aaSAndroid Build Coastguard Worker return this->errorCount() == 0;
311*c8dee2aaSAndroid Build Coastguard Worker }
312*c8dee2aaSAndroid Build Coastguard Worker
optimizeModuleAfterLoading(ProgramKind kind,Module & module)313*c8dee2aaSAndroid Build Coastguard Worker bool Compiler::optimizeModuleAfterLoading(ProgramKind kind, Module& module) {
314*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->errorCount() == 0);
315*c8dee2aaSAndroid Build Coastguard Worker
316*c8dee2aaSAndroid Build Coastguard Worker #ifndef SK_ENABLE_OPTIMIZE_SIZE
317*c8dee2aaSAndroid Build Coastguard Worker // Create a temporary program configuration with default settings.
318*c8dee2aaSAndroid Build Coastguard Worker ProgramConfig config;
319*c8dee2aaSAndroid Build Coastguard Worker config.fModuleType = module.fModuleType;
320*c8dee2aaSAndroid Build Coastguard Worker config.fKind = kind;
321*c8dee2aaSAndroid Build Coastguard Worker AutoProgramConfig autoConfig(this->context(), &config);
322*c8dee2aaSAndroid Build Coastguard Worker
323*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
324*c8dee2aaSAndroid Build Coastguard Worker
325*c8dee2aaSAndroid Build Coastguard Worker // Perform inline-candidate analysis and inline any functions deemed suitable.
326*c8dee2aaSAndroid Build Coastguard Worker Inliner inliner(fContext.get());
327*c8dee2aaSAndroid Build Coastguard Worker while (this->errorCount() == 0) {
328*c8dee2aaSAndroid Build Coastguard Worker if (!this->runInliner(&inliner, module.fElements, module.fSymbols.get(), usage.get())) {
329*c8dee2aaSAndroid Build Coastguard Worker break;
330*c8dee2aaSAndroid Build Coastguard Worker }
331*c8dee2aaSAndroid Build Coastguard Worker }
332*c8dee2aaSAndroid Build Coastguard Worker // Make sure that program usage is still correct after the optimization pass is complete.
333*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*usage == *Analysis::GetUsage(module));
334*c8dee2aaSAndroid Build Coastguard Worker #endif
335*c8dee2aaSAndroid Build Coastguard Worker
336*c8dee2aaSAndroid Build Coastguard Worker return this->errorCount() == 0;
337*c8dee2aaSAndroid Build Coastguard Worker }
338*c8dee2aaSAndroid Build Coastguard Worker
optimize(Program & program)339*c8dee2aaSAndroid Build Coastguard Worker bool Compiler::optimize(Program& program) {
340*c8dee2aaSAndroid Build Coastguard Worker // The optimizer only needs to run when it is enabled.
341*c8dee2aaSAndroid Build Coastguard Worker if (!program.fConfig->fSettings.fOptimize) {
342*c8dee2aaSAndroid Build Coastguard Worker return true;
343*c8dee2aaSAndroid Build Coastguard Worker }
344*c8dee2aaSAndroid Build Coastguard Worker
345*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!this->errorCount());
346*c8dee2aaSAndroid Build Coastguard Worker if (this->errorCount() == 0) {
347*c8dee2aaSAndroid Build Coastguard Worker #ifndef SK_ENABLE_OPTIMIZE_SIZE
348*c8dee2aaSAndroid Build Coastguard Worker // Run the inliner only once; it is expensive! Multiple passes can occasionally shake out
349*c8dee2aaSAndroid Build Coastguard Worker // more wins, but it's diminishing returns.
350*c8dee2aaSAndroid Build Coastguard Worker Inliner inliner(fContext.get());
351*c8dee2aaSAndroid Build Coastguard Worker this->runInliner(&inliner, program.fOwnedElements, program.fSymbols.get(),
352*c8dee2aaSAndroid Build Coastguard Worker program.fUsage.get());
353*c8dee2aaSAndroid Build Coastguard Worker #endif
354*c8dee2aaSAndroid Build Coastguard Worker
355*c8dee2aaSAndroid Build Coastguard Worker // Unreachable code can confuse some drivers, so it's worth removing. (skia:12012)
356*c8dee2aaSAndroid Build Coastguard Worker Transform::EliminateUnreachableCode(program);
357*c8dee2aaSAndroid Build Coastguard Worker
358*c8dee2aaSAndroid Build Coastguard Worker while (Transform::EliminateDeadFunctions(program)) {
359*c8dee2aaSAndroid Build Coastguard Worker // Removing dead functions may cause more functions to become unreferenced. Try again.
360*c8dee2aaSAndroid Build Coastguard Worker }
361*c8dee2aaSAndroid Build Coastguard Worker while (Transform::EliminateDeadLocalVariables(program)) {
362*c8dee2aaSAndroid Build Coastguard Worker // Removing dead variables may cause more variables to become unreferenced. Try again.
363*c8dee2aaSAndroid Build Coastguard Worker }
364*c8dee2aaSAndroid Build Coastguard Worker while (Transform::EliminateDeadGlobalVariables(program)) {
365*c8dee2aaSAndroid Build Coastguard Worker // Repeat until no changes occur.
366*c8dee2aaSAndroid Build Coastguard Worker }
367*c8dee2aaSAndroid Build Coastguard Worker // Make sure that program usage is still correct after the optimization pass is complete.
368*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*program.usage() == *Analysis::GetUsage(program));
369*c8dee2aaSAndroid Build Coastguard Worker
370*c8dee2aaSAndroid Build Coastguard Worker // Make sure that variables are still declared in the correct symbol tables.
371*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(Analysis::CheckSymbolTableCorrectness(program));
372*c8dee2aaSAndroid Build Coastguard Worker }
373*c8dee2aaSAndroid Build Coastguard Worker
374*c8dee2aaSAndroid Build Coastguard Worker return this->errorCount() == 0;
375*c8dee2aaSAndroid Build Coastguard Worker }
376*c8dee2aaSAndroid Build Coastguard Worker
runInliner(Program & program)377*c8dee2aaSAndroid Build Coastguard Worker void Compiler::runInliner(Program& program) {
378*c8dee2aaSAndroid Build Coastguard Worker #ifndef SK_ENABLE_OPTIMIZE_SIZE
379*c8dee2aaSAndroid Build Coastguard Worker AutoProgramConfig autoConfig(this->context(), program.fConfig.get());
380*c8dee2aaSAndroid Build Coastguard Worker Inliner inliner(fContext.get());
381*c8dee2aaSAndroid Build Coastguard Worker this->runInliner(&inliner, program.fOwnedElements, program.fSymbols.get(),
382*c8dee2aaSAndroid Build Coastguard Worker program.fUsage.get());
383*c8dee2aaSAndroid Build Coastguard Worker #endif
384*c8dee2aaSAndroid Build Coastguard Worker }
385*c8dee2aaSAndroid Build Coastguard Worker
runInliner(Inliner * inliner,const std::vector<std::unique_ptr<ProgramElement>> & elements,SymbolTable * symbols,ProgramUsage * usage)386*c8dee2aaSAndroid Build Coastguard Worker bool Compiler::runInliner(Inliner* inliner,
387*c8dee2aaSAndroid Build Coastguard Worker const std::vector<std::unique_ptr<ProgramElement>>& elements,
388*c8dee2aaSAndroid Build Coastguard Worker SymbolTable* symbols,
389*c8dee2aaSAndroid Build Coastguard Worker ProgramUsage* usage) {
390*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_ENABLE_OPTIMIZE_SIZE
391*c8dee2aaSAndroid Build Coastguard Worker return true;
392*c8dee2aaSAndroid Build Coastguard Worker #else
393*c8dee2aaSAndroid Build Coastguard Worker // The program's SymbolTable was taken out of the context when the program was bundled, but
394*c8dee2aaSAndroid Build Coastguard Worker // the inliner creates IR objects which may expect the context to hold a valid SymbolTable.
395*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!fContext->fSymbolTable);
396*c8dee2aaSAndroid Build Coastguard Worker fContext->fSymbolTable = symbols;
397*c8dee2aaSAndroid Build Coastguard Worker
398*c8dee2aaSAndroid Build Coastguard Worker bool result = inliner->analyze(elements, symbols, usage);
399*c8dee2aaSAndroid Build Coastguard Worker
400*c8dee2aaSAndroid Build Coastguard Worker fContext->fSymbolTable = nullptr;
401*c8dee2aaSAndroid Build Coastguard Worker return result;
402*c8dee2aaSAndroid Build Coastguard Worker #endif
403*c8dee2aaSAndroid Build Coastguard Worker }
404*c8dee2aaSAndroid Build Coastguard Worker
finalize(Program & program)405*c8dee2aaSAndroid Build Coastguard Worker bool Compiler::finalize(Program& program) {
406*c8dee2aaSAndroid Build Coastguard Worker // Copy all referenced built-in functions into the Program.
407*c8dee2aaSAndroid Build Coastguard Worker Transform::FindAndDeclareBuiltinFunctions(program);
408*c8dee2aaSAndroid Build Coastguard Worker
409*c8dee2aaSAndroid Build Coastguard Worker // Variables defined in modules need their declaring elements added to the program.
410*c8dee2aaSAndroid Build Coastguard Worker Transform::FindAndDeclareBuiltinVariables(program);
411*c8dee2aaSAndroid Build Coastguard Worker
412*c8dee2aaSAndroid Build Coastguard Worker // Structs from module code need to be added to the program's shared elements.
413*c8dee2aaSAndroid Build Coastguard Worker Transform::FindAndDeclareBuiltinStructs(program);
414*c8dee2aaSAndroid Build Coastguard Worker
415*c8dee2aaSAndroid Build Coastguard Worker // Do one last correctness-check pass. This looks for dangling FunctionReference/TypeReference
416*c8dee2aaSAndroid Build Coastguard Worker // expressions, and reports them as errors.
417*c8dee2aaSAndroid Build Coastguard Worker Analysis::DoFinalizationChecks(program);
418*c8dee2aaSAndroid Build Coastguard Worker
419*c8dee2aaSAndroid Build Coastguard Worker if (fContext->fConfig->strictES2Mode() && this->errorCount() == 0) {
420*c8dee2aaSAndroid Build Coastguard Worker // Enforce Appendix A, Section 5 of the GLSL ES 1.00 spec -- Indexing. This logic assumes
421*c8dee2aaSAndroid Build Coastguard Worker // that all loops meet the criteria of Section 4, and if they don't, could crash.
422*c8dee2aaSAndroid Build Coastguard Worker for (const auto& pe : program.fOwnedElements) {
423*c8dee2aaSAndroid Build Coastguard Worker Analysis::ValidateIndexingForES2(*pe, this->errorReporter());
424*c8dee2aaSAndroid Build Coastguard Worker }
425*c8dee2aaSAndroid Build Coastguard Worker }
426*c8dee2aaSAndroid Build Coastguard Worker if (this->errorCount() == 0) {
427*c8dee2aaSAndroid Build Coastguard Worker Analysis::CheckProgramStructure(program);
428*c8dee2aaSAndroid Build Coastguard Worker
429*c8dee2aaSAndroid Build Coastguard Worker // Make sure that variables are declared in the symbol tables that immediately enclose them.
430*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGCODE(Analysis::CheckSymbolTableCorrectness(program));
431*c8dee2aaSAndroid Build Coastguard Worker }
432*c8dee2aaSAndroid Build Coastguard Worker
433*c8dee2aaSAndroid Build Coastguard Worker // Make sure that program usage is still correct after finalization is complete.
434*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(*program.usage() == *Analysis::GetUsage(program));
435*c8dee2aaSAndroid Build Coastguard Worker
436*c8dee2aaSAndroid Build Coastguard Worker return this->errorCount() == 0;
437*c8dee2aaSAndroid Build Coastguard Worker }
438*c8dee2aaSAndroid Build Coastguard Worker
handleError(std::string_view msg,Position pos)439*c8dee2aaSAndroid Build Coastguard Worker void Compiler::handleError(std::string_view msg, Position pos) {
440*c8dee2aaSAndroid Build Coastguard Worker fErrorText += "error: ";
441*c8dee2aaSAndroid Build Coastguard Worker bool printLocation = false;
442*c8dee2aaSAndroid Build Coastguard Worker std::string_view src = this->errorReporter().source();
443*c8dee2aaSAndroid Build Coastguard Worker int line = -1;
444*c8dee2aaSAndroid Build Coastguard Worker if (pos.valid()) {
445*c8dee2aaSAndroid Build Coastguard Worker line = pos.line(src);
446*c8dee2aaSAndroid Build Coastguard Worker printLocation = pos.startOffset() < (int)src.length();
447*c8dee2aaSAndroid Build Coastguard Worker fErrorText += std::to_string(line) + ": ";
448*c8dee2aaSAndroid Build Coastguard Worker }
449*c8dee2aaSAndroid Build Coastguard Worker fErrorText += std::string(msg) + "\n";
450*c8dee2aaSAndroid Build Coastguard Worker if (printLocation) {
451*c8dee2aaSAndroid Build Coastguard Worker const int kMaxSurroundingChars = 100;
452*c8dee2aaSAndroid Build Coastguard Worker
453*c8dee2aaSAndroid Build Coastguard Worker // Find the beginning of the line.
454*c8dee2aaSAndroid Build Coastguard Worker int lineStart = pos.startOffset();
455*c8dee2aaSAndroid Build Coastguard Worker while (lineStart > 0) {
456*c8dee2aaSAndroid Build Coastguard Worker if (src[lineStart - 1] == '\n') {
457*c8dee2aaSAndroid Build Coastguard Worker break;
458*c8dee2aaSAndroid Build Coastguard Worker }
459*c8dee2aaSAndroid Build Coastguard Worker --lineStart;
460*c8dee2aaSAndroid Build Coastguard Worker }
461*c8dee2aaSAndroid Build Coastguard Worker
462*c8dee2aaSAndroid Build Coastguard Worker // We don't want to show more than 100 characters surrounding the error, so push the line
463*c8dee2aaSAndroid Build Coastguard Worker // start forward and add a leading ellipsis if there would be more than this.
464*c8dee2aaSAndroid Build Coastguard Worker std::string lineText;
465*c8dee2aaSAndroid Build Coastguard Worker std::string caretText;
466*c8dee2aaSAndroid Build Coastguard Worker if ((pos.startOffset() - lineStart) > kMaxSurroundingChars) {
467*c8dee2aaSAndroid Build Coastguard Worker lineStart = pos.startOffset() - kMaxSurroundingChars;
468*c8dee2aaSAndroid Build Coastguard Worker lineText = "...";
469*c8dee2aaSAndroid Build Coastguard Worker caretText = " ";
470*c8dee2aaSAndroid Build Coastguard Worker }
471*c8dee2aaSAndroid Build Coastguard Worker
472*c8dee2aaSAndroid Build Coastguard Worker // Echo the line. Again, we don't want to show more than 100 characters after the end of the
473*c8dee2aaSAndroid Build Coastguard Worker // error, so truncate with a trailing ellipsis if needed.
474*c8dee2aaSAndroid Build Coastguard Worker const char* lineSuffix = "...\n";
475*c8dee2aaSAndroid Build Coastguard Worker int lineStop = pos.endOffset() + kMaxSurroundingChars;
476*c8dee2aaSAndroid Build Coastguard Worker if (lineStop >= (int)src.length()) {
477*c8dee2aaSAndroid Build Coastguard Worker lineStop = src.length() - 1;
478*c8dee2aaSAndroid Build Coastguard Worker lineSuffix = "\n"; // no ellipsis if we reach end-of-file
479*c8dee2aaSAndroid Build Coastguard Worker }
480*c8dee2aaSAndroid Build Coastguard Worker for (int i = lineStart; i < lineStop; ++i) {
481*c8dee2aaSAndroid Build Coastguard Worker char c = src[i];
482*c8dee2aaSAndroid Build Coastguard Worker if (c == '\n') {
483*c8dee2aaSAndroid Build Coastguard Worker lineSuffix = "\n"; // no ellipsis if we reach end-of-line
484*c8dee2aaSAndroid Build Coastguard Worker break;
485*c8dee2aaSAndroid Build Coastguard Worker }
486*c8dee2aaSAndroid Build Coastguard Worker switch (c) {
487*c8dee2aaSAndroid Build Coastguard Worker case '\t': lineText += " "; break;
488*c8dee2aaSAndroid Build Coastguard Worker case '\0': lineText += " "; break;
489*c8dee2aaSAndroid Build Coastguard Worker default: lineText += src[i]; break;
490*c8dee2aaSAndroid Build Coastguard Worker }
491*c8dee2aaSAndroid Build Coastguard Worker }
492*c8dee2aaSAndroid Build Coastguard Worker fErrorText += lineText + lineSuffix;
493*c8dee2aaSAndroid Build Coastguard Worker
494*c8dee2aaSAndroid Build Coastguard Worker // print the carets underneath it, pointing to the range in question
495*c8dee2aaSAndroid Build Coastguard Worker for (int i = lineStart; i < (int)src.length(); i++) {
496*c8dee2aaSAndroid Build Coastguard Worker if (i >= pos.endOffset()) {
497*c8dee2aaSAndroid Build Coastguard Worker break;
498*c8dee2aaSAndroid Build Coastguard Worker }
499*c8dee2aaSAndroid Build Coastguard Worker switch (src[i]) {
500*c8dee2aaSAndroid Build Coastguard Worker case '\t':
501*c8dee2aaSAndroid Build Coastguard Worker caretText += (i >= pos.startOffset()) ? "^^^^" : " ";
502*c8dee2aaSAndroid Build Coastguard Worker break;
503*c8dee2aaSAndroid Build Coastguard Worker case '\n':
504*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(i >= pos.startOffset());
505*c8dee2aaSAndroid Build Coastguard Worker // use an ellipsis if the error continues past the end of the line
506*c8dee2aaSAndroid Build Coastguard Worker caretText += (pos.endOffset() > i + 1) ? "..." : "^";
507*c8dee2aaSAndroid Build Coastguard Worker i = src.length();
508*c8dee2aaSAndroid Build Coastguard Worker break;
509*c8dee2aaSAndroid Build Coastguard Worker default:
510*c8dee2aaSAndroid Build Coastguard Worker caretText += (i >= pos.startOffset()) ? '^' : ' ';
511*c8dee2aaSAndroid Build Coastguard Worker break;
512*c8dee2aaSAndroid Build Coastguard Worker }
513*c8dee2aaSAndroid Build Coastguard Worker }
514*c8dee2aaSAndroid Build Coastguard Worker fErrorText += caretText + '\n';
515*c8dee2aaSAndroid Build Coastguard Worker }
516*c8dee2aaSAndroid Build Coastguard Worker }
517*c8dee2aaSAndroid Build Coastguard Worker
errorText(bool showCount)518*c8dee2aaSAndroid Build Coastguard Worker std::string Compiler::errorText(bool showCount) {
519*c8dee2aaSAndroid Build Coastguard Worker if (showCount) {
520*c8dee2aaSAndroid Build Coastguard Worker this->writeErrorCount();
521*c8dee2aaSAndroid Build Coastguard Worker }
522*c8dee2aaSAndroid Build Coastguard Worker std::string result = fErrorText;
523*c8dee2aaSAndroid Build Coastguard Worker this->resetErrors();
524*c8dee2aaSAndroid Build Coastguard Worker return result;
525*c8dee2aaSAndroid Build Coastguard Worker }
526*c8dee2aaSAndroid Build Coastguard Worker
writeErrorCount()527*c8dee2aaSAndroid Build Coastguard Worker void Compiler::writeErrorCount() {
528*c8dee2aaSAndroid Build Coastguard Worker int count = this->errorCount();
529*c8dee2aaSAndroid Build Coastguard Worker if (count) {
530*c8dee2aaSAndroid Build Coastguard Worker fErrorText += std::to_string(count) +
531*c8dee2aaSAndroid Build Coastguard Worker ((count == 1) ? " error\n" : " errors\n");
532*c8dee2aaSAndroid Build Coastguard Worker }
533*c8dee2aaSAndroid Build Coastguard Worker }
534*c8dee2aaSAndroid Build Coastguard Worker
535*c8dee2aaSAndroid Build Coastguard Worker } // namespace SkSL
536