| /* |
| * Copyright 2019 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef SKSL_BYTECODEGENERATOR |
| #define SKSL_BYTECODEGENERATOR |
| |
| #include <algorithm> |
| #include <stack> |
| #include <unordered_map> |
| |
| #include "src/sksl/SkSLByteCode.h" |
| #include "src/sksl/SkSLCodeGenerator.h" |
| #include "src/sksl/SkSLMemoryLayout.h" |
| #include "src/sksl/ir/SkSLBinaryExpression.h" |
| #include "src/sksl/ir/SkSLBlock.h" |
| #include "src/sksl/ir/SkSLBoolLiteral.h" |
| #include "src/sksl/ir/SkSLBreakStatement.h" |
| #include "src/sksl/ir/SkSLConstructor.h" |
| #include "src/sksl/ir/SkSLContinueStatement.h" |
| #include "src/sksl/ir/SkSLDoStatement.h" |
| #include "src/sksl/ir/SkSLExpressionStatement.h" |
| #include "src/sksl/ir/SkSLExternalFunctionCall.h" |
| #include "src/sksl/ir/SkSLExternalValueReference.h" |
| #include "src/sksl/ir/SkSLFieldAccess.h" |
| #include "src/sksl/ir/SkSLFloatLiteral.h" |
| #include "src/sksl/ir/SkSLForStatement.h" |
| #include "src/sksl/ir/SkSLFunctionCall.h" |
| #include "src/sksl/ir/SkSLFunctionDeclaration.h" |
| #include "src/sksl/ir/SkSLFunctionDefinition.h" |
| #include "src/sksl/ir/SkSLIfStatement.h" |
| #include "src/sksl/ir/SkSLIndexExpression.h" |
| #include "src/sksl/ir/SkSLIntLiteral.h" |
| #include "src/sksl/ir/SkSLInterfaceBlock.h" |
| #include "src/sksl/ir/SkSLNullLiteral.h" |
| #include "src/sksl/ir/SkSLPostfixExpression.h" |
| #include "src/sksl/ir/SkSLPrefixExpression.h" |
| #include "src/sksl/ir/SkSLProgramElement.h" |
| #include "src/sksl/ir/SkSLReturnStatement.h" |
| #include "src/sksl/ir/SkSLStatement.h" |
| #include "src/sksl/ir/SkSLSwitchStatement.h" |
| #include "src/sksl/ir/SkSLSwizzle.h" |
| #include "src/sksl/ir/SkSLTernaryExpression.h" |
| #include "src/sksl/ir/SkSLVarDeclarations.h" |
| #include "src/sksl/ir/SkSLVarDeclarationsStatement.h" |
| #include "src/sksl/ir/SkSLVariableReference.h" |
| #include "src/sksl/ir/SkSLWhileStatement.h" |
| #include "src/sksl/spirv.h" |
| |
| namespace SkSL { |
| |
| class ByteCodeGenerator : public CodeGenerator { |
| public: |
| class LValue { |
| public: |
| LValue(ByteCodeGenerator& generator) |
| : fGenerator(generator) {} |
| |
| virtual ~LValue() {} |
| |
| /** |
| * Stack before call: ... lvalue |
| * Stack after call: ... lvalue load |
| */ |
| virtual void load() = 0; |
| |
| /** |
| * Stack before call: ... lvalue value |
| * Stack after call: ... |
| */ |
| virtual void store(bool discard) = 0; |
| |
| protected: |
| ByteCodeGenerator& fGenerator; |
| }; |
| |
| ByteCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors, |
| ByteCode* output); |
| |
| bool generateCode() override; |
| |
| void write8(uint8_t b); |
| |
| void write16(uint16_t b); |
| |
| void write32(uint32_t b); |
| |
| void write(ByteCodeInstruction inst, int count = kUnusedStackCount); |
| |
| /** |
| * Based on 'type', writes the s (signed), u (unsigned), or f (float) instruction. |
| */ |
| void writeTypedInstruction(const Type& type, ByteCodeInstruction s, ByteCodeInstruction u, |
| ByteCodeInstruction f, int count, bool writeCount = true); |
| |
| static int SlotCount(const Type& type); |
| |
| private: |
| static constexpr int kUnusedStackCount = INT32_MAX; |
| static int StackUsage(ByteCodeInstruction, int count); |
| |
| // reserves 16 bits in the output code, to be filled in later with an address once we determine |
| // it |
| class DeferredLocation { |
| public: |
| DeferredLocation(ByteCodeGenerator* generator) |
| : fGenerator(*generator) |
| , fOffset(generator->fCode->size()) { |
| generator->write16(0); |
| } |
| |
| #ifdef SK_DEBUG |
| ~DeferredLocation() { |
| SkASSERT(fSet); |
| } |
| #endif |
| |
| void set() { |
| int target = fGenerator.fCode->size(); |
| SkASSERT(target <= 65535); |
| (*fGenerator.fCode)[fOffset] = target; |
| (*fGenerator.fCode)[fOffset + 1] = target >> 8; |
| #ifdef SK_DEBUG |
| fSet = true; |
| #endif |
| } |
| |
| private: |
| ByteCodeGenerator& fGenerator; |
| size_t fOffset; |
| #ifdef SK_DEBUG |
| bool fSet = false; |
| #endif |
| }; |
| |
| // Intrinsics which do not simply map to a single opcode |
| enum class SpecialIntrinsic { |
| kDot, |
| }; |
| |
| struct Intrinsic { |
| Intrinsic(ByteCodeInstruction instruction) |
| : fIsSpecial(false) |
| , fValue(instruction) {} |
| |
| Intrinsic(SpecialIntrinsic special) |
| : fIsSpecial(true) |
| , fValue(special) {} |
| |
| bool fIsSpecial; |
| |
| union Value { |
| Value(ByteCodeInstruction instruction) |
| : fInstruction(instruction) {} |
| |
| Value(SpecialIntrinsic special) |
| : fSpecial(special) {} |
| |
| ByteCodeInstruction fInstruction; |
| SpecialIntrinsic fSpecial; |
| } fValue; |
| }; |
| |
| |
| // Similar to Variable::Storage, but locals and parameters are grouped together, and globals |
| // are further subidivided into uniforms and other (writable) globals. |
| enum class Storage { |
| kLocal, // include parameters |
| kGlobal, // non-uniform globals |
| kUniform, // uniform globals |
| }; |
| |
| struct Location { |
| int fSlot; |
| Storage fStorage; |
| |
| // Not really invalid, but a "safe" placeholder to be more explicit at call-sites |
| static Location MakeInvalid() { return { 0, Storage::kLocal }; } |
| |
| Location makeOnStack() { return { -1, fStorage }; } |
| bool isOnStack() const { return fSlot < 0; } |
| |
| Location operator+(int offset) { |
| SkASSERT(fSlot >= 0); |
| return { fSlot + offset, fStorage }; |
| } |
| |
| ByteCodeInstruction selectLoad(ByteCodeInstruction local, |
| ByteCodeInstruction global, |
| ByteCodeInstruction uniform) const { |
| switch (fStorage) { |
| case Storage::kLocal: return local; |
| case Storage::kGlobal: return global; |
| case Storage::kUniform: return uniform; |
| } |
| SkUNREACHABLE; |
| } |
| |
| ByteCodeInstruction selectStore(ByteCodeInstruction local, |
| ByteCodeInstruction global) const { |
| switch (fStorage) { |
| case Storage::kLocal: return local; |
| case Storage::kGlobal: return global; |
| case Storage::kUniform: ABORT("Trying to store to a uniform"); break; |
| } |
| return local; |
| } |
| }; |
| |
| /** |
| * Returns the local slot into which var should be stored, allocating a new slot if it has not |
| * already been assigned one. Compound variables (e.g. vectors) will consume more than one local |
| * slot, with the getLocation return value indicating where the first element should be stored. |
| */ |
| Location getLocation(const Variable& var); |
| |
| /** |
| * As above, but computes the (possibly dynamic) address of an expression involving indexing & |
| * field access. If the address is known, it's returned. If not, -1 is returned, and the |
| * location will be left on the top of the stack. |
| */ |
| Location getLocation(const Expression& expr); |
| |
| void gatherUniforms(const Type& type, const String& name); |
| |
| std::unique_ptr<ByteCodeFunction> writeFunction(const FunctionDefinition& f); |
| |
| void writeVarDeclarations(const VarDeclarations& decl); |
| |
| void writeVariableExpression(const Expression& expr); |
| |
| void writeExpression(const Expression& expr, bool discard = false); |
| |
| /** |
| * Pushes whatever values are required by the lvalue onto the stack, and returns an LValue |
| * permitting loads and stores to it. |
| */ |
| std::unique_ptr<LValue> getLValue(const Expression& expr); |
| |
| void writeIntrinsicCall(const FunctionCall& c); |
| |
| void writeFunctionCall(const FunctionCall& c); |
| |
| void writeConstructor(const Constructor& c); |
| |
| void writeExternalFunctionCall(const ExternalFunctionCall& c); |
| |
| void writeExternalValue(const ExternalValueReference& r); |
| |
| void writeSwizzle(const Swizzle& swizzle); |
| |
| bool writeBinaryExpression(const BinaryExpression& b, bool discard); |
| |
| void writeTernaryExpression(const TernaryExpression& t); |
| |
| void writeNullLiteral(const NullLiteral& n); |
| |
| bool writePrefixExpression(const PrefixExpression& p, bool discard); |
| |
| bool writePostfixExpression(const PostfixExpression& p, bool discard); |
| |
| void writeBoolLiteral(const BoolLiteral& b); |
| |
| void writeIntLiteral(const IntLiteral& i); |
| |
| void writeFloatLiteral(const FloatLiteral& f); |
| |
| void writeStatement(const Statement& s); |
| |
| void writeBlock(const Block& b); |
| |
| void writeBreakStatement(const BreakStatement& b); |
| |
| void writeContinueStatement(const ContinueStatement& c); |
| |
| void writeIfStatement(const IfStatement& stmt); |
| |
| void writeForStatement(const ForStatement& f); |
| |
| void writeWhileStatement(const WhileStatement& w); |
| |
| void writeDoStatement(const DoStatement& d); |
| |
| void writeSwitchStatement(const SwitchStatement& s); |
| |
| void writeReturnStatement(const ReturnStatement& r); |
| |
| // updates the current set of breaks to branch to the current location |
| void setBreakTargets(); |
| |
| // updates the current set of continues to branch to the current location |
| void setContinueTargets(); |
| |
| void enterLoop() { |
| fLoopCount++; |
| fMaxLoopCount = std::max(fMaxLoopCount, fLoopCount); |
| } |
| |
| void exitLoop() { |
| SkASSERT(fLoopCount > 0); |
| fLoopCount--; |
| } |
| |
| void enterCondition() { |
| fConditionCount++; |
| fMaxConditionCount = std::max(fMaxConditionCount, fConditionCount); |
| } |
| |
| void exitCondition() { |
| SkASSERT(fConditionCount > 0); |
| fConditionCount--; |
| } |
| |
| const Context& fContext; |
| |
| ByteCode* fOutput; |
| |
| const FunctionDefinition* fFunction; |
| |
| std::vector<uint8_t>* fCode; |
| |
| std::vector<const Variable*> fLocals; |
| |
| std::stack<std::vector<DeferredLocation>> fContinueTargets; |
| |
| std::stack<std::vector<DeferredLocation>> fBreakTargets; |
| |
| std::vector<const FunctionDefinition*> fFunctions; |
| |
| int fParameterCount; |
| int fStackCount; |
| int fMaxStackCount; |
| |
| int fLoopCount; |
| int fMaxLoopCount; |
| int fConditionCount; |
| int fMaxConditionCount; |
| |
| const std::unordered_map<String, Intrinsic> fIntrinsics; |
| |
| friend class DeferredLocation; |
| friend class ByteCodeExpressionLValue; |
| friend class ByteCodeSwizzleLValue; |
| |
| typedef CodeGenerator INHERITED; |
| }; |
| |
| } |
| |
| #endif |