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, bool writeCount = true); |
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 | kDot, |
142 | }; |
143 | |
144 | struct Intrinsic { |
145 | Intrinsic(ByteCodeInstruction instruction) |
146 | : fIsSpecial(false) |
147 | , fValue(instruction) {} |
148 | |
149 | Intrinsic(SpecialIntrinsic special) |
150 | : fIsSpecial(true) |
151 | , fValue(special) {} |
152 | |
153 | bool fIsSpecial; |
154 | |
155 | union Value { |
156 | Value(ByteCodeInstruction instruction) |
157 | : fInstruction(instruction) {} |
158 | |
159 | Value(SpecialIntrinsic special) |
160 | : fSpecial(special) {} |
161 | |
162 | ByteCodeInstruction fInstruction; |
163 | SpecialIntrinsic fSpecial; |
164 | } fValue; |
165 | }; |
166 | |
167 | |
168 | // Similar to Variable::Storage, but locals and parameters are grouped together, and globals |
169 | // are further subidivided into uniforms and other (writable) globals. |
170 | enum class Storage { |
171 | kLocal, // include parameters |
172 | kGlobal, // non-uniform globals |
173 | kUniform, // uniform globals |
174 | }; |
175 | |
176 | struct Location { |
177 | int fSlot; |
178 | Storage fStorage; |
179 | |
180 | // Not really invalid, but a "safe" placeholder to be more explicit at call-sites |
181 | static Location MakeInvalid() { return { 0, Storage::kLocal }; } |
182 | |
183 | Location makeOnStack() { return { -1, fStorage }; } |
184 | bool isOnStack() const { return fSlot < 0; } |
185 | |
186 | Location operator+(int offset) { |
187 | SkASSERT(fSlot >= 0); |
188 | return { fSlot + offset, fStorage }; |
189 | } |
190 | |
191 | ByteCodeInstruction selectLoad(ByteCodeInstruction local, |
192 | ByteCodeInstruction global, |
193 | ByteCodeInstruction uniform) const { |
194 | switch (fStorage) { |
195 | case Storage::kLocal: return local; |
196 | case Storage::kGlobal: return global; |
197 | case Storage::kUniform: return uniform; |
198 | } |
199 | SkUNREACHABLE; |
200 | } |
201 | |
202 | ByteCodeInstruction selectStore(ByteCodeInstruction local, |
203 | ByteCodeInstruction global) const { |
204 | switch (fStorage) { |
205 | case Storage::kLocal: return local; |
206 | case Storage::kGlobal: return global; |
207 | case Storage::kUniform: ABORT("Trying to store to a uniform" ); break; |
208 | } |
209 | return local; |
210 | } |
211 | }; |
212 | |
213 | /** |
214 | * Returns the local slot into which var should be stored, allocating a new slot if it has not |
215 | * already been assigned one. Compound variables (e.g. vectors) will consume more than one local |
216 | * slot, with the getLocation return value indicating where the first element should be stored. |
217 | */ |
218 | Location getLocation(const Variable& var); |
219 | |
220 | /** |
221 | * As above, but computes the (possibly dynamic) address of an expression involving indexing & |
222 | * field access. If the address is known, it's returned. If not, -1 is returned, and the |
223 | * location will be left on the top of the stack. |
224 | */ |
225 | Location getLocation(const Expression& expr); |
226 | |
227 | void gatherUniforms(const Type& type, const String& name); |
228 | |
229 | std::unique_ptr<ByteCodeFunction> writeFunction(const FunctionDefinition& f); |
230 | |
231 | void writeVarDeclarations(const VarDeclarations& decl); |
232 | |
233 | void writeVariableExpression(const Expression& expr); |
234 | |
235 | void writeExpression(const Expression& expr, bool discard = false); |
236 | |
237 | /** |
238 | * Pushes whatever values are required by the lvalue onto the stack, and returns an LValue |
239 | * permitting loads and stores to it. |
240 | */ |
241 | std::unique_ptr<LValue> getLValue(const Expression& expr); |
242 | |
243 | void writeIntrinsicCall(const FunctionCall& c); |
244 | |
245 | void writeFunctionCall(const FunctionCall& c); |
246 | |
247 | void writeConstructor(const Constructor& c); |
248 | |
249 | void writeExternalFunctionCall(const ExternalFunctionCall& c); |
250 | |
251 | void writeExternalValue(const ExternalValueReference& r); |
252 | |
253 | void writeSwizzle(const Swizzle& swizzle); |
254 | |
255 | bool writeBinaryExpression(const BinaryExpression& b, bool discard); |
256 | |
257 | void writeTernaryExpression(const TernaryExpression& t); |
258 | |
259 | void writeNullLiteral(const NullLiteral& n); |
260 | |
261 | bool writePrefixExpression(const PrefixExpression& p, bool discard); |
262 | |
263 | bool writePostfixExpression(const PostfixExpression& p, bool discard); |
264 | |
265 | void writeBoolLiteral(const BoolLiteral& b); |
266 | |
267 | void writeIntLiteral(const IntLiteral& i); |
268 | |
269 | void writeFloatLiteral(const FloatLiteral& f); |
270 | |
271 | void writeStatement(const Statement& s); |
272 | |
273 | void writeBlock(const Block& b); |
274 | |
275 | void writeBreakStatement(const BreakStatement& b); |
276 | |
277 | void writeContinueStatement(const ContinueStatement& c); |
278 | |
279 | void writeIfStatement(const IfStatement& stmt); |
280 | |
281 | void writeForStatement(const ForStatement& f); |
282 | |
283 | void writeWhileStatement(const WhileStatement& w); |
284 | |
285 | void writeDoStatement(const DoStatement& d); |
286 | |
287 | void writeSwitchStatement(const SwitchStatement& s); |
288 | |
289 | void writeReturnStatement(const ReturnStatement& r); |
290 | |
291 | // updates the current set of breaks to branch to the current location |
292 | void setBreakTargets(); |
293 | |
294 | // updates the current set of continues to branch to the current location |
295 | void setContinueTargets(); |
296 | |
297 | void enterLoop() { |
298 | fLoopCount++; |
299 | fMaxLoopCount = std::max(fMaxLoopCount, fLoopCount); |
300 | } |
301 | |
302 | void exitLoop() { |
303 | SkASSERT(fLoopCount > 0); |
304 | fLoopCount--; |
305 | } |
306 | |
307 | void enterCondition() { |
308 | fConditionCount++; |
309 | fMaxConditionCount = std::max(fMaxConditionCount, fConditionCount); |
310 | } |
311 | |
312 | void exitCondition() { |
313 | SkASSERT(fConditionCount > 0); |
314 | fConditionCount--; |
315 | } |
316 | |
317 | const Context& fContext; |
318 | |
319 | ByteCode* fOutput; |
320 | |
321 | const FunctionDefinition* fFunction; |
322 | |
323 | std::vector<uint8_t>* fCode; |
324 | |
325 | std::vector<const Variable*> fLocals; |
326 | |
327 | std::stack<std::vector<DeferredLocation>> fContinueTargets; |
328 | |
329 | std::stack<std::vector<DeferredLocation>> fBreakTargets; |
330 | |
331 | std::vector<const FunctionDefinition*> fFunctions; |
332 | |
333 | int fParameterCount; |
334 | int fStackCount; |
335 | int fMaxStackCount; |
336 | |
337 | int fLoopCount; |
338 | int fMaxLoopCount; |
339 | int fConditionCount; |
340 | int fMaxConditionCount; |
341 | |
342 | const std::unordered_map<String, Intrinsic> fIntrinsics; |
343 | |
344 | friend class DeferredLocation; |
345 | friend class ByteCodeExpressionLValue; |
346 | friend class ByteCodeSwizzleLValue; |
347 | |
348 | typedef CodeGenerator INHERITED; |
349 | }; |
350 | |
351 | } |
352 | |
353 | #endif |
354 | |