xref: /aosp_15_r20/external/skia/src/sksl/SkSLCompiler.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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