1 /* 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 */ 7 8 #ifndef SKSL_SPIRVCODEGENERATOR 9 #define SKSL_SPIRVCODEGENERATOR 10 11 #include "include/private/SkSLDefines.h" 12 #include "include/private/SkSLLayout.h" 13 #include "include/private/SkSLModifiers.h" 14 #include "include/private/base/SkTArray.h" 15 #include "src/core/SkTHash.h" 16 #include "src/sksl/SkSLMemoryLayout.h" 17 #include "src/sksl/SkSLStringStream.h" 18 #include "src/sksl/codegen/SkSLCodeGenerator.h" 19 #include "src/sksl/ir/SkSLFunctionDeclaration.h" 20 #include "src/sksl/ir/SkSLFunctionDefinition.h" 21 #include "src/sksl/ir/SkSLInterfaceBlock.h" 22 #include "src/sksl/ir/SkSLSymbolTable.h" 23 #include "src/sksl/ir/SkSLType.h" 24 #include "src/sksl/ir/SkSLVariable.h" 25 #include "src/sksl/spirv.h" 26 27 #include <cstdint> 28 #include <memory> 29 #include <string> 30 #include <string_view> 31 #include <tuple> 32 #include <vector> 33 34 template <typename T> class SkSpan; 35 36 namespace SkSL { 37 38 class AnyConstructor; 39 class BinaryExpression; 40 class Block; 41 class ConstructorCompound; 42 class ConstructorCompoundCast; 43 class ConstructorDiagonalMatrix; 44 class ConstructorMatrixResize; 45 class ConstructorScalarCast; 46 class ConstructorSplat; 47 class Context; 48 class DoStatement; 49 class Expression; 50 class FieldAccess; 51 class ForStatement; 52 class FunctionCall; 53 class IfStatement; 54 class Literal; 55 class Operator; 56 class OutputStream; 57 class Position; 58 class PostfixExpression; 59 class PrefixExpression; 60 class ProgramElement; 61 class ReturnStatement; 62 class Statement; 63 class SwitchStatement; 64 class TernaryExpression; 65 class VarDeclaration; 66 class VariableReference; 67 enum class ProgramKind : int8_t; 68 enum IntrinsicKind : int8_t; 69 struct IndexExpression; 70 struct Program; 71 struct Swizzle; 72 73 /** 74 * Converts a Program into a SPIR-V binary. 75 */ 76 class SPIRVCodeGenerator : public CodeGenerator { 77 public: 78 // We reserve an impossible SpvId as a sentinel. (NA meaning none, n/a, etc.) 79 static constexpr SpvId NA = (SpvId)-1; 80 81 class LValue { 82 public: ~LValue()83 virtual ~LValue() {} 84 85 // returns a pointer to the lvalue, if possible. If the lvalue cannot be directly referenced 86 // by a pointer (e.g. vector swizzles), returns NA. getPointer()87 virtual SpvId getPointer() { return NA; } 88 89 // Returns true if a valid pointer returned by getPointer represents a memory object 90 // (see https://github.com/KhronosGroup/SPIRV-Tools/issues/2892). Has no meaning if 91 // getPointer() returns NA. isMemoryObjectPointer()92 virtual bool isMemoryObjectPointer() const { return true; } 93 94 // Applies a swizzle to the components of the LValue, if possible. This is used to create 95 // LValues that are swizzes-of-swizzles. Non-swizzle LValues can just return false. applySwizzle(const ComponentArray & components,const Type & newType)96 virtual bool applySwizzle(const ComponentArray& components, const Type& newType) { 97 return false; 98 } 99 100 virtual SpvId load(OutputStream& out) = 0; 101 102 virtual void store(SpvId value, OutputStream& out) = 0; 103 }; 104 SPIRVCodeGenerator(const Context * context,const Program * program,OutputStream * out)105 SPIRVCodeGenerator(const Context* context, const Program* program, OutputStream* out) 106 : INHERITED(context, program, out) 107 , fDefaultLayout(MemoryLayout::Standard::k140) 108 , fCapabilities(0) 109 , fIdCount(1) 110 , fCurrentBlock(0) 111 , fSynthetics(/*builtin=*/true) {} 112 113 bool generateCode() override; 114 115 private: 116 enum IntrinsicOpcodeKind { 117 kGLSL_STD_450_IntrinsicOpcodeKind, 118 kSPIRV_IntrinsicOpcodeKind, 119 kSpecial_IntrinsicOpcodeKind, 120 kInvalid_IntrinsicOpcodeKind, 121 }; 122 123 enum SpecialIntrinsic { 124 kAtan_SpecialIntrinsic, 125 kClamp_SpecialIntrinsic, 126 kMatrixCompMult_SpecialIntrinsic, 127 kMax_SpecialIntrinsic, 128 kMin_SpecialIntrinsic, 129 kMix_SpecialIntrinsic, 130 kMod_SpecialIntrinsic, 131 kDFdy_SpecialIntrinsic, 132 kSaturate_SpecialIntrinsic, 133 kSampledImage_SpecialIntrinsic, 134 kSmoothStep_SpecialIntrinsic, 135 kStep_SpecialIntrinsic, 136 kSubpassLoad_SpecialIntrinsic, 137 kTexture_SpecialIntrinsic, 138 kTextureGrad_SpecialIntrinsic, 139 kTextureLod_SpecialIntrinsic, 140 }; 141 142 enum class Precision { 143 kDefault, 144 kRelaxed, 145 }; 146 147 struct TempVar { 148 SpvId spvId; 149 const Type* type; 150 std::unique_ptr<SPIRVCodeGenerator::LValue> lvalue; 151 }; 152 153 /** 154 * Pass in the type to automatically add a RelaxedPrecision decoration for the id when 155 * appropriate, or null to never add one. 156 */ 157 SpvId nextId(const Type* type); 158 159 SpvId nextId(Precision precision); 160 161 SpvId getType(const Type& type); 162 163 SpvId getType(const Type& type, const MemoryLayout& layout); 164 165 SpvId getFunctionType(const FunctionDeclaration& function); 166 167 SpvId getFunctionParameterType(const Type& parameterType); 168 169 SpvId getPointerType(const Type& type, SpvStorageClass_ storageClass); 170 171 SpvId getPointerType(const Type& type, const MemoryLayout& layout, 172 SpvStorageClass_ storageClass); 173 174 SkTArray<SpvId> getAccessChain(const Expression& expr, OutputStream& out); 175 176 void writeLayout(const Layout& layout, SpvId target, Position pos); 177 178 void writeFieldLayout(const Layout& layout, SpvId target, int member); 179 180 SpvId writeStruct(const Type& type, const MemoryLayout& memoryLayout); 181 182 void writeProgramElement(const ProgramElement& pe, OutputStream& out); 183 184 SpvId writeInterfaceBlock(const InterfaceBlock& intf, bool appendRTFlip = true); 185 186 SpvId writeFunctionStart(const FunctionDeclaration& f, OutputStream& out); 187 188 SpvId writeFunctionDeclaration(const FunctionDeclaration& f, OutputStream& out); 189 190 SpvId writeFunction(const FunctionDefinition& f, OutputStream& out); 191 192 bool writeGlobalVarDeclaration(ProgramKind kind, const VarDeclaration& v); 193 194 SpvId writeGlobalVar(ProgramKind kind, SpvStorageClass_, const Variable& v); 195 196 void writeVarDeclaration(const VarDeclaration& var, OutputStream& out); 197 198 SpvId writeVariableReference(const VariableReference& ref, OutputStream& out); 199 200 int findUniformFieldIndex(const Variable& var) const; 201 202 std::unique_ptr<LValue> getLValue(const Expression& value, OutputStream& out); 203 204 SpvId writeExpression(const Expression& expr, OutputStream& out); 205 206 SpvId writeIntrinsicCall(const FunctionCall& c, OutputStream& out); 207 208 SpvId writeFunctionCallArgument(const FunctionCall& call, 209 int argIndex, 210 std::vector<TempVar>* tempVars, 211 OutputStream& out, 212 SpvId* outSynthesizedSamplerId = nullptr); 213 214 void copyBackTempVars(const std::vector<TempVar>& tempVars, OutputStream& out); 215 216 SpvId writeFunctionCall(const FunctionCall& c, OutputStream& out); 217 218 219 void writeGLSLExtendedInstruction(const Type& type, SpvId id, SpvId floatInst, 220 SpvId signedInst, SpvId unsignedInst, 221 const SkTArray<SpvId>& args, OutputStream& out); 222 223 /** 224 * Promotes an expression to a vector. If the expression is already a vector with vectorSize 225 * columns, returns it unmodified. If the expression is a scalar, either promotes it to a 226 * vector (if vectorSize > 1) or returns it unmodified (if vectorSize == 1). Asserts if the 227 * expression is already a vector and it does not have vectorSize columns. 228 */ 229 SpvId vectorize(const Expression& expr, int vectorSize, OutputStream& out); 230 231 /** 232 * Given a list of potentially mixed scalars and vectors, promotes the scalars to match the 233 * size of the vectors and returns the ids of the written expressions. e.g. given (float, vec2), 234 * returns (vec2(float), vec2). It is an error to use mismatched vector sizes, e.g. (float, 235 * vec2, vec3). 236 */ 237 SkTArray<SpvId> vectorize(const ExpressionArray& args, OutputStream& out); 238 239 SpvId writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, OutputStream& out); 240 241 SpvId writeScalarToMatrixSplat(const Type& matrixType, SpvId scalarId, OutputStream& out); 242 243 SpvId writeFloatConstructor(const AnyConstructor& c, OutputStream& out); 244 245 SpvId castScalarToFloat(SpvId inputId, const Type& inputType, const Type& outputType, 246 OutputStream& out); 247 248 SpvId writeIntConstructor(const AnyConstructor& c, OutputStream& out); 249 250 SpvId castScalarToSignedInt(SpvId inputId, const Type& inputType, const Type& outputType, 251 OutputStream& out); 252 253 SpvId writeUIntConstructor(const AnyConstructor& c, OutputStream& out); 254 255 SpvId castScalarToUnsignedInt(SpvId inputId, const Type& inputType, const Type& outputType, 256 OutputStream& out); 257 258 SpvId writeBooleanConstructor(const AnyConstructor& c, OutputStream& out); 259 260 SpvId castScalarToBoolean(SpvId inputId, const Type& inputType, const Type& outputType, 261 OutputStream& out); 262 263 SpvId castScalarToType(SpvId inputExprId, const Type& inputType, const Type& outputType, 264 OutputStream& out); 265 266 /** 267 * Writes a potentially-different-sized copy of a matrix. Entries which do not exist in the 268 * source matrix are filled with zero; entries which do not exist in the destination matrix are 269 * ignored. 270 */ 271 SpvId writeMatrixCopy(SpvId src, const Type& srcType, const Type& dstType, OutputStream& out); 272 273 void addColumnEntry(const Type& columnType, SkTArray<SpvId>* currentColumn, 274 SkTArray<SpvId>* columnIds, int rows, SpvId entry, OutputStream& out); 275 276 SpvId writeConstructorCompound(const ConstructorCompound& c, OutputStream& out); 277 278 SpvId writeMatrixConstructor(const ConstructorCompound& c, OutputStream& out); 279 280 SpvId writeVectorConstructor(const ConstructorCompound& c, OutputStream& out); 281 282 SpvId writeCompositeConstructor(const AnyConstructor& c, OutputStream& out); 283 284 SpvId writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c, OutputStream& out); 285 286 SpvId writeConstructorMatrixResize(const ConstructorMatrixResize& c, OutputStream& out); 287 288 SpvId writeConstructorScalarCast(const ConstructorScalarCast& c, OutputStream& out); 289 290 SpvId writeConstructorSplat(const ConstructorSplat& c, OutputStream& out); 291 292 SpvId writeConstructorCompoundCast(const ConstructorCompoundCast& c, OutputStream& out); 293 294 SpvId writeFieldAccess(const FieldAccess& f, OutputStream& out); 295 296 SpvId writeSwizzle(const Swizzle& swizzle, OutputStream& out); 297 298 /** 299 * Folds the potentially-vector result of a logical operation down to a single bool. If 300 * operandType is a vector type, assumes that the intermediate result in id is a bvec of the 301 * same dimensions, and applys all() to it to fold it down to a single bool value. Otherwise, 302 * returns the original id value. 303 */ 304 SpvId foldToBool(SpvId id, const Type& operandType, SpvOp op, OutputStream& out); 305 306 SpvId writeMatrixComparison(const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ floatOperator, 307 SpvOp_ intOperator, SpvOp_ vectorMergeOperator, 308 SpvOp_ mergeOperator, OutputStream& out); 309 310 SpvId writeStructComparison(const Type& structType, SpvId lhs, Operator op, SpvId rhs, 311 OutputStream& out); 312 313 SpvId writeArrayComparison(const Type& structType, SpvId lhs, Operator op, SpvId rhs, 314 OutputStream& out); 315 316 // Used by writeStructComparison and writeArrayComparison to logically combine field-by-field 317 // comparisons into an overall comparison result. 318 // - `a.x == b.x` merged with `a.y == b.y` generates `(a.x == b.x) && (a.y == b.y)` 319 // - `a.x != b.x` merged with `a.y != b.y` generates `(a.x != b.x) || (a.y != b.y)` 320 SpvId mergeComparisons(SpvId comparison, SpvId allComparisons, Operator op, OutputStream& out); 321 322 SpvId writeComponentwiseMatrixUnary(const Type& operandType, 323 SpvId operand, 324 SpvOp_ op, 325 OutputStream& out); 326 327 SpvId writeComponentwiseMatrixBinary(const Type& operandType, SpvId lhs, SpvId rhs, 328 SpvOp_ op, OutputStream& out); 329 330 SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs, 331 SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt, 332 SpvOp_ ifBool, OutputStream& out); 333 334 SpvId writeReciprocal(const Type& type, SpvId value, OutputStream& out); 335 336 SpvId writeBinaryExpression(const Type& leftType, SpvId lhs, Operator op, 337 const Type& rightType, SpvId rhs, const Type& resultType, 338 OutputStream& out); 339 340 SpvId writeBinaryExpression(const BinaryExpression& b, OutputStream& out); 341 342 SpvId writeTernaryExpression(const TernaryExpression& t, OutputStream& out); 343 344 SpvId writeIndexExpression(const IndexExpression& expr, OutputStream& out); 345 346 SpvId writeLogicalAnd(const Expression& left, const Expression& right, OutputStream& out); 347 348 SpvId writeLogicalOr(const Expression& left, const Expression& right, OutputStream& out); 349 350 SpvId writePrefixExpression(const PrefixExpression& p, OutputStream& out); 351 352 SpvId writePostfixExpression(const PostfixExpression& p, OutputStream& out); 353 354 SpvId writeLiteral(const Literal& f); 355 356 SpvId writeLiteral(double value, const Type& type); 357 358 void writeStatement(const Statement& s, OutputStream& out); 359 360 void writeBlock(const Block& b, OutputStream& out); 361 362 void writeIfStatement(const IfStatement& stmt, OutputStream& out); 363 364 void writeForStatement(const ForStatement& f, OutputStream& out); 365 366 void writeDoStatement(const DoStatement& d, OutputStream& out); 367 368 void writeSwitchStatement(const SwitchStatement& s, OutputStream& out); 369 370 void writeReturnStatement(const ReturnStatement& r, OutputStream& out); 371 372 void writeCapabilities(OutputStream& out); 373 374 void writeInstructions(const Program& program, OutputStream& out); 375 376 void writeOpCode(SpvOp_ opCode, int length, OutputStream& out); 377 378 void writeWord(int32_t word, OutputStream& out); 379 380 void writeString(std::string_view s, OutputStream& out); 381 382 void writeInstruction(SpvOp_ opCode, OutputStream& out); 383 384 void writeInstruction(SpvOp_ opCode, std::string_view string, OutputStream& out); 385 386 void writeInstruction(SpvOp_ opCode, int32_t word1, OutputStream& out); 387 388 void writeInstruction(SpvOp_ opCode, int32_t word1, std::string_view string, 389 OutputStream& out); 390 391 void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, std::string_view string, 392 OutputStream& out); 393 394 void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, OutputStream& out); 395 396 void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, 397 OutputStream& out); 398 399 void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, 400 OutputStream& out); 401 402 void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, 403 int32_t word5, OutputStream& out); 404 405 void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, 406 int32_t word5, int32_t word6, OutputStream& out); 407 408 void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, 409 int32_t word5, int32_t word6, int32_t word7, OutputStream& out); 410 411 void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, 412 int32_t word5, int32_t word6, int32_t word7, int32_t word8, 413 OutputStream& out); 414 415 // This form of writeInstruction can deduplicate redundant ops. 416 struct Word; 417 // 8 Words is enough for nearly all instructions (except variable-length instructions like 418 // OpAccessChain or OpConstantComposite). 419 using Words = SkSTArray<8, Word, true>; 420 SpvId writeInstruction(SpvOp_ opCode, const SkTArray<Word, true>& words, OutputStream& out); 421 422 struct Instruction { 423 SpvId fOp; 424 int32_t fResultKind; 425 SkSTArray<8, int32_t> fWords; 426 427 bool operator==(const Instruction& that) const; 428 struct Hash; 429 }; 430 431 static Instruction BuildInstructionKey(SpvOp_ opCode, const SkTArray<Word, true>& words); 432 433 // The writeOpXxxxx calls will simplify and deduplicate ops where possible. 434 SpvId writeOpConstantTrue(const Type& type); 435 SpvId writeOpConstantFalse(const Type& type); 436 SpvId writeOpConstant(const Type& type, int32_t valueBits); 437 SpvId writeOpConstantComposite(const Type& type, const SkTArray<SpvId>& values); 438 SpvId writeOpCompositeConstruct(const Type& type, const SkTArray<SpvId>&, OutputStream& out); 439 SpvId writeOpCompositeExtract(const Type& type, SpvId base, int component, OutputStream& out); 440 SpvId writeOpCompositeExtract(const Type& type, SpvId base, int componentA, int componentB, 441 OutputStream& out); 442 SpvId writeOpLoad(SpvId type, Precision precision, SpvId pointer, OutputStream& out); 443 void writeOpStore(SpvStorageClass_ storageClass, SpvId pointer, SpvId value, OutputStream& out); 444 445 // Converts the provided SpvId(s) into an array of scalar OpConstants, if it can be done. 446 bool toConstants(SpvId value, SkTArray<SpvId>* constants); 447 bool toConstants(SkSpan<const SpvId> values, SkTArray<SpvId>* constants); 448 449 // Extracts the requested component SpvId from a composite instruction, if it can be done. 450 Instruction* resultTypeForInstruction(const Instruction& instr); 451 int numComponentsForVecInstruction(const Instruction& instr); 452 SpvId toComponent(SpvId id, int component); 453 454 struct ConditionalOpCounts { 455 int numReachableOps; 456 int numStoreOps; 457 }; 458 ConditionalOpCounts getConditionalOpCounts(); 459 void pruneConditionalOps(ConditionalOpCounts ops); 460 461 enum StraightLineLabelType { 462 // Use "BranchlessBlock" for blocks which are never explicitly branched-to at all. This 463 // happens at the start of a function, or when we find unreachable code. 464 kBranchlessBlock, 465 466 // Use "BranchIsOnPreviousLine" when writing a label that comes immediately after its 467 // associated branch. Example usage: 468 // - SPIR-V does not implicitly fall through from one block to the next, so you may need to 469 // use an OpBranch to explicitly jump to the next block, even when they are adjacent in 470 // the code. 471 // - The block immediately following an OpBranchConditional or OpSwitch. 472 kBranchIsOnPreviousLine, 473 }; 474 475 enum BranchingLabelType { 476 // Use "BranchIsAbove" for labels which are referenced by OpBranch or OpBranchConditional 477 // ops that are above the label in the code--i.e., the branch skips forward in the code. 478 kBranchIsAbove, 479 480 // Use "BranchIsBelow" for labels which are referenced by OpBranch or OpBranchConditional 481 // ops below the label in the code--i.e., the branch jumps backward in the code. 482 kBranchIsBelow, 483 484 // Use "BranchesOnBothSides" for labels which have branches coming from both directions. 485 kBranchesOnBothSides, 486 }; 487 void writeLabel(SpvId label, StraightLineLabelType type, OutputStream& out); 488 void writeLabel(SpvId label, BranchingLabelType type, ConditionalOpCounts ops, 489 OutputStream& out); 490 491 bool isDead(const Variable& var) const; 492 493 MemoryLayout memoryLayoutForStorageClass(SpvStorageClass_ storageClass); 494 MemoryLayout memoryLayoutForVariable(const Variable&) const; 495 496 struct EntrypointAdapter { 497 std::unique_ptr<FunctionDefinition> entrypointDef; 498 std::unique_ptr<FunctionDeclaration> entrypointDecl; 499 Layout fLayout; 500 Modifiers fModifiers; 501 }; 502 503 EntrypointAdapter writeEntrypointAdapter(const FunctionDeclaration& main); 504 505 struct UniformBuffer { 506 std::unique_ptr<InterfaceBlock> fInterfaceBlock; 507 std::unique_ptr<Variable> fInnerVariable; 508 std::unique_ptr<Type> fStruct; 509 }; 510 511 void writeUniformBuffer(std::shared_ptr<SymbolTable> topLevelSymbolTable); 512 513 void addRTFlipUniform(Position pos); 514 515 std::tuple<const Variable*, const Variable*> synthesizeTextureAndSampler( 516 const Variable& combinedSampler); 517 518 const MemoryLayout fDefaultLayout; 519 520 uint64_t fCapabilities; 521 SpvId fIdCount; 522 SpvId fGLSLExtendedInstructions; 523 struct Intrinsic { 524 IntrinsicOpcodeKind opKind; 525 int32_t floatOp; 526 int32_t signedOp; 527 int32_t unsignedOp; 528 int32_t boolOp; 529 }; 530 Intrinsic getIntrinsic(IntrinsicKind) const; 531 SkTHashMap<const FunctionDeclaration*, SpvId> fFunctionMap; 532 SkTHashMap<const Variable*, SpvId> fVariableMap; 533 SkTHashMap<const Type*, SpvId> fStructMap; 534 StringStream fGlobalInitializersBuffer; 535 StringStream fConstantBuffer; 536 StringStream fVariableBuffer; 537 StringStream fNameBuffer; 538 StringStream fDecorationBuffer; 539 540 // Mapping from combined sampler declarations to synthesized texture/sampler variables. 541 // This is only used if the SPIRVDawnCompatMode setting is enabled. 542 // TODO(skia:14023): Remove when WGSL codegen is complete 543 struct SynthesizedTextureSamplerPair { 544 // The names of the synthesized variables. The Variable objects themselves store string 545 // views referencing these strings. It is important for the std::string instances to have a 546 // fixed memory location after the string views get created, which is why 547 // `fSynthesizedSamplerMap` stores unique_ptr instead of values. 548 std::string fTextureName; 549 std::string fSamplerName; 550 std::unique_ptr<Variable> fTexture; 551 std::unique_ptr<Variable> fSampler; 552 }; 553 SkTHashMap<const Variable*, std::unique_ptr<SynthesizedTextureSamplerPair>> 554 fSynthesizedSamplerMap; 555 556 // These caches map SpvIds to Instructions, and vice-versa. This enables us to deduplicate code 557 // (by detecting an Instruction we've already issued and reusing the SpvId), and to introspect 558 // and simplify code we've already emitted (by taking a SpvId from an Instruction and following 559 // it back to its source). 560 SkTHashMap<Instruction, SpvId, Instruction::Hash> fOpCache; // maps instruction -> SpvId 561 SkTHashMap<SpvId, Instruction> fSpvIdCache; // maps SpvId -> instruction 562 SkTHashMap<SpvId, SpvId> fStoreCache; // maps ptr SpvId -> value SpvId 563 564 // "Reachable" ops are instructions which can safely be accessed from the current block. 565 // For instance, if our SPIR-V contains `%3 = OpFAdd %1 %2`, we would be able to access and 566 // reuse that computation on following lines. However, if that Add operation occurred inside an 567 // `if` block, then its SpvId becomes inaccessible once we complete the if statement (since 568 // depending on the if condition, we may or may not have actually done that computation). The 569 // same logic applies to other control-flow blocks as well. Once an instruction becomes 570 // unreachable, we remove it from both op-caches. 571 SkTArray<SpvId> fReachableOps; 572 573 // The "store-ops" list contains a running list of all the pointers in the store cache. If a 574 // store occurs inside of a conditional block, once that block exits, we no longer know what is 575 // stored in that particular SpvId. At that point, we must remove any associated entry from the 576 // store cache. 577 SkTArray<SpvId> fStoreOps; 578 579 // label of the current block, or 0 if we are not in a block 580 SpvId fCurrentBlock; 581 SkTArray<SpvId> fBreakTarget; 582 SkTArray<SpvId> fContinueTarget; 583 bool fWroteRTFlip = false; 584 // holds variables synthesized during output, for lifetime purposes 585 SymbolTable fSynthetics; 586 // Holds a list of uniforms that were declared as globals at the top-level instead of in an 587 // interface block. 588 UniformBuffer fUniformBuffer; 589 std::vector<const VarDeclaration*> fTopLevelUniforms; 590 SkTHashMap<const Variable*, int> fTopLevelUniformMap; // <var, UniformBuffer field index> 591 SkTHashSet<const Variable*> fSPIRVBonusVariables; 592 SpvId fUniformBufferId = NA; 593 594 friend class PointerLValue; 595 friend class SwizzleLValue; 596 597 using INHERITED = CodeGenerator; 598 }; 599 600 } // namespace SkSL 601 602 #endif 603