1 | /* |
2 | * Copyright 2019 Google LLC |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #ifndef SKSL_BYTECODEGENERATOR |
9 | #define SKSL_BYTECODEGENERATOR |
10 | |
11 | #include <algorithm> |
12 | #include <stack> |
13 | #include <unordered_map> |
14 | |
15 | #include "src/sksl/SkSLByteCode.h" |
16 | #include "src/sksl/SkSLCodeGenerator.h" |
17 | #include "src/sksl/SkSLMemoryLayout.h" |
18 | #include "src/sksl/ir/SkSLBinaryExpression.h" |
19 | #include "src/sksl/ir/SkSLBlock.h" |
20 | #include "src/sksl/ir/SkSLBoolLiteral.h" |
21 | #include "src/sksl/ir/SkSLBreakStatement.h" |
22 | #include "src/sksl/ir/SkSLConstructor.h" |
23 | #include "src/sksl/ir/SkSLContinueStatement.h" |
24 | #include "src/sksl/ir/SkSLDoStatement.h" |
25 | #include "src/sksl/ir/SkSLExpressionStatement.h" |
26 | #include "src/sksl/ir/SkSLExternalFunctionCall.h" |
27 | #include "src/sksl/ir/SkSLExternalValueReference.h" |
28 | #include "src/sksl/ir/SkSLFieldAccess.h" |
29 | #include "src/sksl/ir/SkSLFloatLiteral.h" |
30 | #include "src/sksl/ir/SkSLForStatement.h" |
31 | #include "src/sksl/ir/SkSLFunctionCall.h" |
32 | #include "src/sksl/ir/SkSLFunctionDeclaration.h" |
33 | #include "src/sksl/ir/SkSLFunctionDefinition.h" |
34 | #include "src/sksl/ir/SkSLIfStatement.h" |
35 | #include "src/sksl/ir/SkSLIndexExpression.h" |
36 | #include "src/sksl/ir/SkSLIntLiteral.h" |
37 | #include "src/sksl/ir/SkSLInterfaceBlock.h" |
38 | #include "src/sksl/ir/SkSLNullLiteral.h" |
39 | #include "src/sksl/ir/SkSLPostfixExpression.h" |
40 | #include "src/sksl/ir/SkSLPrefixExpression.h" |
41 | #include "src/sksl/ir/SkSLProgramElement.h" |
42 | #include "src/sksl/ir/SkSLReturnStatement.h" |
43 | #include "src/sksl/ir/SkSLStatement.h" |
44 | #include "src/sksl/ir/SkSLSwitchStatement.h" |
45 | #include "src/sksl/ir/SkSLSwizzle.h" |
46 | #include "src/sksl/ir/SkSLTernaryExpression.h" |
47 | #include "src/sksl/ir/SkSLVarDeclarations.h" |
48 | #include "src/sksl/ir/SkSLVarDeclarationsStatement.h" |
49 | #include "src/sksl/ir/SkSLVariableReference.h" |
50 | #include "src/sksl/ir/SkSLWhileStatement.h" |
51 | #include "src/sksl/spirv.h" |
52 | |
53 | namespace SkSL { |
54 | |
55 | class ByteCodeGenerator : public CodeGenerator { |
56 | public: |
57 | class LValue { |
58 | public: |
59 | LValue(ByteCodeGenerator& generator) |
60 | : fGenerator(generator) {} |
61 | |
62 | virtual ~LValue() {} |
63 | |
64 | /** |
65 | * Stack before call: ... lvalue |
66 | * Stack after call: ... lvalue load |
67 | */ |
68 | virtual void load() = 0; |
69 | |
70 | /** |
71 | * Stack before call: ... lvalue value |
72 | * Stack after call: ... |
73 | */ |
74 | virtual void store(bool discard) = 0; |
75 | |
76 | protected: |
77 | ByteCodeGenerator& fGenerator; |
78 | }; |
79 | |
80 | ByteCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors, |
81 | ByteCode* output); |
82 | |
83 | bool generateCode() override; |
84 | |
85 | void write8(uint8_t b); |
86 | |
87 | void write16(uint16_t b); |
88 | |
89 | void write32(uint32_t b); |
90 | |
91 | void write(ByteCodeInstruction inst, int count = kUnusedStackCount); |
92 | |
93 | /** |
94 | * Based on 'type', writes the s (signed), u (unsigned), or f (float) instruction. |
95 | */ |
96 | void writeTypedInstruction(const Type& type, ByteCodeInstruction s, ByteCodeInstruction u, |
97 | ByteCodeInstruction f, int count); |
98 | |
99 | static int SlotCount(const Type& type); |
100 | |
101 | private: |
102 | static constexpr int kUnusedStackCount = INT32_MAX; |
103 | static int StackUsage(ByteCodeInstruction, int count); |
104 | |
105 | // reserves 16 bits in the output code, to be filled in later with an address once we determine |
106 | // it |
107 | class DeferredLocation { |
108 | public: |
109 | DeferredLocation(ByteCodeGenerator* generator) |
110 | : fGenerator(*generator) |
111 | , fOffset(generator->fCode->size()) { |
112 | generator->write16(0); |
113 | } |
114 | |
115 | #ifdef SK_DEBUG |
116 | ~DeferredLocation() { |
117 | SkASSERT(fSet); |
118 | } |
119 | #endif |
120 | |
121 | void set() { |
122 | int target = fGenerator.fCode->size(); |
123 | SkASSERT(target <= 65535); |
124 | (*fGenerator.fCode)[fOffset] = target; |
125 | (*fGenerator.fCode)[fOffset + 1] = target >> 8; |
126 | #ifdef SK_DEBUG |
127 | fSet = true; |
128 | #endif |
129 | } |
130 | |
131 | private: |
132 | ByteCodeGenerator& fGenerator; |
133 | size_t fOffset; |
134 | #ifdef SK_DEBUG |
135 | bool fSet = false; |
136 | #endif |
137 | }; |
138 | |
139 | // Intrinsics which do not simply map to a single opcode |
140 | enum class SpecialIntrinsic { |
141 | kAll, |
142 | kAny, |
143 | kClamp, |
144 | kDot, |
145 | kLength, |
146 | kMax, |
147 | kMin, |
148 | kMix, |
149 | kNormalize, |
150 | kSample, |
151 | kSaturate, |
152 | }; |
153 | |
154 | struct Intrinsic { |
155 | Intrinsic(SpecialIntrinsic s) : is_special(true), special(s) {} |
156 | Intrinsic(ByteCodeInstruction i) : Intrinsic(i, i, i) {} |
157 | Intrinsic(ByteCodeInstruction f, |
158 | ByteCodeInstruction s, |
159 | ByteCodeInstruction u) : is_special(false), inst_f(f), inst_s(s), inst_u(u) {} |
160 | |
161 | bool is_special; |
162 | SpecialIntrinsic special; |
163 | ByteCodeInstruction inst_f; |
164 | ByteCodeInstruction inst_s; |
165 | ByteCodeInstruction inst_u; |
166 | }; |
167 | |
168 | |
169 | // Similar to Variable::Storage, but locals and parameters are grouped together, and globals |
170 | // are further subidivided into uniforms and other (writable) globals. |
171 | enum class Storage { |
172 | kLocal, // include parameters |
173 | kGlobal, // non-uniform globals |
174 | kUniform, // uniform globals |
175 | kChildFP, // child fragment processors |
176 | }; |
177 | |
178 | struct Location { |
179 | int fSlot; |
180 | Storage fStorage; |
181 | |
182 | // Not really invalid, but a "safe" placeholder to be more explicit at call-sites |
183 | static Location MakeInvalid() { return { 0, Storage::kLocal }; } |
184 | |
185 | Location makeOnStack() { |
186 | SkASSERT(fStorage != Storage::kChildFP); |
187 | return { -1, fStorage }; |
188 | } |
189 | bool isOnStack() const { return fSlot < 0; } |
190 | |
191 | Location operator+(int offset) { |
192 | SkASSERT(fStorage != Storage::kChildFP); |
193 | SkASSERT(fSlot >= 0); |
194 | return { fSlot + offset, fStorage }; |
195 | } |
196 | |
197 | ByteCodeInstruction selectLoad(ByteCodeInstruction local, |
198 | ByteCodeInstruction global, |
199 | ByteCodeInstruction uniform) const { |
200 | switch (fStorage) { |
201 | case Storage::kLocal: return local; |
202 | case Storage::kGlobal: return global; |
203 | case Storage::kUniform: return uniform; |
204 | case Storage::kChildFP: ABORT("Trying to load an FP" ); break; |
205 | } |
206 | return local; |
207 | } |
208 | |
209 | ByteCodeInstruction selectStore(ByteCodeInstruction local, |
210 | ByteCodeInstruction global) const { |
211 | switch (fStorage) { |
212 | case Storage::kLocal: return local; |
213 | case Storage::kGlobal: return global; |
214 | case Storage::kUniform: ABORT("Trying to store to a uniform" ); break; |
215 | case Storage::kChildFP: ABORT("Trying to store an FP" ); break; |
216 | } |
217 | return local; |
218 | } |
219 | }; |
220 | |
221 | /** |
222 | * Returns the local slot into which var should be stored, allocating a new slot if it has not |
223 | * already been assigned one. Compound variables (e.g. vectors) will consume more than one local |
224 | * slot, with the getLocation return value indicating where the first element should be stored. |
225 | */ |
226 | Location getLocation(const Variable& var); |
227 | |
228 | /** |
229 | * As above, but computes the (possibly dynamic) address of an expression involving indexing & |
230 | * field access. If the address is known, it's returned. If not, -1 is returned, and the |
231 | * location will be left on the top of the stack. |
232 | */ |
233 | Location getLocation(const Expression& expr); |
234 | |
235 | void gatherUniforms(const Type& type, const String& name); |
236 | |
237 | std::unique_ptr<ByteCodeFunction> writeFunction(const FunctionDefinition& f); |
238 | |
239 | void writeVarDeclarations(const VarDeclarations& decl); |
240 | |
241 | void writeVariableExpression(const Expression& expr); |
242 | |
243 | void writeExpression(const Expression& expr, bool discard = false); |
244 | |
245 | /** |
246 | * Pushes whatever values are required by the lvalue onto the stack, and returns an LValue |
247 | * permitting loads and stores to it. |
248 | */ |
249 | std::unique_ptr<LValue> getLValue(const Expression& expr); |
250 | |
251 | void writeIntrinsicCall(const FunctionCall& c); |
252 | |
253 | void writeFunctionCall(const FunctionCall& c); |
254 | |
255 | void writeConstructor(const Constructor& c); |
256 | |
257 | void writeExternalFunctionCall(const ExternalFunctionCall& c); |
258 | |
259 | void writeExternalValue(const ExternalValueReference& r); |
260 | |
261 | void writeSwizzle(const Swizzle& swizzle); |
262 | |
263 | bool writeBinaryExpression(const BinaryExpression& b, bool discard); |
264 | |
265 | void writeTernaryExpression(const TernaryExpression& t); |
266 | |
267 | void writeNullLiteral(const NullLiteral& n); |
268 | |
269 | bool writePrefixExpression(const PrefixExpression& p, bool discard); |
270 | |
271 | bool writePostfixExpression(const PostfixExpression& p, bool discard); |
272 | |
273 | void writeBoolLiteral(const BoolLiteral& b); |
274 | |
275 | void writeIntLiteral(const IntLiteral& i); |
276 | |
277 | void writeFloatLiteral(const FloatLiteral& f); |
278 | |
279 | void writeStatement(const Statement& s); |
280 | |
281 | void writeBlock(const Block& b); |
282 | |
283 | void writeBreakStatement(const BreakStatement& b); |
284 | |
285 | void writeContinueStatement(const ContinueStatement& c); |
286 | |
287 | void writeIfStatement(const IfStatement& stmt); |
288 | |
289 | void writeForStatement(const ForStatement& f); |
290 | |
291 | void writeWhileStatement(const WhileStatement& w); |
292 | |
293 | void writeDoStatement(const DoStatement& d); |
294 | |
295 | void writeSwitchStatement(const SwitchStatement& s); |
296 | |
297 | void writeReturnStatement(const ReturnStatement& r); |
298 | |
299 | // updates the current set of breaks to branch to the current location |
300 | void setBreakTargets(); |
301 | |
302 | // updates the current set of continues to branch to the current location |
303 | void setContinueTargets(); |
304 | |
305 | void enterLoop() { |
306 | fLoopCount++; |
307 | fMaxLoopCount = std::max(fMaxLoopCount, fLoopCount); |
308 | } |
309 | |
310 | void exitLoop() { |
311 | SkASSERT(fLoopCount > 0); |
312 | fLoopCount--; |
313 | } |
314 | |
315 | void enterCondition() { |
316 | fConditionCount++; |
317 | fMaxConditionCount = std::max(fMaxConditionCount, fConditionCount); |
318 | } |
319 | |
320 | void exitCondition() { |
321 | SkASSERT(fConditionCount > 0); |
322 | fConditionCount--; |
323 | } |
324 | |
325 | const Context& fContext; |
326 | |
327 | ByteCode* fOutput; |
328 | |
329 | const FunctionDefinition* fFunction; |
330 | |
331 | std::vector<uint8_t>* fCode; |
332 | |
333 | std::vector<const Variable*> fLocals; |
334 | |
335 | std::stack<std::vector<DeferredLocation>> fContinueTargets; |
336 | |
337 | std::stack<std::vector<DeferredLocation>> fBreakTargets; |
338 | |
339 | std::vector<const FunctionDefinition*> fFunctions; |
340 | |
341 | int fParameterCount; |
342 | int fStackCount; |
343 | int fMaxStackCount; |
344 | |
345 | int fLoopCount; |
346 | int fMaxLoopCount; |
347 | int fConditionCount; |
348 | int fMaxConditionCount; |
349 | |
350 | const std::unordered_map<String, Intrinsic> fIntrinsics; |
351 | |
352 | friend class DeferredLocation; |
353 | friend class ByteCodeExpressionLValue; |
354 | friend class ByteCodeSwizzleLValue; |
355 | |
356 | typedef CodeGenerator INHERITED; |
357 | }; |
358 | |
359 | } // namespace SkSL |
360 | |
361 | #endif |
362 | |