blob: cc00c955d218106f9c2c7d847ec4c6a63475e010 [file] [log] [blame]
ethannicholasb3058bd2016-07-01 08:22:01 -07001/*
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 */
Mike Klein6ad99092016-10-26 10:35:22 -04007
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/sksl/SkSLCompiler.h"
ethannicholasb3058bd2016-07-01 08:22:01 -07009
John Stilesfbd050b2020-08-03 13:21:46 -040010#include <memory>
John Stilesb8e010c2020-08-11 18:05:39 -040011#include <unordered_set>
John Stilesfbd050b2020-08-03 13:21:46 -040012
John Stilesb92641c2020-08-31 18:09:01 -040013#include "src/sksl/SkSLAnalysis.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050014#include "src/sksl/SkSLCFGGenerator.h"
15#include "src/sksl/SkSLCPPCodeGenerator.h"
16#include "src/sksl/SkSLGLSLCodeGenerator.h"
17#include "src/sksl/SkSLHCodeGenerator.h"
18#include "src/sksl/SkSLIRGenerator.h"
19#include "src/sksl/SkSLMetalCodeGenerator.h"
Brian Osman00185012021-02-04 16:07:11 -050020#include "src/sksl/SkSLOperators.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "src/sksl/SkSLPipelineStageCodeGenerator.h"
Ethan Nicholasc18bb512020-07-28 14:46:53 -040022#include "src/sksl/SkSLRehydrator.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050023#include "src/sksl/SkSLSPIRVCodeGenerator.h"
Brian Osmanc0243912020-02-19 15:35:26 -050024#include "src/sksl/SkSLSPIRVtoHLSL.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "src/sksl/ir/SkSLEnum.h"
26#include "src/sksl/ir/SkSLExpression.h"
27#include "src/sksl/ir/SkSLExpressionStatement.h"
28#include "src/sksl/ir/SkSLFunctionCall.h"
29#include "src/sksl/ir/SkSLIntLiteral.h"
30#include "src/sksl/ir/SkSLModifiersDeclaration.h"
31#include "src/sksl/ir/SkSLNop.h"
32#include "src/sksl/ir/SkSLSymbolTable.h"
33#include "src/sksl/ir/SkSLTernaryExpression.h"
34#include "src/sksl/ir/SkSLUnresolvedFunction.h"
35#include "src/sksl/ir/SkSLVarDeclarations.h"
John Stilese6150002020-10-05 12:03:53 -040036#include "src/utils/SkBitSet.h"
ethannicholasb3058bd2016-07-01 08:22:01 -070037
Ethan Nicholasb33fa3f2020-08-06 13:00:19 -040038#include <fstream>
39
Ethan Nicholasa11035b2019-11-26 16:27:47 -050040#if !defined(SKSL_STANDALONE) & SK_SUPPORT_GPU
41#include "include/gpu/GrContextOptions.h"
42#include "src/gpu/GrShaderCaps.h"
43#endif
44
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -040045#ifdef SK_ENABLE_SPIRV_VALIDATION
46#include "spirv-tools/libspirv.hpp"
47#endif
48
Brian Osman3d87e9f2020-10-08 11:50:22 -040049#if defined(SKSL_STANDALONE)
Ethan Nicholasc18bb512020-07-28 14:46:53 -040050
Brian Osman3d87e9f2020-10-08 11:50:22 -040051// In standalone mode, we load the textual sksl source files. GN generates or copies these files
52// to the skslc executable directory. The "data" in this mode is just the filename.
53#define MODULE_DATA(name) MakeModulePath("sksl_" #name ".sksl")
54
55#else
56
57// At runtime, we load the dehydrated sksl data files. The data is a (pointer, size) pair.
Ethan Nicholasc18bb512020-07-28 14:46:53 -040058#include "src/sksl/generated/sksl_fp.dehydrated.sksl"
59#include "src/sksl/generated/sksl_frag.dehydrated.sksl"
60#include "src/sksl/generated/sksl_geom.dehydrated.sksl"
61#include "src/sksl/generated/sksl_gpu.dehydrated.sksl"
Brian Osmanb06301e2020-11-06 11:45:36 -050062#include "src/sksl/generated/sksl_public.dehydrated.sksl"
Brian Osman91946752020-12-21 13:20:40 -050063#include "src/sksl/generated/sksl_runtime.dehydrated.sksl"
Ethan Nicholasc18bb512020-07-28 14:46:53 -040064#include "src/sksl/generated/sksl_vert.dehydrated.sksl"
65
Brian Osman3d87e9f2020-10-08 11:50:22 -040066#define MODULE_DATA(name) MakeModuleData(SKSL_INCLUDE_sksl_##name,\
67 SKSL_INCLUDE_sksl_##name##_LENGTH)
Ethan Nicholasc18bb512020-07-28 14:46:53 -040068
69#endif
Ethan Nicholas0d997662019-04-08 09:46:01 -040070
ethannicholasb3058bd2016-07-01 08:22:01 -070071namespace SkSL {
72
Brian Osman88cda172020-10-09 12:05:16 -040073class AutoSource {
74public:
75 AutoSource(Compiler* compiler, const String* source)
76 : fCompiler(compiler), fOldSource(fCompiler->fSource) {
77 fCompiler->fSource = source;
78 }
79
80 ~AutoSource() { fCompiler->fSource = fOldSource; }
81
82 Compiler* fCompiler;
83 const String* fOldSource;
84};
85
Brian Osmand7e76592020-11-02 12:26:22 -050086Compiler::Compiler(const ShaderCapsClass* caps, Flags flags)
John Stilesb30151e2021-01-11 16:13:08 -050087 : fContext(std::make_shared<Context>(/*errors=*/*this))
Brian Osman0006ad02020-11-18 15:38:39 -050088 , fCaps(caps)
John Stiles7b920442020-12-17 10:43:41 -050089 , fInliner(fContext.get())
Brian Osman0006ad02020-11-18 15:38:39 -050090 , fFlags(flags)
91 , fErrorCount(0) {
92 SkASSERT(fCaps);
John Stiles7c3515b2020-10-16 18:38:39 -040093 fRootSymbolTable = std::make_shared<SymbolTable>(this, /*builtin=*/true);
Brian Osmanb06301e2020-11-06 11:45:36 -050094 fPrivateSymbolTable = std::make_shared<SymbolTable>(fRootSymbolTable, /*builtin=*/true);
John Stilesb30151e2021-01-11 16:13:08 -050095 fIRGenerator = std::make_unique<IRGenerator>(fContext.get(), fCaps);
ethannicholasb3058bd2016-07-01 08:22:01 -070096
John Stiles54e7c052021-01-11 14:22:36 -050097#define TYPE(t) fContext->fTypes.f ## t .get()
ethannicholasb3058bd2016-07-01 08:22:01 -070098
Brian Osmanb06301e2020-11-06 11:45:36 -050099 const SkSL::Symbol* rootTypes[] = {
100 TYPE(Void),
Brian Salomonbf7b6202016-11-11 16:08:03 -0500101
Brian Osmanb06301e2020-11-06 11:45:36 -0500102 TYPE( Float), TYPE( Float2), TYPE( Float3), TYPE( Float4),
103 TYPE( Half), TYPE( Half2), TYPE( Half3), TYPE( Half4),
104 TYPE( Int), TYPE( Int2), TYPE( Int3), TYPE( Int4),
Brian Osmanb06301e2020-11-06 11:45:36 -0500105 TYPE( Bool), TYPE( Bool2), TYPE( Bool3), TYPE( Bool4),
Brian Salomon2a51de82016-11-16 12:06:01 -0500106
Brian Osmanc0f2b642020-12-22 13:35:55 -0500107 TYPE(Float2x2), TYPE(Float3x3), TYPE(Float4x4),
Brian Osmanc63f4312020-12-23 11:44:14 -0500108 TYPE( Half2x2), TYPE( Half3x3), TYPE(Half4x4),
Greg Daniel64773e62016-11-22 09:44:03 -0500109
Brian Osmanc63f4312020-12-23 11:44:14 -0500110 TYPE(SquareMat), TYPE(SquareHMat),
ethannicholasb3058bd2016-07-01 08:22:01 -0700111
Brian Osman20fad322020-12-23 12:42:33 -0500112 TYPE(GenType), TYPE(GenHType), TYPE(GenIType), TYPE(GenBType),
113 TYPE(Vec), TYPE(HVec), TYPE(IVec), TYPE(BVec),
Brian Osmanb06301e2020-11-06 11:45:36 -0500114
115 TYPE(FragmentProcessor),
116 };
117
118 const SkSL::Symbol* privateTypes[] = {
Brian Osman20fad322020-12-23 12:42:33 -0500119 TYPE( UInt), TYPE( UInt2), TYPE( UInt3), TYPE( UInt4),
120 TYPE( Short), TYPE( Short2), TYPE( Short3), TYPE( Short4),
121 TYPE(UShort), TYPE(UShort2), TYPE(UShort3), TYPE(UShort4),
122 TYPE( Byte), TYPE( Byte2), TYPE( Byte3), TYPE( Byte4),
123 TYPE( UByte), TYPE( UByte2), TYPE( UByte3), TYPE( UByte4),
124
125 TYPE(GenUType), TYPE(UVec),
126 TYPE(SVec), TYPE(USVec), TYPE(ByteVec), TYPE(UByteVec),
127
Brian Osmanc0f2b642020-12-22 13:35:55 -0500128 TYPE(Float2x3), TYPE(Float2x4),
129 TYPE(Float3x2), TYPE(Float3x4),
130 TYPE(Float4x2), TYPE(Float4x3),
131
Brian Osmanc63f4312020-12-23 11:44:14 -0500132 TYPE(Half2x3), TYPE(Half2x4),
133 TYPE(Half3x2), TYPE(Half3x4),
134 TYPE(Half4x2), TYPE(Half4x3),
135
Brian Osmanc0f2b642020-12-22 13:35:55 -0500136 TYPE(Mat), TYPE(HMat),
137
Brian Osmanb06301e2020-11-06 11:45:36 -0500138 TYPE(Sampler1D), TYPE(Sampler2D), TYPE(Sampler3D),
139 TYPE(SamplerExternalOES),
Brian Osmanb06301e2020-11-06 11:45:36 -0500140 TYPE(Sampler2DRect),
Brian Osmanb06301e2020-11-06 11:45:36 -0500141
142 TYPE(ISampler2D),
143 TYPE(Image2D), TYPE(IImage2D),
144 TYPE(SubpassInput), TYPE(SubpassInputMS),
145
Brian Osmanb06301e2020-11-06 11:45:36 -0500146 TYPE(Sampler),
147 TYPE(Texture2D),
148 };
149
150 for (const SkSL::Symbol* type : rootTypes) {
151 fRootSymbolTable->addWithoutOwnership(type);
152 }
153 for (const SkSL::Symbol* type : privateTypes) {
154 fPrivateSymbolTable->addWithoutOwnership(type);
155 }
156
157#undef TYPE
ethannicholasb3058bd2016-07-01 08:22:01 -0700158
Brian Osman3887a012020-09-30 13:22:27 -0400159 // sk_Caps is "builtin", but all references to it are resolved to Settings, so we don't need to
160 // treat it as builtin (ie, no need to clone it into the Program).
Brian Osmanb06301e2020-11-06 11:45:36 -0500161 fPrivateSymbolTable->add(
162 std::make_unique<Variable>(/*offset=*/-1,
John Stiles586df952020-11-12 18:27:13 -0500163 fIRGenerator->fModifiers->addToPool(Modifiers()),
Brian Osmanb06301e2020-11-06 11:45:36 -0500164 "sk_Caps",
John Stiles54e7c052021-01-11 14:22:36 -0500165 fContext->fTypes.fSkCaps.get(),
Brian Osmanb06301e2020-11-06 11:45:36 -0500166 /*builtin=*/false,
167 Variable::Storage::kGlobal));
Ethan Nicholas3605ace2016-11-21 15:59:48 -0500168
Brian Osman3d87e9f2020-10-08 11:50:22 -0400169 fRootModule = {fRootSymbolTable, /*fIntrinsics=*/nullptr};
Brian Osmanb06301e2020-11-06 11:45:36 -0500170 fPrivateModule = {fPrivateSymbolTable, /*fIntrinsics=*/nullptr};
ethannicholasb3058bd2016-07-01 08:22:01 -0700171}
172
John Stilesdd13dba2020-10-29 10:45:34 -0400173Compiler::~Compiler() {}
ethannicholasb3058bd2016-07-01 08:22:01 -0700174
Brian Osman56269982020-11-20 12:38:07 -0500175const ParsedModule& Compiler::loadGPUModule() {
176 if (!fGPUModule.fSymbols) {
177 fGPUModule = this->parseModule(Program::kFragment_Kind, MODULE_DATA(gpu), fPrivateModule);
178 }
179 return fGPUModule;
180}
181
182const ParsedModule& Compiler::loadFragmentModule() {
183 if (!fFragmentModule.fSymbols) {
184 fFragmentModule = this->parseModule(Program::kFragment_Kind, MODULE_DATA(frag),
185 this->loadGPUModule());
186 }
187 return fFragmentModule;
188}
189
190const ParsedModule& Compiler::loadVertexModule() {
191 if (!fVertexModule.fSymbols) {
192 fVertexModule = this->parseModule(Program::kVertex_Kind, MODULE_DATA(vert),
193 this->loadGPUModule());
194 }
195 return fVertexModule;
196}
197
Brian Osman88cda172020-10-09 12:05:16 -0400198const ParsedModule& Compiler::loadGeometryModule() {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400199 if (!fGeometryModule.fSymbols) {
Brian Osman56269982020-11-20 12:38:07 -0500200 fGeometryModule = this->parseModule(Program::kGeometry_Kind, MODULE_DATA(geom),
201 this->loadGPUModule());
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400202 }
Brian Osman88cda172020-10-09 12:05:16 -0400203 return fGeometryModule;
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400204}
205
Brian Osman88cda172020-10-09 12:05:16 -0400206const ParsedModule& Compiler::loadFPModule() {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400207 if (!fFPModule.fSymbols) {
Brian Osman56269982020-11-20 12:38:07 -0500208 fFPModule = this->parseModule(Program::kFragmentProcessor_Kind, MODULE_DATA(fp),
209 this->loadGPUModule());
Brian Osman8e2ef022020-09-30 13:26:43 -0400210 }
Brian Osman88cda172020-10-09 12:05:16 -0400211 return fFPModule;
Brian Osman8e2ef022020-09-30 13:26:43 -0400212}
213
Brian Osmanb06301e2020-11-06 11:45:36 -0500214const ParsedModule& Compiler::loadPublicModule() {
215 if (!fPublicModule.fSymbols) {
216 fPublicModule = this->parseModule(Program::kGeneric_Kind, MODULE_DATA(public), fRootModule);
217 }
218 return fPublicModule;
219}
220
Brian Osman91946752020-12-21 13:20:40 -0500221const ParsedModule& Compiler::loadRuntimeEffectModule() {
222 if (!fRuntimeEffectModule.fSymbols) {
223 fRuntimeEffectModule = this->parseModule(Program::kRuntimeEffect_Kind, MODULE_DATA(runtime),
224 this->loadPublicModule());
Brian Osmanf1319c32020-10-13 09:34:23 -0400225
Brian Osman91946752020-12-21 13:20:40 -0500226 // Add some aliases to the runtime effect module so that it's friendlier, and more like GLSL
John Stiles54e7c052021-01-11 14:22:36 -0500227 fRuntimeEffectModule.fSymbols->addAlias("shader", fContext->fTypes.fFragmentProcessor.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400228
John Stiles54e7c052021-01-11 14:22:36 -0500229 fRuntimeEffectModule.fSymbols->addAlias("vec2", fContext->fTypes.fFloat2.get());
230 fRuntimeEffectModule.fSymbols->addAlias("vec3", fContext->fTypes.fFloat3.get());
231 fRuntimeEffectModule.fSymbols->addAlias("vec4", fContext->fTypes.fFloat4.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400232
John Stiles54e7c052021-01-11 14:22:36 -0500233 fRuntimeEffectModule.fSymbols->addAlias("bvec2", fContext->fTypes.fBool2.get());
234 fRuntimeEffectModule.fSymbols->addAlias("bvec3", fContext->fTypes.fBool3.get());
235 fRuntimeEffectModule.fSymbols->addAlias("bvec4", fContext->fTypes.fBool4.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400236
John Stiles54e7c052021-01-11 14:22:36 -0500237 fRuntimeEffectModule.fSymbols->addAlias("mat2", fContext->fTypes.fFloat2x2.get());
238 fRuntimeEffectModule.fSymbols->addAlias("mat3", fContext->fTypes.fFloat3x3.get());
239 fRuntimeEffectModule.fSymbols->addAlias("mat4", fContext->fTypes.fFloat4x4.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400240
John Stiles54e7c052021-01-11 14:22:36 -0500241 fRuntimeEffectModule.fSymbols->addAlias("mat2x2", fContext->fTypes.fFloat2x2.get());
242 fRuntimeEffectModule.fSymbols->addAlias("mat2x3", fContext->fTypes.fFloat2x3.get());
243 fRuntimeEffectModule.fSymbols->addAlias("mat2x4", fContext->fTypes.fFloat2x4.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400244
John Stiles54e7c052021-01-11 14:22:36 -0500245 fRuntimeEffectModule.fSymbols->addAlias("mat3x2", fContext->fTypes.fFloat3x2.get());
246 fRuntimeEffectModule.fSymbols->addAlias("mat3x3", fContext->fTypes.fFloat3x3.get());
247 fRuntimeEffectModule.fSymbols->addAlias("mat3x4", fContext->fTypes.fFloat3x4.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400248
John Stiles54e7c052021-01-11 14:22:36 -0500249 fRuntimeEffectModule.fSymbols->addAlias("mat4x2", fContext->fTypes.fFloat4x2.get());
250 fRuntimeEffectModule.fSymbols->addAlias("mat4x3", fContext->fTypes.fFloat4x3.get());
251 fRuntimeEffectModule.fSymbols->addAlias("mat4x4", fContext->fTypes.fFloat4x4.get());
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400252 }
Brian Osman91946752020-12-21 13:20:40 -0500253 return fRuntimeEffectModule;
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400254}
255
Brian Osman88cda172020-10-09 12:05:16 -0400256const ParsedModule& Compiler::moduleForProgramKind(Program::Kind kind) {
257 switch (kind) {
Brian Osman91946752020-12-21 13:20:40 -0500258 case Program::kVertex_Kind: return this->loadVertexModule(); break;
259 case Program::kFragment_Kind: return this->loadFragmentModule(); break;
260 case Program::kGeometry_Kind: return this->loadGeometryModule(); break;
261 case Program::kFragmentProcessor_Kind: return this->loadFPModule(); break;
262 case Program::kRuntimeEffect_Kind: return this->loadRuntimeEffectModule(); break;
Brian Osmance750362021-01-21 16:33:06 -0500263 case Program::kGeneric_Kind: return this->loadPublicModule(); break;
Brian Osman88cda172020-10-09 12:05:16 -0400264 }
265 SkUNREACHABLE;
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400266}
267
Brian Osman3d87e9f2020-10-08 11:50:22 -0400268LoadedModule Compiler::loadModule(Program::Kind kind,
269 ModuleData data,
270 std::shared_ptr<SymbolTable> base) {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400271 if (!base) {
Brian Osmanb06301e2020-11-06 11:45:36 -0500272 // NOTE: This is a workaround. The only time 'base' is null is when dehydrating includes.
273 // In that case, skslc doesn't know which module it's preparing, nor what the correct base
274 // module is. We can't use 'Root', because many GPU intrinsics reference private types,
275 // like samplers or textures. Today, 'Private' does contain the union of all known types,
276 // so this is safe. If we ever have types that only exist in 'Public' (for example), this
277 // logic needs to be smarter (by choosing the correct base for the module we're compiling).
278 base = fPrivateSymbolTable;
Brian Osman3d87e9f2020-10-08 11:50:22 -0400279 }
280
281#if defined(SKSL_STANDALONE)
282 SkASSERT(data.fPath);
283 std::ifstream in(data.fPath);
Brian Osmane498b3c2020-09-23 14:42:11 -0400284 std::unique_ptr<String> text = std::make_unique<String>(std::istreambuf_iterator<char>(in),
285 std::istreambuf_iterator<char>());
Ethan Nicholasb33fa3f2020-08-06 13:00:19 -0400286 if (in.rdstate()) {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400287 printf("error reading %s\n", data.fPath);
Ethan Nicholasb33fa3f2020-08-06 13:00:19 -0400288 abort();
289 }
Brian Osmane498b3c2020-09-23 14:42:11 -0400290 const String* source = fRootSymbolTable->takeOwnershipOfString(std::move(text));
Brian Osman88cda172020-10-09 12:05:16 -0400291 AutoSource as(this, source);
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400292 Program::Settings settings;
John Stiles881a10c2020-09-19 10:13:24 -0400293 SkASSERT(fIRGenerator->fCanInline);
294 fIRGenerator->fCanInline = false;
Brian Osman88cda172020-10-09 12:05:16 -0400295 ParsedModule baseModule = {base, /*fIntrinsics=*/nullptr};
Brian Osmand7e76592020-11-02 12:26:22 -0500296 IRGenerator::IRBundle ir =
Brian Osman0006ad02020-11-18 15:38:39 -0500297 fIRGenerator->convertProgram(kind, &settings, baseModule,
Brian Osmand7e76592020-11-02 12:26:22 -0500298 /*isBuiltinCode=*/true, source->c_str(), source->length(),
Brian Osmanbe0b3b72021-01-06 14:27:35 -0500299 /*externalFunctions=*/nullptr);
Brian Osman133724c2020-10-28 14:14:39 -0400300 SkASSERT(ir.fSharedElements.empty());
Brian Osman0006ad02020-11-18 15:38:39 -0500301 LoadedModule module = { kind, std::move(ir.fSymbolTable), std::move(ir.fElements) };
John Stiles881a10c2020-09-19 10:13:24 -0400302 fIRGenerator->fCanInline = true;
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400303 if (this->fErrorCount) {
304 printf("Unexpected errors: %s\n", this->fErrorText.c_str());
Brian Osman3d87e9f2020-10-08 11:50:22 -0400305 SkDEBUGFAILF("%s %s\n", data.fPath, this->fErrorText.c_str());
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400306 }
Brian Osman88cda172020-10-09 12:05:16 -0400307 fModifiers.push_back(std::move(ir.fModifiers));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400308#else
309 SkASSERT(data.fData && (data.fSize != 0));
310 Rehydrator rehydrator(fContext.get(), fIRGenerator->fModifiers.get(), base, this,
311 data.fData, data.fSize);
Brian Osman0006ad02020-11-18 15:38:39 -0500312 LoadedModule module = { kind, rehydrator.symbolTable(), rehydrator.elements() };
Brian Osman3d87e9f2020-10-08 11:50:22 -0400313 fModifiers.push_back(fIRGenerator->releaseModifiers());
314#endif
315
316 return module;
317}
318
319ParsedModule Compiler::parseModule(Program::Kind kind, ModuleData data, const ParsedModule& base) {
Brian Osman0006ad02020-11-18 15:38:39 -0500320 LoadedModule module = this->loadModule(kind, data, base.fSymbols);
321 this->optimize(module);
Brian Osman3d87e9f2020-10-08 11:50:22 -0400322
323 // For modules that just declare (but don't define) intrinsic functions, there will be no new
324 // program elements. In that case, we can share our parent's intrinsic map:
Brian Osman0006ad02020-11-18 15:38:39 -0500325 if (module.fElements.empty()) {
326 return {module.fSymbols, base.fIntrinsics};
Brian Osman3d87e9f2020-10-08 11:50:22 -0400327 }
328
329 auto intrinsics = std::make_shared<IRIntrinsicMap>(base.fIntrinsics.get());
330
331 // Now, transfer all of the program elements to an intrinsic map. This maps certain types of
332 // global objects to the declaring ProgramElement.
Brian Osman0006ad02020-11-18 15:38:39 -0500333 for (std::unique_ptr<ProgramElement>& element : module.fElements) {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400334 switch (element->kind()) {
335 case ProgramElement::Kind::kFunction: {
336 const FunctionDefinition& f = element->as<FunctionDefinition>();
Ethan Nicholas0a5d0962020-10-14 13:33:18 -0400337 SkASSERT(f.declaration().isBuiltin());
338 intrinsics->insertOrDie(f.declaration().description(), std::move(element));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400339 break;
340 }
John Stiles569249b2020-11-03 12:18:22 -0500341 case ProgramElement::Kind::kFunctionPrototype: {
342 // These are already in the symbol table.
343 break;
344 }
Brian Osman3d87e9f2020-10-08 11:50:22 -0400345 case ProgramElement::Kind::kEnum: {
346 const Enum& e = element->as<Enum>();
347 SkASSERT(e.isBuiltin());
348 intrinsics->insertOrDie(e.typeName(), std::move(element));
349 break;
350 }
351 case ProgramElement::Kind::kGlobalVar: {
Ethan Nicholasc51f33e2020-10-13 13:49:44 -0400352 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
353 const Variable& var = global.declaration()->as<VarDeclaration>().var();
354 SkASSERT(var.isBuiltin());
355 intrinsics->insertOrDie(var.name(), std::move(element));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400356 break;
357 }
358 case ProgramElement::Kind::kInterfaceBlock: {
Ethan Nicholaseaf47882020-10-15 10:10:08 -0400359 const Variable& var = element->as<InterfaceBlock>().variable();
360 SkASSERT(var.isBuiltin());
361 intrinsics->insertOrDie(var.name(), std::move(element));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400362 break;
363 }
364 default:
365 printf("Unsupported element: %s\n", element->description().c_str());
366 SkASSERT(false);
367 break;
368 }
369 }
370
Brian Osman0006ad02020-11-18 15:38:39 -0500371 return {module.fSymbols, std::move(intrinsics)};
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400372}
373
ethannicholas22f939e2016-10-13 13:25:34 -0700374// add the definition created by assigning to the lvalue to the definition set
Ethan Nicholas86a43402017-01-19 13:32:00 -0500375void Compiler::addDefinition(const Expression* lvalue, std::unique_ptr<Expression>* expr,
376 DefinitionMap* definitions) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400377 switch (lvalue->kind()) {
378 case Expression::Kind::kVariableReference: {
Ethan Nicholas78686922020-10-08 06:46:27 -0400379 const Variable& var = *lvalue->as<VariableReference>().variable();
Ethan Nicholas453f67f2020-10-09 10:43:45 -0400380 if (var.storage() == Variable::Storage::kLocal) {
John Stiles796cdb72020-10-08 12:06:53 -0400381 definitions->set(&var, expr);
ethannicholas22f939e2016-10-13 13:25:34 -0700382 }
383 break;
384 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400385 case Expression::Kind::kSwizzle:
ethannicholas22f939e2016-10-13 13:25:34 -0700386 // We consider the variable written to as long as at least some of its components have
387 // been written to. This will lead to some false negatives (we won't catch it if you
388 // write to foo.x and then read foo.y), but being stricter could lead to false positives
Mike Klein6ad99092016-10-26 10:35:22 -0400389 // (we write to foo.x, and then pass foo to a function which happens to only read foo.x,
390 // but since we pass foo as a whole it is flagged as an error) unless we perform a much
ethannicholas22f939e2016-10-13 13:25:34 -0700391 // more complicated whole-program analysis. This is probably good enough.
Ethan Nicholas6b4d5812020-10-12 16:11:51 -0400392 this->addDefinition(lvalue->as<Swizzle>().base().get(),
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400393 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
ethannicholas22f939e2016-10-13 13:25:34 -0700394 definitions);
395 break;
Ethan Nicholase6592142020-09-08 10:22:09 -0400396 case Expression::Kind::kIndex:
ethannicholas22f939e2016-10-13 13:25:34 -0700397 // see comments in Swizzle
Ethan Nicholas2a4952d2020-10-08 15:35:56 -0400398 this->addDefinition(lvalue->as<IndexExpression>().base().get(),
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400399 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
ethannicholas22f939e2016-10-13 13:25:34 -0700400 definitions);
401 break;
Ethan Nicholase6592142020-09-08 10:22:09 -0400402 case Expression::Kind::kFieldAccess:
ethannicholas22f939e2016-10-13 13:25:34 -0700403 // see comments in Swizzle
Ethan Nicholas7a95b202020-10-09 11:55:40 -0400404 this->addDefinition(lvalue->as<FieldAccess>().base().get(),
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400405 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
ethannicholas22f939e2016-10-13 13:25:34 -0700406 definitions);
407 break;
Ethan Nicholase6592142020-09-08 10:22:09 -0400408 case Expression::Kind::kTernary:
Ethan Nicholasa583b812018-01-18 13:32:11 -0500409 // To simplify analysis, we just pretend that we write to both sides of the ternary.
410 // This allows for false positives (meaning we fail to detect that a variable might not
411 // have been assigned), but is preferable to false negatives.
Ethan Nicholasdd218162020-10-08 05:48:01 -0400412 this->addDefinition(lvalue->as<TernaryExpression>().ifTrue().get(),
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400413 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
Ethan Nicholasa583b812018-01-18 13:32:11 -0500414 definitions);
Ethan Nicholasdd218162020-10-08 05:48:01 -0400415 this->addDefinition(lvalue->as<TernaryExpression>().ifFalse().get(),
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400416 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
Ethan Nicholasa583b812018-01-18 13:32:11 -0500417 definitions);
418 break;
ethannicholas22f939e2016-10-13 13:25:34 -0700419 default:
420 // not an lvalue, can't happen
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400421 SkASSERT(false);
ethannicholas22f939e2016-10-13 13:25:34 -0700422 }
423}
424
425// add local variables defined by this node to the set
John Stiles796cdb72020-10-08 12:06:53 -0400426void Compiler::addDefinitions(const BasicBlock::Node& node, DefinitionMap* definitions) {
John Stiles70025e52020-09-28 16:08:58 -0400427 if (node.isExpression()) {
428 Expression* expr = node.expression()->get();
429 switch (expr->kind()) {
430 case Expression::Kind::kBinary: {
431 BinaryExpression* b = &expr->as<BinaryExpression>();
432 if (b->getOperator() == Token::Kind::TK_EQ) {
John Stiles2d4f9592020-10-30 10:29:12 -0400433 this->addDefinition(b->left().get(), &b->right(), definitions);
Brian Osman00185012021-02-04 16:07:11 -0500434 } else if (Operators::IsAssignment(b->getOperator())) {
John Stiles70025e52020-09-28 16:08:58 -0400435 this->addDefinition(
John Stiles2d4f9592020-10-30 10:29:12 -0400436 b->left().get(),
John Stiles70025e52020-09-28 16:08:58 -0400437 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
438 definitions);
Ethan Nicholas86a43402017-01-19 13:32:00 -0500439
ethannicholas22f939e2016-10-13 13:25:34 -0700440 }
John Stiles70025e52020-09-28 16:08:58 -0400441 break;
ethannicholas22f939e2016-10-13 13:25:34 -0700442 }
John Stiles70025e52020-09-28 16:08:58 -0400443 case Expression::Kind::kFunctionCall: {
444 const FunctionCall& c = expr->as<FunctionCall>();
Brian Osman5bf3e202020-10-13 10:34:18 -0400445 const std::vector<const Variable*>& parameters = c.function().parameters();
Ethan Nicholased84b732020-10-08 11:45:44 -0400446 for (size_t i = 0; i < parameters.size(); ++i) {
447 if (parameters[i]->modifiers().fFlags & Modifiers::kOut_Flag) {
John Stiles70025e52020-09-28 16:08:58 -0400448 this->addDefinition(
Ethan Nicholas0dec9922020-10-05 15:51:52 -0400449 c.arguments()[i].get(),
John Stiles70025e52020-09-28 16:08:58 -0400450 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
451 definitions);
452 }
453 }
454 break;
455 }
456 case Expression::Kind::kPrefix: {
457 const PrefixExpression* p = &expr->as<PrefixExpression>();
Ethan Nicholas444ccc62020-10-09 10:16:22 -0400458 if (p->getOperator() == Token::Kind::TK_MINUSMINUS ||
459 p->getOperator() == Token::Kind::TK_PLUSPLUS) {
John Stiles70025e52020-09-28 16:08:58 -0400460 this->addDefinition(
Ethan Nicholas444ccc62020-10-09 10:16:22 -0400461 p->operand().get(),
John Stiles70025e52020-09-28 16:08:58 -0400462 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
463 definitions);
464 }
465 break;
466 }
467 case Expression::Kind::kPostfix: {
468 const PostfixExpression* p = &expr->as<PostfixExpression>();
Ethan Nicholas444ccc62020-10-09 10:16:22 -0400469 if (p->getOperator() == Token::Kind::TK_MINUSMINUS ||
470 p->getOperator() == Token::Kind::TK_PLUSPLUS) {
John Stiles70025e52020-09-28 16:08:58 -0400471 this->addDefinition(
Ethan Nicholas444ccc62020-10-09 10:16:22 -0400472 p->operand().get(),
John Stiles70025e52020-09-28 16:08:58 -0400473 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
474 definitions);
475 }
476 break;
477 }
478 case Expression::Kind::kVariableReference: {
479 const VariableReference* v = &expr->as<VariableReference>();
Ethan Nicholas453f67f2020-10-09 10:43:45 -0400480 if (v->refKind() != VariableReference::RefKind::kRead) {
John Stiles70025e52020-09-28 16:08:58 -0400481 this->addDefinition(
482 v,
483 (std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
484 definitions);
485 }
486 break;
487 }
488 default:
489 break;
ethannicholas22f939e2016-10-13 13:25:34 -0700490 }
John Stiles70025e52020-09-28 16:08:58 -0400491 } else if (node.isStatement()) {
492 Statement* stmt = node.statement()->get();
493 if (stmt->is<VarDeclaration>()) {
494 VarDeclaration& vd = stmt->as<VarDeclaration>();
Ethan Nicholasc51f33e2020-10-13 13:49:44 -0400495 if (vd.value()) {
496 definitions->set(&vd.var(), &vd.value());
ethannicholas22f939e2016-10-13 13:25:34 -0700497 }
ethannicholas22f939e2016-10-13 13:25:34 -0700498 }
499 }
500}
501
John Stilese6150002020-10-05 12:03:53 -0400502void Compiler::scanCFG(CFG* cfg, BlockId blockId, SkBitSet* processedSet) {
ethannicholas22f939e2016-10-13 13:25:34 -0700503 BasicBlock& block = cfg->fBlocks[blockId];
504
505 // compute definitions after this block
Ethan Nicholas86a43402017-01-19 13:32:00 -0500506 DefinitionMap after = block.fBefore;
ethannicholas22f939e2016-10-13 13:25:34 -0700507 for (const BasicBlock::Node& n : block.fNodes) {
508 this->addDefinitions(n, &after);
509 }
510
511 // propagate definitions to exits
512 for (BlockId exitId : block.fExits) {
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400513 if (exitId == blockId) {
514 continue;
515 }
ethannicholas22f939e2016-10-13 13:25:34 -0700516 BasicBlock& exit = cfg->fBlocks[exitId];
John Stiles65b48272020-12-22 17:18:34 -0500517 for (const auto& [var, e1] : after) {
John Stiles796cdb72020-10-08 12:06:53 -0400518 std::unique_ptr<Expression>** exitDef = exit.fBefore.find(var);
519 if (!exitDef) {
John Stilese6150002020-10-05 12:03:53 -0400520 // exit has no definition for it, just copy it and reprocess exit block
521 processedSet->reset(exitId);
John Stiles796cdb72020-10-08 12:06:53 -0400522 exit.fBefore[var] = e1;
ethannicholas22f939e2016-10-13 13:25:34 -0700523 } else {
Ethan Nicholas86a43402017-01-19 13:32:00 -0500524 // exit has a (possibly different) value already defined
John Stiles796cdb72020-10-08 12:06:53 -0400525 std::unique_ptr<Expression>* e2 = *exitDef;
ethannicholas22f939e2016-10-13 13:25:34 -0700526 if (e1 != e2) {
John Stilese6150002020-10-05 12:03:53 -0400527 // definition has changed, merge and reprocess the exit block
528 processedSet->reset(exitId);
Ethan Nicholasaf197692017-02-27 13:26:45 -0500529 if (e1 && e2) {
John Stiles796cdb72020-10-08 12:06:53 -0400530 *exitDef = (std::unique_ptr<Expression>*)&fContext->fDefined_Expression;
Ethan Nicholasaf197692017-02-27 13:26:45 -0500531 } else {
John Stiles796cdb72020-10-08 12:06:53 -0400532 *exitDef = nullptr;
Ethan Nicholasaf197692017-02-27 13:26:45 -0500533 }
ethannicholas22f939e2016-10-13 13:25:34 -0700534 }
535 }
John Stiles65b48272020-12-22 17:18:34 -0500536 }
ethannicholas22f939e2016-10-13 13:25:34 -0700537 }
538}
539
540// returns a map which maps all local variables in the function to null, indicating that their value
541// is initially unknown
Ethan Nicholas86a43402017-01-19 13:32:00 -0500542static DefinitionMap compute_start_state(const CFG& cfg) {
543 DefinitionMap result;
Mike Klein6ad99092016-10-26 10:35:22 -0400544 for (const auto& block : cfg.fBlocks) {
545 for (const auto& node : block.fNodes) {
John Stiles70025e52020-09-28 16:08:58 -0400546 if (node.isStatement()) {
Ethan Nicholascb670962017-04-20 19:31:52 -0400547 const Statement* s = node.statement()->get();
Brian Osmanc0213602020-10-06 14:43:32 -0400548 if (s->is<VarDeclaration>()) {
Ethan Nicholasc51f33e2020-10-13 13:49:44 -0400549 result[&s->as<VarDeclaration>().var()] = nullptr;
ethannicholas22f939e2016-10-13 13:25:34 -0700550 }
551 }
552 }
553 }
554 return result;
555}
556
Ethan Nicholascb670962017-04-20 19:31:52 -0400557/**
558 * Returns true if assigning to this lvalue has no effect.
559 */
Brian Osman010ce6a2020-10-19 16:34:10 -0400560static bool is_dead(const Expression& lvalue, ProgramUsage* usage) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400561 switch (lvalue.kind()) {
562 case Expression::Kind::kVariableReference:
Brian Osman010ce6a2020-10-19 16:34:10 -0400563 return usage->isDead(*lvalue.as<VariableReference>().variable());
Ethan Nicholase6592142020-09-08 10:22:09 -0400564 case Expression::Kind::kSwizzle:
Brian Osman010ce6a2020-10-19 16:34:10 -0400565 return is_dead(*lvalue.as<Swizzle>().base(), usage);
Ethan Nicholase6592142020-09-08 10:22:09 -0400566 case Expression::Kind::kFieldAccess:
Brian Osman010ce6a2020-10-19 16:34:10 -0400567 return is_dead(*lvalue.as<FieldAccess>().base(), usage);
Ethan Nicholase6592142020-09-08 10:22:09 -0400568 case Expression::Kind::kIndex: {
John Stilesa5a97b42020-08-18 11:19:07 -0400569 const IndexExpression& idx = lvalue.as<IndexExpression>();
Brian Osman010ce6a2020-10-19 16:34:10 -0400570 return is_dead(*idx.base(), usage) &&
Ethan Nicholas2a4952d2020-10-08 15:35:56 -0400571 !idx.index()->hasProperty(Expression::Property::kSideEffects);
Ethan Nicholascb670962017-04-20 19:31:52 -0400572 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400573 case Expression::Kind::kTernary: {
John Stilesa5a97b42020-08-18 11:19:07 -0400574 const TernaryExpression& t = lvalue.as<TernaryExpression>();
Brian Osman010ce6a2020-10-19 16:34:10 -0400575 return !t.test()->hasSideEffects() &&
576 is_dead(*t.ifTrue(), usage) &&
577 is_dead(*t.ifFalse(), usage);
Ethan Nicholasa583b812018-01-18 13:32:11 -0500578 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400579 default:
John Stileseada7bc2021-02-02 16:29:32 -0500580 SkDEBUGFAILF("invalid lvalue: %s\n", lvalue.description().c_str());
Ethan Nicholas2a099da2020-01-02 14:40:54 -0500581 return false;
Ethan Nicholascb670962017-04-20 19:31:52 -0400582 }
583}
ethannicholas22f939e2016-10-13 13:25:34 -0700584
Ethan Nicholascb670962017-04-20 19:31:52 -0400585/**
586 * Returns true if this is an assignment which can be collapsed down to just the right hand side due
587 * to a dead target and lack of side effects on the left hand side.
588 */
Brian Osman010ce6a2020-10-19 16:34:10 -0400589static bool dead_assignment(const BinaryExpression& b, ProgramUsage* usage) {
Brian Osman00185012021-02-04 16:07:11 -0500590 if (!Operators::IsAssignment(b.getOperator())) {
Ethan Nicholascb670962017-04-20 19:31:52 -0400591 return false;
592 }
John Stiles2d4f9592020-10-30 10:29:12 -0400593 return is_dead(*b.left(), usage);
Ethan Nicholascb670962017-04-20 19:31:52 -0400594}
595
596void Compiler::computeDataFlow(CFG* cfg) {
597 cfg->fBlocks[cfg->fStart].fBefore = compute_start_state(*cfg);
John Stilese6150002020-10-05 12:03:53 -0400598
599 // We set bits in the "processed" set after a block has been scanned.
600 SkBitSet processedSet(cfg->fBlocks.size());
601 while (SkBitSet::OptionalIndex blockId = processedSet.findFirstUnset()) {
602 processedSet.set(*blockId);
603 this->scanCFG(cfg, *blockId, &processedSet);
ethannicholas22f939e2016-10-13 13:25:34 -0700604 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400605}
606
607/**
608 * Attempts to replace the expression pointed to by iter with a new one (in both the CFG and the
609 * IR). If the expression can be cleanly removed, returns true and updates the iterator to point to
610 * the newly-inserted element. Otherwise updates only the IR and returns false (and the CFG will
611 * need to be regenerated).
612 */
John Stilesafbf8992020-08-18 10:08:21 -0400613static bool try_replace_expression(BasicBlock* b,
614 std::vector<BasicBlock::Node>::iterator* iter,
615 std::unique_ptr<Expression>* newExpression) {
Ethan Nicholascb670962017-04-20 19:31:52 -0400616 std::unique_ptr<Expression>* target = (*iter)->expression();
617 if (!b->tryRemoveExpression(iter)) {
618 *target = std::move(*newExpression);
619 return false;
620 }
621 *target = std::move(*newExpression);
622 return b->tryInsertExpression(iter, target);
623}
624
625/**
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400626 * Returns true if the expression is a constant numeric literal with the specified value, or a
627 * constant vector with all elements equal to the specified value.
Ethan Nicholascb670962017-04-20 19:31:52 -0400628 */
Ethan Nicholasa3f22f12020-10-01 12:13:17 -0400629template <typename T = SKSL_FLOAT>
John Stiles9d944232020-08-19 09:56:49 -0400630static bool is_constant(const Expression& expr, T value) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400631 switch (expr.kind()) {
632 case Expression::Kind::kIntLiteral:
Ethan Nicholase96cdd12020-09-28 16:27:18 -0400633 return expr.as<IntLiteral>().value() == value;
John Stiles9d944232020-08-19 09:56:49 -0400634
Ethan Nicholase6592142020-09-08 10:22:09 -0400635 case Expression::Kind::kFloatLiteral:
Ethan Nicholasa3f22f12020-10-01 12:13:17 -0400636 return expr.as<FloatLiteral>().value() == value;
John Stiles9d944232020-08-19 09:56:49 -0400637
Ethan Nicholase6592142020-09-08 10:22:09 -0400638 case Expression::Kind::kConstructor: {
John Stiles9d944232020-08-19 09:56:49 -0400639 const Constructor& constructor = expr.as<Constructor>();
640 if (constructor.isCompileTimeConstant()) {
Ethan Nicholas30d30222020-09-11 12:27:26 -0400641 const Type& constructorType = constructor.type();
Ethan Nicholas30d30222020-09-11 12:27:26 -0400642 switch (constructorType.typeKind()) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400643 case Type::TypeKind::kVector:
John Stilesbc75ebb2020-11-24 12:04:47 -0500644 if (constructor.componentType().isFloat()) {
645 for (int i = 0; i < constructorType.columns(); ++i) {
John Stiles9d944232020-08-19 09:56:49 -0400646 if (constructor.getFVecComponent(i) != value) {
647 return false;
648 }
John Stilesbc75ebb2020-11-24 12:04:47 -0500649 }
650 return true;
651 } else if (constructor.componentType().isInteger()) {
652 for (int i = 0; i < constructorType.columns(); ++i) {
John Stiles9d944232020-08-19 09:56:49 -0400653 if (constructor.getIVecComponent(i) != value) {
654 return false;
655 }
656 }
John Stilesbc75ebb2020-11-24 12:04:47 -0500657 return true;
Ethan Nicholasd188c182019-06-10 15:55:38 -0400658 }
John Stilesbc75ebb2020-11-24 12:04:47 -0500659 // Other types (e.g. boolean) might occur, but aren't supported here.
660 return false;
John Stiles9d944232020-08-19 09:56:49 -0400661
Ethan Nicholase6592142020-09-08 10:22:09 -0400662 case Type::TypeKind::kScalar:
Ethan Nicholasf70f0442020-09-29 12:41:35 -0400663 SkASSERT(constructor.arguments().size() == 1);
664 return is_constant<T>(*constructor.arguments()[0], value);
John Stiles9d944232020-08-19 09:56:49 -0400665
666 default:
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400667 return false;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400668 }
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400669 }
670 return false;
671 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400672 default:
673 return false;
674 }
675}
676
677/**
678 * Collapses the binary expression pointed to by iter down to just the right side (in both the IR
679 * and CFG structures).
680 */
John Stilesafbf8992020-08-18 10:08:21 -0400681static void delete_left(BasicBlock* b,
682 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400683 Compiler::OptimizationContext* optimizationContext) {
684 optimizationContext->fUpdated = true;
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400685 std::unique_ptr<Expression>* target = (*iter)->expression();
John Stilesa5a97b42020-08-18 11:19:07 -0400686 BinaryExpression& bin = (*target)->as<BinaryExpression>();
John Stiles2d4f9592020-10-30 10:29:12 -0400687 Expression& left = *bin.left();
688 std::unique_ptr<Expression>& rightPointer = bin.right();
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400689 SkASSERT(!left.hasSideEffects());
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400690 bool result;
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400691 if (bin.getOperator() == Token::Kind::TK_EQ) {
692 result = b->tryRemoveLValueBefore(iter, &left);
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400693 } else {
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400694 result = b->tryRemoveExpressionBefore(iter, &left);
Ethan Nicholascb670962017-04-20 19:31:52 -0400695 }
Brian Osman010ce6a2020-10-19 16:34:10 -0400696 // Remove references within LHS.
697 optimizationContext->fUsage->remove(&left);
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400698 *target = std::move(rightPointer);
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400699 if (!result) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400700 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400701 return;
702 }
703 if (*iter == b->fNodes.begin()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400704 optimizationContext->fNeedsRescan = true;
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400705 return;
706 }
707 --(*iter);
John Stiles70025e52020-09-28 16:08:58 -0400708 if (!(*iter)->isExpression() || (*iter)->expression() != &rightPointer) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400709 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400710 return;
711 }
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400712 *iter = b->fNodes.erase(*iter);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400713 SkASSERT((*iter)->expression() == target);
Ethan Nicholascb670962017-04-20 19:31:52 -0400714}
715
716/**
717 * Collapses the binary expression pointed to by iter down to just the left side (in both the IR and
718 * CFG structures).
719 */
John Stilesafbf8992020-08-18 10:08:21 -0400720static void delete_right(BasicBlock* b,
721 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400722 Compiler::OptimizationContext* optimizationContext) {
723 optimizationContext->fUpdated = true;
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400724 std::unique_ptr<Expression>* target = (*iter)->expression();
John Stilesa5a97b42020-08-18 11:19:07 -0400725 BinaryExpression& bin = (*target)->as<BinaryExpression>();
John Stiles2d4f9592020-10-30 10:29:12 -0400726 std::unique_ptr<Expression>& leftPointer = bin.left();
727 Expression& right = *bin.right();
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400728 SkASSERT(!right.hasSideEffects());
Brian Osman010ce6a2020-10-19 16:34:10 -0400729 // Remove references within RHS.
730 optimizationContext->fUsage->remove(&right);
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400731 if (!b->tryRemoveExpressionBefore(iter, &right)) {
732 *target = std::move(leftPointer);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400733 optimizationContext->fNeedsRescan = true;
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400734 return;
Ethan Nicholascb670962017-04-20 19:31:52 -0400735 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400736 *target = std::move(leftPointer);
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400737 if (*iter == b->fNodes.begin()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400738 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400739 return;
740 }
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400741 --(*iter);
John Stiles70025e52020-09-28 16:08:58 -0400742 if ((!(*iter)->isExpression() || (*iter)->expression() != &leftPointer)) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400743 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400744 return;
745 }
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400746 *iter = b->fNodes.erase(*iter);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400747 SkASSERT((*iter)->expression() == target);
Ethan Nicholascb670962017-04-20 19:31:52 -0400748}
749
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400750/**
751 * Constructs the specified type using a single argument.
752 */
Ethan Nicholas30d30222020-09-11 12:27:26 -0400753static std::unique_ptr<Expression> construct(const Type* type, std::unique_ptr<Expression> v) {
John Stiles8e3b6be2020-10-13 11:14:08 -0400754 ExpressionArray args;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400755 args.push_back(std::move(v));
Ethan Nicholase6592142020-09-08 10:22:09 -0400756 std::unique_ptr<Expression> result = std::make_unique<Constructor>(-1, type, std::move(args));
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400757 return result;
758}
759
760/**
761 * Used in the implementations of vectorize_left and vectorize_right. Given a vector type and an
762 * expression x, deletes the expression pointed to by iter and replaces it with <type>(x).
763 */
764static void vectorize(BasicBlock* b,
765 std::vector<BasicBlock::Node>::iterator* iter,
766 const Type& type,
767 std::unique_ptr<Expression>* otherExpression,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400768 Compiler::OptimizationContext* optimizationContext) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400769 SkASSERT((*(*iter)->expression())->kind() == Expression::Kind::kBinary);
John Stiles9aeed132020-11-24 17:36:06 -0500770 SkASSERT(type.isVector());
771 SkASSERT((*otherExpression)->type().isScalar());
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400772 optimizationContext->fUpdated = true;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400773 std::unique_ptr<Expression>* target = (*iter)->expression();
774 if (!b->tryRemoveExpression(iter)) {
Ethan Nicholas30d30222020-09-11 12:27:26 -0400775 *target = construct(&type, std::move(*otherExpression));
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400776 optimizationContext->fNeedsRescan = true;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400777 } else {
Ethan Nicholas30d30222020-09-11 12:27:26 -0400778 *target = construct(&type, std::move(*otherExpression));
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400779 if (!b->tryInsertExpression(iter, target)) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400780 optimizationContext->fNeedsRescan = true;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400781 }
782 }
783}
784
785/**
786 * Given a binary expression of the form x <op> vec<n>(y), deletes the right side and vectorizes the
787 * left to yield vec<n>(x).
788 */
789static void vectorize_left(BasicBlock* b,
790 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400791 Compiler::OptimizationContext* optimizationContext) {
John Stilesa5a97b42020-08-18 11:19:07 -0400792 BinaryExpression& bin = (*(*iter)->expression())->as<BinaryExpression>();
Brian Osman010ce6a2020-10-19 16:34:10 -0400793 // Remove references within RHS. Vectorization of LHS doesn't change reference counts.
John Stiles2d4f9592020-10-30 10:29:12 -0400794 optimizationContext->fUsage->remove(bin.right().get());
795 vectorize(b, iter, bin.right()->type(), &bin.left(), optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400796}
797
798/**
799 * Given a binary expression of the form vec<n>(x) <op> y, deletes the left side and vectorizes the
800 * right to yield vec<n>(y).
801 */
802static void vectorize_right(BasicBlock* b,
803 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400804 Compiler::OptimizationContext* optimizationContext) {
John Stilesa5a97b42020-08-18 11:19:07 -0400805 BinaryExpression& bin = (*(*iter)->expression())->as<BinaryExpression>();
Brian Osman010ce6a2020-10-19 16:34:10 -0400806 // Remove references within LHS. Vectorization of RHS doesn't change reference counts.
John Stiles2d4f9592020-10-30 10:29:12 -0400807 optimizationContext->fUsage->remove(bin.left().get());
808 vectorize(b, iter, bin.left()->type(), &bin.right(), optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400809}
810
811// Mark that an expression which we were writing to is no longer being written to
John Stilesa5a97b42020-08-18 11:19:07 -0400812static void clear_write(Expression& expr) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400813 switch (expr.kind()) {
814 case Expression::Kind::kVariableReference: {
Ethan Nicholas453f67f2020-10-09 10:43:45 -0400815 expr.as<VariableReference>().setRefKind(VariableReference::RefKind::kRead);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400816 break;
817 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400818 case Expression::Kind::kFieldAccess:
Ethan Nicholas7a95b202020-10-09 11:55:40 -0400819 clear_write(*expr.as<FieldAccess>().base());
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400820 break;
Ethan Nicholase6592142020-09-08 10:22:09 -0400821 case Expression::Kind::kSwizzle:
Ethan Nicholas6b4d5812020-10-12 16:11:51 -0400822 clear_write(*expr.as<Swizzle>().base());
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400823 break;
Ethan Nicholase6592142020-09-08 10:22:09 -0400824 case Expression::Kind::kIndex:
Ethan Nicholas2a4952d2020-10-08 15:35:56 -0400825 clear_write(*expr.as<IndexExpression>().base());
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400826 break;
827 default:
John Stilesf57207b2021-02-02 17:50:34 -0500828 SK_ABORT("shouldn't be writing to this kind of expression\n");
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400829 break;
830 }
831}
832
Ethan Nicholascb670962017-04-20 19:31:52 -0400833void Compiler::simplifyExpression(DefinitionMap& definitions,
834 BasicBlock& b,
835 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400836 OptimizationContext* optimizationContext) {
Ethan Nicholascb670962017-04-20 19:31:52 -0400837 Expression* expr = (*iter)->expression()->get();
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400838 SkASSERT(expr);
John Stiles108bbe22020-11-18 11:10:38 -0500839
Ethan Nicholascb670962017-04-20 19:31:52 -0400840 if ((*iter)->fConstantPropagation) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400841 std::unique_ptr<Expression> optimized = expr->constantPropagate(*fIRGenerator,
842 definitions);
Ethan Nicholascb670962017-04-20 19:31:52 -0400843 if (optimized) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400844 optimizationContext->fUpdated = true;
Ethan Nicholas30d30222020-09-11 12:27:26 -0400845 optimized = fIRGenerator->coerce(std::move(optimized), expr->type());
Ethan Nicholas1d881522020-09-11 09:32:54 -0400846 SkASSERT(optimized);
Brian Osman010ce6a2020-10-19 16:34:10 -0400847 // Remove references within 'expr', add references within 'optimized'
848 optimizationContext->fUsage->replace(expr, optimized.get());
Ethan Nicholascb670962017-04-20 19:31:52 -0400849 if (!try_replace_expression(&b, iter, &optimized)) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400850 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400851 return;
Ethan Nicholascb670962017-04-20 19:31:52 -0400852 }
John Stiles70025e52020-09-28 16:08:58 -0400853 SkASSERT((*iter)->isExpression());
Ethan Nicholascb670962017-04-20 19:31:52 -0400854 expr = (*iter)->expression()->get();
Ethan Nicholascb670962017-04-20 19:31:52 -0400855 }
856 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400857 switch (expr->kind()) {
858 case Expression::Kind::kVariableReference: {
John Stilesa5a97b42020-08-18 11:19:07 -0400859 const VariableReference& ref = expr->as<VariableReference>();
Ethan Nicholas78686922020-10-08 06:46:27 -0400860 const Variable* var = ref.variable();
Ethan Nicholas453f67f2020-10-09 10:43:45 -0400861 if (ref.refKind() != VariableReference::RefKind::kWrite &&
862 ref.refKind() != VariableReference::RefKind::kPointer &&
863 var->storage() == Variable::Storage::kLocal && !definitions[var] &&
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400864 optimizationContext->fSilences.find(var) == optimizationContext->fSilences.end()) {
865 optimizationContext->fSilences.insert(var);
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000866 this->error(expr->fOffset,
Ethan Nicholase2c49992020-10-05 11:49:11 -0400867 "'" + var->name() + "' has not been assigned");
Ethan Nicholascb670962017-04-20 19:31:52 -0400868 }
869 break;
870 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400871 case Expression::Kind::kTernary: {
John Stiles403a3632020-08-20 12:11:48 -0400872 TernaryExpression* t = &expr->as<TernaryExpression>();
Ethan Nicholasdd218162020-10-08 05:48:01 -0400873 if (t->test()->is<BoolLiteral>()) {
Ethan Nicholascb670962017-04-20 19:31:52 -0400874 // ternary has a constant test, replace it with either the true or
875 // false branch
Ethan Nicholasdd218162020-10-08 05:48:01 -0400876 if (t->test()->as<BoolLiteral>().value()) {
Brian Osman010ce6a2020-10-19 16:34:10 -0400877 (*iter)->setExpression(std::move(t->ifTrue()), optimizationContext->fUsage);
Ethan Nicholascb670962017-04-20 19:31:52 -0400878 } else {
Brian Osman010ce6a2020-10-19 16:34:10 -0400879 (*iter)->setExpression(std::move(t->ifFalse()), optimizationContext->fUsage);
Ethan Nicholascb670962017-04-20 19:31:52 -0400880 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400881 optimizationContext->fUpdated = true;
882 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -0400883 }
884 break;
885 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400886 case Expression::Kind::kBinary: {
John Stiles403a3632020-08-20 12:11:48 -0400887 BinaryExpression* bin = &expr->as<BinaryExpression>();
Brian Osman010ce6a2020-10-19 16:34:10 -0400888 if (dead_assignment(*bin, optimizationContext->fUsage)) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400889 delete_left(&b, iter, optimizationContext);
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400890 break;
891 }
John Stiles2d4f9592020-10-30 10:29:12 -0400892 Expression& left = *bin->left();
893 Expression& right = *bin->right();
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400894 const Type& leftType = left.type();
895 const Type& rightType = right.type();
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400896 // collapse useless expressions like x * 1 or x + 0
John Stiles9aeed132020-11-24 17:36:06 -0500897 if ((!leftType.isScalar() && !leftType.isVector()) ||
898 (!rightType.isScalar() && !rightType.isVector())) {
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400899 break;
900 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400901 switch (bin->getOperator()) {
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400902 case Token::Kind::TK_STAR:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400903 if (is_constant(left, 1)) {
John Stiles9aeed132020-11-24 17:36:06 -0500904 if (leftType.isVector() && rightType.isScalar()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400905 // float4(1) * x -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400906 vectorize_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400907 } else {
908 // 1 * x -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400909 // 1 * float4(x) -> float4(x)
910 // float4(1) * float4(x) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400911 delete_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400912 }
913 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400914 else if (is_constant(left, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500915 if (leftType.isScalar() && rightType.isVector() &&
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400916 !right.hasSideEffects()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400917 // 0 * float4(x) -> float4(0)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400918 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400919 } else {
920 // 0 * x -> 0
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400921 // float4(0) * x -> float4(0)
922 // float4(0) * float4(x) -> float4(0)
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400923 if (!right.hasSideEffects()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400924 delete_right(&b, iter, optimizationContext);
Ethan Nicholas51493ee2017-12-11 12:34:33 -0500925 }
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400926 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400927 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400928 else if (is_constant(right, 1)) {
John Stiles9aeed132020-11-24 17:36:06 -0500929 if (leftType.isScalar() && rightType.isVector()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400930 // x * float4(1) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400931 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400932 } else {
933 // x * 1 -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400934 // float4(x) * 1 -> float4(x)
935 // float4(x) * float4(1) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400936 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400937 }
938 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400939 else if (is_constant(right, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500940 if (leftType.isVector() && rightType.isScalar() && !left.hasSideEffects()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400941 // float4(x) * 0 -> float4(0)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400942 vectorize_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400943 } else {
944 // x * 0 -> 0
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400945 // x * float4(0) -> float4(0)
946 // float4(x) * float4(0) -> float4(0)
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400947 if (!left.hasSideEffects()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400948 delete_left(&b, iter, optimizationContext);
Ethan Nicholas51493ee2017-12-11 12:34:33 -0500949 }
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400950 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400951 }
952 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400953 case Token::Kind::TK_PLUS:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400954 if (is_constant(left, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500955 if (leftType.isVector() && rightType.isScalar()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400956 // float4(0) + x -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400957 vectorize_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400958 } else {
959 // 0 + x -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400960 // 0 + float4(x) -> float4(x)
961 // float4(0) + float4(x) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400962 delete_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400963 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400964 } else if (is_constant(right, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500965 if (leftType.isScalar() && rightType.isVector()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400966 // x + float4(0) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400967 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400968 } else {
969 // x + 0 -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400970 // float4(x) + 0 -> float4(x)
971 // float4(x) + float4(0) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400972 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400973 }
Ethan Nicholas56e42712017-04-21 10:23:37 -0400974 }
975 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400976 case Token::Kind::TK_MINUS:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400977 if (is_constant(right, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500978 if (leftType.isScalar() && rightType.isVector()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400979 // x - float4(0) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400980 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400981 } else {
982 // x - 0 -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400983 // float4(x) - 0 -> float4(x)
984 // float4(x) - float4(0) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400985 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400986 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400987 }
988 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400989 case Token::Kind::TK_SLASH:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400990 if (is_constant(right, 1)) {
John Stiles9aeed132020-11-24 17:36:06 -0500991 if (leftType.isScalar() && rightType.isVector()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400992 // x / float4(1) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400993 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400994 } else {
995 // x / 1 -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400996 // float4(x) / 1 -> float4(x)
997 // float4(x) / float4(1) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400998 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400999 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -04001000 } else if (is_constant(left, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -05001001 if (leftType.isScalar() && rightType.isVector() &&
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -04001002 !right.hasSideEffects()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -04001003 // 0 / float4(x) -> float4(0)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001004 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -04001005 } else {
1006 // 0 / x -> 0
Ethan Nicholas5af9ea32017-07-28 15:19:46 -04001007 // float4(0) / x -> float4(0)
1008 // float4(0) / float4(x) -> float4(0)
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -04001009 if (!right.hasSideEffects()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001010 delete_right(&b, iter, optimizationContext);
Ethan Nicholas51493ee2017-12-11 12:34:33 -05001011 }
Ethan Nicholasfe53e582017-04-27 16:24:51 -04001012 }
1013 }
1014 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -04001015 case Token::Kind::TK_PLUSEQ:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -04001016 if (is_constant(right, 0)) {
1017 clear_write(left);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001018 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -04001019 }
1020 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -04001021 case Token::Kind::TK_MINUSEQ:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -04001022 if (is_constant(right, 0)) {
1023 clear_write(left);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001024 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -04001025 }
1026 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -04001027 case Token::Kind::TK_STAREQ:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -04001028 if (is_constant(right, 1)) {
1029 clear_write(left);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001030 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -04001031 }
1032 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -04001033 case Token::Kind::TK_SLASHEQ:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -04001034 if (is_constant(right, 1)) {
1035 clear_write(left);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001036 delete_right(&b, iter, optimizationContext);
Ethan Nicholascb670962017-04-20 19:31:52 -04001037 }
1038 break;
1039 default:
1040 break;
1041 }
Ethan Nicholas409f6f02019-09-17 12:34:39 -04001042 break;
1043 }
John Stilesf5c1d042020-11-21 23:26:07 -05001044 case Expression::Kind::kConstructor: {
1045 // Find constructors embedded inside constructors and flatten them out where possible.
1046 // - float4(float2(1, 2), 3, 4) --> float4(1, 2, 3, 4)
1047 // - float4(w, float3(sin(x), cos(y), tan(z))) --> float4(w, sin(x), cos(y), tan(z))
1048 // Leave single-argument constructors alone, though. These might be casts or splats.
1049 Constructor& c = expr->as<Constructor>();
1050 if (c.type().columns() > 1) {
1051 // Inspect each constructor argument to see if it's a candidate for flattening.
1052 // Remember matched arguments in a bitfield, "argsToOptimize".
1053 int argsToOptimize = 0;
1054 int currBit = 1;
1055 for (const std::unique_ptr<Expression>& arg : c.arguments()) {
1056 if (arg->is<Constructor>()) {
1057 Constructor& inner = arg->as<Constructor>();
1058 if (inner.arguments().size() > 1 &&
1059 inner.type().componentType() == c.type().componentType()) {
1060 argsToOptimize |= currBit;
1061 }
1062 }
1063 currBit <<= 1;
1064 }
1065 if (argsToOptimize) {
1066 // We found at least one argument that could be flattened out. Re-walk the
1067 // constructor args and flatten the candidates we found during our initial pass.
1068 ExpressionArray flattened;
1069 flattened.reserve_back(c.type().columns());
1070 currBit = 1;
1071 for (const std::unique_ptr<Expression>& arg : c.arguments()) {
1072 if (argsToOptimize & currBit) {
1073 Constructor& inner = arg->as<Constructor>();
1074 for (const std::unique_ptr<Expression>& innerArg : inner.arguments()) {
1075 flattened.push_back(innerArg->clone());
1076 }
1077 } else {
1078 flattened.push_back(arg->clone());
1079 }
1080 currBit <<= 1;
1081 }
1082 auto optimized = std::unique_ptr<Expression>(
1083 new Constructor(c.fOffset, &c.type(), std::move(flattened)));
1084 // No fUsage change; no references have been added or removed anywhere.
1085 optimizationContext->fUpdated = true;
1086 if (!try_replace_expression(&b, iter, &optimized)) {
1087 optimizationContext->fNeedsRescan = true;
1088 return;
1089 }
1090 SkASSERT((*iter)->isExpression());
1091 break;
1092 }
1093 }
1094 break;
1095 }
Ethan Nicholase6592142020-09-08 10:22:09 -04001096 case Expression::Kind::kSwizzle: {
John Stiles403a3632020-08-20 12:11:48 -04001097 Swizzle& s = expr->as<Swizzle>();
John Stiles108bbe22020-11-18 11:10:38 -05001098 // Detect identity swizzles like `foo.rgba`.
Ethan Nicholas6b4d5812020-10-12 16:11:51 -04001099 if ((int) s.components().size() == s.base()->type().columns()) {
Ethan Nicholas409f6f02019-09-17 12:34:39 -04001100 bool identity = true;
Ethan Nicholas6b4d5812020-10-12 16:11:51 -04001101 for (int i = 0; i < (int) s.components().size(); ++i) {
1102 if (s.components()[i] != i) {
Ethan Nicholas409f6f02019-09-17 12:34:39 -04001103 identity = false;
1104 break;
1105 }
1106 }
1107 if (identity) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001108 optimizationContext->fUpdated = true;
Brian Osman010ce6a2020-10-19 16:34:10 -04001109 // No fUsage change: foo.rgba and foo have equivalent reference counts
Ethan Nicholas6b4d5812020-10-12 16:11:51 -04001110 if (!try_replace_expression(&b, iter, &s.base())) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001111 optimizationContext->fNeedsRescan = true;
Ethan Nicholas409f6f02019-09-17 12:34:39 -04001112 return;
1113 }
John Stiles70025e52020-09-28 16:08:58 -04001114 SkASSERT((*iter)->isExpression());
Ethan Nicholas409f6f02019-09-17 12:34:39 -04001115 break;
1116 }
1117 }
John Stiles108bbe22020-11-18 11:10:38 -05001118 // Detect swizzles of swizzles, e.g. replace `foo.argb.r000` with `foo.a000`.
1119 if (s.base()->is<Swizzle>()) {
Ethan Nicholas6b4d5812020-10-12 16:11:51 -04001120 Swizzle& base = s.base()->as<Swizzle>();
John Stiles750109b2020-10-30 13:45:46 -04001121 ComponentArray final;
Ethan Nicholas6b4d5812020-10-12 16:11:51 -04001122 for (int c : s.components()) {
1123 final.push_back(base.components()[c]);
Ethan Nicholas409f6f02019-09-17 12:34:39 -04001124 }
Ethan Nicholas6b4d5812020-10-12 16:11:51 -04001125 std::unique_ptr<Expression> replacement(new Swizzle(*fContext, base.base()->clone(),
John Stiles750109b2020-10-30 13:45:46 -04001126 final));
John Stilesd2f51b12021-01-07 18:12:31 -05001127 // We're replacing an expression with a cloned version; we'll need a rescan.
John Stiles108bbe22020-11-18 11:10:38 -05001128 // No fUsage change: `foo.gbr.gbr` and `foo.brg` have equivalent reference counts
John Stilesd2f51b12021-01-07 18:12:31 -05001129 try_replace_expression(&b, iter, &replacement);
1130 optimizationContext->fUpdated = true;
1131 optimizationContext->fNeedsRescan = true;
John Stiles108bbe22020-11-18 11:10:38 -05001132 break;
1133 }
1134 // Optimize swizzles of constructors.
1135 if (s.base()->is<Constructor>()) {
1136 Constructor& base = s.base()->as<Constructor>();
1137 std::unique_ptr<Expression> replacement;
1138 const Type& componentType = base.type().componentType();
1139 int swizzleSize = s.components().size();
1140
1141 // The IR generator has already converted any zero/one swizzle components into
1142 // constructors containing zero/one args. Confirm that this is true by checking that
1143 // our swizzle components are all `xyzw` (values 0 through 3).
1144 SkASSERT(std::all_of(s.components().begin(), s.components().end(),
1145 [](int8_t c) { return c >= 0 && c <= 3; }));
1146
John Stiles9aeed132020-11-24 17:36:06 -05001147 if (base.arguments().size() == 1 && base.arguments().front()->type().isScalar()) {
John Stiles108bbe22020-11-18 11:10:38 -05001148 // `half4(scalar).zyy` can be optimized to `half3(scalar)`. The swizzle
1149 // components don't actually matter since all fields are the same.
John Stilesf0cb7332021-01-08 18:39:00 -05001150 const Expression& argument = *base.arguments().front();
1151 const Type& constructorType = componentType.toCompound(*fContext, swizzleSize,
1152 /*rows=*/1);
1153 replacement = Constructor::SimplifyConversion(constructorType, argument);
1154 if (!replacement) {
1155 ExpressionArray newArgs;
1156 newArgs.push_back(argument.clone());
1157 replacement = std::make_unique<Constructor>(base.fOffset, &constructorType,
1158 std::move(newArgs));
1159 }
John Stiles108bbe22020-11-18 11:10:38 -05001160
John Stilesa60ac0c2020-12-22 08:59:51 -05001161 // We're replacing an expression with a cloned version; we'll need a rescan.
1162 // There's no fUsage change: `half4(foo).xy` and `half2(foo)` have equivalent
1163 // reference counts.
1164 try_replace_expression(&b, iter, &replacement);
John Stiles0777ac42020-11-19 11:06:47 -05001165 optimizationContext->fUpdated = true;
John Stilesa60ac0c2020-12-22 08:59:51 -05001166 optimizationContext->fNeedsRescan = true;
John Stiles108bbe22020-11-18 11:10:38 -05001167 break;
1168 }
1169
John Stiles0777ac42020-11-19 11:06:47 -05001170 // Swizzles can duplicate some elements and discard others, e.g.
1171 // `half4(1, 2, 3, 4).xxz` --> `half3(1, 1, 3)`. However, there are constraints:
1172 // - Expressions with side effects need to occur exactly once, even if they
1173 // would otherwise be swizzle-eliminated
1174 // - Non-trivial expressions should not be repeated, but elimination is OK.
1175 //
1176 // Look up the argument for the constructor at each index. This is typically simple
1177 // but for weird cases like `half4(bar.yz, half2(foo))`, it can be harder than it
1178 // seems. This example would result in:
1179 // argMap[0] = {.fArgIndex = 0, .fComponent = 0} (bar.yz .x)
1180 // argMap[1] = {.fArgIndex = 0, .fComponent = 1} (bar.yz .y)
1181 // argMap[2] = {.fArgIndex = 1, .fComponent = 0} (half2(foo) .x)
1182 // argMap[3] = {.fArgIndex = 1, .fComponent = 1} (half2(foo) .y)
1183 struct ConstructorArgMap {
1184 int8_t fArgIndex;
1185 int8_t fComponent;
1186 };
1187
1188 int numConstructorArgs = base.type().columns();
1189 ConstructorArgMap argMap[4] = {};
1190 int writeIdx = 0;
1191 for (int argIdx = 0; argIdx < (int) base.arguments().size(); ++argIdx) {
1192 const Expression& expr = *base.arguments()[argIdx];
1193 int argWidth = expr.type().columns();
1194 for (int componentIdx = 0; componentIdx < argWidth; ++componentIdx) {
1195 argMap[writeIdx].fArgIndex = argIdx;
1196 argMap[writeIdx].fComponent = componentIdx;
1197 ++writeIdx;
1198 }
1199 }
1200 SkASSERT(writeIdx == numConstructorArgs);
1201
1202 // Count up the number of times each constructor argument is used by the
1203 // swizzle.
1204 // `half4(bar.yz, half2(foo)).xwxy` -> { 3, 1 }
1205 // - bar.yz is referenced 3 times, by `.x_xy`
1206 // - half(foo) is referenced 1 time, by `._w__`
1207 int8_t exprUsed[4] = {};
1208 for (int c : s.components()) {
1209 exprUsed[argMap[c].fArgIndex]++;
1210 }
1211
1212 bool safeToOptimize = true;
1213 for (int index = 0; index < numConstructorArgs; ++index) {
1214 int8_t constructorArgIndex = argMap[index].fArgIndex;
1215 const Expression& baseArg = *base.arguments()[constructorArgIndex];
1216
1217 // Check that non-trivial expressions are not swizzled in more than once.
John Stilesc30fbca2020-11-19 16:25:49 -05001218 if (exprUsed[constructorArgIndex] > 1 &&
1219 !Analysis::IsTrivialExpression(baseArg)) {
John Stiles0777ac42020-11-19 11:06:47 -05001220 safeToOptimize = false;
1221 break;
1222 }
1223 // Check that side-effect-bearing expressions are swizzled in exactly once.
1224 if (exprUsed[constructorArgIndex] != 1 && baseArg.hasSideEffects()) {
1225 safeToOptimize = false;
1226 break;
1227 }
1228 }
1229
1230 if (safeToOptimize) {
John Stilesd9076cb2020-11-19 12:18:36 -05001231 struct ReorderedArgument {
1232 int8_t fArgIndex;
1233 ComponentArray fComponents;
1234 };
1235 SkSTArray<4, ReorderedArgument> reorderedArgs;
John Stiles0777ac42020-11-19 11:06:47 -05001236 for (int c : s.components()) {
1237 const ConstructorArgMap& argument = argMap[c];
1238 const Expression& baseArg = *base.arguments()[argument.fArgIndex];
1239
John Stiles9aeed132020-11-24 17:36:06 -05001240 if (baseArg.type().isScalar()) {
John Stilesd9076cb2020-11-19 12:18:36 -05001241 // This argument is a scalar; add it to the list as-is.
John Stiles0777ac42020-11-19 11:06:47 -05001242 SkASSERT(argument.fComponent == 0);
John Stilesd9076cb2020-11-19 12:18:36 -05001243 reorderedArgs.push_back({argument.fArgIndex,
1244 ComponentArray{}});
John Stiles0777ac42020-11-19 11:06:47 -05001245 } else {
John Stilesd9076cb2020-11-19 12:18:36 -05001246 // This argument is a component from a vector.
John Stiles0777ac42020-11-19 11:06:47 -05001247 SkASSERT(argument.fComponent < baseArg.type().columns());
John Stilesd9076cb2020-11-19 12:18:36 -05001248 if (reorderedArgs.empty() ||
1249 reorderedArgs.back().fArgIndex != argument.fArgIndex) {
1250 // This can't be combined with the previous argument. Add a new one.
1251 reorderedArgs.push_back({argument.fArgIndex,
1252 ComponentArray{argument.fComponent}});
1253 } else {
1254 // Since we know this argument uses components, it should already
1255 // have at least one component set.
1256 SkASSERT(!reorderedArgs.back().fComponents.empty());
1257 // Build up the current argument with one more component.
1258 reorderedArgs.back().fComponents.push_back(argument.fComponent);
1259 }
John Stiles0777ac42020-11-19 11:06:47 -05001260 }
1261 }
John Stilesd9076cb2020-11-19 12:18:36 -05001262
1263 // Convert our reordered argument list to an actual array of expressions, with
1264 // the new order and any new inner swizzles that need to be applied. Note that
1265 // we expect followup passes to clean up the inner swizzles.
1266 ExpressionArray newArgs;
1267 newArgs.reserve_back(swizzleSize);
1268 for (const ReorderedArgument& reorderedArg : reorderedArgs) {
1269 const Expression& baseArg = *base.arguments()[reorderedArg.fArgIndex];
1270 if (reorderedArg.fComponents.empty()) {
1271 newArgs.push_back(baseArg.clone());
1272 } else {
1273 newArgs.push_back(std::make_unique<Swizzle>(*fContext, baseArg.clone(),
1274 reorderedArg.fComponents));
1275 }
1276 }
1277
1278 // Create a new constructor.
John Stiles0777ac42020-11-19 11:06:47 -05001279 replacement = std::make_unique<Constructor>(
1280 base.fOffset,
1281 &componentType.toCompound(*fContext, swizzleSize, /*rows=*/1),
1282 std::move(newArgs));
1283
John Stilesa60ac0c2020-12-22 08:59:51 -05001284 // Remove references within 'expr', add references within 'replacement.'
John Stiles0777ac42020-11-19 11:06:47 -05001285 optimizationContext->fUsage->replace(expr, replacement.get());
John Stilesa60ac0c2020-12-22 08:59:51 -05001286
1287 // We're replacing an expression with a cloned version; we'll need a rescan.
1288 try_replace_expression(&b, iter, &replacement);
1289 optimizationContext->fUpdated = true;
1290 optimizationContext->fNeedsRescan = true;
John Stiles0777ac42020-11-19 11:06:47 -05001291 }
John Stiles108bbe22020-11-18 11:10:38 -05001292 break;
Ethan Nicholas409f6f02019-09-17 12:34:39 -04001293 }
John Stiles30212b72020-06-11 17:55:07 -04001294 break;
Ethan Nicholascb670962017-04-20 19:31:52 -04001295 }
1296 default:
1297 break;
1298 }
1299}
1300
John Stiles92219b42020-06-15 12:32:24 -04001301// Returns true if this statement could potentially execute a break at the current level. We ignore
1302// nested loops and switches, since any breaks inside of them will merely break the loop / switch.
John Stilesb92641c2020-08-31 18:09:01 -04001303static bool contains_conditional_break(Statement& stmt) {
1304 class ContainsConditionalBreak : public ProgramVisitor {
1305 public:
1306 bool visitStatement(const Statement& stmt) override {
Ethan Nicholase6592142020-09-08 10:22:09 -04001307 switch (stmt.kind()) {
1308 case Statement::Kind::kBlock:
John Stilesb92641c2020-08-31 18:09:01 -04001309 return this->INHERITED::visitStatement(stmt);
1310
Ethan Nicholase6592142020-09-08 10:22:09 -04001311 case Statement::Kind::kBreak:
John Stilesb92641c2020-08-31 18:09:01 -04001312 return fInConditional > 0;
1313
Ethan Nicholase6592142020-09-08 10:22:09 -04001314 case Statement::Kind::kIf: {
John Stilesb92641c2020-08-31 18:09:01 -04001315 ++fInConditional;
1316 bool result = this->INHERITED::visitStatement(stmt);
1317 --fInConditional;
1318 return result;
1319 }
1320
1321 default:
1322 return false;
1323 }
1324 }
1325
1326 int fInConditional = 0;
1327 using INHERITED = ProgramVisitor;
1328 };
1329
1330 return ContainsConditionalBreak{}.visitStatement(stmt);
John Stiles92219b42020-06-15 12:32:24 -04001331}
1332
Ethan Nicholas5005a222018-08-24 13:06:27 -04001333// returns true if this statement definitely executes a break at the current level (we ignore
1334// nested loops and switches, since any breaks inside of them will merely break the loop / switch)
John Stilesb92641c2020-08-31 18:09:01 -04001335static bool contains_unconditional_break(Statement& stmt) {
1336 class ContainsUnconditionalBreak : public ProgramVisitor {
1337 public:
1338 bool visitStatement(const Statement& stmt) override {
Ethan Nicholase6592142020-09-08 10:22:09 -04001339 switch (stmt.kind()) {
1340 case Statement::Kind::kBlock:
John Stilesb92641c2020-08-31 18:09:01 -04001341 return this->INHERITED::visitStatement(stmt);
1342
Ethan Nicholase6592142020-09-08 10:22:09 -04001343 case Statement::Kind::kBreak:
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001344 return true;
John Stilesb92641c2020-08-31 18:09:01 -04001345
1346 default:
1347 return false;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001348 }
John Stilesb92641c2020-08-31 18:09:01 -04001349 }
John Stiles92219b42020-06-15 12:32:24 -04001350
John Stilesb92641c2020-08-31 18:09:01 -04001351 using INHERITED = ProgramVisitor;
1352 };
John Stiles92219b42020-06-15 12:32:24 -04001353
John Stilesb92641c2020-08-31 18:09:01 -04001354 return ContainsUnconditionalBreak{}.visitStatement(stmt);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001355}
1356
John Stiles8f2a0cf2020-10-13 12:48:21 -04001357static void move_all_but_break(std::unique_ptr<Statement>& stmt, StatementArray* target) {
Ethan Nicholase6592142020-09-08 10:22:09 -04001358 switch (stmt->kind()) {
1359 case Statement::Kind::kBlock: {
John Stiles92219b42020-06-15 12:32:24 -04001360 // Recurse into the block.
1361 Block& block = static_cast<Block&>(*stmt);
1362
John Stiles8f2a0cf2020-10-13 12:48:21 -04001363 StatementArray blockStmts;
John Stilesf4bda742020-10-14 16:57:41 -04001364 blockStmts.reserve_back(block.children().size());
Ethan Nicholas7bd60432020-09-25 14:31:59 -04001365 for (std::unique_ptr<Statement>& stmt : block.children()) {
1366 move_all_but_break(stmt, &blockStmts);
Ethan Nicholas739e1ca2020-06-11 12:16:14 -04001367 }
John Stiles92219b42020-06-15 12:32:24 -04001368
1369 target->push_back(std::make_unique<Block>(block.fOffset, std::move(blockStmts),
Ethan Nicholas7bd60432020-09-25 14:31:59 -04001370 block.symbolTable(), block.isScope()));
Ethan Nicholas739e1ca2020-06-11 12:16:14 -04001371 break;
John Stiles92219b42020-06-15 12:32:24 -04001372 }
1373
Ethan Nicholase6592142020-09-08 10:22:09 -04001374 case Statement::Kind::kBreak:
John Stiles92219b42020-06-15 12:32:24 -04001375 // Do not append a break to the target.
1376 break;
1377
Ethan Nicholas739e1ca2020-06-11 12:16:14 -04001378 default:
John Stiles92219b42020-06-15 12:32:24 -04001379 // Append normal statements to the target.
1380 target->push_back(std::move(stmt));
1381 break;
Ethan Nicholas739e1ca2020-06-11 12:16:14 -04001382 }
1383}
1384
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001385// Returns a block containing all of the statements that will be run if the given case matches
1386// (which, owing to the statements being owned by unique_ptrs, means the switch itself will be
1387// broken by this call and must then be discarded).
1388// Returns null (and leaves the switch unmodified) if no such simple reduction is possible, such as
1389// when break statements appear inside conditionals.
John Stiles92219b42020-06-15 12:32:24 -04001390static std::unique_ptr<Statement> block_for_case(SwitchStatement* switchStatement,
1391 SwitchCase* caseToCapture) {
1392 // We have to be careful to not move any of the pointers until after we're sure we're going to
1393 // succeed, so before we make any changes at all, we check the switch-cases to decide on a plan
1394 // of action. First, find the switch-case we are interested in.
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001395 auto iter = switchStatement->cases().begin();
1396 for (; iter != switchStatement->cases().end(); ++iter) {
John Stiles2d4f9592020-10-30 10:29:12 -04001397 if (iter->get() == caseToCapture) {
John Stiles92219b42020-06-15 12:32:24 -04001398 break;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001399 }
John Stiles92219b42020-06-15 12:32:24 -04001400 }
1401
1402 // Next, walk forward through the rest of the switch. If we find a conditional break, we're
1403 // stuck and can't simplify at all. If we find an unconditional break, we have a range of
1404 // statements that we can use for simplification.
1405 auto startIter = iter;
1406 Statement* unconditionalBreakStmt = nullptr;
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001407 for (; iter != switchStatement->cases().end(); ++iter) {
John Stiles2d4f9592020-10-30 10:29:12 -04001408 for (std::unique_ptr<Statement>& stmt : (*iter)->statements()) {
John Stiles92219b42020-06-15 12:32:24 -04001409 if (contains_conditional_break(*stmt)) {
1410 // We can't reduce switch-cases to a block when they have conditional breaks.
1411 return nullptr;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001412 }
John Stiles92219b42020-06-15 12:32:24 -04001413
1414 if (contains_unconditional_break(*stmt)) {
1415 // We found an unconditional break. We can use this block, but we need to strip
1416 // out the break statement.
1417 unconditionalBreakStmt = stmt.get();
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001418 break;
1419 }
1420 }
John Stiles92219b42020-06-15 12:32:24 -04001421
1422 if (unconditionalBreakStmt != nullptr) {
1423 break;
1424 }
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001425 }
John Stiles92219b42020-06-15 12:32:24 -04001426
1427 // We fell off the bottom of the switch or encountered a break. We know the range of statements
1428 // that we need to move over, and we know it's safe to do so.
John Stiles8f2a0cf2020-10-13 12:48:21 -04001429 StatementArray caseStmts;
John Stiles92219b42020-06-15 12:32:24 -04001430
1431 // We can move over most of the statements as-is.
1432 while (startIter != iter) {
John Stiles2d4f9592020-10-30 10:29:12 -04001433 for (std::unique_ptr<Statement>& stmt : (*startIter)->statements()) {
John Stiles92219b42020-06-15 12:32:24 -04001434 caseStmts.push_back(std::move(stmt));
1435 }
1436 ++startIter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001437 }
John Stiles92219b42020-06-15 12:32:24 -04001438
1439 // If we found an unconditional break at the end, we need to move what we can while avoiding
1440 // that break.
1441 if (unconditionalBreakStmt != nullptr) {
John Stiles2d4f9592020-10-30 10:29:12 -04001442 for (std::unique_ptr<Statement>& stmt : (*startIter)->statements()) {
John Stiles92219b42020-06-15 12:32:24 -04001443 if (stmt.get() == unconditionalBreakStmt) {
1444 move_all_but_break(stmt, &caseStmts);
1445 unconditionalBreakStmt = nullptr;
1446 break;
1447 }
1448
1449 caseStmts.push_back(std::move(stmt));
1450 }
1451 }
1452
1453 SkASSERT(unconditionalBreakStmt == nullptr); // Verify that we fixed the unconditional break.
1454
1455 // Return our newly-synthesized block.
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001456 return std::make_unique<Block>(/*offset=*/-1, std::move(caseStmts), switchStatement->symbols());
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001457}
1458
Ethan Nicholascb670962017-04-20 19:31:52 -04001459void Compiler::simplifyStatement(DefinitionMap& definitions,
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001460 BasicBlock& b,
1461 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001462 OptimizationContext* optimizationContext) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001463 ProgramUsage* usage = optimizationContext->fUsage;
Ethan Nicholascb670962017-04-20 19:31:52 -04001464 Statement* stmt = (*iter)->statement()->get();
Ethan Nicholase6592142020-09-08 10:22:09 -04001465 switch (stmt->kind()) {
1466 case Statement::Kind::kVarDeclaration: {
John Stilesa5a97b42020-08-18 11:19:07 -04001467 const auto& varDecl = stmt->as<VarDeclaration>();
Brian Osman010ce6a2020-10-19 16:34:10 -04001468 if (usage->isDead(varDecl.var()) &&
Ethan Nicholasc51f33e2020-10-13 13:49:44 -04001469 (!varDecl.value() ||
1470 !varDecl.value()->hasSideEffects())) {
1471 if (varDecl.value()) {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -04001472 SkASSERT((*iter)->statement()->get() == stmt);
Ethan Nicholasc51f33e2020-10-13 13:49:44 -04001473 if (!b.tryRemoveExpressionBefore(iter, varDecl.value().get())) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001474 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001475 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001476 }
Ethan Nicholas5b9b0db2021-01-21 13:12:01 -05001477 // There can still be (soon to be removed) references to the variable at this point.
1478 // Allowing the VarDeclaration to be destroyed here will break those variable's
1479 // initialValue()s, so we hang on to them until optimization is finished.
1480 std::unique_ptr<Statement> old = (*iter)->setStatement(std::make_unique<Nop>(),
1481 usage);
1482 optimizationContext->fOwnedStatements.push_back(std::move(old));
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001483 optimizationContext->fUpdated = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001484 }
1485 break;
1486 }
Ethan Nicholase6592142020-09-08 10:22:09 -04001487 case Statement::Kind::kIf: {
John Stilesa5a97b42020-08-18 11:19:07 -04001488 IfStatement& i = stmt->as<IfStatement>();
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001489 if (i.test()->kind() == Expression::Kind::kBoolLiteral) {
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001490 // constant if, collapse down to a single branch
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001491 if (i.test()->as<BoolLiteral>().value()) {
1492 SkASSERT(i.ifTrue());
Brian Osman010ce6a2020-10-19 16:34:10 -04001493 (*iter)->setStatement(std::move(i.ifTrue()), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001494 } else {
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001495 if (i.ifFalse()) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001496 (*iter)->setStatement(std::move(i.ifFalse()), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001497 } else {
Brian Osman010ce6a2020-10-19 16:34:10 -04001498 (*iter)->setStatement(std::make_unique<Nop>(), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001499 }
1500 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001501 optimizationContext->fUpdated = true;
1502 optimizationContext->fNeedsRescan = true;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001503 break;
1504 }
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001505 if (i.ifFalse() && i.ifFalse()->isEmpty()) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001506 // else block doesn't do anything, remove it
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001507 i.ifFalse().reset();
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001508 optimizationContext->fUpdated = true;
1509 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001510 }
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001511 if (!i.ifFalse() && i.ifTrue()->isEmpty()) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001512 // if block doesn't do anything, no else block
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001513 if (i.test()->hasSideEffects()) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001514 // test has side effects, keep it
Brian Osman010ce6a2020-10-19 16:34:10 -04001515 (*iter)->setStatement(
1516 std::make_unique<ExpressionStatement>(std::move(i.test())), usage);
Ethan Nicholascb670962017-04-20 19:31:52 -04001517 } else {
1518 // no if, no else, no test side effects, kill the whole if
1519 // statement
Brian Osman010ce6a2020-10-19 16:34:10 -04001520 (*iter)->setStatement(std::make_unique<Nop>(), usage);
Ethan Nicholascb670962017-04-20 19:31:52 -04001521 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001522 optimizationContext->fUpdated = true;
1523 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001524 }
1525 break;
1526 }
Ethan Nicholase6592142020-09-08 10:22:09 -04001527 case Statement::Kind::kSwitch: {
John Stilesa5a97b42020-08-18 11:19:07 -04001528 SwitchStatement& s = stmt->as<SwitchStatement>();
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001529 int64_t switchValue;
Ethan Nicholasc0f98152021-02-05 16:21:10 -05001530 if (ConstantFolder::GetConstantInt(*s.value(), &switchValue)) {
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001531 // switch is constant, replace it with the case that matches
1532 bool found = false;
1533 SwitchCase* defaultCase = nullptr;
John Stiles2d4f9592020-10-30 10:29:12 -04001534 for (const std::unique_ptr<SwitchCase>& c : s.cases()) {
1535 if (!c->value()) {
1536 defaultCase = c.get();
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001537 continue;
1538 }
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001539 int64_t caseValue;
Ethan Nicholasc0f98152021-02-05 16:21:10 -05001540 SkAssertResult(ConstantFolder::GetConstantInt(*c->value(), &caseValue));
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001541 if (caseValue == switchValue) {
John Stiles2d4f9592020-10-30 10:29:12 -04001542 std::unique_ptr<Statement> newBlock = block_for_case(&s, c.get());
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001543 if (newBlock) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001544 (*iter)->setStatement(std::move(newBlock), usage);
John Stiles9d944232020-08-19 09:56:49 -04001545 found = true;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001546 break;
1547 } else {
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001548 if (s.isStatic() && !(fFlags & kPermitInvalidStaticTests_Flag) &&
1549 optimizationContext->fSilences.find(&s) ==
1550 optimizationContext->fSilences.end()) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001551 this->error(s.fOffset,
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001552 "static switch contains non-static conditional break");
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001553 optimizationContext->fSilences.insert(&s);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001554 }
1555 return; // can't simplify
1556 }
1557 }
1558 }
1559 if (!found) {
1560 // no matching case. use default if it exists, or kill the whole thing
1561 if (defaultCase) {
1562 std::unique_ptr<Statement> newBlock = block_for_case(&s, defaultCase);
1563 if (newBlock) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001564 (*iter)->setStatement(std::move(newBlock), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001565 } else {
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001566 if (s.isStatic() && !(fFlags & kPermitInvalidStaticTests_Flag) &&
1567 optimizationContext->fSilences.find(&s) ==
1568 optimizationContext->fSilences.end()) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001569 this->error(s.fOffset,
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001570 "static switch contains non-static conditional break");
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001571 optimizationContext->fSilences.insert(&s);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001572 }
1573 return; // can't simplify
1574 }
1575 } else {
Brian Osman010ce6a2020-10-19 16:34:10 -04001576 (*iter)->setStatement(std::make_unique<Nop>(), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001577 }
1578 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001579 optimizationContext->fUpdated = true;
1580 optimizationContext->fNeedsRescan = true;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001581 }
1582 break;
1583 }
Ethan Nicholase6592142020-09-08 10:22:09 -04001584 case Statement::Kind::kExpression: {
John Stilesa5a97b42020-08-18 11:19:07 -04001585 ExpressionStatement& e = stmt->as<ExpressionStatement>();
Ethan Nicholasd9d33c32018-06-12 11:05:59 -04001586 SkASSERT((*iter)->statement()->get() == &e);
Ethan Nicholasd503a5a2020-09-30 09:29:55 -04001587 if (!e.expression()->hasSideEffects()) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001588 // Expression statement with no side effects, kill it
Ethan Nicholasd503a5a2020-09-30 09:29:55 -04001589 if (!b.tryRemoveExpressionBefore(iter, e.expression().get())) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001590 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001591 }
Ethan Nicholasd9d33c32018-06-12 11:05:59 -04001592 SkASSERT((*iter)->statement()->get() == stmt);
Brian Osman010ce6a2020-10-19 16:34:10 -04001593 (*iter)->setStatement(std::make_unique<Nop>(), usage);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001594 optimizationContext->fUpdated = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001595 }
1596 break;
1597 }
1598 default:
1599 break;
1600 }
1601}
1602
Brian Osman010ce6a2020-10-19 16:34:10 -04001603bool Compiler::scanCFG(FunctionDefinition& f, ProgramUsage* usage) {
John Stiles0cc193a2020-09-09 09:39:34 -04001604 bool madeChanges = false;
1605
Ethan Nicholascb670962017-04-20 19:31:52 -04001606 CFG cfg = CFGGenerator().getCFG(f);
1607 this->computeDataFlow(&cfg);
ethannicholas22f939e2016-10-13 13:25:34 -07001608
1609 // check for unreachable code
1610 for (size_t i = 0; i < cfg.fBlocks.size(); i++) {
John Stiles0cc193a2020-09-09 09:39:34 -04001611 const BasicBlock& block = cfg.fBlocks[i];
John Stiles40525102020-12-16 18:10:44 -05001612 if (!block.fIsReachable && !block.fAllowUnreachable && block.fNodes.size()) {
John Stiles0cc193a2020-09-09 09:39:34 -04001613 const BasicBlock::Node& node = block.fNodes[0];
John Stilesc1129322021-02-03 10:13:42 -05001614 int offset = node.isStatement() ? (*node.statement())->fOffset
1615 : (*node.expression())->fOffset;
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001616 this->error(offset, String("unreachable"));
ethannicholas22f939e2016-10-13 13:25:34 -07001617 }
1618 }
1619 if (fErrorCount) {
John Stiles0cc193a2020-09-09 09:39:34 -04001620 return madeChanges;
ethannicholas22f939e2016-10-13 13:25:34 -07001621 }
1622
Ethan Nicholascb670962017-04-20 19:31:52 -04001623 // check for dead code & undefined variables, perform constant propagation
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001624 OptimizationContext optimizationContext;
Brian Osman010ce6a2020-10-19 16:34:10 -04001625 optimizationContext.fUsage = usage;
John Stiles7d3f0892020-11-03 11:35:01 -05001626 SkBitSet eliminatedBlockIds(cfg.fBlocks.size());
Ethan Nicholascb670962017-04-20 19:31:52 -04001627 do {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001628 if (optimizationContext.fNeedsRescan) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001629 cfg = CFGGenerator().getCFG(f);
1630 this->computeDataFlow(&cfg);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001631 optimizationContext.fNeedsRescan = false;
Ethan Nicholas113628d2017-02-02 16:11:39 -05001632 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001633
John Stiles7d3f0892020-11-03 11:35:01 -05001634 eliminatedBlockIds.reset();
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001635 optimizationContext.fUpdated = false;
John Stiles7d3f0892020-11-03 11:35:01 -05001636
1637 for (BlockId blockId = 0; blockId < cfg.fBlocks.size(); ++blockId) {
1638 if (eliminatedBlockIds.test(blockId)) {
1639 // We reached a block ID that might have been eliminated. Be cautious and rescan.
1640 optimizationContext.fUpdated = true;
1641 optimizationContext.fNeedsRescan = true;
1642 break;
1643 }
1644
1645 BasicBlock& b = cfg.fBlocks[blockId];
1646 if (blockId > 0 && !b.fIsReachable) {
Ethan Nicholas1de14812020-06-19 15:32:49 -04001647 // Block was reachable before optimization, but has since become unreachable. In
1648 // addition to being dead code, it's broken - since control flow can't reach it, no
1649 // prior variable definitions can reach it, and therefore variables might look to
Brian Osmanc0213602020-10-06 14:43:32 -04001650 // have not been properly assigned. Kill it by replacing all statements with Nops.
Ethan Nicholas1de14812020-06-19 15:32:49 -04001651 for (BasicBlock::Node& node : b.fNodes) {
John Stiles70025e52020-09-28 16:08:58 -04001652 if (node.isStatement() && !(*node.statement())->is<Nop>()) {
John Stiles7d3f0892020-11-03 11:35:01 -05001653 // Eliminating a node runs the risk of eliminating that node's exits as
1654 // well. Keep track of this and do a rescan if we are about to access one
1655 // of these.
1656 for (BlockId id : b.fExits) {
1657 eliminatedBlockIds.set(id);
1658 }
Brian Osman010ce6a2020-10-19 16:34:10 -04001659 node.setStatement(std::make_unique<Nop>(), usage);
John Stiles0cc193a2020-09-09 09:39:34 -04001660 madeChanges = true;
Ethan Nicholas1de14812020-06-19 15:32:49 -04001661 }
1662 }
1663 continue;
1664 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001665 DefinitionMap definitions = b.fBefore;
1666
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001667 for (auto iter = b.fNodes.begin(); iter != b.fNodes.end() &&
1668 !optimizationContext.fNeedsRescan; ++iter) {
John Stiles70025e52020-09-28 16:08:58 -04001669 if (iter->isExpression()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001670 this->simplifyExpression(definitions, b, &iter, &optimizationContext);
Ethan Nicholascb670962017-04-20 19:31:52 -04001671 } else {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001672 this->simplifyStatement(definitions, b, &iter, &optimizationContext);
Ethan Nicholascb670962017-04-20 19:31:52 -04001673 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001674 if (optimizationContext.fNeedsRescan) {
Ethan Nicholas4b330df2017-05-17 10:52:55 -04001675 break;
1676 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001677 this->addDefinitions(*iter, &definitions);
1678 }
Brian Osman01a3eb42020-09-14 11:32:49 -04001679
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001680 if (optimizationContext.fNeedsRescan) {
Brian Osman01a3eb42020-09-14 11:32:49 -04001681 break;
1682 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001683 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001684 madeChanges |= optimizationContext.fUpdated;
1685 } while (optimizationContext.fUpdated);
1686 SkASSERT(!optimizationContext.fNeedsRescan);
ethannicholas22f939e2016-10-13 13:25:34 -07001687
Ethan Nicholas91a10532017-06-22 11:24:38 -04001688 // verify static ifs & switches, clean up dead variable decls
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001689 for (BasicBlock& b : cfg.fBlocks) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001690 for (auto iter = b.fNodes.begin(); iter != b.fNodes.end() &&
1691 !optimizationContext.fNeedsRescan;) {
John Stiles70025e52020-09-28 16:08:58 -04001692 if (iter->isStatement()) {
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001693 const Statement& s = **iter->statement();
Ethan Nicholase6592142020-09-08 10:22:09 -04001694 switch (s.kind()) {
1695 case Statement::Kind::kIf:
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001696 if (s.as<IfStatement>().isStatic() &&
Ethan Nicholas6e1cbc02017-07-14 10:12:15 -04001697 !(fFlags & kPermitInvalidStaticTests_Flag)) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001698 this->error(s.fOffset, "static if has non-static test");
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001699 }
Ethan Nicholas91a10532017-06-22 11:24:38 -04001700 ++iter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001701 break;
Ethan Nicholase6592142020-09-08 10:22:09 -04001702 case Statement::Kind::kSwitch:
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001703 if (s.as<SwitchStatement>().isStatic() &&
1704 !(fFlags & kPermitInvalidStaticTests_Flag) &&
1705 optimizationContext.fSilences.find(&s) ==
1706 optimizationContext.fSilences.end()) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001707 this->error(s.fOffset, "static switch has non-static test");
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001708 }
Ethan Nicholas91a10532017-06-22 11:24:38 -04001709 ++iter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001710 break;
1711 default:
Ethan Nicholas91a10532017-06-22 11:24:38 -04001712 ++iter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001713 break;
1714 }
Ethan Nicholas91a10532017-06-22 11:24:38 -04001715 } else {
1716 ++iter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001717 }
1718 }
1719 }
1720
ethannicholas22f939e2016-10-13 13:25:34 -07001721 // check for missing return
John Stiles54e7c052021-01-11 14:22:36 -05001722 if (f.declaration().returnType() != *fContext->fTypes.fVoid) {
John Stiles61e75e32020-10-01 15:42:37 -04001723 if (cfg.fBlocks[cfg.fExit].fIsReachable) {
Ethan Nicholas0a5d0962020-10-14 13:33:18 -04001724 this->error(f.fOffset, String("function '" + String(f.declaration().name()) +
Ethan Nicholasdb80f692019-11-22 14:06:12 -05001725 "' can exit without returning a value"));
ethannicholas22f939e2016-10-13 13:25:34 -07001726 }
1727 }
John Stiles0cc193a2020-09-09 09:39:34 -04001728
1729 return madeChanges;
ethannicholas22f939e2016-10-13 13:25:34 -07001730}
1731
Brian Osman32d53552020-09-23 13:55:20 -04001732std::unique_ptr<Program> Compiler::convertProgram(
1733 Program::Kind kind,
1734 String text,
1735 const Program::Settings& settings,
Brian Osmanbe0b3b72021-01-06 14:27:35 -05001736 const std::vector<std::unique_ptr<ExternalFunction>>* externalFunctions) {
1737 SkASSERT(!externalFunctions || (kind == Program::kGeneric_Kind));
Ethan Nicholas91164d12019-05-15 15:29:54 -04001738
Brian Osman0006ad02020-11-18 15:38:39 -05001739 // Loading and optimizing our base module might reset the inliner, so do that first,
1740 // *then* configure the inliner with the settings for this program.
1741 const ParsedModule& baseModule = this->moduleForProgramKind(kind);
1742
ethannicholasb3058bd2016-07-01 08:22:01 -07001743 fErrorText = "";
1744 fErrorCount = 0;
Brian Osman0006ad02020-11-18 15:38:39 -05001745 fInliner.reset(fIRGenerator->fModifiers.get(), &settings);
Brian Osman88cda172020-10-09 12:05:16 -04001746
1747 // Not using AutoSource, because caller is likely to call errorText() if we fail to compile
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001748 std::unique_ptr<String> textPtr(new String(std::move(text)));
1749 fSource = textPtr.get();
Brian Osman88cda172020-10-09 12:05:16 -04001750
John Stiles5c7bb322020-10-22 11:09:15 -04001751 // Enable node pooling while converting and optimizing the program for a performance boost.
1752 // The Program will take ownership of the pool.
Brian Osman28f702c2021-02-02 11:52:07 -05001753 std::unique_ptr<Pool> pool;
1754 if (fCaps->useNodePools()) {
1755 pool = Pool::Create();
1756 pool->attachToThread();
1757 }
Brian Osman0006ad02020-11-18 15:38:39 -05001758 IRGenerator::IRBundle ir =
1759 fIRGenerator->convertProgram(kind, &settings, baseModule, /*isBuiltinCode=*/false,
Brian Osmanbe0b3b72021-01-06 14:27:35 -05001760 textPtr->c_str(), textPtr->size(), externalFunctions);
John Stiles5c7bb322020-10-22 11:09:15 -04001761 auto program = std::make_unique<Program>(kind,
1762 std::move(textPtr),
1763 settings,
Brian Osmand7e76592020-11-02 12:26:22 -05001764 fCaps,
John Stiles5c7bb322020-10-22 11:09:15 -04001765 fContext,
1766 std::move(ir.fElements),
Brian Osman133724c2020-10-28 14:14:39 -04001767 std::move(ir.fSharedElements),
John Stiles5c7bb322020-10-22 11:09:15 -04001768 std::move(ir.fModifiers),
1769 std::move(ir.fSymbolTable),
1770 std::move(pool),
1771 ir.fInputs);
1772 bool success = false;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001773 if (fErrorCount) {
John Stiles5c7bb322020-10-22 11:09:15 -04001774 // Do not return programs that failed to compile.
1775 } else if (settings.fOptimize && !this->optimize(*program)) {
1776 // Do not return programs that failed to optimize.
1777 } else {
1778 // We have a successful program!
1779 success = true;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001780 }
John Stiles5c7bb322020-10-22 11:09:15 -04001781
Brian Osman28f702c2021-02-02 11:52:07 -05001782 if (program->fPool) {
1783 program->fPool->detachFromThread();
1784 }
John Stiles5c7bb322020-10-22 11:09:15 -04001785 return success ? std::move(program) : nullptr;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001786}
1787
Brian Osman0006ad02020-11-18 15:38:39 -05001788bool Compiler::optimize(LoadedModule& module) {
1789 SkASSERT(!fErrorCount);
1790 Program::Settings settings;
1791 fIRGenerator->fKind = module.fKind;
1792 fIRGenerator->fSettings = &settings;
1793 std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
1794
1795 fInliner.reset(fModifiers.back().get(), &settings);
1796
1797 while (fErrorCount == 0) {
1798 bool madeChanges = false;
1799
1800 // Scan and optimize based on the control-flow graph for each function.
1801 for (const auto& element : module.fElements) {
1802 if (element->is<FunctionDefinition>()) {
1803 madeChanges |= this->scanCFG(element->as<FunctionDefinition>(), usage.get());
1804 }
1805 }
1806
1807 // Perform inline-candidate analysis and inline any functions deemed suitable.
John Stiles78047582020-12-16 16:17:41 -05001808 madeChanges |= fInliner.analyze(module.fElements, module.fSymbols, usage.get());
Brian Osman0006ad02020-11-18 15:38:39 -05001809
1810 if (!madeChanges) {
1811 break;
1812 }
1813 }
1814 return fErrorCount == 0;
1815}
1816
Ethan Nicholas00543112018-07-31 09:44:36 -04001817bool Compiler::optimize(Program& program) {
1818 SkASSERT(!fErrorCount);
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001819 fIRGenerator->fKind = program.fKind;
1820 fIRGenerator->fSettings = &program.fSettings;
Brian Osman010ce6a2020-10-19 16:34:10 -04001821 ProgramUsage* usage = program.fUsage.get();
John Stiles7954d6c2020-09-01 10:53:02 -04001822
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001823 while (fErrorCount == 0) {
1824 bool madeChanges = false;
John Stiles7954d6c2020-09-01 10:53:02 -04001825
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001826 // Scan and optimize based on the control-flow graph for each function.
Brian Osman133724c2020-10-28 14:14:39 -04001827 for (const auto& element : program.ownedElements()) {
Brian Osman1179fcf2020-10-08 16:04:40 -04001828 if (element->is<FunctionDefinition>()) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001829 madeChanges |= this->scanCFG(element->as<FunctionDefinition>(), usage);
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001830 }
1831 }
1832
1833 // Perform inline-candidate analysis and inline any functions deemed suitable.
John Stiles78047582020-12-16 16:17:41 -05001834 madeChanges |= fInliner.analyze(program.ownedElements(), program.fSymbols, usage);
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001835
1836 // Remove dead functions. We wait until after analysis so that we still report errors,
1837 // even in unused code.
1838 if (program.fSettings.fRemoveDeadFunctions) {
Brian Osman133724c2020-10-28 14:14:39 -04001839 auto isDeadFunction = [&](const ProgramElement* element) {
1840 if (!element->is<FunctionDefinition>()) {
1841 return false;
1842 }
1843 const FunctionDefinition& fn = element->as<FunctionDefinition>();
1844 if (fn.declaration().name() != "main" && usage->get(fn.declaration()) == 0) {
1845 usage->remove(*element);
1846 madeChanges = true;
1847 return true;
1848 }
1849 return false;
1850 };
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001851 program.fElements.erase(
Brian Osman133724c2020-10-28 14:14:39 -04001852 std::remove_if(program.fElements.begin(), program.fElements.end(),
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001853 [&](const std::unique_ptr<ProgramElement>& element) {
Brian Osman133724c2020-10-28 14:14:39 -04001854 return isDeadFunction(element.get());
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001855 }),
1856 program.fElements.end());
Brian Osman133724c2020-10-28 14:14:39 -04001857 program.fSharedElements.erase(
1858 std::remove_if(program.fSharedElements.begin(), program.fSharedElements.end(),
1859 isDeadFunction),
1860 program.fSharedElements.end());
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001861 }
1862
1863 if (program.fKind != Program::kFragmentProcessor_Kind) {
Brian Osmanc0213602020-10-06 14:43:32 -04001864 // Remove declarations of dead global variables
Brian Osman133724c2020-10-28 14:14:39 -04001865 auto isDeadVariable = [&](const ProgramElement* element) {
1866 if (!element->is<GlobalVarDeclaration>()) {
1867 return false;
1868 }
1869 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
1870 const VarDeclaration& varDecl = global.declaration()->as<VarDeclaration>();
1871 if (usage->isDead(varDecl.var())) {
1872 madeChanges = true;
1873 return true;
1874 }
1875 return false;
1876 };
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001877 program.fElements.erase(
1878 std::remove_if(program.fElements.begin(), program.fElements.end(),
1879 [&](const std::unique_ptr<ProgramElement>& element) {
Brian Osman133724c2020-10-28 14:14:39 -04001880 return isDeadVariable(element.get());
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001881 }),
1882 program.fElements.end());
Brian Osman133724c2020-10-28 14:14:39 -04001883 program.fSharedElements.erase(
1884 std::remove_if(program.fSharedElements.begin(), program.fSharedElements.end(),
1885 isDeadVariable),
1886 program.fSharedElements.end());
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001887 }
John Stiles73a6bff2020-09-09 13:40:37 -04001888
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001889 if (!madeChanges) {
1890 break;
Ethan Nicholas0dc80872019-02-08 15:46:24 -05001891 }
Ethan Nicholas00543112018-07-31 09:44:36 -04001892 }
1893 return fErrorCount == 0;
1894}
1895
Brian Osmanfb32ddf2019-06-18 10:14:20 -04001896#if defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
1897
Ethan Nicholas00543112018-07-31 09:44:36 -04001898bool Compiler::toSPIRV(Program& program, OutputStream& out) {
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001899#ifdef SK_ENABLE_SPIRV_VALIDATION
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001900 StringStream buffer;
Brian Osman88cda172020-10-09 12:05:16 -04001901 AutoSource as(this, program.fSource.get());
Brian Osman8b43dad2020-10-09 13:31:42 -04001902 SPIRVCodeGenerator cg(fContext.get(), &program, this, &buffer);
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001903 bool result = cg.generateCode();
Ethan Nicholasba9a04f2020-11-06 09:28:04 -05001904 if (result && program.fSettings.fValidateSPIRV) {
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001905 spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001906 const String& data = buffer.str();
Ethan Nicholasd9d33c32018-06-12 11:05:59 -04001907 SkASSERT(0 == data.size() % 4);
Brian Osman8d09d4a2020-11-24 15:51:06 -05001908 String errors;
1909 auto dumpmsg = [&errors](spv_message_level_t, const char*, const spv_position_t&,
1910 const char* m) {
1911 errors.appendf("SPIR-V validation error: %s\n", m);
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001912 };
1913 tools.SetMessageConsumer(dumpmsg);
Brian Osman8d09d4a2020-11-24 15:51:06 -05001914
1915 // Verify that the SPIR-V we produced is valid. At runtime, we will abort() with a message
1916 // explaining the error. In standalone mode (skslc), we will send the message, plus the
1917 // entire disassembled SPIR-V (for easier context & debugging) as *our* error message.
1918 result = tools.Validate((const uint32_t*) data.c_str(), data.size() / 4);
1919
1920 if (!result) {
1921#if defined(SKSL_STANDALONE)
1922 // Convert the string-stream to a SPIR-V disassembly.
1923 std::string disassembly;
1924 if (tools.Disassemble((const uint32_t*)data.data(), data.size() / 4, &disassembly)) {
1925 errors.append(disassembly);
1926 }
1927 this->error(-1, errors);
1928#else
1929 SkDEBUGFAILF("%s", errors.c_str());
1930#endif
1931 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001932 out.write(data.c_str(), data.size());
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001933 }
1934#else
Brian Osman88cda172020-10-09 12:05:16 -04001935 AutoSource as(this, program.fSource.get());
Brian Osman8b43dad2020-10-09 13:31:42 -04001936 SPIRVCodeGenerator cg(fContext.get(), &program, this, &out);
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001937 bool result = cg.generateCode();
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001938#endif
Ethan Nicholasce33f102016-12-09 17:22:59 -05001939 return result;
1940}
1941
Ethan Nicholas00543112018-07-31 09:44:36 -04001942bool Compiler::toSPIRV(Program& program, String* out) {
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001943 StringStream buffer;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001944 bool result = this->toSPIRV(program, buffer);
1945 if (result) {
Ethan Nicholas762466e2017-06-29 10:03:38 -04001946 *out = buffer.str();
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001947 }
1948 return result;
1949}
1950
Ethan Nicholas00543112018-07-31 09:44:36 -04001951bool Compiler::toGLSL(Program& program, OutputStream& out) {
Brian Osman88cda172020-10-09 12:05:16 -04001952 AutoSource as(this, program.fSource.get());
Ethan Nicholas26a9aad2018-03-27 14:10:52 -04001953 GLSLCodeGenerator cg(fContext.get(), &program, this, &out);
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001954 bool result = cg.generateCode();
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001955 return result;
1956}
1957
Ethan Nicholas00543112018-07-31 09:44:36 -04001958bool Compiler::toGLSL(Program& program, String* out) {
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001959 StringStream buffer;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001960 bool result = this->toGLSL(program, buffer);
1961 if (result) {
Ethan Nicholas762466e2017-06-29 10:03:38 -04001962 *out = buffer.str();
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001963 }
1964 return result;
1965}
1966
Brian Osmanc0243912020-02-19 15:35:26 -05001967bool Compiler::toHLSL(Program& program, String* out) {
1968 String spirv;
1969 if (!this->toSPIRV(program, &spirv)) {
1970 return false;
1971 }
1972
1973 return SPIRVtoHLSL(spirv, out);
1974}
1975
Ethan Nicholas00543112018-07-31 09:44:36 -04001976bool Compiler::toMetal(Program& program, OutputStream& out) {
Ethan Nicholas26a9aad2018-03-27 14:10:52 -04001977 MetalCodeGenerator cg(fContext.get(), &program, this, &out);
Ethan Nicholascc305772017-10-13 16:17:45 -04001978 bool result = cg.generateCode();
Ethan Nicholascc305772017-10-13 16:17:45 -04001979 return result;
1980}
1981
Ethan Nicholas00543112018-07-31 09:44:36 -04001982bool Compiler::toMetal(Program& program, String* out) {
Timothy Liangb8eeb802018-07-23 16:46:16 -04001983 StringStream buffer;
1984 bool result = this->toMetal(program, buffer);
1985 if (result) {
1986 *out = buffer.str();
1987 }
1988 return result;
1989}
1990
Greg Daniela28ea672020-09-25 11:12:56 -04001991#if defined(SKSL_STANDALONE) || GR_TEST_UTILS
Ethan Nicholas00543112018-07-31 09:44:36 -04001992bool Compiler::toCPP(Program& program, String name, OutputStream& out) {
Brian Osman88cda172020-10-09 12:05:16 -04001993 AutoSource as(this, program.fSource.get());
Ethan Nicholas26a9aad2018-03-27 14:10:52 -04001994 CPPCodeGenerator cg(fContext.get(), &program, this, name, &out);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001995 bool result = cg.generateCode();
Ethan Nicholas762466e2017-06-29 10:03:38 -04001996 return result;
1997}
1998
Ethan Nicholas00543112018-07-31 09:44:36 -04001999bool Compiler::toH(Program& program, String name, OutputStream& out) {
Brian Osman88cda172020-10-09 12:05:16 -04002000 AutoSource as(this, program.fSource.get());
Ethan Nicholas26a9aad2018-03-27 14:10:52 -04002001 HCodeGenerator cg(fContext.get(), &program, this, name, &out);
Ethan Nicholas762466e2017-06-29 10:03:38 -04002002 bool result = cg.generateCode();
Ethan Nicholas00543112018-07-31 09:44:36 -04002003 return result;
2004}
Greg Daniela28ea672020-09-25 11:12:56 -04002005#endif // defined(SKSL_STANDALONE) || GR_TEST_UTILS
Ethan Nicholas00543112018-07-31 09:44:36 -04002006
Ethan Nicholas2a479a52020-08-18 16:29:45 -04002007#endif // defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
Brian Osman2e29ab52019-09-20 12:19:11 -04002008
2009#if !defined(SKSL_STANDALONE) && SK_SUPPORT_GPU
Brian Osmana4b91692020-08-10 14:26:16 -04002010bool Compiler::toPipelineStage(Program& program, PipelineStageArgs* outArgs) {
Brian Osman88cda172020-10-09 12:05:16 -04002011 AutoSource as(this, program.fSource.get());
Ethan Nicholas00543112018-07-31 09:44:36 -04002012 StringStream buffer;
Brian Osman300fe1d2020-01-23 15:42:43 -05002013 PipelineStageCodeGenerator cg(fContext.get(), &program, this, &buffer, outArgs);
Ethan Nicholas00543112018-07-31 09:44:36 -04002014 bool result = cg.generateCode();
Ethan Nicholas00543112018-07-31 09:44:36 -04002015 if (result) {
Brian Osman107c6662019-12-30 15:02:30 -05002016 outArgs->fCode = buffer.str();
Ethan Nicholas00543112018-07-31 09:44:36 -04002017 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04002018 return result;
2019}
Brian Osmanfb32ddf2019-06-18 10:14:20 -04002020#endif
2021
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07002022Position Compiler::position(int offset) {
Ethan Nicholas3c729892020-12-07 12:47:17 -05002023 if (fSource && offset >= 0) {
2024 int line = 1;
2025 int column = 1;
2026 for (int i = 0; i < offset; i++) {
2027 if ((*fSource)[i] == '\n') {
2028 ++line;
2029 column = 1;
2030 }
2031 else {
2032 ++column;
2033 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07002034 }
Ethan Nicholas3c729892020-12-07 12:47:17 -05002035 return Position(line, column);
2036 } else {
2037 return Position(-1, -1);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07002038 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07002039}
2040
2041void Compiler::error(int offset, String msg) {
ethannicholasb3058bd2016-07-01 08:22:01 -07002042 fErrorCount++;
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07002043 Position pos = this->position(offset);
John Stiles8d3642e2021-01-22 09:50:04 -05002044 fErrorTextLength.push_back(fErrorText.length());
Ethan Nicholas3c729892020-12-07 12:47:17 -05002045 fErrorText += "error: " + (pos.fLine >= 1 ? to_string(pos.fLine) + ": " : "") + msg + "\n";
ethannicholasb3058bd2016-07-01 08:22:01 -07002046}
2047
John Stiles8d3642e2021-01-22 09:50:04 -05002048void Compiler::setErrorCount(int c) {
2049 if (c < fErrorCount) {
2050 fErrorText.resize(fErrorTextLength[c]);
2051 fErrorTextLength.resize(c);
2052 fErrorCount = c;
2053 }
2054}
2055
Ethan Nicholas95046142021-01-07 10:57:27 -05002056String Compiler::errorText(bool showCount) {
2057 if (showCount) {
2058 this->writeErrorCount();
2059 }
Ethan Nicholas00543112018-07-31 09:44:36 -04002060 fErrorCount = 0;
Ethan Nicholas0df1b042017-03-31 13:56:23 -04002061 String result = fErrorText;
Ethan Nicholas95046142021-01-07 10:57:27 -05002062 fErrorText = "";
ethannicholasb3058bd2016-07-01 08:22:01 -07002063 return result;
2064}
2065
2066void Compiler::writeErrorCount() {
2067 if (fErrorCount) {
2068 fErrorText += to_string(fErrorCount) + " error";
2069 if (fErrorCount > 1) {
2070 fErrorText += "s";
2071 }
2072 fErrorText += "\n";
2073 }
2074}
2075
John Stilesa6841be2020-08-06 14:11:56 -04002076} // namespace SkSL