blob: 6c15890fe89bb18b6d46b4781c4df69df12969b7 [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"
Ethan Nicholasc18bb512020-07-28 14:46:53 -040021#include "src/sksl/SkSLRehydrator.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050022#include "src/sksl/SkSLSPIRVCodeGenerator.h"
Brian Osmanc0243912020-02-19 15:35:26 -050023#include "src/sksl/SkSLSPIRVtoHLSL.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "src/sksl/ir/SkSLEnum.h"
25#include "src/sksl/ir/SkSLExpression.h"
26#include "src/sksl/ir/SkSLExpressionStatement.h"
27#include "src/sksl/ir/SkSLFunctionCall.h"
28#include "src/sksl/ir/SkSLIntLiteral.h"
29#include "src/sksl/ir/SkSLModifiersDeclaration.h"
30#include "src/sksl/ir/SkSLNop.h"
31#include "src/sksl/ir/SkSLSymbolTable.h"
32#include "src/sksl/ir/SkSLTernaryExpression.h"
33#include "src/sksl/ir/SkSLUnresolvedFunction.h"
34#include "src/sksl/ir/SkSLVarDeclarations.h"
John Stilese6150002020-10-05 12:03:53 -040035#include "src/utils/SkBitSet.h"
ethannicholasb3058bd2016-07-01 08:22:01 -070036
Ethan Nicholasb33fa3f2020-08-06 13:00:19 -040037#include <fstream>
38
Ethan Nicholasa11035b2019-11-26 16:27:47 -050039#if !defined(SKSL_STANDALONE) & SK_SUPPORT_GPU
40#include "include/gpu/GrContextOptions.h"
41#include "src/gpu/GrShaderCaps.h"
42#endif
43
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -040044#ifdef SK_ENABLE_SPIRV_VALIDATION
45#include "spirv-tools/libspirv.hpp"
46#endif
47
Brian Osman3d87e9f2020-10-08 11:50:22 -040048#if defined(SKSL_STANDALONE)
Ethan Nicholasc18bb512020-07-28 14:46:53 -040049
Brian Osman3d87e9f2020-10-08 11:50:22 -040050// In standalone mode, we load the textual sksl source files. GN generates or copies these files
51// to the skslc executable directory. The "data" in this mode is just the filename.
52#define MODULE_DATA(name) MakeModulePath("sksl_" #name ".sksl")
53
54#else
55
56// At runtime, we load the dehydrated sksl data files. The data is a (pointer, size) pair.
Ethan Nicholasc18bb512020-07-28 14:46:53 -040057#include "src/sksl/generated/sksl_fp.dehydrated.sksl"
58#include "src/sksl/generated/sksl_frag.dehydrated.sksl"
59#include "src/sksl/generated/sksl_geom.dehydrated.sksl"
60#include "src/sksl/generated/sksl_gpu.dehydrated.sksl"
Brian Osmanb06301e2020-11-06 11:45:36 -050061#include "src/sksl/generated/sksl_public.dehydrated.sksl"
Brian Osman91946752020-12-21 13:20:40 -050062#include "src/sksl/generated/sksl_runtime.dehydrated.sksl"
Ethan Nicholasc18bb512020-07-28 14:46:53 -040063#include "src/sksl/generated/sksl_vert.dehydrated.sksl"
64
Brian Osman3d87e9f2020-10-08 11:50:22 -040065#define MODULE_DATA(name) MakeModuleData(SKSL_INCLUDE_sksl_##name,\
66 SKSL_INCLUDE_sksl_##name##_LENGTH)
Ethan Nicholasc18bb512020-07-28 14:46:53 -040067
68#endif
Ethan Nicholas0d997662019-04-08 09:46:01 -040069
ethannicholasb3058bd2016-07-01 08:22:01 -070070namespace SkSL {
71
John Stiles47c0a742021-02-09 09:30:35 -050072using RefKind = VariableReference::RefKind;
73
Brian Osman88cda172020-10-09 12:05:16 -040074class AutoSource {
75public:
76 AutoSource(Compiler* compiler, const String* source)
77 : fCompiler(compiler), fOldSource(fCompiler->fSource) {
78 fCompiler->fSource = source;
79 }
80
81 ~AutoSource() { fCompiler->fSource = fOldSource; }
82
83 Compiler* fCompiler;
84 const String* fOldSource;
85};
86
Brian Osmand7e76592020-11-02 12:26:22 -050087Compiler::Compiler(const ShaderCapsClass* caps, Flags flags)
John Stilesb30151e2021-01-11 16:13:08 -050088 : fContext(std::make_shared<Context>(/*errors=*/*this))
Brian Osman0006ad02020-11-18 15:38:39 -050089 , fCaps(caps)
John Stiles7b920442020-12-17 10:43:41 -050090 , fInliner(fContext.get())
Brian Osman0006ad02020-11-18 15:38:39 -050091 , fFlags(flags)
92 , fErrorCount(0) {
93 SkASSERT(fCaps);
John Stiles7c3515b2020-10-16 18:38:39 -040094 fRootSymbolTable = std::make_shared<SymbolTable>(this, /*builtin=*/true);
Brian Osmanb06301e2020-11-06 11:45:36 -050095 fPrivateSymbolTable = std::make_shared<SymbolTable>(fRootSymbolTable, /*builtin=*/true);
John Stilesb30151e2021-01-11 16:13:08 -050096 fIRGenerator = std::make_unique<IRGenerator>(fContext.get(), fCaps);
ethannicholasb3058bd2016-07-01 08:22:01 -070097
John Stiles54e7c052021-01-11 14:22:36 -050098#define TYPE(t) fContext->fTypes.f ## t .get()
ethannicholasb3058bd2016-07-01 08:22:01 -070099
Brian Osmanb06301e2020-11-06 11:45:36 -0500100 const SkSL::Symbol* rootTypes[] = {
101 TYPE(Void),
Brian Salomonbf7b6202016-11-11 16:08:03 -0500102
Brian Osmanb06301e2020-11-06 11:45:36 -0500103 TYPE( Float), TYPE( Float2), TYPE( Float3), TYPE( Float4),
104 TYPE( Half), TYPE( Half2), TYPE( Half3), TYPE( Half4),
105 TYPE( Int), TYPE( Int2), TYPE( Int3), TYPE( Int4),
Brian Osmanb06301e2020-11-06 11:45:36 -0500106 TYPE( Bool), TYPE( Bool2), TYPE( Bool3), TYPE( Bool4),
Brian Salomon2a51de82016-11-16 12:06:01 -0500107
Brian Osmanc0f2b642020-12-22 13:35:55 -0500108 TYPE(Float2x2), TYPE(Float3x3), TYPE(Float4x4),
Brian Osmanc63f4312020-12-23 11:44:14 -0500109 TYPE( Half2x2), TYPE( Half3x3), TYPE(Half4x4),
Greg Daniel64773e62016-11-22 09:44:03 -0500110
Brian Osmanc63f4312020-12-23 11:44:14 -0500111 TYPE(SquareMat), TYPE(SquareHMat),
ethannicholasb3058bd2016-07-01 08:22:01 -0700112
Brian Osman20fad322020-12-23 12:42:33 -0500113 TYPE(GenType), TYPE(GenHType), TYPE(GenIType), TYPE(GenBType),
114 TYPE(Vec), TYPE(HVec), TYPE(IVec), TYPE(BVec),
Brian Osmanb06301e2020-11-06 11:45:36 -0500115
116 TYPE(FragmentProcessor),
117 };
118
119 const SkSL::Symbol* privateTypes[] = {
Brian Osman20fad322020-12-23 12:42:33 -0500120 TYPE( UInt), TYPE( UInt2), TYPE( UInt3), TYPE( UInt4),
121 TYPE( Short), TYPE( Short2), TYPE( Short3), TYPE( Short4),
122 TYPE(UShort), TYPE(UShort2), TYPE(UShort3), TYPE(UShort4),
123 TYPE( Byte), TYPE( Byte2), TYPE( Byte3), TYPE( Byte4),
124 TYPE( UByte), TYPE( UByte2), TYPE( UByte3), TYPE( UByte4),
125
126 TYPE(GenUType), TYPE(UVec),
127 TYPE(SVec), TYPE(USVec), TYPE(ByteVec), TYPE(UByteVec),
128
Brian Osmanc0f2b642020-12-22 13:35:55 -0500129 TYPE(Float2x3), TYPE(Float2x4),
130 TYPE(Float3x2), TYPE(Float3x4),
131 TYPE(Float4x2), TYPE(Float4x3),
132
Brian Osmanc63f4312020-12-23 11:44:14 -0500133 TYPE(Half2x3), TYPE(Half2x4),
134 TYPE(Half3x2), TYPE(Half3x4),
135 TYPE(Half4x2), TYPE(Half4x3),
136
Brian Osmanc0f2b642020-12-22 13:35:55 -0500137 TYPE(Mat), TYPE(HMat),
138
Brian Osmanb06301e2020-11-06 11:45:36 -0500139 TYPE(Sampler1D), TYPE(Sampler2D), TYPE(Sampler3D),
140 TYPE(SamplerExternalOES),
Brian Osmanb06301e2020-11-06 11:45:36 -0500141 TYPE(Sampler2DRect),
Brian Osmanb06301e2020-11-06 11:45:36 -0500142
143 TYPE(ISampler2D),
144 TYPE(Image2D), TYPE(IImage2D),
145 TYPE(SubpassInput), TYPE(SubpassInputMS),
146
Brian Osmanb06301e2020-11-06 11:45:36 -0500147 TYPE(Sampler),
148 TYPE(Texture2D),
149 };
150
151 for (const SkSL::Symbol* type : rootTypes) {
152 fRootSymbolTable->addWithoutOwnership(type);
153 }
154 for (const SkSL::Symbol* type : privateTypes) {
155 fPrivateSymbolTable->addWithoutOwnership(type);
156 }
157
158#undef TYPE
ethannicholasb3058bd2016-07-01 08:22:01 -0700159
Brian Osman3887a012020-09-30 13:22:27 -0400160 // sk_Caps is "builtin", but all references to it are resolved to Settings, so we don't need to
161 // treat it as builtin (ie, no need to clone it into the Program).
Brian Osmanb06301e2020-11-06 11:45:36 -0500162 fPrivateSymbolTable->add(
163 std::make_unique<Variable>(/*offset=*/-1,
John Stiles586df952020-11-12 18:27:13 -0500164 fIRGenerator->fModifiers->addToPool(Modifiers()),
Brian Osmanb06301e2020-11-06 11:45:36 -0500165 "sk_Caps",
John Stiles54e7c052021-01-11 14:22:36 -0500166 fContext->fTypes.fSkCaps.get(),
Brian Osmanb06301e2020-11-06 11:45:36 -0500167 /*builtin=*/false,
168 Variable::Storage::kGlobal));
Ethan Nicholas3605ace2016-11-21 15:59:48 -0500169
Brian Osman3d87e9f2020-10-08 11:50:22 -0400170 fRootModule = {fRootSymbolTable, /*fIntrinsics=*/nullptr};
Brian Osmanb06301e2020-11-06 11:45:36 -0500171 fPrivateModule = {fPrivateSymbolTable, /*fIntrinsics=*/nullptr};
ethannicholasb3058bd2016-07-01 08:22:01 -0700172}
173
John Stilesdd13dba2020-10-29 10:45:34 -0400174Compiler::~Compiler() {}
ethannicholasb3058bd2016-07-01 08:22:01 -0700175
Brian Osman56269982020-11-20 12:38:07 -0500176const ParsedModule& Compiler::loadGPUModule() {
177 if (!fGPUModule.fSymbols) {
178 fGPUModule = this->parseModule(Program::kFragment_Kind, MODULE_DATA(gpu), fPrivateModule);
179 }
180 return fGPUModule;
181}
182
183const ParsedModule& Compiler::loadFragmentModule() {
184 if (!fFragmentModule.fSymbols) {
185 fFragmentModule = this->parseModule(Program::kFragment_Kind, MODULE_DATA(frag),
186 this->loadGPUModule());
187 }
188 return fFragmentModule;
189}
190
191const ParsedModule& Compiler::loadVertexModule() {
192 if (!fVertexModule.fSymbols) {
193 fVertexModule = this->parseModule(Program::kVertex_Kind, MODULE_DATA(vert),
194 this->loadGPUModule());
195 }
196 return fVertexModule;
197}
198
Brian Osman88cda172020-10-09 12:05:16 -0400199const ParsedModule& Compiler::loadGeometryModule() {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400200 if (!fGeometryModule.fSymbols) {
Brian Osman56269982020-11-20 12:38:07 -0500201 fGeometryModule = this->parseModule(Program::kGeometry_Kind, MODULE_DATA(geom),
202 this->loadGPUModule());
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400203 }
Brian Osman88cda172020-10-09 12:05:16 -0400204 return fGeometryModule;
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400205}
206
Brian Osman88cda172020-10-09 12:05:16 -0400207const ParsedModule& Compiler::loadFPModule() {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400208 if (!fFPModule.fSymbols) {
Brian Osman56269982020-11-20 12:38:07 -0500209 fFPModule = this->parseModule(Program::kFragmentProcessor_Kind, MODULE_DATA(fp),
210 this->loadGPUModule());
Brian Osman8e2ef022020-09-30 13:26:43 -0400211 }
Brian Osman88cda172020-10-09 12:05:16 -0400212 return fFPModule;
Brian Osman8e2ef022020-09-30 13:26:43 -0400213}
214
Brian Osmanb06301e2020-11-06 11:45:36 -0500215const ParsedModule& Compiler::loadPublicModule() {
216 if (!fPublicModule.fSymbols) {
217 fPublicModule = this->parseModule(Program::kGeneric_Kind, MODULE_DATA(public), fRootModule);
218 }
219 return fPublicModule;
220}
221
Brian Osman91946752020-12-21 13:20:40 -0500222const ParsedModule& Compiler::loadRuntimeEffectModule() {
223 if (!fRuntimeEffectModule.fSymbols) {
224 fRuntimeEffectModule = this->parseModule(Program::kRuntimeEffect_Kind, MODULE_DATA(runtime),
225 this->loadPublicModule());
Brian Osmanf1319c32020-10-13 09:34:23 -0400226
Brian Osman91946752020-12-21 13:20:40 -0500227 // Add some aliases to the runtime effect module so that it's friendlier, and more like GLSL
John Stiles54e7c052021-01-11 14:22:36 -0500228 fRuntimeEffectModule.fSymbols->addAlias("shader", fContext->fTypes.fFragmentProcessor.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400229
John Stiles54e7c052021-01-11 14:22:36 -0500230 fRuntimeEffectModule.fSymbols->addAlias("vec2", fContext->fTypes.fFloat2.get());
231 fRuntimeEffectModule.fSymbols->addAlias("vec3", fContext->fTypes.fFloat3.get());
232 fRuntimeEffectModule.fSymbols->addAlias("vec4", fContext->fTypes.fFloat4.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400233
John Stiles54e7c052021-01-11 14:22:36 -0500234 fRuntimeEffectModule.fSymbols->addAlias("bvec2", fContext->fTypes.fBool2.get());
235 fRuntimeEffectModule.fSymbols->addAlias("bvec3", fContext->fTypes.fBool3.get());
236 fRuntimeEffectModule.fSymbols->addAlias("bvec4", fContext->fTypes.fBool4.get());
Brian Osmanf1319c32020-10-13 09:34:23 -0400237
John Stiles54e7c052021-01-11 14:22:36 -0500238 fRuntimeEffectModule.fSymbols->addAlias("mat2", fContext->fTypes.fFloat2x2.get());
239 fRuntimeEffectModule.fSymbols->addAlias("mat3", fContext->fTypes.fFloat3x3.get());
240 fRuntimeEffectModule.fSymbols->addAlias("mat4", fContext->fTypes.fFloat4x4.get());
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400241 }
Brian Osman91946752020-12-21 13:20:40 -0500242 return fRuntimeEffectModule;
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400243}
244
Brian Osman88cda172020-10-09 12:05:16 -0400245const ParsedModule& Compiler::moduleForProgramKind(Program::Kind kind) {
246 switch (kind) {
Brian Osman91946752020-12-21 13:20:40 -0500247 case Program::kVertex_Kind: return this->loadVertexModule(); break;
248 case Program::kFragment_Kind: return this->loadFragmentModule(); break;
249 case Program::kGeometry_Kind: return this->loadGeometryModule(); break;
250 case Program::kFragmentProcessor_Kind: return this->loadFPModule(); break;
251 case Program::kRuntimeEffect_Kind: return this->loadRuntimeEffectModule(); break;
Brian Osmance750362021-01-21 16:33:06 -0500252 case Program::kGeneric_Kind: return this->loadPublicModule(); break;
Brian Osman88cda172020-10-09 12:05:16 -0400253 }
254 SkUNREACHABLE;
Ethan Nicholasc18bb512020-07-28 14:46:53 -0400255}
256
Brian Osman3d87e9f2020-10-08 11:50:22 -0400257LoadedModule Compiler::loadModule(Program::Kind kind,
258 ModuleData data,
259 std::shared_ptr<SymbolTable> base) {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400260 if (!base) {
Brian Osmanb06301e2020-11-06 11:45:36 -0500261 // NOTE: This is a workaround. The only time 'base' is null is when dehydrating includes.
262 // In that case, skslc doesn't know which module it's preparing, nor what the correct base
263 // module is. We can't use 'Root', because many GPU intrinsics reference private types,
264 // like samplers or textures. Today, 'Private' does contain the union of all known types,
265 // so this is safe. If we ever have types that only exist in 'Public' (for example), this
266 // logic needs to be smarter (by choosing the correct base for the module we're compiling).
267 base = fPrivateSymbolTable;
Brian Osman3d87e9f2020-10-08 11:50:22 -0400268 }
269
270#if defined(SKSL_STANDALONE)
271 SkASSERT(data.fPath);
272 std::ifstream in(data.fPath);
Brian Osmane498b3c2020-09-23 14:42:11 -0400273 std::unique_ptr<String> text = std::make_unique<String>(std::istreambuf_iterator<char>(in),
274 std::istreambuf_iterator<char>());
Ethan Nicholasb33fa3f2020-08-06 13:00:19 -0400275 if (in.rdstate()) {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400276 printf("error reading %s\n", data.fPath);
Ethan Nicholasb33fa3f2020-08-06 13:00:19 -0400277 abort();
278 }
Brian Osmane498b3c2020-09-23 14:42:11 -0400279 const String* source = fRootSymbolTable->takeOwnershipOfString(std::move(text));
Brian Osman88cda172020-10-09 12:05:16 -0400280 AutoSource as(this, source);
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400281 Program::Settings settings;
John Stiles881a10c2020-09-19 10:13:24 -0400282 SkASSERT(fIRGenerator->fCanInline);
283 fIRGenerator->fCanInline = false;
Brian Osman88cda172020-10-09 12:05:16 -0400284 ParsedModule baseModule = {base, /*fIntrinsics=*/nullptr};
Brian Osmand7e76592020-11-02 12:26:22 -0500285 IRGenerator::IRBundle ir =
Brian Osman0006ad02020-11-18 15:38:39 -0500286 fIRGenerator->convertProgram(kind, &settings, baseModule,
Brian Osmand7e76592020-11-02 12:26:22 -0500287 /*isBuiltinCode=*/true, source->c_str(), source->length(),
Brian Osmanbe0b3b72021-01-06 14:27:35 -0500288 /*externalFunctions=*/nullptr);
Brian Osman133724c2020-10-28 14:14:39 -0400289 SkASSERT(ir.fSharedElements.empty());
Brian Osman0006ad02020-11-18 15:38:39 -0500290 LoadedModule module = { kind, std::move(ir.fSymbolTable), std::move(ir.fElements) };
John Stiles881a10c2020-09-19 10:13:24 -0400291 fIRGenerator->fCanInline = true;
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400292 if (this->fErrorCount) {
293 printf("Unexpected errors: %s\n", this->fErrorText.c_str());
Brian Osman3d87e9f2020-10-08 11:50:22 -0400294 SkDEBUGFAILF("%s %s\n", data.fPath, this->fErrorText.c_str());
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400295 }
Brian Osman88cda172020-10-09 12:05:16 -0400296 fModifiers.push_back(std::move(ir.fModifiers));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400297#else
298 SkASSERT(data.fData && (data.fSize != 0));
299 Rehydrator rehydrator(fContext.get(), fIRGenerator->fModifiers.get(), base, this,
300 data.fData, data.fSize);
Brian Osman0006ad02020-11-18 15:38:39 -0500301 LoadedModule module = { kind, rehydrator.symbolTable(), rehydrator.elements() };
Brian Osman3d87e9f2020-10-08 11:50:22 -0400302 fModifiers.push_back(fIRGenerator->releaseModifiers());
303#endif
304
305 return module;
306}
307
308ParsedModule Compiler::parseModule(Program::Kind kind, ModuleData data, const ParsedModule& base) {
Brian Osman0006ad02020-11-18 15:38:39 -0500309 LoadedModule module = this->loadModule(kind, data, base.fSymbols);
310 this->optimize(module);
Brian Osman3d87e9f2020-10-08 11:50:22 -0400311
312 // For modules that just declare (but don't define) intrinsic functions, there will be no new
313 // program elements. In that case, we can share our parent's intrinsic map:
Brian Osman0006ad02020-11-18 15:38:39 -0500314 if (module.fElements.empty()) {
315 return {module.fSymbols, base.fIntrinsics};
Brian Osman3d87e9f2020-10-08 11:50:22 -0400316 }
317
318 auto intrinsics = std::make_shared<IRIntrinsicMap>(base.fIntrinsics.get());
319
320 // Now, transfer all of the program elements to an intrinsic map. This maps certain types of
321 // global objects to the declaring ProgramElement.
Brian Osman0006ad02020-11-18 15:38:39 -0500322 for (std::unique_ptr<ProgramElement>& element : module.fElements) {
Brian Osman3d87e9f2020-10-08 11:50:22 -0400323 switch (element->kind()) {
324 case ProgramElement::Kind::kFunction: {
325 const FunctionDefinition& f = element->as<FunctionDefinition>();
Ethan Nicholas0a5d0962020-10-14 13:33:18 -0400326 SkASSERT(f.declaration().isBuiltin());
327 intrinsics->insertOrDie(f.declaration().description(), std::move(element));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400328 break;
329 }
John Stiles569249b2020-11-03 12:18:22 -0500330 case ProgramElement::Kind::kFunctionPrototype: {
331 // These are already in the symbol table.
332 break;
333 }
Brian Osman3d87e9f2020-10-08 11:50:22 -0400334 case ProgramElement::Kind::kEnum: {
335 const Enum& e = element->as<Enum>();
336 SkASSERT(e.isBuiltin());
337 intrinsics->insertOrDie(e.typeName(), std::move(element));
338 break;
339 }
340 case ProgramElement::Kind::kGlobalVar: {
Ethan Nicholasc51f33e2020-10-13 13:49:44 -0400341 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
342 const Variable& var = global.declaration()->as<VarDeclaration>().var();
343 SkASSERT(var.isBuiltin());
344 intrinsics->insertOrDie(var.name(), std::move(element));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400345 break;
346 }
347 case ProgramElement::Kind::kInterfaceBlock: {
Ethan Nicholaseaf47882020-10-15 10:10:08 -0400348 const Variable& var = element->as<InterfaceBlock>().variable();
349 SkASSERT(var.isBuiltin());
350 intrinsics->insertOrDie(var.name(), std::move(element));
Brian Osman3d87e9f2020-10-08 11:50:22 -0400351 break;
352 }
353 default:
354 printf("Unsupported element: %s\n", element->description().c_str());
355 SkASSERT(false);
356 break;
357 }
358 }
359
Brian Osman0006ad02020-11-18 15:38:39 -0500360 return {module.fSymbols, std::move(intrinsics)};
Ethan Nicholas8da1e652019-05-24 11:01:59 -0400361}
362
John Stilese6150002020-10-05 12:03:53 -0400363void Compiler::scanCFG(CFG* cfg, BlockId blockId, SkBitSet* processedSet) {
ethannicholas22f939e2016-10-13 13:25:34 -0700364 BasicBlock& block = cfg->fBlocks[blockId];
365
366 // compute definitions after this block
Ethan Nicholas86a43402017-01-19 13:32:00 -0500367 DefinitionMap after = block.fBefore;
ethannicholas22f939e2016-10-13 13:25:34 -0700368 for (const BasicBlock::Node& n : block.fNodes) {
John Stilese8a24922021-02-08 17:54:08 -0500369 after.addDefinitions(*fContext, n);
ethannicholas22f939e2016-10-13 13:25:34 -0700370 }
371
372 // propagate definitions to exits
373 for (BlockId exitId : block.fExits) {
Ethan Nicholas26a9aad2018-03-27 14:10:52 -0400374 if (exitId == blockId) {
375 continue;
376 }
ethannicholas22f939e2016-10-13 13:25:34 -0700377 BasicBlock& exit = cfg->fBlocks[exitId];
John Stiles65b48272020-12-22 17:18:34 -0500378 for (const auto& [var, e1] : after) {
John Stiles796cdb72020-10-08 12:06:53 -0400379 std::unique_ptr<Expression>** exitDef = exit.fBefore.find(var);
380 if (!exitDef) {
John Stilese6150002020-10-05 12:03:53 -0400381 // exit has no definition for it, just copy it and reprocess exit block
382 processedSet->reset(exitId);
John Stilese8a24922021-02-08 17:54:08 -0500383 exit.fBefore.set(var, e1);
ethannicholas22f939e2016-10-13 13:25:34 -0700384 } else {
Ethan Nicholas86a43402017-01-19 13:32:00 -0500385 // exit has a (possibly different) value already defined
John Stiles796cdb72020-10-08 12:06:53 -0400386 std::unique_ptr<Expression>* e2 = *exitDef;
ethannicholas22f939e2016-10-13 13:25:34 -0700387 if (e1 != e2) {
John Stilese6150002020-10-05 12:03:53 -0400388 // definition has changed, merge and reprocess the exit block
389 processedSet->reset(exitId);
Ethan Nicholasaf197692017-02-27 13:26:45 -0500390 if (e1 && e2) {
John Stiles796cdb72020-10-08 12:06:53 -0400391 *exitDef = (std::unique_ptr<Expression>*)&fContext->fDefined_Expression;
Ethan Nicholasaf197692017-02-27 13:26:45 -0500392 } else {
John Stiles796cdb72020-10-08 12:06:53 -0400393 *exitDef = nullptr;
Ethan Nicholasaf197692017-02-27 13:26:45 -0500394 }
ethannicholas22f939e2016-10-13 13:25:34 -0700395 }
396 }
John Stiles65b48272020-12-22 17:18:34 -0500397 }
ethannicholas22f939e2016-10-13 13:25:34 -0700398 }
399}
400
Ethan Nicholascb670962017-04-20 19:31:52 -0400401/**
402 * Returns true if assigning to this lvalue has no effect.
403 */
Brian Osman010ce6a2020-10-19 16:34:10 -0400404static bool is_dead(const Expression& lvalue, ProgramUsage* usage) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400405 switch (lvalue.kind()) {
406 case Expression::Kind::kVariableReference:
Brian Osman010ce6a2020-10-19 16:34:10 -0400407 return usage->isDead(*lvalue.as<VariableReference>().variable());
Ethan Nicholase6592142020-09-08 10:22:09 -0400408 case Expression::Kind::kSwizzle:
Brian Osman010ce6a2020-10-19 16:34:10 -0400409 return is_dead(*lvalue.as<Swizzle>().base(), usage);
Ethan Nicholase6592142020-09-08 10:22:09 -0400410 case Expression::Kind::kFieldAccess:
Brian Osman010ce6a2020-10-19 16:34:10 -0400411 return is_dead(*lvalue.as<FieldAccess>().base(), usage);
Ethan Nicholase6592142020-09-08 10:22:09 -0400412 case Expression::Kind::kIndex: {
John Stilesa5a97b42020-08-18 11:19:07 -0400413 const IndexExpression& idx = lvalue.as<IndexExpression>();
Brian Osman010ce6a2020-10-19 16:34:10 -0400414 return is_dead(*idx.base(), usage) &&
Ethan Nicholas2a4952d2020-10-08 15:35:56 -0400415 !idx.index()->hasProperty(Expression::Property::kSideEffects);
Ethan Nicholascb670962017-04-20 19:31:52 -0400416 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400417 case Expression::Kind::kTernary: {
John Stilesa5a97b42020-08-18 11:19:07 -0400418 const TernaryExpression& t = lvalue.as<TernaryExpression>();
Brian Osman010ce6a2020-10-19 16:34:10 -0400419 return !t.test()->hasSideEffects() &&
420 is_dead(*t.ifTrue(), usage) &&
421 is_dead(*t.ifFalse(), usage);
Ethan Nicholasa583b812018-01-18 13:32:11 -0500422 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400423 default:
John Stileseada7bc2021-02-02 16:29:32 -0500424 SkDEBUGFAILF("invalid lvalue: %s\n", lvalue.description().c_str());
Ethan Nicholas2a099da2020-01-02 14:40:54 -0500425 return false;
Ethan Nicholascb670962017-04-20 19:31:52 -0400426 }
427}
ethannicholas22f939e2016-10-13 13:25:34 -0700428
Ethan Nicholascb670962017-04-20 19:31:52 -0400429/**
430 * Returns true if this is an assignment which can be collapsed down to just the right hand side due
431 * to a dead target and lack of side effects on the left hand side.
432 */
Brian Osman010ce6a2020-10-19 16:34:10 -0400433static bool dead_assignment(const BinaryExpression& b, ProgramUsage* usage) {
Brian Osman00185012021-02-04 16:07:11 -0500434 if (!Operators::IsAssignment(b.getOperator())) {
Ethan Nicholascb670962017-04-20 19:31:52 -0400435 return false;
436 }
John Stiles2d4f9592020-10-30 10:29:12 -0400437 return is_dead(*b.left(), usage);
Ethan Nicholascb670962017-04-20 19:31:52 -0400438}
439
John Stiles0ac6c152021-02-10 14:04:24 -0500440/**
441 * Returns true if both expression trees are the same. The left side is expected to be an lvalue.
442 * This only needs to check for trees that can plausibly terminate in a variable, so some basic
443 * candidates like `FloatLiteral` are missing.
444 */
445static bool is_matching_expression_tree(const Expression& left, const Expression& right) {
446 if (left.kind() != right.kind() || left.type() != right.type()) {
447 return false;
448 }
449
450 switch (left.kind()) {
451 case Expression::Kind::kIntLiteral:
452 return left.as<IntLiteral>().value() == right.as<IntLiteral>().value();
453
454 case Expression::Kind::kFieldAccess:
455 return left.as<FieldAccess>().fieldIndex() == right.as<FieldAccess>().fieldIndex() &&
456 is_matching_expression_tree(*left.as<FieldAccess>().base(),
457 *right.as<FieldAccess>().base());
458
459 case Expression::Kind::kIndex:
460 return is_matching_expression_tree(*left.as<IndexExpression>().index(),
461 *right.as<IndexExpression>().index()) &&
462 is_matching_expression_tree(*left.as<IndexExpression>().base(),
463 *right.as<IndexExpression>().base());
464
465 case Expression::Kind::kSwizzle:
466 return left.as<Swizzle>().components() == right.as<Swizzle>().components() &&
467 is_matching_expression_tree(*left.as<Swizzle>().base(),
468 *right.as<Swizzle>().base());
469
470 case Expression::Kind::kVariableReference:
471 return left.as<VariableReference>().variable() ==
472 right.as<VariableReference>().variable();
473
474 default:
475 return false;
476 }
477}
478
479static bool self_assignment(const BinaryExpression& b) {
480 return b.getOperator() == Token::Kind::TK_EQ &&
481 is_matching_expression_tree(*b.left(), *b.right());
482}
483
Ethan Nicholascb670962017-04-20 19:31:52 -0400484void Compiler::computeDataFlow(CFG* cfg) {
John Stilese8a24922021-02-08 17:54:08 -0500485 cfg->fBlocks[cfg->fStart].fBefore.computeStartState(*cfg);
John Stilese6150002020-10-05 12:03:53 -0400486
487 // We set bits in the "processed" set after a block has been scanned.
488 SkBitSet processedSet(cfg->fBlocks.size());
489 while (SkBitSet::OptionalIndex blockId = processedSet.findFirstUnset()) {
490 processedSet.set(*blockId);
491 this->scanCFG(cfg, *blockId, &processedSet);
ethannicholas22f939e2016-10-13 13:25:34 -0700492 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400493}
494
495/**
496 * Attempts to replace the expression pointed to by iter with a new one (in both the CFG and the
497 * IR). If the expression can be cleanly removed, returns true and updates the iterator to point to
498 * the newly-inserted element. Otherwise updates only the IR and returns false (and the CFG will
499 * need to be regenerated).
500 */
John Stilesafbf8992020-08-18 10:08:21 -0400501static bool try_replace_expression(BasicBlock* b,
502 std::vector<BasicBlock::Node>::iterator* iter,
503 std::unique_ptr<Expression>* newExpression) {
Ethan Nicholascb670962017-04-20 19:31:52 -0400504 std::unique_ptr<Expression>* target = (*iter)->expression();
505 if (!b->tryRemoveExpression(iter)) {
506 *target = std::move(*newExpression);
507 return false;
508 }
509 *target = std::move(*newExpression);
510 return b->tryInsertExpression(iter, target);
511}
512
513/**
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400514 * Returns true if the expression is a constant numeric literal with the specified value, or a
515 * constant vector with all elements equal to the specified value.
Ethan Nicholascb670962017-04-20 19:31:52 -0400516 */
Ethan Nicholasa3f22f12020-10-01 12:13:17 -0400517template <typename T = SKSL_FLOAT>
John Stiles9d944232020-08-19 09:56:49 -0400518static bool is_constant(const Expression& expr, T value) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400519 switch (expr.kind()) {
520 case Expression::Kind::kIntLiteral:
Ethan Nicholase96cdd12020-09-28 16:27:18 -0400521 return expr.as<IntLiteral>().value() == value;
John Stiles9d944232020-08-19 09:56:49 -0400522
Ethan Nicholase6592142020-09-08 10:22:09 -0400523 case Expression::Kind::kFloatLiteral:
Ethan Nicholasa3f22f12020-10-01 12:13:17 -0400524 return expr.as<FloatLiteral>().value() == value;
John Stiles9d944232020-08-19 09:56:49 -0400525
Ethan Nicholase6592142020-09-08 10:22:09 -0400526 case Expression::Kind::kConstructor: {
John Stiles9d944232020-08-19 09:56:49 -0400527 const Constructor& constructor = expr.as<Constructor>();
528 if (constructor.isCompileTimeConstant()) {
Ethan Nicholas30d30222020-09-11 12:27:26 -0400529 const Type& constructorType = constructor.type();
Ethan Nicholas30d30222020-09-11 12:27:26 -0400530 switch (constructorType.typeKind()) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400531 case Type::TypeKind::kVector:
John Stilesbc75ebb2020-11-24 12:04:47 -0500532 if (constructor.componentType().isFloat()) {
533 for (int i = 0; i < constructorType.columns(); ++i) {
John Stiles9d944232020-08-19 09:56:49 -0400534 if (constructor.getFVecComponent(i) != value) {
535 return false;
536 }
John Stilesbc75ebb2020-11-24 12:04:47 -0500537 }
538 return true;
539 } else if (constructor.componentType().isInteger()) {
540 for (int i = 0; i < constructorType.columns(); ++i) {
John Stiles9d944232020-08-19 09:56:49 -0400541 if (constructor.getIVecComponent(i) != value) {
542 return false;
543 }
544 }
John Stilesbc75ebb2020-11-24 12:04:47 -0500545 return true;
Ethan Nicholasd188c182019-06-10 15:55:38 -0400546 }
John Stilesbc75ebb2020-11-24 12:04:47 -0500547 // Other types (e.g. boolean) might occur, but aren't supported here.
548 return false;
John Stiles9d944232020-08-19 09:56:49 -0400549
Ethan Nicholase6592142020-09-08 10:22:09 -0400550 case Type::TypeKind::kScalar:
Ethan Nicholasf70f0442020-09-29 12:41:35 -0400551 SkASSERT(constructor.arguments().size() == 1);
552 return is_constant<T>(*constructor.arguments()[0], value);
John Stiles9d944232020-08-19 09:56:49 -0400553
554 default:
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400555 return false;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400556 }
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400557 }
558 return false;
559 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400560 default:
561 return false;
562 }
563}
564
565/**
566 * Collapses the binary expression pointed to by iter down to just the right side (in both the IR
567 * and CFG structures).
568 */
John Stilesafbf8992020-08-18 10:08:21 -0400569static void delete_left(BasicBlock* b,
570 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400571 Compiler::OptimizationContext* optimizationContext) {
572 optimizationContext->fUpdated = true;
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400573 std::unique_ptr<Expression>* target = (*iter)->expression();
John Stilesa5a97b42020-08-18 11:19:07 -0400574 BinaryExpression& bin = (*target)->as<BinaryExpression>();
John Stiles2d4f9592020-10-30 10:29:12 -0400575 Expression& left = *bin.left();
576 std::unique_ptr<Expression>& rightPointer = bin.right();
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400577 SkASSERT(!left.hasSideEffects());
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400578 bool result;
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400579 if (bin.getOperator() == Token::Kind::TK_EQ) {
580 result = b->tryRemoveLValueBefore(iter, &left);
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400581 } else {
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400582 result = b->tryRemoveExpressionBefore(iter, &left);
Ethan Nicholascb670962017-04-20 19:31:52 -0400583 }
Brian Osman010ce6a2020-10-19 16:34:10 -0400584 // Remove references within LHS.
585 optimizationContext->fUsage->remove(&left);
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400586 *target = std::move(rightPointer);
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400587 if (!result) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400588 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400589 return;
590 }
591 if (*iter == b->fNodes.begin()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400592 optimizationContext->fNeedsRescan = true;
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400593 return;
594 }
595 --(*iter);
John Stiles70025e52020-09-28 16:08:58 -0400596 if (!(*iter)->isExpression() || (*iter)->expression() != &rightPointer) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400597 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400598 return;
599 }
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400600 *iter = b->fNodes.erase(*iter);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400601 SkASSERT((*iter)->expression() == target);
Ethan Nicholascb670962017-04-20 19:31:52 -0400602}
603
604/**
605 * Collapses the binary expression pointed to by iter down to just the left side (in both the IR and
606 * CFG structures).
607 */
John Stilesafbf8992020-08-18 10:08:21 -0400608static void delete_right(BasicBlock* b,
609 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400610 Compiler::OptimizationContext* optimizationContext) {
611 optimizationContext->fUpdated = true;
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400612 std::unique_ptr<Expression>* target = (*iter)->expression();
John Stilesa5a97b42020-08-18 11:19:07 -0400613 BinaryExpression& bin = (*target)->as<BinaryExpression>();
John Stiles2d4f9592020-10-30 10:29:12 -0400614 std::unique_ptr<Expression>& leftPointer = bin.left();
615 Expression& right = *bin.right();
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400616 SkASSERT(!right.hasSideEffects());
Brian Osman010ce6a2020-10-19 16:34:10 -0400617 // Remove references within RHS.
618 optimizationContext->fUsage->remove(&right);
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400619 if (!b->tryRemoveExpressionBefore(iter, &right)) {
620 *target = std::move(leftPointer);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400621 optimizationContext->fNeedsRescan = true;
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400622 return;
Ethan Nicholascb670962017-04-20 19:31:52 -0400623 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400624 *target = std::move(leftPointer);
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400625 if (*iter == b->fNodes.begin()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400626 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400627 return;
628 }
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400629 --(*iter);
John Stiles70025e52020-09-28 16:08:58 -0400630 if ((!(*iter)->isExpression() || (*iter)->expression() != &leftPointer)) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400631 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400632 return;
633 }
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400634 *iter = b->fNodes.erase(*iter);
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400635 SkASSERT((*iter)->expression() == target);
Ethan Nicholascb670962017-04-20 19:31:52 -0400636}
637
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400638/**
639 * Constructs the specified type using a single argument.
640 */
Ethan Nicholas30d30222020-09-11 12:27:26 -0400641static std::unique_ptr<Expression> construct(const Type* type, std::unique_ptr<Expression> v) {
John Stiles8e3b6be2020-10-13 11:14:08 -0400642 ExpressionArray args;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400643 args.push_back(std::move(v));
Ethan Nicholase6592142020-09-08 10:22:09 -0400644 std::unique_ptr<Expression> result = std::make_unique<Constructor>(-1, type, std::move(args));
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400645 return result;
646}
647
648/**
649 * Used in the implementations of vectorize_left and vectorize_right. Given a vector type and an
650 * expression x, deletes the expression pointed to by iter and replaces it with <type>(x).
651 */
652static void vectorize(BasicBlock* b,
653 std::vector<BasicBlock::Node>::iterator* iter,
654 const Type& type,
655 std::unique_ptr<Expression>* otherExpression,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400656 Compiler::OptimizationContext* optimizationContext) {
Ethan Nicholase6592142020-09-08 10:22:09 -0400657 SkASSERT((*(*iter)->expression())->kind() == Expression::Kind::kBinary);
John Stiles9aeed132020-11-24 17:36:06 -0500658 SkASSERT(type.isVector());
659 SkASSERT((*otherExpression)->type().isScalar());
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400660 optimizationContext->fUpdated = true;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400661 std::unique_ptr<Expression>* target = (*iter)->expression();
662 if (!b->tryRemoveExpression(iter)) {
Ethan Nicholas30d30222020-09-11 12:27:26 -0400663 *target = construct(&type, std::move(*otherExpression));
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400664 optimizationContext->fNeedsRescan = true;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400665 } else {
Ethan Nicholas30d30222020-09-11 12:27:26 -0400666 *target = construct(&type, std::move(*otherExpression));
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400667 if (!b->tryInsertExpression(iter, target)) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400668 optimizationContext->fNeedsRescan = true;
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400669 }
670 }
671}
672
673/**
674 * Given a binary expression of the form x <op> vec<n>(y), deletes the right side and vectorizes the
675 * left to yield vec<n>(x).
676 */
677static void vectorize_left(BasicBlock* b,
678 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400679 Compiler::OptimizationContext* optimizationContext) {
John Stilesa5a97b42020-08-18 11:19:07 -0400680 BinaryExpression& bin = (*(*iter)->expression())->as<BinaryExpression>();
Brian Osman010ce6a2020-10-19 16:34:10 -0400681 // Remove references within RHS. Vectorization of LHS doesn't change reference counts.
John Stiles2d4f9592020-10-30 10:29:12 -0400682 optimizationContext->fUsage->remove(bin.right().get());
683 vectorize(b, iter, bin.right()->type(), &bin.left(), optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400684}
685
686/**
687 * Given a binary expression of the form vec<n>(x) <op> y, deletes the left side and vectorizes the
688 * right to yield vec<n>(y).
689 */
690static void vectorize_right(BasicBlock* b,
691 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400692 Compiler::OptimizationContext* optimizationContext) {
John Stilesa5a97b42020-08-18 11:19:07 -0400693 BinaryExpression& bin = (*(*iter)->expression())->as<BinaryExpression>();
Brian Osman010ce6a2020-10-19 16:34:10 -0400694 // Remove references within LHS. Vectorization of RHS doesn't change reference counts.
John Stiles2d4f9592020-10-30 10:29:12 -0400695 optimizationContext->fUsage->remove(bin.left().get());
696 vectorize(b, iter, bin.left()->type(), &bin.right(), optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400697}
698
Ethan Nicholascb670962017-04-20 19:31:52 -0400699void Compiler::simplifyExpression(DefinitionMap& definitions,
700 BasicBlock& b,
701 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400702 OptimizationContext* optimizationContext) {
Ethan Nicholascb670962017-04-20 19:31:52 -0400703 Expression* expr = (*iter)->expression()->get();
Ethan Nicholasd9d33c32018-06-12 11:05:59 -0400704 SkASSERT(expr);
John Stiles108bbe22020-11-18 11:10:38 -0500705
Ethan Nicholascb670962017-04-20 19:31:52 -0400706 if ((*iter)->fConstantPropagation) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400707 std::unique_ptr<Expression> optimized = expr->constantPropagate(*fIRGenerator,
708 definitions);
Ethan Nicholascb670962017-04-20 19:31:52 -0400709 if (optimized) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400710 optimizationContext->fUpdated = true;
Ethan Nicholas30d30222020-09-11 12:27:26 -0400711 optimized = fIRGenerator->coerce(std::move(optimized), expr->type());
Ethan Nicholas1d881522020-09-11 09:32:54 -0400712 SkASSERT(optimized);
Brian Osman010ce6a2020-10-19 16:34:10 -0400713 // Remove references within 'expr', add references within 'optimized'
714 optimizationContext->fUsage->replace(expr, optimized.get());
Ethan Nicholascb670962017-04-20 19:31:52 -0400715 if (!try_replace_expression(&b, iter, &optimized)) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400716 optimizationContext->fNeedsRescan = true;
Ethan Nicholas4b330df2017-05-17 10:52:55 -0400717 return;
Ethan Nicholascb670962017-04-20 19:31:52 -0400718 }
John Stiles70025e52020-09-28 16:08:58 -0400719 SkASSERT((*iter)->isExpression());
Ethan Nicholascb670962017-04-20 19:31:52 -0400720 expr = (*iter)->expression()->get();
Ethan Nicholascb670962017-04-20 19:31:52 -0400721 }
722 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400723 switch (expr->kind()) {
724 case Expression::Kind::kVariableReference: {
John Stilesa5a97b42020-08-18 11:19:07 -0400725 const VariableReference& ref = expr->as<VariableReference>();
Ethan Nicholas78686922020-10-08 06:46:27 -0400726 const Variable* var = ref.variable();
Ethan Nicholas453f67f2020-10-09 10:43:45 -0400727 if (ref.refKind() != VariableReference::RefKind::kWrite &&
728 ref.refKind() != VariableReference::RefKind::kPointer &&
John Stilese8a24922021-02-08 17:54:08 -0500729 var->storage() == Variable::Storage::kLocal && !definitions.get(var) &&
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400730 optimizationContext->fSilences.find(var) == optimizationContext->fSilences.end()) {
731 optimizationContext->fSilences.insert(var);
Ethan Nicholas82a62d22017-11-07 14:42:10 +0000732 this->error(expr->fOffset,
Ethan Nicholase2c49992020-10-05 11:49:11 -0400733 "'" + var->name() + "' has not been assigned");
Ethan Nicholascb670962017-04-20 19:31:52 -0400734 }
735 break;
736 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400737 case Expression::Kind::kTernary: {
John Stiles403a3632020-08-20 12:11:48 -0400738 TernaryExpression* t = &expr->as<TernaryExpression>();
Ethan Nicholasdd218162020-10-08 05:48:01 -0400739 if (t->test()->is<BoolLiteral>()) {
Ethan Nicholascb670962017-04-20 19:31:52 -0400740 // ternary has a constant test, replace it with either the true or
741 // false branch
Ethan Nicholasdd218162020-10-08 05:48:01 -0400742 if (t->test()->as<BoolLiteral>().value()) {
Brian Osman010ce6a2020-10-19 16:34:10 -0400743 (*iter)->setExpression(std::move(t->ifTrue()), optimizationContext->fUsage);
Ethan Nicholascb670962017-04-20 19:31:52 -0400744 } else {
Brian Osman010ce6a2020-10-19 16:34:10 -0400745 (*iter)->setExpression(std::move(t->ifFalse()), optimizationContext->fUsage);
Ethan Nicholascb670962017-04-20 19:31:52 -0400746 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400747 optimizationContext->fUpdated = true;
748 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -0400749 }
750 break;
751 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400752 case Expression::Kind::kBinary: {
John Stiles403a3632020-08-20 12:11:48 -0400753 BinaryExpression* bin = &expr->as<BinaryExpression>();
John Stiles0ac6c152021-02-10 14:04:24 -0500754 if (dead_assignment(*bin, optimizationContext->fUsage) || self_assignment(*bin)) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400755 delete_left(&b, iter, optimizationContext);
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400756 break;
757 }
John Stiles2d4f9592020-10-30 10:29:12 -0400758 Expression& left = *bin->left();
759 Expression& right = *bin->right();
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400760 const Type& leftType = left.type();
761 const Type& rightType = right.type();
Ethan Nicholasc2371a42017-05-05 10:04:06 -0400762 // collapse useless expressions like x * 1 or x + 0
John Stiles9aeed132020-11-24 17:36:06 -0500763 if ((!leftType.isScalar() && !leftType.isVector()) ||
764 (!rightType.isScalar() && !rightType.isVector())) {
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400765 break;
766 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400767 switch (bin->getOperator()) {
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400768 case Token::Kind::TK_STAR:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400769 if (is_constant(left, 1)) {
John Stiles9aeed132020-11-24 17:36:06 -0500770 if (leftType.isVector() && rightType.isScalar()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400771 // float4(1) * x -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400772 vectorize_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400773 } else {
774 // 1 * x -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400775 // 1 * float4(x) -> float4(x)
776 // float4(1) * float4(x) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400777 delete_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400778 }
779 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400780 else if (is_constant(left, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500781 if (leftType.isScalar() && rightType.isVector() &&
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400782 !right.hasSideEffects()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400783 // 0 * float4(x) -> float4(0)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400784 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400785 } else {
786 // 0 * x -> 0
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400787 // float4(0) * x -> float4(0)
788 // float4(0) * float4(x) -> float4(0)
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400789 if (!right.hasSideEffects()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400790 delete_right(&b, iter, optimizationContext);
Ethan Nicholas51493ee2017-12-11 12:34:33 -0500791 }
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400792 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400793 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400794 else if (is_constant(right, 1)) {
John Stiles9aeed132020-11-24 17:36:06 -0500795 if (leftType.isScalar() && rightType.isVector()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400796 // x * float4(1) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400797 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400798 } else {
799 // x * 1 -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400800 // float4(x) * 1 -> float4(x)
801 // float4(x) * float4(1) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400802 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400803 }
804 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400805 else if (is_constant(right, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500806 if (leftType.isVector() && rightType.isScalar() && !left.hasSideEffects()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400807 // float4(x) * 0 -> float4(0)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400808 vectorize_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400809 } else {
810 // x * 0 -> 0
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400811 // x * float4(0) -> float4(0)
812 // float4(x) * float4(0) -> float4(0)
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400813 if (!left.hasSideEffects()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400814 delete_left(&b, iter, optimizationContext);
Ethan Nicholas51493ee2017-12-11 12:34:33 -0500815 }
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400816 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400817 }
818 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400819 case Token::Kind::TK_PLUS:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400820 if (is_constant(left, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500821 if (leftType.isVector() && rightType.isScalar()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400822 // float4(0) + x -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400823 vectorize_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400824 } else {
825 // 0 + x -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400826 // 0 + float4(x) -> float4(x)
827 // float4(0) + float4(x) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400828 delete_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400829 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400830 } else if (is_constant(right, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500831 if (leftType.isScalar() && rightType.isVector()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400832 // x + float4(0) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400833 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400834 } else {
835 // x + 0 -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400836 // float4(x) + 0 -> float4(x)
837 // float4(x) + float4(0) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400838 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400839 }
Ethan Nicholas56e42712017-04-21 10:23:37 -0400840 }
841 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400842 case Token::Kind::TK_MINUS:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400843 if (is_constant(right, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500844 if (leftType.isScalar() && rightType.isVector()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400845 // x - float4(0) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400846 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400847 } else {
848 // x - 0 -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400849 // float4(x) - 0 -> float4(x)
850 // float4(x) - float4(0) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400851 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400852 }
Ethan Nicholascb670962017-04-20 19:31:52 -0400853 }
854 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400855 case Token::Kind::TK_SLASH:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400856 if (is_constant(right, 1)) {
John Stiles9aeed132020-11-24 17:36:06 -0500857 if (leftType.isScalar() && rightType.isVector()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400858 // x / float4(1) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400859 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400860 } else {
861 // x / 1 -> x
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400862 // float4(x) / 1 -> float4(x)
863 // float4(x) / float4(1) -> float4(x)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400864 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400865 }
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400866 } else if (is_constant(left, 0)) {
John Stiles9aeed132020-11-24 17:36:06 -0500867 if (leftType.isScalar() && rightType.isVector() &&
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400868 !right.hasSideEffects()) {
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400869 // 0 / float4(x) -> float4(0)
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400870 vectorize_left(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400871 } else {
872 // 0 / x -> 0
Ethan Nicholas5af9ea32017-07-28 15:19:46 -0400873 // float4(0) / x -> float4(0)
874 // float4(0) / float4(x) -> float4(0)
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400875 if (!right.hasSideEffects()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400876 delete_right(&b, iter, optimizationContext);
Ethan Nicholas51493ee2017-12-11 12:34:33 -0500877 }
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400878 }
879 }
880 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400881 case Token::Kind::TK_PLUSEQ:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400882 if (is_constant(right, 0)) {
John Stiles47c0a742021-02-09 09:30:35 -0500883 Analysis::UpdateRefKind(&left, RefKind::kRead);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400884 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400885 }
886 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400887 case Token::Kind::TK_MINUSEQ:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400888 if (is_constant(right, 0)) {
John Stiles47c0a742021-02-09 09:30:35 -0500889 Analysis::UpdateRefKind(&left, RefKind::kRead);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400890 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400891 }
892 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400893 case Token::Kind::TK_STAREQ:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400894 if (is_constant(right, 1)) {
John Stiles47c0a742021-02-09 09:30:35 -0500895 Analysis::UpdateRefKind(&left, RefKind::kRead);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400896 delete_right(&b, iter, optimizationContext);
Ethan Nicholasfe53e582017-04-27 16:24:51 -0400897 }
898 break;
Ethan Nicholas5a9e7fb2020-04-17 12:45:51 -0400899 case Token::Kind::TK_SLASHEQ:
Ethan Nicholasc8d9c8e2020-09-22 15:05:37 -0400900 if (is_constant(right, 1)) {
John Stiles47c0a742021-02-09 09:30:35 -0500901 Analysis::UpdateRefKind(&left, RefKind::kRead);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400902 delete_right(&b, iter, optimizationContext);
Ethan Nicholascb670962017-04-20 19:31:52 -0400903 }
904 break;
905 default:
906 break;
907 }
Ethan Nicholas409f6f02019-09-17 12:34:39 -0400908 break;
909 }
John Stilesf5c1d042020-11-21 23:26:07 -0500910 case Expression::Kind::kConstructor: {
911 // Find constructors embedded inside constructors and flatten them out where possible.
912 // - float4(float2(1, 2), 3, 4) --> float4(1, 2, 3, 4)
913 // - float4(w, float3(sin(x), cos(y), tan(z))) --> float4(w, sin(x), cos(y), tan(z))
914 // Leave single-argument constructors alone, though. These might be casts or splats.
915 Constructor& c = expr->as<Constructor>();
916 if (c.type().columns() > 1) {
917 // Inspect each constructor argument to see if it's a candidate for flattening.
918 // Remember matched arguments in a bitfield, "argsToOptimize".
919 int argsToOptimize = 0;
920 int currBit = 1;
921 for (const std::unique_ptr<Expression>& arg : c.arguments()) {
922 if (arg->is<Constructor>()) {
923 Constructor& inner = arg->as<Constructor>();
924 if (inner.arguments().size() > 1 &&
925 inner.type().componentType() == c.type().componentType()) {
926 argsToOptimize |= currBit;
927 }
928 }
929 currBit <<= 1;
930 }
931 if (argsToOptimize) {
932 // We found at least one argument that could be flattened out. Re-walk the
933 // constructor args and flatten the candidates we found during our initial pass.
934 ExpressionArray flattened;
935 flattened.reserve_back(c.type().columns());
936 currBit = 1;
937 for (const std::unique_ptr<Expression>& arg : c.arguments()) {
938 if (argsToOptimize & currBit) {
939 Constructor& inner = arg->as<Constructor>();
940 for (const std::unique_ptr<Expression>& innerArg : inner.arguments()) {
941 flattened.push_back(innerArg->clone());
942 }
943 } else {
944 flattened.push_back(arg->clone());
945 }
946 currBit <<= 1;
947 }
John Stiles1b91c0e2021-02-11 11:43:09 -0500948 std::unique_ptr<Expression> replacement(new Constructor(c.fOffset, &c.type(),
949 std::move(flattened)));
950 // We're replacing an expression with a cloned version; we'll need a rescan.
951 // No fUsage change: `float2(float(x), y)` and `float2(x, y)` have equivalent
952 // reference counts.
953 try_replace_expression(&b, iter, &replacement);
John Stilesf5c1d042020-11-21 23:26:07 -0500954 optimizationContext->fUpdated = true;
John Stiles1b91c0e2021-02-11 11:43:09 -0500955 optimizationContext->fNeedsRescan = true;
John Stilesf5c1d042020-11-21 23:26:07 -0500956 break;
957 }
958 }
959 break;
960 }
Ethan Nicholase6592142020-09-08 10:22:09 -0400961 case Expression::Kind::kSwizzle: {
John Stiles403a3632020-08-20 12:11:48 -0400962 Swizzle& s = expr->as<Swizzle>();
John Stiles108bbe22020-11-18 11:10:38 -0500963 // Detect identity swizzles like `foo.rgba`.
Ethan Nicholas6b4d5812020-10-12 16:11:51 -0400964 if ((int) s.components().size() == s.base()->type().columns()) {
Ethan Nicholas409f6f02019-09-17 12:34:39 -0400965 bool identity = true;
Ethan Nicholas6b4d5812020-10-12 16:11:51 -0400966 for (int i = 0; i < (int) s.components().size(); ++i) {
967 if (s.components()[i] != i) {
Ethan Nicholas409f6f02019-09-17 12:34:39 -0400968 identity = false;
969 break;
970 }
971 }
972 if (identity) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400973 optimizationContext->fUpdated = true;
Brian Osman010ce6a2020-10-19 16:34:10 -0400974 // No fUsage change: foo.rgba and foo have equivalent reference counts
Ethan Nicholas6b4d5812020-10-12 16:11:51 -0400975 if (!try_replace_expression(&b, iter, &s.base())) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -0400976 optimizationContext->fNeedsRescan = true;
Ethan Nicholas409f6f02019-09-17 12:34:39 -0400977 return;
978 }
John Stiles70025e52020-09-28 16:08:58 -0400979 SkASSERT((*iter)->isExpression());
Ethan Nicholas409f6f02019-09-17 12:34:39 -0400980 break;
981 }
982 }
John Stiles108bbe22020-11-18 11:10:38 -0500983 // Detect swizzles of swizzles, e.g. replace `foo.argb.r000` with `foo.a000`.
984 if (s.base()->is<Swizzle>()) {
Ethan Nicholas6b4d5812020-10-12 16:11:51 -0400985 Swizzle& base = s.base()->as<Swizzle>();
John Stiles750109b2020-10-30 13:45:46 -0400986 ComponentArray final;
Ethan Nicholas6b4d5812020-10-12 16:11:51 -0400987 for (int c : s.components()) {
988 final.push_back(base.components()[c]);
Ethan Nicholas409f6f02019-09-17 12:34:39 -0400989 }
Ethan Nicholas6b4d5812020-10-12 16:11:51 -0400990 std::unique_ptr<Expression> replacement(new Swizzle(*fContext, base.base()->clone(),
John Stiles750109b2020-10-30 13:45:46 -0400991 final));
John Stilesd2f51b12021-01-07 18:12:31 -0500992 // We're replacing an expression with a cloned version; we'll need a rescan.
John Stiles108bbe22020-11-18 11:10:38 -0500993 // No fUsage change: `foo.gbr.gbr` and `foo.brg` have equivalent reference counts
John Stilesd2f51b12021-01-07 18:12:31 -0500994 try_replace_expression(&b, iter, &replacement);
995 optimizationContext->fUpdated = true;
996 optimizationContext->fNeedsRescan = true;
John Stiles108bbe22020-11-18 11:10:38 -0500997 break;
998 }
999 // Optimize swizzles of constructors.
1000 if (s.base()->is<Constructor>()) {
1001 Constructor& base = s.base()->as<Constructor>();
1002 std::unique_ptr<Expression> replacement;
1003 const Type& componentType = base.type().componentType();
1004 int swizzleSize = s.components().size();
1005
1006 // The IR generator has already converted any zero/one swizzle components into
1007 // constructors containing zero/one args. Confirm that this is true by checking that
1008 // our swizzle components are all `xyzw` (values 0 through 3).
1009 SkASSERT(std::all_of(s.components().begin(), s.components().end(),
1010 [](int8_t c) { return c >= 0 && c <= 3; }));
1011
John Stiles9aeed132020-11-24 17:36:06 -05001012 if (base.arguments().size() == 1 && base.arguments().front()->type().isScalar()) {
John Stiles108bbe22020-11-18 11:10:38 -05001013 // `half4(scalar).zyy` can be optimized to `half3(scalar)`. The swizzle
1014 // components don't actually matter since all fields are the same.
John Stilesf0cb7332021-01-08 18:39:00 -05001015 const Expression& argument = *base.arguments().front();
1016 const Type& constructorType = componentType.toCompound(*fContext, swizzleSize,
1017 /*rows=*/1);
1018 replacement = Constructor::SimplifyConversion(constructorType, argument);
1019 if (!replacement) {
1020 ExpressionArray newArgs;
1021 newArgs.push_back(argument.clone());
1022 replacement = std::make_unique<Constructor>(base.fOffset, &constructorType,
1023 std::move(newArgs));
1024 }
John Stiles108bbe22020-11-18 11:10:38 -05001025
John Stilesa60ac0c2020-12-22 08:59:51 -05001026 // We're replacing an expression with a cloned version; we'll need a rescan.
1027 // There's no fUsage change: `half4(foo).xy` and `half2(foo)` have equivalent
1028 // reference counts.
1029 try_replace_expression(&b, iter, &replacement);
John Stiles0777ac42020-11-19 11:06:47 -05001030 optimizationContext->fUpdated = true;
John Stilesa60ac0c2020-12-22 08:59:51 -05001031 optimizationContext->fNeedsRescan = true;
John Stiles108bbe22020-11-18 11:10:38 -05001032 break;
1033 }
1034
John Stiles0777ac42020-11-19 11:06:47 -05001035 // Swizzles can duplicate some elements and discard others, e.g.
1036 // `half4(1, 2, 3, 4).xxz` --> `half3(1, 1, 3)`. However, there are constraints:
1037 // - Expressions with side effects need to occur exactly once, even if they
1038 // would otherwise be swizzle-eliminated
1039 // - Non-trivial expressions should not be repeated, but elimination is OK.
1040 //
1041 // Look up the argument for the constructor at each index. This is typically simple
1042 // but for weird cases like `half4(bar.yz, half2(foo))`, it can be harder than it
1043 // seems. This example would result in:
1044 // argMap[0] = {.fArgIndex = 0, .fComponent = 0} (bar.yz .x)
1045 // argMap[1] = {.fArgIndex = 0, .fComponent = 1} (bar.yz .y)
1046 // argMap[2] = {.fArgIndex = 1, .fComponent = 0} (half2(foo) .x)
1047 // argMap[3] = {.fArgIndex = 1, .fComponent = 1} (half2(foo) .y)
1048 struct ConstructorArgMap {
1049 int8_t fArgIndex;
1050 int8_t fComponent;
1051 };
1052
1053 int numConstructorArgs = base.type().columns();
1054 ConstructorArgMap argMap[4] = {};
1055 int writeIdx = 0;
1056 for (int argIdx = 0; argIdx < (int) base.arguments().size(); ++argIdx) {
1057 const Expression& expr = *base.arguments()[argIdx];
1058 int argWidth = expr.type().columns();
1059 for (int componentIdx = 0; componentIdx < argWidth; ++componentIdx) {
1060 argMap[writeIdx].fArgIndex = argIdx;
1061 argMap[writeIdx].fComponent = componentIdx;
1062 ++writeIdx;
1063 }
1064 }
1065 SkASSERT(writeIdx == numConstructorArgs);
1066
1067 // Count up the number of times each constructor argument is used by the
1068 // swizzle.
1069 // `half4(bar.yz, half2(foo)).xwxy` -> { 3, 1 }
1070 // - bar.yz is referenced 3 times, by `.x_xy`
1071 // - half(foo) is referenced 1 time, by `._w__`
1072 int8_t exprUsed[4] = {};
1073 for (int c : s.components()) {
1074 exprUsed[argMap[c].fArgIndex]++;
1075 }
1076
1077 bool safeToOptimize = true;
1078 for (int index = 0; index < numConstructorArgs; ++index) {
1079 int8_t constructorArgIndex = argMap[index].fArgIndex;
1080 const Expression& baseArg = *base.arguments()[constructorArgIndex];
1081
1082 // Check that non-trivial expressions are not swizzled in more than once.
John Stilesc30fbca2020-11-19 16:25:49 -05001083 if (exprUsed[constructorArgIndex] > 1 &&
1084 !Analysis::IsTrivialExpression(baseArg)) {
John Stiles0777ac42020-11-19 11:06:47 -05001085 safeToOptimize = false;
1086 break;
1087 }
1088 // Check that side-effect-bearing expressions are swizzled in exactly once.
1089 if (exprUsed[constructorArgIndex] != 1 && baseArg.hasSideEffects()) {
1090 safeToOptimize = false;
1091 break;
1092 }
1093 }
1094
1095 if (safeToOptimize) {
John Stilesd9076cb2020-11-19 12:18:36 -05001096 struct ReorderedArgument {
1097 int8_t fArgIndex;
1098 ComponentArray fComponents;
1099 };
1100 SkSTArray<4, ReorderedArgument> reorderedArgs;
John Stiles0777ac42020-11-19 11:06:47 -05001101 for (int c : s.components()) {
1102 const ConstructorArgMap& argument = argMap[c];
1103 const Expression& baseArg = *base.arguments()[argument.fArgIndex];
1104
John Stiles9aeed132020-11-24 17:36:06 -05001105 if (baseArg.type().isScalar()) {
John Stilesd9076cb2020-11-19 12:18:36 -05001106 // This argument is a scalar; add it to the list as-is.
John Stiles0777ac42020-11-19 11:06:47 -05001107 SkASSERT(argument.fComponent == 0);
John Stilesd9076cb2020-11-19 12:18:36 -05001108 reorderedArgs.push_back({argument.fArgIndex,
1109 ComponentArray{}});
John Stiles0777ac42020-11-19 11:06:47 -05001110 } else {
John Stilesd9076cb2020-11-19 12:18:36 -05001111 // This argument is a component from a vector.
John Stiles0777ac42020-11-19 11:06:47 -05001112 SkASSERT(argument.fComponent < baseArg.type().columns());
John Stilesd9076cb2020-11-19 12:18:36 -05001113 if (reorderedArgs.empty() ||
1114 reorderedArgs.back().fArgIndex != argument.fArgIndex) {
1115 // This can't be combined with the previous argument. Add a new one.
1116 reorderedArgs.push_back({argument.fArgIndex,
1117 ComponentArray{argument.fComponent}});
1118 } else {
1119 // Since we know this argument uses components, it should already
1120 // have at least one component set.
1121 SkASSERT(!reorderedArgs.back().fComponents.empty());
1122 // Build up the current argument with one more component.
1123 reorderedArgs.back().fComponents.push_back(argument.fComponent);
1124 }
John Stiles0777ac42020-11-19 11:06:47 -05001125 }
1126 }
John Stilesd9076cb2020-11-19 12:18:36 -05001127
1128 // Convert our reordered argument list to an actual array of expressions, with
1129 // the new order and any new inner swizzles that need to be applied. Note that
1130 // we expect followup passes to clean up the inner swizzles.
1131 ExpressionArray newArgs;
1132 newArgs.reserve_back(swizzleSize);
1133 for (const ReorderedArgument& reorderedArg : reorderedArgs) {
1134 const Expression& baseArg = *base.arguments()[reorderedArg.fArgIndex];
1135 if (reorderedArg.fComponents.empty()) {
1136 newArgs.push_back(baseArg.clone());
1137 } else {
1138 newArgs.push_back(std::make_unique<Swizzle>(*fContext, baseArg.clone(),
1139 reorderedArg.fComponents));
1140 }
1141 }
1142
1143 // Create a new constructor.
John Stiles0777ac42020-11-19 11:06:47 -05001144 replacement = std::make_unique<Constructor>(
1145 base.fOffset,
1146 &componentType.toCompound(*fContext, swizzleSize, /*rows=*/1),
1147 std::move(newArgs));
1148
John Stilesa60ac0c2020-12-22 08:59:51 -05001149 // Remove references within 'expr', add references within 'replacement.'
John Stiles0777ac42020-11-19 11:06:47 -05001150 optimizationContext->fUsage->replace(expr, replacement.get());
John Stilesa60ac0c2020-12-22 08:59:51 -05001151
1152 // We're replacing an expression with a cloned version; we'll need a rescan.
1153 try_replace_expression(&b, iter, &replacement);
1154 optimizationContext->fUpdated = true;
1155 optimizationContext->fNeedsRescan = true;
John Stiles0777ac42020-11-19 11:06:47 -05001156 }
John Stiles108bbe22020-11-18 11:10:38 -05001157 break;
Ethan Nicholas409f6f02019-09-17 12:34:39 -04001158 }
John Stiles30212b72020-06-11 17:55:07 -04001159 break;
Ethan Nicholascb670962017-04-20 19:31:52 -04001160 }
1161 default:
1162 break;
1163 }
1164}
1165
John Stiles92219b42020-06-15 12:32:24 -04001166// Returns true if this statement could potentially execute a break at the current level. We ignore
1167// nested loops and switches, since any breaks inside of them will merely break the loop / switch.
John Stilesb92641c2020-08-31 18:09:01 -04001168static bool contains_conditional_break(Statement& stmt) {
1169 class ContainsConditionalBreak : public ProgramVisitor {
1170 public:
1171 bool visitStatement(const Statement& stmt) override {
Ethan Nicholase6592142020-09-08 10:22:09 -04001172 switch (stmt.kind()) {
1173 case Statement::Kind::kBlock:
John Stilesb92641c2020-08-31 18:09:01 -04001174 return this->INHERITED::visitStatement(stmt);
1175
Ethan Nicholase6592142020-09-08 10:22:09 -04001176 case Statement::Kind::kBreak:
John Stilesb92641c2020-08-31 18:09:01 -04001177 return fInConditional > 0;
1178
Ethan Nicholase6592142020-09-08 10:22:09 -04001179 case Statement::Kind::kIf: {
John Stilesb92641c2020-08-31 18:09:01 -04001180 ++fInConditional;
1181 bool result = this->INHERITED::visitStatement(stmt);
1182 --fInConditional;
1183 return result;
1184 }
1185
1186 default:
1187 return false;
1188 }
1189 }
1190
1191 int fInConditional = 0;
1192 using INHERITED = ProgramVisitor;
1193 };
1194
1195 return ContainsConditionalBreak{}.visitStatement(stmt);
John Stiles92219b42020-06-15 12:32:24 -04001196}
1197
Ethan Nicholas5005a222018-08-24 13:06:27 -04001198// returns true if this statement definitely executes a break at the current level (we ignore
1199// nested loops and switches, since any breaks inside of them will merely break the loop / switch)
John Stilesb92641c2020-08-31 18:09:01 -04001200static bool contains_unconditional_break(Statement& stmt) {
1201 class ContainsUnconditionalBreak : public ProgramVisitor {
1202 public:
1203 bool visitStatement(const Statement& stmt) override {
Ethan Nicholase6592142020-09-08 10:22:09 -04001204 switch (stmt.kind()) {
1205 case Statement::Kind::kBlock:
John Stilesb92641c2020-08-31 18:09:01 -04001206 return this->INHERITED::visitStatement(stmt);
1207
Ethan Nicholase6592142020-09-08 10:22:09 -04001208 case Statement::Kind::kBreak:
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001209 return true;
John Stilesb92641c2020-08-31 18:09:01 -04001210
1211 default:
1212 return false;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001213 }
John Stilesb92641c2020-08-31 18:09:01 -04001214 }
John Stiles92219b42020-06-15 12:32:24 -04001215
John Stilesb92641c2020-08-31 18:09:01 -04001216 using INHERITED = ProgramVisitor;
1217 };
John Stiles92219b42020-06-15 12:32:24 -04001218
John Stilesb92641c2020-08-31 18:09:01 -04001219 return ContainsUnconditionalBreak{}.visitStatement(stmt);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001220}
1221
John Stiles8f2a0cf2020-10-13 12:48:21 -04001222static void move_all_but_break(std::unique_ptr<Statement>& stmt, StatementArray* target) {
Ethan Nicholase6592142020-09-08 10:22:09 -04001223 switch (stmt->kind()) {
1224 case Statement::Kind::kBlock: {
John Stiles92219b42020-06-15 12:32:24 -04001225 // Recurse into the block.
1226 Block& block = static_cast<Block&>(*stmt);
1227
John Stiles8f2a0cf2020-10-13 12:48:21 -04001228 StatementArray blockStmts;
John Stilesf4bda742020-10-14 16:57:41 -04001229 blockStmts.reserve_back(block.children().size());
Ethan Nicholas7bd60432020-09-25 14:31:59 -04001230 for (std::unique_ptr<Statement>& stmt : block.children()) {
1231 move_all_but_break(stmt, &blockStmts);
Ethan Nicholas739e1ca2020-06-11 12:16:14 -04001232 }
John Stiles92219b42020-06-15 12:32:24 -04001233
1234 target->push_back(std::make_unique<Block>(block.fOffset, std::move(blockStmts),
Ethan Nicholas7bd60432020-09-25 14:31:59 -04001235 block.symbolTable(), block.isScope()));
Ethan Nicholas739e1ca2020-06-11 12:16:14 -04001236 break;
John Stiles92219b42020-06-15 12:32:24 -04001237 }
1238
Ethan Nicholase6592142020-09-08 10:22:09 -04001239 case Statement::Kind::kBreak:
John Stiles92219b42020-06-15 12:32:24 -04001240 // Do not append a break to the target.
1241 break;
1242
Ethan Nicholas739e1ca2020-06-11 12:16:14 -04001243 default:
John Stiles92219b42020-06-15 12:32:24 -04001244 // Append normal statements to the target.
1245 target->push_back(std::move(stmt));
1246 break;
Ethan Nicholas739e1ca2020-06-11 12:16:14 -04001247 }
1248}
1249
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001250// Returns a block containing all of the statements that will be run if the given case matches
1251// (which, owing to the statements being owned by unique_ptrs, means the switch itself will be
1252// broken by this call and must then be discarded).
1253// Returns null (and leaves the switch unmodified) if no such simple reduction is possible, such as
1254// when break statements appear inside conditionals.
John Stiles92219b42020-06-15 12:32:24 -04001255static std::unique_ptr<Statement> block_for_case(SwitchStatement* switchStatement,
1256 SwitchCase* caseToCapture) {
1257 // We have to be careful to not move any of the pointers until after we're sure we're going to
1258 // succeed, so before we make any changes at all, we check the switch-cases to decide on a plan
1259 // of action. First, find the switch-case we are interested in.
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001260 auto iter = switchStatement->cases().begin();
1261 for (; iter != switchStatement->cases().end(); ++iter) {
John Stiles2d4f9592020-10-30 10:29:12 -04001262 if (iter->get() == caseToCapture) {
John Stiles92219b42020-06-15 12:32:24 -04001263 break;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001264 }
John Stiles92219b42020-06-15 12:32:24 -04001265 }
1266
1267 // Next, walk forward through the rest of the switch. If we find a conditional break, we're
1268 // stuck and can't simplify at all. If we find an unconditional break, we have a range of
1269 // statements that we can use for simplification.
1270 auto startIter = iter;
1271 Statement* unconditionalBreakStmt = nullptr;
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001272 for (; iter != switchStatement->cases().end(); ++iter) {
John Stiles2d4f9592020-10-30 10:29:12 -04001273 for (std::unique_ptr<Statement>& stmt : (*iter)->statements()) {
John Stiles92219b42020-06-15 12:32:24 -04001274 if (contains_conditional_break(*stmt)) {
1275 // We can't reduce switch-cases to a block when they have conditional breaks.
1276 return nullptr;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001277 }
John Stiles92219b42020-06-15 12:32:24 -04001278
1279 if (contains_unconditional_break(*stmt)) {
1280 // We found an unconditional break. We can use this block, but we need to strip
1281 // out the break statement.
1282 unconditionalBreakStmt = stmt.get();
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001283 break;
1284 }
1285 }
John Stiles92219b42020-06-15 12:32:24 -04001286
1287 if (unconditionalBreakStmt != nullptr) {
1288 break;
1289 }
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001290 }
John Stiles92219b42020-06-15 12:32:24 -04001291
1292 // We fell off the bottom of the switch or encountered a break. We know the range of statements
1293 // that we need to move over, and we know it's safe to do so.
John Stiles8f2a0cf2020-10-13 12:48:21 -04001294 StatementArray caseStmts;
John Stiles92219b42020-06-15 12:32:24 -04001295
1296 // We can move over most of the statements as-is.
1297 while (startIter != iter) {
John Stiles2d4f9592020-10-30 10:29:12 -04001298 for (std::unique_ptr<Statement>& stmt : (*startIter)->statements()) {
John Stiles92219b42020-06-15 12:32:24 -04001299 caseStmts.push_back(std::move(stmt));
1300 }
1301 ++startIter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001302 }
John Stiles92219b42020-06-15 12:32:24 -04001303
1304 // If we found an unconditional break at the end, we need to move what we can while avoiding
1305 // that break.
1306 if (unconditionalBreakStmt != nullptr) {
John Stiles2d4f9592020-10-30 10:29:12 -04001307 for (std::unique_ptr<Statement>& stmt : (*startIter)->statements()) {
John Stiles92219b42020-06-15 12:32:24 -04001308 if (stmt.get() == unconditionalBreakStmt) {
1309 move_all_but_break(stmt, &caseStmts);
1310 unconditionalBreakStmt = nullptr;
1311 break;
1312 }
1313
1314 caseStmts.push_back(std::move(stmt));
1315 }
1316 }
1317
1318 SkASSERT(unconditionalBreakStmt == nullptr); // Verify that we fixed the unconditional break.
1319
1320 // Return our newly-synthesized block.
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001321 return std::make_unique<Block>(/*offset=*/-1, std::move(caseStmts), switchStatement->symbols());
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001322}
1323
Ethan Nicholascb670962017-04-20 19:31:52 -04001324void Compiler::simplifyStatement(DefinitionMap& definitions,
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001325 BasicBlock& b,
1326 std::vector<BasicBlock::Node>::iterator* iter,
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001327 OptimizationContext* optimizationContext) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001328 ProgramUsage* usage = optimizationContext->fUsage;
Ethan Nicholascb670962017-04-20 19:31:52 -04001329 Statement* stmt = (*iter)->statement()->get();
Ethan Nicholase6592142020-09-08 10:22:09 -04001330 switch (stmt->kind()) {
1331 case Statement::Kind::kVarDeclaration: {
John Stilesa5a97b42020-08-18 11:19:07 -04001332 const auto& varDecl = stmt->as<VarDeclaration>();
Brian Osman010ce6a2020-10-19 16:34:10 -04001333 if (usage->isDead(varDecl.var()) &&
Ethan Nicholasc51f33e2020-10-13 13:49:44 -04001334 (!varDecl.value() ||
1335 !varDecl.value()->hasSideEffects())) {
1336 if (varDecl.value()) {
Ethan Nicholasd9d33c32018-06-12 11:05:59 -04001337 SkASSERT((*iter)->statement()->get() == stmt);
Ethan Nicholasc51f33e2020-10-13 13:49:44 -04001338 if (!b.tryRemoveExpressionBefore(iter, varDecl.value().get())) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001339 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001340 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001341 }
Ethan Nicholas5b9b0db2021-01-21 13:12:01 -05001342 // There can still be (soon to be removed) references to the variable at this point.
1343 // Allowing the VarDeclaration to be destroyed here will break those variable's
1344 // initialValue()s, so we hang on to them until optimization is finished.
1345 std::unique_ptr<Statement> old = (*iter)->setStatement(std::make_unique<Nop>(),
1346 usage);
1347 optimizationContext->fOwnedStatements.push_back(std::move(old));
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001348 optimizationContext->fUpdated = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001349 }
1350 break;
1351 }
Ethan Nicholase6592142020-09-08 10:22:09 -04001352 case Statement::Kind::kIf: {
John Stilesa5a97b42020-08-18 11:19:07 -04001353 IfStatement& i = stmt->as<IfStatement>();
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001354 if (i.test()->kind() == Expression::Kind::kBoolLiteral) {
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001355 // constant if, collapse down to a single branch
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001356 if (i.test()->as<BoolLiteral>().value()) {
1357 SkASSERT(i.ifTrue());
Brian Osman010ce6a2020-10-19 16:34:10 -04001358 (*iter)->setStatement(std::move(i.ifTrue()), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001359 } else {
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001360 if (i.ifFalse()) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001361 (*iter)->setStatement(std::move(i.ifFalse()), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001362 } else {
Brian Osman010ce6a2020-10-19 16:34:10 -04001363 (*iter)->setStatement(std::make_unique<Nop>(), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001364 }
1365 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001366 optimizationContext->fUpdated = true;
1367 optimizationContext->fNeedsRescan = true;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001368 break;
1369 }
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001370 if (i.ifFalse() && i.ifFalse()->isEmpty()) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001371 // else block doesn't do anything, remove it
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001372 i.ifFalse().reset();
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001373 optimizationContext->fUpdated = true;
1374 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001375 }
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001376 if (!i.ifFalse() && i.ifTrue()->isEmpty()) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001377 // if block doesn't do anything, no else block
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001378 if (i.test()->hasSideEffects()) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001379 // test has side effects, keep it
Brian Osman010ce6a2020-10-19 16:34:10 -04001380 (*iter)->setStatement(
1381 std::make_unique<ExpressionStatement>(std::move(i.test())), usage);
Ethan Nicholascb670962017-04-20 19:31:52 -04001382 } else {
1383 // no if, no else, no test side effects, kill the whole if
1384 // statement
Brian Osman010ce6a2020-10-19 16:34:10 -04001385 (*iter)->setStatement(std::make_unique<Nop>(), usage);
Ethan Nicholascb670962017-04-20 19:31:52 -04001386 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001387 optimizationContext->fUpdated = true;
1388 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001389 }
1390 break;
1391 }
Ethan Nicholase6592142020-09-08 10:22:09 -04001392 case Statement::Kind::kSwitch: {
John Stilesa5a97b42020-08-18 11:19:07 -04001393 SwitchStatement& s = stmt->as<SwitchStatement>();
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001394 int64_t switchValue;
Ethan Nicholasc0f98152021-02-05 16:21:10 -05001395 if (ConstantFolder::GetConstantInt(*s.value(), &switchValue)) {
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001396 // switch is constant, replace it with the case that matches
1397 bool found = false;
1398 SwitchCase* defaultCase = nullptr;
John Stiles2d4f9592020-10-30 10:29:12 -04001399 for (const std::unique_ptr<SwitchCase>& c : s.cases()) {
1400 if (!c->value()) {
1401 defaultCase = c.get();
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001402 continue;
1403 }
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001404 int64_t caseValue;
Ethan Nicholasc0f98152021-02-05 16:21:10 -05001405 SkAssertResult(ConstantFolder::GetConstantInt(*c->value(), &caseValue));
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001406 if (caseValue == switchValue) {
John Stiles2d4f9592020-10-30 10:29:12 -04001407 std::unique_ptr<Statement> newBlock = block_for_case(&s, c.get());
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001408 if (newBlock) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001409 (*iter)->setStatement(std::move(newBlock), usage);
John Stiles9d944232020-08-19 09:56:49 -04001410 found = true;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001411 break;
1412 } else {
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001413 if (s.isStatic() && !(fFlags & kPermitInvalidStaticTests_Flag) &&
1414 optimizationContext->fSilences.find(&s) ==
1415 optimizationContext->fSilences.end()) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001416 this->error(s.fOffset,
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001417 "static switch contains non-static conditional break");
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001418 optimizationContext->fSilences.insert(&s);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001419 }
1420 return; // can't simplify
1421 }
1422 }
1423 }
1424 if (!found) {
1425 // no matching case. use default if it exists, or kill the whole thing
1426 if (defaultCase) {
1427 std::unique_ptr<Statement> newBlock = block_for_case(&s, defaultCase);
1428 if (newBlock) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001429 (*iter)->setStatement(std::move(newBlock), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001430 } else {
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001431 if (s.isStatic() && !(fFlags & kPermitInvalidStaticTests_Flag) &&
1432 optimizationContext->fSilences.find(&s) ==
1433 optimizationContext->fSilences.end()) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001434 this->error(s.fOffset,
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001435 "static switch contains non-static conditional break");
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001436 optimizationContext->fSilences.insert(&s);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001437 }
1438 return; // can't simplify
1439 }
1440 } else {
Brian Osman010ce6a2020-10-19 16:34:10 -04001441 (*iter)->setStatement(std::make_unique<Nop>(), usage);
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001442 }
1443 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001444 optimizationContext->fUpdated = true;
1445 optimizationContext->fNeedsRescan = true;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001446 }
1447 break;
1448 }
Ethan Nicholase6592142020-09-08 10:22:09 -04001449 case Statement::Kind::kExpression: {
John Stilesa5a97b42020-08-18 11:19:07 -04001450 ExpressionStatement& e = stmt->as<ExpressionStatement>();
Ethan Nicholasd9d33c32018-06-12 11:05:59 -04001451 SkASSERT((*iter)->statement()->get() == &e);
Ethan Nicholasd503a5a2020-09-30 09:29:55 -04001452 if (!e.expression()->hasSideEffects()) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001453 // Expression statement with no side effects, kill it
Ethan Nicholasd503a5a2020-09-30 09:29:55 -04001454 if (!b.tryRemoveExpressionBefore(iter, e.expression().get())) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001455 optimizationContext->fNeedsRescan = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001456 }
Ethan Nicholasd9d33c32018-06-12 11:05:59 -04001457 SkASSERT((*iter)->statement()->get() == stmt);
Brian Osman010ce6a2020-10-19 16:34:10 -04001458 (*iter)->setStatement(std::make_unique<Nop>(), usage);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001459 optimizationContext->fUpdated = true;
Ethan Nicholascb670962017-04-20 19:31:52 -04001460 }
1461 break;
1462 }
1463 default:
1464 break;
1465 }
1466}
1467
Brian Osman010ce6a2020-10-19 16:34:10 -04001468bool Compiler::scanCFG(FunctionDefinition& f, ProgramUsage* usage) {
John Stiles0cc193a2020-09-09 09:39:34 -04001469 bool madeChanges = false;
1470
Ethan Nicholascb670962017-04-20 19:31:52 -04001471 CFG cfg = CFGGenerator().getCFG(f);
1472 this->computeDataFlow(&cfg);
ethannicholas22f939e2016-10-13 13:25:34 -07001473
1474 // check for unreachable code
1475 for (size_t i = 0; i < cfg.fBlocks.size(); i++) {
John Stiles0cc193a2020-09-09 09:39:34 -04001476 const BasicBlock& block = cfg.fBlocks[i];
John Stiles40525102020-12-16 18:10:44 -05001477 if (!block.fIsReachable && !block.fAllowUnreachable && block.fNodes.size()) {
John Stiles0cc193a2020-09-09 09:39:34 -04001478 const BasicBlock::Node& node = block.fNodes[0];
John Stilesc1129322021-02-03 10:13:42 -05001479 int offset = node.isStatement() ? (*node.statement())->fOffset
1480 : (*node.expression())->fOffset;
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001481 this->error(offset, String("unreachable"));
ethannicholas22f939e2016-10-13 13:25:34 -07001482 }
1483 }
1484 if (fErrorCount) {
John Stiles0cc193a2020-09-09 09:39:34 -04001485 return madeChanges;
ethannicholas22f939e2016-10-13 13:25:34 -07001486 }
1487
Ethan Nicholascb670962017-04-20 19:31:52 -04001488 // check for dead code & undefined variables, perform constant propagation
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001489 OptimizationContext optimizationContext;
Brian Osman010ce6a2020-10-19 16:34:10 -04001490 optimizationContext.fUsage = usage;
John Stiles7d3f0892020-11-03 11:35:01 -05001491 SkBitSet eliminatedBlockIds(cfg.fBlocks.size());
Ethan Nicholascb670962017-04-20 19:31:52 -04001492 do {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001493 if (optimizationContext.fNeedsRescan) {
Ethan Nicholascb670962017-04-20 19:31:52 -04001494 cfg = CFGGenerator().getCFG(f);
1495 this->computeDataFlow(&cfg);
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001496 optimizationContext.fNeedsRescan = false;
Ethan Nicholas113628d2017-02-02 16:11:39 -05001497 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001498
John Stiles7d3f0892020-11-03 11:35:01 -05001499 eliminatedBlockIds.reset();
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001500 optimizationContext.fUpdated = false;
John Stiles7d3f0892020-11-03 11:35:01 -05001501
1502 for (BlockId blockId = 0; blockId < cfg.fBlocks.size(); ++blockId) {
1503 if (eliminatedBlockIds.test(blockId)) {
1504 // We reached a block ID that might have been eliminated. Be cautious and rescan.
1505 optimizationContext.fUpdated = true;
1506 optimizationContext.fNeedsRescan = true;
1507 break;
1508 }
1509
1510 BasicBlock& b = cfg.fBlocks[blockId];
1511 if (blockId > 0 && !b.fIsReachable) {
Ethan Nicholas1de14812020-06-19 15:32:49 -04001512 // Block was reachable before optimization, but has since become unreachable. In
1513 // addition to being dead code, it's broken - since control flow can't reach it, no
1514 // prior variable definitions can reach it, and therefore variables might look to
Brian Osmanc0213602020-10-06 14:43:32 -04001515 // have not been properly assigned. Kill it by replacing all statements with Nops.
Ethan Nicholas1de14812020-06-19 15:32:49 -04001516 for (BasicBlock::Node& node : b.fNodes) {
John Stiles70025e52020-09-28 16:08:58 -04001517 if (node.isStatement() && !(*node.statement())->is<Nop>()) {
John Stiles7d3f0892020-11-03 11:35:01 -05001518 // Eliminating a node runs the risk of eliminating that node's exits as
1519 // well. Keep track of this and do a rescan if we are about to access one
1520 // of these.
1521 for (BlockId id : b.fExits) {
1522 eliminatedBlockIds.set(id);
1523 }
Brian Osman010ce6a2020-10-19 16:34:10 -04001524 node.setStatement(std::make_unique<Nop>(), usage);
John Stiles0cc193a2020-09-09 09:39:34 -04001525 madeChanges = true;
Ethan Nicholas1de14812020-06-19 15:32:49 -04001526 }
1527 }
1528 continue;
1529 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001530 DefinitionMap definitions = b.fBefore;
1531
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001532 for (auto iter = b.fNodes.begin(); iter != b.fNodes.end() &&
1533 !optimizationContext.fNeedsRescan; ++iter) {
John Stiles70025e52020-09-28 16:08:58 -04001534 if (iter->isExpression()) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001535 this->simplifyExpression(definitions, b, &iter, &optimizationContext);
Ethan Nicholascb670962017-04-20 19:31:52 -04001536 } else {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001537 this->simplifyStatement(definitions, b, &iter, &optimizationContext);
Ethan Nicholascb670962017-04-20 19:31:52 -04001538 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001539 if (optimizationContext.fNeedsRescan) {
Ethan Nicholas4b330df2017-05-17 10:52:55 -04001540 break;
1541 }
John Stilese8a24922021-02-08 17:54:08 -05001542 definitions.addDefinitions(*fContext, *iter);
Ethan Nicholascb670962017-04-20 19:31:52 -04001543 }
Brian Osman01a3eb42020-09-14 11:32:49 -04001544
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001545 if (optimizationContext.fNeedsRescan) {
Brian Osman01a3eb42020-09-14 11:32:49 -04001546 break;
1547 }
Ethan Nicholascb670962017-04-20 19:31:52 -04001548 }
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001549 madeChanges |= optimizationContext.fUpdated;
1550 } while (optimizationContext.fUpdated);
1551 SkASSERT(!optimizationContext.fNeedsRescan);
ethannicholas22f939e2016-10-13 13:25:34 -07001552
Ethan Nicholas91a10532017-06-22 11:24:38 -04001553 // verify static ifs & switches, clean up dead variable decls
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001554 for (BasicBlock& b : cfg.fBlocks) {
Ethan Nicholascdeae8c2020-10-22 14:39:46 -04001555 for (auto iter = b.fNodes.begin(); iter != b.fNodes.end() &&
1556 !optimizationContext.fNeedsRescan;) {
John Stiles70025e52020-09-28 16:08:58 -04001557 if (iter->isStatement()) {
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001558 const Statement& s = **iter->statement();
Ethan Nicholase6592142020-09-08 10:22:09 -04001559 switch (s.kind()) {
1560 case Statement::Kind::kIf:
Ethan Nicholas8c44eca2020-10-07 16:47:09 -04001561 if (s.as<IfStatement>().isStatic() &&
Ethan Nicholas6e1cbc02017-07-14 10:12:15 -04001562 !(fFlags & kPermitInvalidStaticTests_Flag)) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001563 this->error(s.fOffset, "static if has non-static test");
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001564 }
Ethan Nicholas91a10532017-06-22 11:24:38 -04001565 ++iter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001566 break;
Ethan Nicholase6592142020-09-08 10:22:09 -04001567 case Statement::Kind::kSwitch:
Ethan Nicholas01b05e52020-10-22 15:53:41 -04001568 if (s.as<SwitchStatement>().isStatic() &&
1569 !(fFlags & kPermitInvalidStaticTests_Flag) &&
1570 optimizationContext.fSilences.find(&s) ==
1571 optimizationContext.fSilences.end()) {
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001572 this->error(s.fOffset, "static switch has non-static test");
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001573 }
Ethan Nicholas91a10532017-06-22 11:24:38 -04001574 ++iter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001575 break;
1576 default:
Ethan Nicholas91a10532017-06-22 11:24:38 -04001577 ++iter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001578 break;
1579 }
Ethan Nicholas91a10532017-06-22 11:24:38 -04001580 } else {
1581 ++iter;
Ethan Nicholas5ac13c22017-05-10 15:06:17 -04001582 }
1583 }
1584 }
1585
ethannicholas22f939e2016-10-13 13:25:34 -07001586 // check for missing return
John Stiles54e7c052021-01-11 14:22:36 -05001587 if (f.declaration().returnType() != *fContext->fTypes.fVoid) {
John Stiles61e75e32020-10-01 15:42:37 -04001588 if (cfg.fBlocks[cfg.fExit].fIsReachable) {
Ethan Nicholas0a5d0962020-10-14 13:33:18 -04001589 this->error(f.fOffset, String("function '" + String(f.declaration().name()) +
Ethan Nicholasdb80f692019-11-22 14:06:12 -05001590 "' can exit without returning a value"));
ethannicholas22f939e2016-10-13 13:25:34 -07001591 }
1592 }
John Stiles0cc193a2020-09-09 09:39:34 -04001593
1594 return madeChanges;
ethannicholas22f939e2016-10-13 13:25:34 -07001595}
1596
Brian Osman32d53552020-09-23 13:55:20 -04001597std::unique_ptr<Program> Compiler::convertProgram(
1598 Program::Kind kind,
1599 String text,
1600 const Program::Settings& settings,
Brian Osmanbe0b3b72021-01-06 14:27:35 -05001601 const std::vector<std::unique_ptr<ExternalFunction>>* externalFunctions) {
1602 SkASSERT(!externalFunctions || (kind == Program::kGeneric_Kind));
Ethan Nicholas91164d12019-05-15 15:29:54 -04001603
Brian Osman0006ad02020-11-18 15:38:39 -05001604 // Loading and optimizing our base module might reset the inliner, so do that first,
1605 // *then* configure the inliner with the settings for this program.
1606 const ParsedModule& baseModule = this->moduleForProgramKind(kind);
1607
ethannicholasb3058bd2016-07-01 08:22:01 -07001608 fErrorText = "";
1609 fErrorCount = 0;
Brian Osman0006ad02020-11-18 15:38:39 -05001610 fInliner.reset(fIRGenerator->fModifiers.get(), &settings);
Brian Osman88cda172020-10-09 12:05:16 -04001611
1612 // Not using AutoSource, because caller is likely to call errorText() if we fail to compile
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001613 std::unique_ptr<String> textPtr(new String(std::move(text)));
1614 fSource = textPtr.get();
Brian Osman88cda172020-10-09 12:05:16 -04001615
John Stiles5c7bb322020-10-22 11:09:15 -04001616 // Enable node pooling while converting and optimizing the program for a performance boost.
1617 // The Program will take ownership of the pool.
Brian Osman28f702c2021-02-02 11:52:07 -05001618 std::unique_ptr<Pool> pool;
1619 if (fCaps->useNodePools()) {
1620 pool = Pool::Create();
1621 pool->attachToThread();
1622 }
Brian Osman0006ad02020-11-18 15:38:39 -05001623 IRGenerator::IRBundle ir =
1624 fIRGenerator->convertProgram(kind, &settings, baseModule, /*isBuiltinCode=*/false,
Brian Osmanbe0b3b72021-01-06 14:27:35 -05001625 textPtr->c_str(), textPtr->size(), externalFunctions);
John Stiles5c7bb322020-10-22 11:09:15 -04001626 auto program = std::make_unique<Program>(kind,
1627 std::move(textPtr),
1628 settings,
Brian Osmand7e76592020-11-02 12:26:22 -05001629 fCaps,
John Stiles5c7bb322020-10-22 11:09:15 -04001630 fContext,
1631 std::move(ir.fElements),
Brian Osman133724c2020-10-28 14:14:39 -04001632 std::move(ir.fSharedElements),
John Stiles5c7bb322020-10-22 11:09:15 -04001633 std::move(ir.fModifiers),
1634 std::move(ir.fSymbolTable),
1635 std::move(pool),
1636 ir.fInputs);
1637 bool success = false;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001638 if (fErrorCount) {
John Stiles5c7bb322020-10-22 11:09:15 -04001639 // Do not return programs that failed to compile.
1640 } else if (settings.fOptimize && !this->optimize(*program)) {
1641 // Do not return programs that failed to optimize.
1642 } else {
1643 // We have a successful program!
1644 success = true;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001645 }
John Stiles5c7bb322020-10-22 11:09:15 -04001646
Brian Osman28f702c2021-02-02 11:52:07 -05001647 if (program->fPool) {
1648 program->fPool->detachFromThread();
1649 }
John Stiles5c7bb322020-10-22 11:09:15 -04001650 return success ? std::move(program) : nullptr;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001651}
1652
Brian Osman0006ad02020-11-18 15:38:39 -05001653bool Compiler::optimize(LoadedModule& module) {
1654 SkASSERT(!fErrorCount);
1655 Program::Settings settings;
1656 fIRGenerator->fKind = module.fKind;
1657 fIRGenerator->fSettings = &settings;
1658 std::unique_ptr<ProgramUsage> usage = Analysis::GetUsage(module);
1659
1660 fInliner.reset(fModifiers.back().get(), &settings);
1661
1662 while (fErrorCount == 0) {
1663 bool madeChanges = false;
1664
1665 // Scan and optimize based on the control-flow graph for each function.
1666 for (const auto& element : module.fElements) {
1667 if (element->is<FunctionDefinition>()) {
1668 madeChanges |= this->scanCFG(element->as<FunctionDefinition>(), usage.get());
1669 }
1670 }
1671
1672 // Perform inline-candidate analysis and inline any functions deemed suitable.
John Stiles78047582020-12-16 16:17:41 -05001673 madeChanges |= fInliner.analyze(module.fElements, module.fSymbols, usage.get());
Brian Osman0006ad02020-11-18 15:38:39 -05001674
1675 if (!madeChanges) {
1676 break;
1677 }
1678 }
1679 return fErrorCount == 0;
1680}
1681
Ethan Nicholas00543112018-07-31 09:44:36 -04001682bool Compiler::optimize(Program& program) {
1683 SkASSERT(!fErrorCount);
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001684 fIRGenerator->fKind = program.fKind;
1685 fIRGenerator->fSettings = &program.fSettings;
Brian Osman010ce6a2020-10-19 16:34:10 -04001686 ProgramUsage* usage = program.fUsage.get();
John Stiles7954d6c2020-09-01 10:53:02 -04001687
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001688 while (fErrorCount == 0) {
1689 bool madeChanges = false;
John Stiles7954d6c2020-09-01 10:53:02 -04001690
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001691 // Scan and optimize based on the control-flow graph for each function.
Brian Osman133724c2020-10-28 14:14:39 -04001692 for (const auto& element : program.ownedElements()) {
Brian Osman1179fcf2020-10-08 16:04:40 -04001693 if (element->is<FunctionDefinition>()) {
Brian Osman010ce6a2020-10-19 16:34:10 -04001694 madeChanges |= this->scanCFG(element->as<FunctionDefinition>(), usage);
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001695 }
1696 }
1697
1698 // Perform inline-candidate analysis and inline any functions deemed suitable.
John Stiles78047582020-12-16 16:17:41 -05001699 madeChanges |= fInliner.analyze(program.ownedElements(), program.fSymbols, usage);
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001700
1701 // Remove dead functions. We wait until after analysis so that we still report errors,
1702 // even in unused code.
1703 if (program.fSettings.fRemoveDeadFunctions) {
Brian Osman133724c2020-10-28 14:14:39 -04001704 auto isDeadFunction = [&](const ProgramElement* element) {
1705 if (!element->is<FunctionDefinition>()) {
1706 return false;
1707 }
1708 const FunctionDefinition& fn = element->as<FunctionDefinition>();
1709 if (fn.declaration().name() != "main" && usage->get(fn.declaration()) == 0) {
1710 usage->remove(*element);
1711 madeChanges = true;
1712 return true;
1713 }
1714 return false;
1715 };
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001716 program.fElements.erase(
Brian Osman133724c2020-10-28 14:14:39 -04001717 std::remove_if(program.fElements.begin(), program.fElements.end(),
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001718 [&](const std::unique_ptr<ProgramElement>& element) {
Brian Osman133724c2020-10-28 14:14:39 -04001719 return isDeadFunction(element.get());
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001720 }),
1721 program.fElements.end());
Brian Osman133724c2020-10-28 14:14:39 -04001722 program.fSharedElements.erase(
1723 std::remove_if(program.fSharedElements.begin(), program.fSharedElements.end(),
1724 isDeadFunction),
1725 program.fSharedElements.end());
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001726 }
1727
1728 if (program.fKind != Program::kFragmentProcessor_Kind) {
Brian Osmanc0213602020-10-06 14:43:32 -04001729 // Remove declarations of dead global variables
Brian Osman133724c2020-10-28 14:14:39 -04001730 auto isDeadVariable = [&](const ProgramElement* element) {
1731 if (!element->is<GlobalVarDeclaration>()) {
1732 return false;
1733 }
1734 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
1735 const VarDeclaration& varDecl = global.declaration()->as<VarDeclaration>();
1736 if (usage->isDead(varDecl.var())) {
1737 madeChanges = true;
1738 return true;
1739 }
1740 return false;
1741 };
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001742 program.fElements.erase(
1743 std::remove_if(program.fElements.begin(), program.fElements.end(),
1744 [&](const std::unique_ptr<ProgramElement>& element) {
Brian Osman133724c2020-10-28 14:14:39 -04001745 return isDeadVariable(element.get());
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001746 }),
1747 program.fElements.end());
Brian Osman133724c2020-10-28 14:14:39 -04001748 program.fSharedElements.erase(
1749 std::remove_if(program.fSharedElements.begin(), program.fSharedElements.end(),
1750 isDeadVariable),
1751 program.fSharedElements.end());
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001752 }
John Stiles73a6bff2020-09-09 13:40:37 -04001753
Ethan Nicholas34b19c52020-09-14 11:33:47 -04001754 if (!madeChanges) {
1755 break;
Ethan Nicholas0dc80872019-02-08 15:46:24 -05001756 }
Ethan Nicholas00543112018-07-31 09:44:36 -04001757 }
1758 return fErrorCount == 0;
1759}
1760
Brian Osmanfb32ddf2019-06-18 10:14:20 -04001761#if defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
1762
Ethan Nicholas00543112018-07-31 09:44:36 -04001763bool Compiler::toSPIRV(Program& program, OutputStream& out) {
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001764#ifdef SK_ENABLE_SPIRV_VALIDATION
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001765 StringStream buffer;
Brian Osman88cda172020-10-09 12:05:16 -04001766 AutoSource as(this, program.fSource.get());
Brian Osman8b43dad2020-10-09 13:31:42 -04001767 SPIRVCodeGenerator cg(fContext.get(), &program, this, &buffer);
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001768 bool result = cg.generateCode();
Ethan Nicholasba9a04f2020-11-06 09:28:04 -05001769 if (result && program.fSettings.fValidateSPIRV) {
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001770 spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001771 const String& data = buffer.str();
Ethan Nicholasd9d33c32018-06-12 11:05:59 -04001772 SkASSERT(0 == data.size() % 4);
Brian Osman8d09d4a2020-11-24 15:51:06 -05001773 String errors;
1774 auto dumpmsg = [&errors](spv_message_level_t, const char*, const spv_position_t&,
1775 const char* m) {
1776 errors.appendf("SPIR-V validation error: %s\n", m);
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001777 };
1778 tools.SetMessageConsumer(dumpmsg);
Brian Osman8d09d4a2020-11-24 15:51:06 -05001779
1780 // Verify that the SPIR-V we produced is valid. At runtime, we will abort() with a message
1781 // explaining the error. In standalone mode (skslc), we will send the message, plus the
1782 // entire disassembled SPIR-V (for easier context & debugging) as *our* error message.
1783 result = tools.Validate((const uint32_t*) data.c_str(), data.size() / 4);
1784
1785 if (!result) {
1786#if defined(SKSL_STANDALONE)
1787 // Convert the string-stream to a SPIR-V disassembly.
1788 std::string disassembly;
1789 if (tools.Disassemble((const uint32_t*)data.data(), data.size() / 4, &disassembly)) {
1790 errors.append(disassembly);
1791 }
1792 this->error(-1, errors);
1793#else
1794 SkDEBUGFAILF("%s", errors.c_str());
1795#endif
1796 }
Ethan Nicholas762466e2017-06-29 10:03:38 -04001797 out.write(data.c_str(), data.size());
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001798 }
1799#else
Brian Osman88cda172020-10-09 12:05:16 -04001800 AutoSource as(this, program.fSource.get());
Brian Osman8b43dad2020-10-09 13:31:42 -04001801 SPIRVCodeGenerator cg(fContext.get(), &program, this, &out);
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001802 bool result = cg.generateCode();
Ethan Nicholasa6ae1f72017-03-16 09:56:54 -04001803#endif
Ethan Nicholasce33f102016-12-09 17:22:59 -05001804 return result;
1805}
1806
Ethan Nicholas00543112018-07-31 09:44:36 -04001807bool Compiler::toSPIRV(Program& program, String* out) {
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001808 StringStream buffer;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001809 bool result = this->toSPIRV(program, buffer);
1810 if (result) {
Ethan Nicholas762466e2017-06-29 10:03:38 -04001811 *out = buffer.str();
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001812 }
1813 return result;
1814}
1815
Ethan Nicholas00543112018-07-31 09:44:36 -04001816bool Compiler::toGLSL(Program& program, OutputStream& out) {
Brian Osman88cda172020-10-09 12:05:16 -04001817 AutoSource as(this, program.fSource.get());
Ethan Nicholas26a9aad2018-03-27 14:10:52 -04001818 GLSLCodeGenerator cg(fContext.get(), &program, this, &out);
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001819 bool result = cg.generateCode();
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001820 return result;
1821}
1822
Ethan Nicholas00543112018-07-31 09:44:36 -04001823bool Compiler::toGLSL(Program& program, String* out) {
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001824 StringStream buffer;
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001825 bool result = this->toGLSL(program, buffer);
1826 if (result) {
Ethan Nicholas762466e2017-06-29 10:03:38 -04001827 *out = buffer.str();
Ethan Nicholas941e7e22016-12-12 15:33:30 -05001828 }
1829 return result;
1830}
1831
Brian Osmanc0243912020-02-19 15:35:26 -05001832bool Compiler::toHLSL(Program& program, String* out) {
1833 String spirv;
1834 if (!this->toSPIRV(program, &spirv)) {
1835 return false;
1836 }
1837
1838 return SPIRVtoHLSL(spirv, out);
1839}
1840
Ethan Nicholas00543112018-07-31 09:44:36 -04001841bool Compiler::toMetal(Program& program, OutputStream& out) {
Ethan Nicholas26a9aad2018-03-27 14:10:52 -04001842 MetalCodeGenerator cg(fContext.get(), &program, this, &out);
Ethan Nicholascc305772017-10-13 16:17:45 -04001843 bool result = cg.generateCode();
Ethan Nicholascc305772017-10-13 16:17:45 -04001844 return result;
1845}
1846
Ethan Nicholas00543112018-07-31 09:44:36 -04001847bool Compiler::toMetal(Program& program, String* out) {
Timothy Liangb8eeb802018-07-23 16:46:16 -04001848 StringStream buffer;
1849 bool result = this->toMetal(program, buffer);
1850 if (result) {
1851 *out = buffer.str();
1852 }
1853 return result;
1854}
1855
Greg Daniela28ea672020-09-25 11:12:56 -04001856#if defined(SKSL_STANDALONE) || GR_TEST_UTILS
Ethan Nicholas00543112018-07-31 09:44:36 -04001857bool Compiler::toCPP(Program& program, String name, OutputStream& out) {
Brian Osman88cda172020-10-09 12:05:16 -04001858 AutoSource as(this, program.fSource.get());
Ethan Nicholas26a9aad2018-03-27 14:10:52 -04001859 CPPCodeGenerator cg(fContext.get(), &program, this, name, &out);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001860 bool result = cg.generateCode();
Ethan Nicholas762466e2017-06-29 10:03:38 -04001861 return result;
1862}
1863
Ethan Nicholas00543112018-07-31 09:44:36 -04001864bool Compiler::toH(Program& program, String name, OutputStream& out) {
Brian Osman88cda172020-10-09 12:05:16 -04001865 AutoSource as(this, program.fSource.get());
Ethan Nicholas26a9aad2018-03-27 14:10:52 -04001866 HCodeGenerator cg(fContext.get(), &program, this, name, &out);
Ethan Nicholas762466e2017-06-29 10:03:38 -04001867 bool result = cg.generateCode();
Ethan Nicholas00543112018-07-31 09:44:36 -04001868 return result;
1869}
Greg Daniela28ea672020-09-25 11:12:56 -04001870#endif // defined(SKSL_STANDALONE) || GR_TEST_UTILS
Ethan Nicholas00543112018-07-31 09:44:36 -04001871
Ethan Nicholas2a479a52020-08-18 16:29:45 -04001872#endif // defined(SKSL_STANDALONE) || SK_SUPPORT_GPU
Brian Osman2e29ab52019-09-20 12:19:11 -04001873
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001874Position Compiler::position(int offset) {
Ethan Nicholas3c729892020-12-07 12:47:17 -05001875 if (fSource && offset >= 0) {
1876 int line = 1;
1877 int column = 1;
1878 for (int i = 0; i < offset; i++) {
1879 if ((*fSource)[i] == '\n') {
1880 ++line;
1881 column = 1;
1882 }
1883 else {
1884 ++column;
1885 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001886 }
Ethan Nicholas3c729892020-12-07 12:47:17 -05001887 return Position(line, column);
1888 } else {
1889 return Position(-1, -1);
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001890 }
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001891}
1892
1893void Compiler::error(int offset, String msg) {
ethannicholasb3058bd2016-07-01 08:22:01 -07001894 fErrorCount++;
Ethan Nicholas5b5f0962017-09-11 13:50:14 -07001895 Position pos = this->position(offset);
John Stiles8d3642e2021-01-22 09:50:04 -05001896 fErrorTextLength.push_back(fErrorText.length());
Ethan Nicholas3c729892020-12-07 12:47:17 -05001897 fErrorText += "error: " + (pos.fLine >= 1 ? to_string(pos.fLine) + ": " : "") + msg + "\n";
ethannicholasb3058bd2016-07-01 08:22:01 -07001898}
1899
John Stiles8d3642e2021-01-22 09:50:04 -05001900void Compiler::setErrorCount(int c) {
1901 if (c < fErrorCount) {
1902 fErrorText.resize(fErrorTextLength[c]);
1903 fErrorTextLength.resize(c);
1904 fErrorCount = c;
1905 }
1906}
1907
Ethan Nicholas95046142021-01-07 10:57:27 -05001908String Compiler::errorText(bool showCount) {
1909 if (showCount) {
1910 this->writeErrorCount();
1911 }
Ethan Nicholas00543112018-07-31 09:44:36 -04001912 fErrorCount = 0;
Ethan Nicholas0df1b042017-03-31 13:56:23 -04001913 String result = fErrorText;
Ethan Nicholas95046142021-01-07 10:57:27 -05001914 fErrorText = "";
ethannicholasb3058bd2016-07-01 08:22:01 -07001915 return result;
1916}
1917
1918void Compiler::writeErrorCount() {
1919 if (fErrorCount) {
1920 fErrorText += to_string(fErrorCount) + " error";
1921 if (fErrorCount > 1) {
1922 fErrorText += "s";
1923 }
1924 fErrorText += "\n";
1925 }
1926}
1927
John Stilesa6841be2020-08-06 14:11:56 -04001928} // namespace SkSL