• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 #include "src/sksl/codegen/SkSLVMCodeGenerator.h"
9 
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorType.h"
13 #include "include/core/SkPoint.h"
14 #include "include/core/SkSpan.h"
15 #include "include/core/SkTypes.h"
16 #include "include/private/SkSLDefines.h"
17 #include "include/private/SkSLIRNode.h"
18 #include "include/private/SkSLLayout.h"
19 #include "include/private/SkSLModifiers.h"
20 #include "include/private/SkSLProgramElement.h"
21 #include "include/private/SkSLStatement.h"
22 #include "include/private/base/SkFloatingPoint.h"
23 #include "include/private/base/SkTArray.h"
24 #include "include/private/base/SkTPin.h"
25 #include "include/sksl/SkSLOperator.h"
26 #include "include/sksl/SkSLPosition.h"
27 #include "src/base/SkStringView.h"
28 #include "src/core/SkTHash.h"
29 #include "src/sksl/SkSLBuiltinTypes.h"
30 #include "src/sksl/SkSLCompiler.h"
31 #include "src/sksl/SkSLIntrinsicList.h"
32 #include "src/sksl/SkSLProgramSettings.h"
33 #include "src/sksl/ir/SkSLBinaryExpression.h"
34 #include "src/sksl/ir/SkSLBlock.h"
35 #include "src/sksl/ir/SkSLChildCall.h"
36 #include "src/sksl/ir/SkSLConstructor.h"
37 #include "src/sksl/ir/SkSLConstructorArrayCast.h"
38 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
39 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
40 #include "src/sksl/ir/SkSLConstructorSplat.h"
41 #include "src/sksl/ir/SkSLExpression.h"
42 #include "src/sksl/ir/SkSLExpressionStatement.h"
43 #include "src/sksl/ir/SkSLFieldAccess.h"
44 #include "src/sksl/ir/SkSLForStatement.h"
45 #include "src/sksl/ir/SkSLFunctionCall.h"
46 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
47 #include "src/sksl/ir/SkSLFunctionDefinition.h"
48 #include "src/sksl/ir/SkSLIfStatement.h"
49 #include "src/sksl/ir/SkSLIndexExpression.h"
50 #include "src/sksl/ir/SkSLLiteral.h"
51 #include "src/sksl/ir/SkSLPostfixExpression.h"
52 #include "src/sksl/ir/SkSLPrefixExpression.h"
53 #include "src/sksl/ir/SkSLProgram.h"
54 #include "src/sksl/ir/SkSLReturnStatement.h"
55 #include "src/sksl/ir/SkSLSwitchCase.h"
56 #include "src/sksl/ir/SkSLSwitchStatement.h"
57 #include "src/sksl/ir/SkSLSwizzle.h"
58 #include "src/sksl/ir/SkSLTernaryExpression.h"
59 #include "src/sksl/ir/SkSLType.h"
60 #include "src/sksl/ir/SkSLVarDeclarations.h"
61 #include "src/sksl/ir/SkSLVariable.h"
62 #include "src/sksl/ir/SkSLVariableReference.h"
63 #include "src/sksl/tracing/SkSLDebugInfo.h"
64 #include "src/sksl/tracing/SkVMDebugTrace.h"
65 
66 #include <algorithm>
67 #include <cstdint>
68 #include <functional>
69 #include <iterator>
70 #include <memory>
71 #include <string>
72 #include <string_view>
73 #include <utility>
74 #include <vector>
75 
76 namespace {
77     // sksl allows the optimizations of fast_mul(), so we want to use that most of the time.
78     // This little sneaky snippet of code lets us use ** as a fast multiply infix operator.
79     struct FastF32 { skvm::F32 val; };
operator *(skvm::F32 y)80     static FastF32 operator*(skvm::F32 y) { return {y}; }
operator *(skvm::F32 x,FastF32 y)81     static skvm::F32 operator*(skvm::F32 x, FastF32 y) { return fast_mul(x, y.val); }
operator *(float x,FastF32 y)82     static skvm::F32 operator*(float     x, FastF32 y) { return fast_mul(x, y.val); }
83 
84     class SkSLTracer : public skvm::TraceHook {
85     public:
Make(SkSL::SkVMDebugTrace * trace)86         static std::unique_ptr<SkSLTracer> Make(SkSL::SkVMDebugTrace* trace) {
87             auto hook = std::make_unique<SkSLTracer>();
88             hook->fTrace = trace;
89             return hook;
90         }
91 
line(int lineNum)92         void line(int lineNum) override {
93             fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kLine,
94                                           /*data=*/{lineNum, 0}});
95         }
var(int slot,int32_t val)96         void var(int slot, int32_t val) override {
97             fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kVar,
98                                           /*data=*/{slot, val}});
99         }
enter(int fnIdx)100         void enter(int fnIdx) override {
101             fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kEnter,
102                                           /*data=*/{fnIdx, 0}});
103         }
exit(int fnIdx)104         void exit(int fnIdx) override {
105             fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kExit,
106                                           /*data=*/{fnIdx, 0}});
107         }
scope(int delta)108         void scope(int delta) override {
109             fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kScope,
110                                           /*data=*/{delta, 0}});
111         }
112 
113     private:
114         SkSL::SkVMDebugTrace* fTrace;
115     };
116 }  // namespace
117 
118 namespace SkSL {
119 
120 namespace {
121 
122 // Holds scalars, vectors, or matrices
123 struct Value {
124     Value() = default;
ValueSkSL::__anon93c122a70211::Value125     explicit Value(size_t slots) {
126         fVals.resize(slots);
127     }
ValueSkSL::__anon93c122a70211::Value128     Value(skvm::F32 x) : fVals({ x.id }) {}
ValueSkSL::__anon93c122a70211::Value129     Value(skvm::I32 x) : fVals({ x.id }) {}
130 
operator boolSkSL::__anon93c122a70211::Value131     explicit operator bool() const { return !fVals.empty(); }
132 
slotsSkSL::__anon93c122a70211::Value133     size_t slots() const { return fVals.size(); }
134 
135     struct ValRef {
ValRefSkSL::__anon93c122a70211::Value::ValRef136         ValRef(skvm::Val& val) : fVal(val) {}
137 
operator =SkSL::__anon93c122a70211::Value::ValRef138         ValRef& operator=(ValRef    v) { fVal = v.fVal; return *this; }
operator =SkSL::__anon93c122a70211::Value::ValRef139         ValRef& operator=(skvm::Val v) { fVal = v;      return *this; }
operator =SkSL::__anon93c122a70211::Value::ValRef140         ValRef& operator=(skvm::F32 v) { fVal = v.id;   return *this; }
operator =SkSL::__anon93c122a70211::Value::ValRef141         ValRef& operator=(skvm::I32 v) { fVal = v.id;   return *this; }
142 
operator skvm::ValSkSL::__anon93c122a70211::Value::ValRef143         operator skvm::Val() { return fVal; }
144 
145         skvm::Val& fVal;
146     };
147 
operator []SkSL::__anon93c122a70211::Value148     ValRef    operator[](int i) {
149         // These redundant asserts work around what we think is a codegen bug in GCC 8.x for
150         // 32-bit x86 Debug builds.
151         SkASSERT(i < fVals.size());
152         return fVals[i];
153     }
operator []SkSL::__anon93c122a70211::Value154     skvm::Val operator[](int i) const {
155         // These redundant asserts work around what we think is a codegen bug in GCC 8.x for
156         // 32-bit x86 Debug builds.
157         SkASSERT(i < fVals.size());
158         return fVals[i];
159     }
160 
asSpanSkSL::__anon93c122a70211::Value161     SkSpan<skvm::Val> asSpan() { return SkSpan(fVals); }
162 
163 private:
164     SkSTArray<4, skvm::Val, true> fVals;
165 };
166 
167 }  // namespace
168 
169 class SkVMGenerator {
170 public:
171     SkVMGenerator(const Program& program,
172                   skvm::Builder* builder,
173                   SkVMDebugTrace* debugTrace,
174                   SkVMCallbacks* callbacks);
175 
176     void writeProgram(SkSpan<skvm::Val> uniforms,
177                       skvm::Coord device,
178                       const FunctionDefinition& function,
179                       SkSpan<skvm::Val> arguments,
180                       SkSpan<skvm::Val> outReturn);
181 
182 private:
183     /**
184      * In SkSL, a Variable represents a named, typed value (along with qualifiers, etc).
185      * Every Variable is mapped to one (or several, contiguous) indices into our vector of
186      * skvm::Val. Those skvm::Val entries hold the current actual value of that variable.
187      *
188      * NOTE: Conceptually, each Variable is just mapped to a Value. We could implement it that way,
189      * (and eliminate the indirection), but it would add overhead for each Variable,
190      * and add additional (different) bookkeeping for things like lvalue-swizzles.
191      *
192      * Any time a variable appears in an expression, that's a VariableReference, which is a kind of
193      * Expression. Evaluating that VariableReference (or any other Expression) produces a Value,
194      * which is a set of skvm::Val. (This allows an Expression to produce a vector or matrix, in
195      * addition to a scalar).
196      *
197      * For a VariableReference, producing a Value is straightforward - we get the slot of the
198      * Variable (from fSlotMap), use that to look up the current skvm::Vals holding the variable's
199      * contents, and construct a Value with those ids.
200      */
201 
202     /** Creates a Value from a collection of adjacent slots. */
203     Value getSlotValue(size_t slot, size_t nslots);
204 
205     /**
206      * Returns the slot index of this function inside the FunctionDebugInfo array in SkVMDebugTrace.
207      * The FunctionDebugInfo slot will be created if it doesn't already exist.
208      */
209     int getDebugFunctionInfo(const FunctionDeclaration& decl);
210 
211     /** Used by `createSlot` to add this variable to SlotDebugInfo inside SkVMDebugTrace. */
212     void addDebugSlotInfo(const std::string& varName, const Type& type, int line,
213                           int fnReturnValue);
214 
215     void addDebugSlotInfoForGroup(const std::string& varName, const Type& type, int line,
216                                   int* groupIndex, int fnReturnValue);
217 
218     /** Used by `getSlot` to create a new slot on its first access. */
219     size_t createSlot(const std::string& name, const Type& type, int line, int fnReturnValue);
220 
221     /**
222      * Returns the slot holding v's Val(s). Allocates storage if this is first time 'v' is
223      * referenced. Compound variables (e.g. vectors) will consume more than one slot, with
224      * getSlot returning the start of the contiguous chunk of slots.
225      */
226     size_t getSlot(const Variable& v);
227 
228     /**
229      * Returns the slot holding fn's return value. Each call site is given a distinct slot, since
230      * multiple calls to the same function can occur in a single statement. This is generally the
231      * FunctionCall or ChildCall node, but main() doesn't have one of these so it uses the
232      * FunctionDefinition. Allocates storage if this is first time accessing the slot.
233      */
234     size_t getFunctionSlot(const IRNode& callSite, const FunctionDefinition& fn);
235 
236     /**
237      * Writes a value to a slot previously created by getSlot.
238      */
239     void writeToSlot(int slot, skvm::Val value);
240 
241     /**
242      * Returns the line number corresponding to a position.
243      */
244     int getLine(Position pos);
245 
246     /**
247      * Emits an trace_line opcode. writeStatement does this, and statements that alter control flow
248      * may need to explicitly add additional traces.
249      */
250     void emitTraceLine(int line);
251 
252     /** Emits an trace_scope opcode, which alters the SkSL variable-scope depth. */
253     void emitTraceScope(skvm::I32 executionMask, int delta);
254 
255     /** Initializes uniforms and global variables at the start of main(). */
256     void setupGlobals(SkSpan<skvm::Val> uniforms, skvm::Coord device);
257 
258     /** Emits an SkSL function. Returns the slot index of the SkSL function's return value. */
259     size_t writeFunction(const IRNode& caller,
260                          const FunctionDefinition& function,
261                          SkSpan<skvm::Val> arguments);
262 
f32(skvm::Val id)263     skvm::F32 f32(skvm::Val id) { SkASSERT(id != skvm::NA); return {fBuilder, id}; }
i32(skvm::Val id)264     skvm::I32 i32(skvm::Val id) { SkASSERT(id != skvm::NA); return {fBuilder, id}; }
265 
266     // Shorthand for scalars
f32(const Value & v)267     skvm::F32 f32(const Value& v) { SkASSERT(v.slots() == 1); return f32(v[0]); }
i32(const Value & v)268     skvm::I32 i32(const Value& v) { SkASSERT(v.slots() == 1); return i32(v[0]); }
269 
270     template <typename Fn>
unary(const Value & v,Fn && fn)271     Value unary(const Value& v, Fn&& fn) {
272         Value result(v.slots());
273         for (size_t i = 0; i < v.slots(); ++i) {
274             result[i] = fn({fBuilder, v[i]});
275         }
276         return result;
277     }
278 
mask()279     skvm::I32 mask() {
280         // Mask off execution if we have encountered `break` or `continue` on this path.
281         skvm::I32 result = fConditionMask & fLoopMask;
282         if (!fFunctionStack.empty()) {
283             // As we encounter (possibly conditional) return statements, fReturned is updated to
284             // store the lanes that have already returned. For the remainder of the current
285             // function, those lanes should be disabled.
286             result = result & ~currentFunction().fReturned;
287         }
288         return result;
289     }
290 
291     size_t indexSlotOffset(const IndexExpression& expr);
292 
293     Value writeExpression(const Expression& expr);
294     Value writeBinaryExpression(const BinaryExpression& b);
295     Value writeAggregationConstructor(const AnyConstructor& c);
296     Value writeChildCall(const ChildCall& c);
297     Value writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c);
298     Value writeConstructorMatrixResize(const ConstructorMatrixResize& c);
299     Value writeConstructorCast(const AnyConstructor& c);
300     Value writeConstructorSplat(const ConstructorSplat& c);
301     Value writeFunctionCall(const FunctionCall& c);
302     Value writeFieldAccess(const FieldAccess& expr);
303     Value writeLiteral(const Literal& l);
304     Value writeIndexExpression(const IndexExpression& expr);
305     Value writeIntrinsicCall(const FunctionCall& c);
306     Value writePostfixExpression(const PostfixExpression& p);
307     Value writePrefixExpression(const PrefixExpression& p);
308     Value writeSwizzle(const Swizzle& swizzle);
309     Value writeTernaryExpression(const TernaryExpression& t);
310     Value writeVariableExpression(const VariableReference& expr);
311 
312     Value writeTypeConversion(const Value& src, Type::NumberKind srcKind, Type::NumberKind dstKind);
313 
314     void writeStatement(const Statement& s);
315     void writeBlock(const Block& b);
316     void writeBreakStatement();
317     void writeContinueStatement();
318     void writeForStatement(const ForStatement& f);
319     void writeIfStatement(const IfStatement& stmt);
320     void writeReturnStatement(const ReturnStatement& r);
321     void writeSwitchStatement(const SwitchStatement& s);
322     void writeVarDeclaration(const VarDeclaration& decl);
323 
324     Value writeStore(const Expression& lhs, const Value& rhs);
325     skvm::Val writeConditionalStore(skvm::Val lhs, skvm::Val rhs, skvm::I32 mask);
326 
327     Value writeMatrixInverse2x2(const Value& m);
328     Value writeMatrixInverse3x3(const Value& m);
329     Value writeMatrixInverse4x4(const Value& m);
330 
331     void recursiveBinaryCompare(const Value& lVal, const Type& lType,
332                                 const Value& rVal, const Type& rType,
333                                 size_t* slotOffset, Value* result,
334                                 const std::function <Value(skvm::F32 x, skvm::F32 y)>& float_comp,
335                                 const std::function <Value(skvm::I32 x, skvm::I32 y)>& int_comp);
336 
337     void determineLineOffsets();
338 
339     //
340     // Global state for the lifetime of the generator:
341     //
342     const Program& fProgram;
343     skvm::Builder* fBuilder;
344     SkVMDebugTrace* fDebugTrace;
345     int fTraceHookID = -1;
346     SkVMCallbacks* fCallbacks;
347     // contains the position of each newline in the source, plus a zero at the beginning and the
348     // total source length at the end as sentinels
349     std::vector<int> fLineOffsets;
350 
351     struct Slot {
352         skvm::Val  val;
353         bool       writtenTo = false;
354     };
355     std::vector<Slot> fSlots;
356 
357     // [Variable/Function, first slot in fSlots]
358     SkTHashMap<const IRNode*, size_t> fSlotMap;
359 
360     // Debug trace mask (set to true when fTraceCoord matches device coordinates)
361     skvm::I32 fTraceMask;
362 
363     // Conditional execution mask (managed by ScopedCondition, and tied to control-flow scopes)
364     skvm::I32 fConditionMask;
365 
366     // Similar: loop execution masks. Each loop starts with all lanes active (fLoopMask).
367     // 'break' disables a lane in fLoopMask until the loop finishes
368     // 'continue' disables a lane in fLoopMask, and sets fContinueMask to be re-enabled on the next
369     //   iteration
370     skvm::I32 fLoopMask;
371     skvm::I32 fContinueMask;
372 
373     // `fInsideCompoundStatement` will be nonzero if we are currently writing statements inside of a
374     // compound-statement Block. (Conceptually those statements should all count as one.)
375     int fInsideCompoundStatement = 0;
376 
377     //
378     // State that's local to the generation of a single function:
379     //
380     struct Function {
381         size_t     fReturnSlot;
382         skvm::I32  fReturned;
383     };
384     std::vector<Function> fFunctionStack;
currentFunction()385     Function& currentFunction() { return fFunctionStack.back(); }
386 
387     class ScopedCondition {
388     public:
ScopedCondition(SkVMGenerator * generator,skvm::I32 mask)389         ScopedCondition(SkVMGenerator* generator, skvm::I32 mask)
390                 : fGenerator(generator), fOldConditionMask(fGenerator->fConditionMask) {
391             fGenerator->fConditionMask &= mask;
392         }
393 
~ScopedCondition()394         ~ScopedCondition() { fGenerator->fConditionMask = fOldConditionMask; }
395 
396     private:
397         SkVMGenerator* fGenerator;
398         skvm::I32 fOldConditionMask;
399     };
400 };
401 
base_number_kind(const Type & type)402 static Type::NumberKind base_number_kind(const Type& type) {
403     if (type.typeKind() == Type::TypeKind::kMatrix || type.typeKind() == Type::TypeKind::kVector) {
404         return base_number_kind(type.componentType());
405     }
406     return type.numberKind();
407 }
408 
is_uniform(const SkSL::Variable & var)409 static inline bool is_uniform(const SkSL::Variable& var) {
410     return var.modifiers().fFlags & Modifiers::kUniform_Flag;
411 }
412 
SkVMGenerator(const Program & program,skvm::Builder * builder,SkVMDebugTrace * debugTrace,SkVMCallbacks * callbacks)413 SkVMGenerator::SkVMGenerator(const Program& program,
414                              skvm::Builder* builder,
415                              SkVMDebugTrace* debugTrace,
416                              SkVMCallbacks* callbacks)
417         : fProgram(program)
418         , fBuilder(builder)
419         , fDebugTrace(debugTrace)
420         , fCallbacks(callbacks) {}
421 
writeProgram(SkSpan<skvm::Val> uniforms,skvm::Coord device,const FunctionDefinition & function,SkSpan<skvm::Val> arguments,SkSpan<skvm::Val> outReturn)422 void SkVMGenerator::writeProgram(SkSpan<skvm::Val> uniforms,
423                                  skvm::Coord device,
424                                  const FunctionDefinition& function,
425                                  SkSpan<skvm::Val> arguments,
426                                  SkSpan<skvm::Val> outReturn) {
427     this->determineLineOffsets();
428     fConditionMask = fLoopMask = fBuilder->splat(0xffff'ffff);
429 
430     this->setupGlobals(uniforms, device);
431     size_t returnSlot = this->writeFunction(function, function, arguments);
432 
433     // Copy the value from the return slot into outReturn.
434     SkASSERT(function.declaration().returnType().slotCount() == outReturn.size());
435     for (size_t i = 0; i < outReturn.size(); ++i) {
436         outReturn[i] = fSlots[returnSlot + i].val;
437     }
438 }
439 
determineLineOffsets()440 void SkVMGenerator::determineLineOffsets() {
441     SkASSERT(fLineOffsets.empty());
442     fLineOffsets.push_back(0);
443     for (size_t i = 0; i < fProgram.fSource->length(); ++i) {
444         if ((*fProgram.fSource)[i] == '\n') {
445             fLineOffsets.push_back(i);
446         }
447     }
448     fLineOffsets.push_back(fProgram.fSource->length());
449 }
450 
setupGlobals(SkSpan<skvm::Val> uniforms,skvm::Coord device)451 void SkVMGenerator::setupGlobals(SkSpan<skvm::Val> uniforms, skvm::Coord device) {
452     if (fDebugTrace) {
453         // Copy the program source into the debug info so that it will be written in the trace file.
454         fDebugTrace->setSource(*fProgram.fSource);
455 
456         // Create a trace hook and attach it to the builder.
457         fDebugTrace->fTraceHook = SkSLTracer::Make(fDebugTrace);
458         fTraceHookID = fBuilder->attachTraceHook(fDebugTrace->fTraceHook.get());
459 
460         // The SkVM blitter generates centered pixel coordinates. (0.5, 1.5, 2.5, 3.5, etc.)
461         // Add 0.5 to the requested trace coordinate to match this.
462         skvm::Coord traceCoord = {to_F32(fBuilder->splat(fDebugTrace->fTraceCoord.fX)) + 0.5f,
463                                   to_F32(fBuilder->splat(fDebugTrace->fTraceCoord.fY)) + 0.5f};
464 
465         // If we are debugging, we need to create a trace mask. This will be true when the current
466         // device coordinates match the requested trace coordinates. We calculate each mask
467         // individually to guarantee consistent order-of-evaluation.
468         skvm::I32 xMask = (device.x == traceCoord.x),
469                   yMask = (device.y == traceCoord.y);
470         fTraceMask = xMask & yMask;
471     }
472 
473     // Add storage for each global variable (including uniforms) to fSlots, and entries in
474     // fSlotMap to remember where every variable is stored.
475     const skvm::Val* uniformIter = uniforms.begin();
476     size_t fpCount = 0;
477     for (const ProgramElement* e : fProgram.elements()) {
478         if (e->is<GlobalVarDeclaration>()) {
479             const GlobalVarDeclaration& gvd = e->as<GlobalVarDeclaration>();
480             const VarDeclaration& decl = gvd.varDeclaration();
481             const Variable* var = decl.var();
482             SkASSERT(!fSlotMap.find(var));
483 
484             // For most variables, fSlotMap stores an index into fSlots, but for children,
485             // fSlotMap stores the index to pass to fSample(Shader|ColorFilter|Blender)
486             if (var->type().isEffectChild()) {
487                 fSlotMap.set(var, fpCount++);
488                 continue;
489             }
490 
491             // Opaque types include child processors and GL objects (samplers, textures, etc).
492             // Of those, only child processors are legal variables.
493             SkASSERT(!var->type().isVoid());
494             SkASSERT(!var->type().isOpaque());
495 
496             // getSlot() allocates space for the variable's value in fSlots, initializes it to zero,
497             // and populates fSlotMap.
498             size_t slot   = this->getSlot(*var),
499                    nslots = var->type().slotCount();
500 
501             // builtin variables are system-defined, with special semantics. The only builtin
502             // variable exposed to runtime effects is sk_FragCoord.
503             if (int builtin = var->modifiers().fLayout.fBuiltin; builtin >= 0) {
504                 switch (builtin) {
505                     case SK_FRAGCOORD_BUILTIN:
506                         SkASSERT(nslots == 4);
507                         this->writeToSlot(slot + 0, device.x.id);
508                         this->writeToSlot(slot + 1, device.y.id);
509                         this->writeToSlot(slot + 2, fBuilder->splat(0.0f).id);
510                         this->writeToSlot(slot + 3, fBuilder->splat(1.0f).id);
511                         break;
512                     default:
513                         SkDEBUGFAILF("Unsupported builtin %d", builtin);
514                 }
515                 continue;
516             }
517 
518             // For uniforms, copy the supplied IDs over
519             if (is_uniform(*var)) {
520                 SkASSERT(uniformIter + nslots <= uniforms.end());
521                 for (size_t i = 0; i < nslots; ++i) {
522                     this->writeToSlot(slot + i, uniformIter[i]);
523                 }
524                 uniformIter += nslots;
525                 continue;
526             }
527 
528             // For other globals, populate with the initializer expression (if there is one)
529             if (decl.value()) {
530                 Value val = this->writeExpression(*decl.value());
531                 for (size_t i = 0; i < nslots; ++i) {
532                     this->writeToSlot(slot + i, val[i]);
533                 }
534             }
535         }
536     }
537     SkASSERT(uniformIter == uniforms.end());
538 }
539 
getSlotValue(size_t slot,size_t nslots)540 Value SkVMGenerator::getSlotValue(size_t slot, size_t nslots) {
541     Value val(nslots);
542     for (size_t i = 0; i < nslots; ++i) {
543         val[i] = fSlots[slot + i].val;
544     }
545     return val;
546 }
547 
getDebugFunctionInfo(const FunctionDeclaration & decl)548 int SkVMGenerator::getDebugFunctionInfo(const FunctionDeclaration& decl) {
549     SkASSERT(fDebugTrace);
550 
551     std::string name = decl.description();
552 
553     // When generating the debug trace, we typically mark every function as `noinline`. This makes
554     // the trace more confusing, since this isn't in the source program, so remove it.
555     static constexpr std::string_view kNoInline = "noinline ";
556     if (skstd::starts_with(name, kNoInline)) {
557         name = name.substr(kNoInline.size());
558     }
559 
560     // Look for a matching FunctionDebugInfo slot.
561     for (size_t index = 0; index < fDebugTrace->fFuncInfo.size(); ++index) {
562         if (fDebugTrace->fFuncInfo[index].name == name) {
563             return index;
564         }
565     }
566 
567     // We've never called this function before; create a new slot to hold its information.
568     int slot = (int)fDebugTrace->fFuncInfo.size();
569     fDebugTrace->fFuncInfo.push_back(FunctionDebugInfo{std::move(name)});
570     return slot;
571 }
572 
writeFunction(const IRNode & caller,const FunctionDefinition & function,SkSpan<skvm::Val> arguments)573 size_t SkVMGenerator::writeFunction(const IRNode& caller,
574                                     const FunctionDefinition& function,
575                                     SkSpan<skvm::Val> arguments) {
576     const FunctionDeclaration& decl = function.declaration();
577 
578     int funcIndex = -1;
579     if (fDebugTrace) {
580         funcIndex = this->getDebugFunctionInfo(decl);
581         fBuilder->trace_enter(fTraceHookID, this->mask(), fTraceMask, funcIndex);
582     }
583 
584     size_t returnSlot = this->getFunctionSlot(caller, function);
585     fFunctionStack.push_back({/*fReturnSlot=*/returnSlot, /*fReturned=*/fBuilder->splat(0)});
586 
587     // For all parameters, copy incoming argument IDs to our vector of (all) variable IDs
588     size_t argIdx = 0;
589     for (const Variable* p : decl.parameters()) {
590         size_t paramSlot = this->getSlot(*p),
591                nslots    = p->type().slotCount();
592 
593         for (size_t i = 0; i < nslots; ++i) {
594             fSlots[paramSlot + i].writtenTo = false;
595             this->writeToSlot(paramSlot + i, arguments[argIdx + i]);
596         }
597         argIdx += nslots;
598     }
599     SkASSERT(argIdx == arguments.size());
600 
601     this->writeBlock(function.body()->as<Block>());
602 
603     // Copy 'out' and 'inout' parameters back to their caller-supplied argument storage
604     argIdx = 0;
605     for (const Variable* p : decl.parameters()) {
606         size_t nslots = p->type().slotCount();
607 
608         if (p->modifiers().fFlags & Modifiers::kOut_Flag) {
609             size_t paramSlot = this->getSlot(*p);
610             for (size_t i = 0; i < nslots; ++i) {
611                 arguments[argIdx + i] = fSlots[paramSlot + i].val;
612             }
613         }
614         argIdx += nslots;
615     }
616     SkASSERT(argIdx == arguments.size());
617 
618     fFunctionStack.pop_back();
619 
620     if (fDebugTrace) {
621         fBuilder->trace_exit(fTraceHookID, this->mask(), fTraceMask, funcIndex);
622     }
623 
624     return returnSlot;
625 }
626 
writeToSlot(int slot,skvm::Val value)627 void SkVMGenerator::writeToSlot(int slot, skvm::Val value) {
628     if (fDebugTrace && (!fSlots[slot].writtenTo || fSlots[slot].val != value)) {
629         if (fProgram.fConfig->fSettings.fAllowTraceVarInSkVMDebugTrace) {
630             fBuilder->trace_var(fTraceHookID, this->mask(), fTraceMask, slot, i32(value));
631         }
632         fSlots[slot].writtenTo = true;
633     }
634 
635     fSlots[slot].val = value;
636 }
637 
addDebugSlotInfoForGroup(const std::string & varName,const Type & type,int line,int * groupIndex,int fnReturnValue)638 void SkVMGenerator::addDebugSlotInfoForGroup(const std::string& varName, const Type& type, int line,
639                                              int* groupIndex, int fnReturnValue) {
640     SkASSERT(fDebugTrace);
641     switch (type.typeKind()) {
642         case Type::TypeKind::kArray: {
643             int nslots = type.columns();
644             const Type& elemType = type.componentType();
645             for (int slot = 0; slot < nslots; ++slot) {
646                 this->addDebugSlotInfoForGroup(varName + "[" + std::to_string(slot) + "]", elemType,
647                                                line, groupIndex, fnReturnValue);
648             }
649             break;
650         }
651         case Type::TypeKind::kStruct: {
652             for (const Type::Field& field : type.fields()) {
653                 this->addDebugSlotInfoForGroup(varName + "." + std::string(field.fName),
654                                                *field.fType, line, groupIndex, fnReturnValue);
655             }
656             break;
657         }
658         default:
659             SkASSERTF(0, "unsupported slot type %d", (int)type.typeKind());
660             [[fallthrough]];
661 
662         case Type::TypeKind::kScalar:
663         case Type::TypeKind::kVector:
664         case Type::TypeKind::kMatrix: {
665             Type::NumberKind numberKind = type.componentType().numberKind();
666             int nslots = type.slotCount();
667 
668             for (int slot = 0; slot < nslots; ++slot) {
669                 SlotDebugInfo slotInfo;
670                 slotInfo.name = varName;
671                 slotInfo.columns = type.columns();
672                 slotInfo.rows = type.rows();
673                 slotInfo.componentIndex = slot;
674                 slotInfo.groupIndex = (*groupIndex)++;
675                 slotInfo.numberKind = numberKind;
676                 slotInfo.line = line;
677                 slotInfo.fnReturnValue = fnReturnValue;
678                 fDebugTrace->fSlotInfo.push_back(std::move(slotInfo));
679             }
680             break;
681         }
682     }
683 }
684 
addDebugSlotInfo(const std::string & varName,const Type & type,int line,int fnReturnValue)685 void SkVMGenerator::addDebugSlotInfo(const std::string& varName, const Type& type, int line,
686                                      int fnReturnValue) {
687     int groupIndex = 0;
688     this->addDebugSlotInfoForGroup(varName, type, line, &groupIndex, fnReturnValue);
689     SkASSERT((size_t)groupIndex == type.slotCount());
690 }
691 
createSlot(const std::string & name,const Type & type,int line,int fnReturnValue)692 size_t SkVMGenerator::createSlot(const std::string& name,
693                                  const Type& type,
694                                  int line,
695                                  int fnReturnValue) {
696     size_t slot   = fSlots.size(),
697            nslots = type.slotCount();
698 
699     if (nslots > 0) {
700         if (fDebugTrace) {
701             // Our debug slot-info table should have the same length as the actual slot table.
702             SkASSERT(fDebugTrace->fSlotInfo.size() == slot);
703 
704             // Append slot names and types to our debug slot-info table.
705             fDebugTrace->fSlotInfo.reserve(slot + nslots);
706             this->addDebugSlotInfo(name, type, line, fnReturnValue);
707 
708             // Confirm that we added the expected number of slots.
709             SkASSERT(fDebugTrace->fSlotInfo.size() == (slot + nslots));
710         }
711 
712         // Create brand new slots initialized to zero.
713         skvm::Val initialValue = fBuilder->splat(0.0f).id;
714         fSlots.insert(fSlots.end(), nslots, Slot{initialValue});
715     }
716     return slot;
717 }
718 
719 // TODO(skia:13058): remove this and track positions directly
getLine(Position pos)720 int SkVMGenerator::getLine(Position pos) {
721     if (pos.valid()) {
722         // Binary search within fLineOffets to find the line.
723         SkASSERT(fLineOffsets.size() >= 2);
724         SkASSERT(fLineOffsets[0] == 0);
725         SkASSERT(fLineOffsets.back() == (int)fProgram.fSource->length());
726         return std::distance(fLineOffsets.begin(), std::upper_bound(fLineOffsets.begin(),
727                 fLineOffsets.end(), pos.startOffset()));
728     } else {
729         return -1;
730     }
731 }
732 
getSlot(const Variable & v)733 size_t SkVMGenerator::getSlot(const Variable& v) {
734     size_t* entry = fSlotMap.find(&v);
735     if (entry != nullptr) {
736         return *entry;
737     }
738 
739     size_t slot = this->createSlot(std::string(v.name()), v.type(), this->getLine(v.fPosition),
740             /*fnReturnValue=*/-1);
741     fSlotMap.set(&v, slot);
742     return slot;
743 }
744 
getFunctionSlot(const IRNode & callSite,const FunctionDefinition & fn)745 size_t SkVMGenerator::getFunctionSlot(const IRNode& callSite, const FunctionDefinition& fn) {
746     size_t* entry = fSlotMap.find(&callSite);
747     if (entry != nullptr) {
748         return *entry;
749     }
750 
751     const FunctionDeclaration& decl = fn.declaration();
752     size_t slot = this->createSlot("[" + std::string(decl.name()) + "].result",
753                                    decl.returnType(),
754                                    this->getLine(fn.fPosition),
755                                    /*fnReturnValue=*/1);
756     fSlotMap.set(&callSite, slot);
757     return slot;
758 }
759 
recursiveBinaryCompare(const Value & lVal,const Type & lType,const Value & rVal,const Type & rType,size_t * slotOffset,Value * result,const std::function<Value (skvm::F32 x,skvm::F32 y)> & float_comp,const std::function<Value (skvm::I32 x,skvm::I32 y)> & int_comp)760 void SkVMGenerator::recursiveBinaryCompare(
761         const Value& lVal,
762         const Type& lType,
763         const Value& rVal,
764         const Type& rType,
765         size_t* slotOffset,
766         Value* result,
767         const std::function<Value(skvm::F32 x, skvm::F32 y)>& float_comp,
768         const std::function<Value(skvm::I32 x, skvm::I32 y)>& int_comp) {
769     switch (lType.typeKind()) {
770         case Type::TypeKind::kStruct:
771             SkASSERT(rType.typeKind() == Type::TypeKind::kStruct);
772             // Go through all the fields
773             for (size_t f = 0; f < lType.fields().size(); ++f) {
774                 const Type::Field& lField = lType.fields()[f];
775                 const Type::Field& rField = rType.fields()[f];
776                 this->recursiveBinaryCompare(lVal,
777                                              *lField.fType,
778                                              rVal,
779                                              *rField.fType,
780                                              slotOffset,
781                                              result,
782                                              float_comp,
783                                              int_comp);
784             }
785             break;
786 
787         case Type::TypeKind::kArray:
788         case Type::TypeKind::kVector:
789         case Type::TypeKind::kMatrix:
790             SkASSERT(lType.typeKind() == rType.typeKind());
791             // Go through all the elements
792             for (int c = 0; c < lType.columns(); ++c) {
793                 this->recursiveBinaryCompare(lVal,
794                                              lType.componentType(),
795                                              rVal,
796                                              rType.componentType(),
797                                              slotOffset,
798                                              result,
799                                              float_comp,
800                                              int_comp);
801             }
802             break;
803         default:
804             SkASSERT(lType.typeKind() == rType.typeKind() &&
805                      lType.slotCount() == rType.slotCount());
806             Type::NumberKind nk = base_number_kind(lType);
807             auto L = lVal[*slotOffset];
808             auto R = rVal[*slotOffset];
809             (*result)[*slotOffset] =
810                     i32(nk == Type::NumberKind::kFloat
811                           ? float_comp(f32(L), f32(R))
812                           : int_comp(i32(L), i32(R))).id;
813             *slotOffset += lType.slotCount();
814             break;
815     }
816 }
817 
writeBinaryExpression(const BinaryExpression & b)818 Value SkVMGenerator::writeBinaryExpression(const BinaryExpression& b) {
819     const Expression& left = *b.left();
820     const Expression& right = *b.right();
821     Operator op = b.getOperator();
822     if (op.kind() == Operator::Kind::EQ) {
823         return this->writeStore(left, this->writeExpression(right));
824     }
825 
826     const Type& lType = left.type();
827     const Type& rType = right.type();
828     bool lVecOrMtx = (lType.isVector() || lType.isMatrix());
829     bool rVecOrMtx = (rType.isVector() || rType.isMatrix());
830     bool isAssignment = op.isAssignment();
831     if (isAssignment) {
832         op = op.removeAssignment();
833     }
834     Type::NumberKind nk = base_number_kind(lType);
835 
836     // A few ops require special treatment:
837     switch (op.kind()) {
838         case Operator::Kind::LOGICALAND: {
839             SkASSERT(!isAssignment);
840             SkASSERT(nk == Type::NumberKind::kBoolean);
841             skvm::I32 lVal = i32(this->writeExpression(left));
842             ScopedCondition shortCircuit(this, lVal);
843             skvm::I32 rVal = i32(this->writeExpression(right));
844             return lVal & rVal;
845         }
846         case Operator::Kind::LOGICALOR: {
847             SkASSERT(!isAssignment);
848             SkASSERT(nk == Type::NumberKind::kBoolean);
849             skvm::I32 lVal = i32(this->writeExpression(left));
850             ScopedCondition shortCircuit(this, ~lVal);
851             skvm::I32 rVal = i32(this->writeExpression(right));
852             return lVal | rVal;
853         }
854         case Operator::Kind::COMMA:
855             // We write the left side of the expression to preserve its side effects, even though we
856             // immediately discard the result.
857             this->writeExpression(left);
858             return this->writeExpression(right);
859         default:
860             break;
861     }
862 
863     // All of the other ops always evaluate both sides of the expression
864     Value lVal = this->writeExpression(left),
865           rVal = this->writeExpression(right);
866 
867     // Special case for M*V, V*M, M*M (but not V*V!)
868     if (op.kind() == Operator::Kind::STAR
869         && lVecOrMtx && rVecOrMtx && !(lType.isVector() && rType.isVector())) {
870         int rCols = rType.columns(),
871             rRows = rType.rows(),
872             lCols = lType.columns(),
873             lRows = lType.rows();
874         // M*V treats the vector as a column
875         if (rType.isVector()) {
876             std::swap(rCols, rRows);
877         }
878         SkASSERT(lCols == rRows);
879         SkASSERT(b.type().slotCount() == static_cast<size_t>(lRows * rCols));
880         Value result(lRows * rCols);
881         size_t resultIdx = 0;
882         const skvm::F32 zero = fBuilder->splat(0.0f);
883         for (int c = 0; c < rCols; ++c)
884         for (int r = 0; r < lRows; ++r) {
885             skvm::F32 sum = zero;
886             for (int j = 0; j < lCols; ++j) {
887                 sum += f32(lVal[j*lRows + r]) * f32(rVal[c*rRows + j]);
888             }
889             result[resultIdx++] = sum;
890         }
891         SkASSERT(resultIdx == result.slots());
892         return isAssignment ? this->writeStore(left, result) : result;
893     }
894 
895     size_t nslots = std::max(lVal.slots(), rVal.slots());
896 
897     auto binary = [&](const std::function <Value(skvm::F32 x, skvm::F32 y)>& f_fn,
898                       const std::function <Value(skvm::I32 x, skvm::I32 y)>& i_fn,
899                       bool foldResults = false) -> Value {
900 
901         Value result(nslots);
902         if (op.isEquality() && (lType.isStruct() || lType.isArray())) {
903             // Shifting over lVal and rVal
904             size_t slotOffset = 0;
905             this->recursiveBinaryCompare(
906                     lVal, lType, rVal, rType, &slotOffset, &result, f_fn, i_fn);
907             SkASSERT(slotOffset == nslots);
908         } else {
909             for (size_t slot = 0; slot < nslots; ++slot) {
910                 // If one side is scalar, replicate it to all channels
911                 skvm::Val L = lVal.slots() == 1 ? lVal[0] : lVal[slot],
912                           R = rVal.slots() == 1 ? rVal[0] : rVal[slot];
913 
914                 if (nk == Type::NumberKind::kFloat) {
915                     result[slot] = i32(f_fn(f32(L), f32(R)));
916                 } else {
917                     result[slot] = i32(i_fn(i32(L), i32(R)));
918                 }
919             }
920         }
921 
922         if (foldResults && nslots > 1) {
923             SkASSERT(op.isEquality());
924             skvm::I32 folded = i32(result[0]);
925             for (size_t i = 1; i < nslots; ++i) {
926                 if (op.kind() == Operator::Kind::NEQ) {
927                     folded |= i32(result[i]);
928                 } else {
929                     folded &= i32(result[i]);
930                 }
931             }
932             return folded;
933         }
934 
935         return isAssignment ? this->writeStore(left, result) : result;
936     };
937 
938     auto unsupported_f = [&](skvm::F32, skvm::F32) {
939         SkDEBUGFAIL("Unsupported operator");
940         return skvm::F32{};
941     };
942 
943     switch (op.kind()) {
944         case Operator::Kind::EQEQ:
945             SkASSERT(!isAssignment);
946             return binary([](skvm::F32 x, skvm::F32 y) { return x == y; },
947                           [](skvm::I32 x, skvm::I32 y) { return x == y; }, /*foldResults=*/ true);
948         case Operator::Kind::NEQ:
949             SkASSERT(!isAssignment);
950             return binary([](skvm::F32 x, skvm::F32 y) { return x != y; },
951                           [](skvm::I32 x, skvm::I32 y) { return x != y; }, /*foldResults=*/ true);
952         case Operator::Kind::GT:
953             return binary([](skvm::F32 x, skvm::F32 y) { return x > y; },
954                           [](skvm::I32 x, skvm::I32 y) { return x > y; });
955         case Operator::Kind::GTEQ:
956             return binary([](skvm::F32 x, skvm::F32 y) { return x >= y; },
957                           [](skvm::I32 x, skvm::I32 y) { return x >= y; });
958         case Operator::Kind::LT:
959             return binary([](skvm::F32 x, skvm::F32 y) { return x < y; },
960                           [](skvm::I32 x, skvm::I32 y) { return x < y; });
961         case Operator::Kind::LTEQ:
962             return binary([](skvm::F32 x, skvm::F32 y) { return x <= y; },
963                           [](skvm::I32 x, skvm::I32 y) { return x <= y; });
964 
965         case Operator::Kind::PLUS:
966             return binary([](skvm::F32 x, skvm::F32 y) { return x + y; },
967                           [](skvm::I32 x, skvm::I32 y) { return x + y; });
968         case Operator::Kind::MINUS:
969             return binary([](skvm::F32 x, skvm::F32 y) { return x - y; },
970                           [](skvm::I32 x, skvm::I32 y) { return x - y; });
971         case Operator::Kind::STAR:
972             return binary([](skvm::F32 x, skvm::F32 y) { return x ** y; },
973                           [](skvm::I32 x, skvm::I32 y) { return x * y; });
974         case Operator::Kind::SLASH:
975             // Minimum spec (GLSL ES 1.0) has very loose requirements for integer operations.
976             // (Low-end GPUs may not have integer ALUs). Given that, we are allowed to do floating
977             // point division plus rounding. Section 10.28 of the spec even clarifies that the
978             // rounding mode is undefined (but round-towards-zero is the obvious/common choice).
979             return binary([](skvm::F32 x, skvm::F32 y) { return x / y; },
980                           [](skvm::I32 x, skvm::I32 y) {
981                               return skvm::trunc(skvm::to_F32(x) / skvm::to_F32(y));
982                           });
983 
984         case Operator::Kind::BITWISEXOR:
985         case Operator::Kind::LOGICALXOR:
986             return binary(unsupported_f, [](skvm::I32 x, skvm::I32 y) { return x ^ y; });
987         case Operator::Kind::BITWISEAND:
988             return binary(unsupported_f, [](skvm::I32 x, skvm::I32 y) { return x & y; });
989         case Operator::Kind::BITWISEOR:
990             return binary(unsupported_f, [](skvm::I32 x, skvm::I32 y) { return x | y; });
991 
992         // These three operators are all 'reserved' (illegal) in our minimum spec, but will require
993         // implementation in the future.
994         case Operator::Kind::PERCENT:
995         case Operator::Kind::SHL:
996         case Operator::Kind::SHR:
997         default:
998             SkDEBUGFAIL("Unsupported operator");
999             return {};
1000     }
1001 }
1002 
writeAggregationConstructor(const AnyConstructor & c)1003 Value SkVMGenerator::writeAggregationConstructor(const AnyConstructor& c) {
1004     Value result(c.type().slotCount());
1005     size_t resultIdx = 0;
1006     for (const auto &arg : c.argumentSpan()) {
1007         Value tmp = this->writeExpression(*arg);
1008         for (size_t tmpSlot = 0; tmpSlot < tmp.slots(); ++tmpSlot) {
1009             result[resultIdx++] = tmp[tmpSlot];
1010         }
1011     }
1012     return result;
1013 }
1014 
writeTypeConversion(const Value & src,Type::NumberKind srcKind,Type::NumberKind dstKind)1015 Value SkVMGenerator::writeTypeConversion(const Value& src,
1016                                          Type::NumberKind srcKind,
1017                                          Type::NumberKind dstKind) {
1018     // Conversion among "similar" types (floatN <-> halfN), (shortN <-> intN), etc. is a no-op.
1019     if (srcKind == dstKind) {
1020         return src;
1021     }
1022 
1023     // TODO: Handle signed vs. unsigned. GLSL ES 1.0 only has 'int', so no problem yet.
1024     Value dst(src.slots());
1025     switch (dstKind) {
1026         case Type::NumberKind::kFloat:
1027             if (srcKind == Type::NumberKind::kSigned) {
1028                 // int -> float
1029                 for (size_t i = 0; i < src.slots(); ++i) {
1030                     dst[i] = skvm::to_F32(i32(src[i]));
1031                 }
1032                 return dst;
1033             }
1034             if (srcKind == Type::NumberKind::kBoolean) {
1035                 // bool -> float
1036                 for (size_t i = 0; i < src.slots(); ++i) {
1037                     dst[i] = skvm::select(i32(src[i]), 1.0f, 0.0f);
1038                 }
1039                 return dst;
1040             }
1041             break;
1042 
1043         case Type::NumberKind::kSigned:
1044             if (srcKind == Type::NumberKind::kFloat) {
1045                 // float -> int
1046                 for (size_t i = 0; i < src.slots(); ++i) {
1047                     dst[i] = skvm::trunc(f32(src[i]));
1048                 }
1049                 return dst;
1050             }
1051             if (srcKind == Type::NumberKind::kBoolean) {
1052                 // bool -> int
1053                 for (size_t i = 0; i < src.slots(); ++i) {
1054                     dst[i] = skvm::select(i32(src[i]), 1, 0);
1055                 }
1056                 return dst;
1057             }
1058             break;
1059 
1060         case Type::NumberKind::kBoolean:
1061             if (srcKind == Type::NumberKind::kSigned) {
1062                 // int -> bool
1063                 for (size_t i = 0; i < src.slots(); ++i) {
1064                     dst[i] = i32(src[i]) != 0;
1065                 }
1066                 return dst;
1067             }
1068             if (srcKind == Type::NumberKind::kFloat) {
1069                 // float -> bool
1070                 for (size_t i = 0; i < src.slots(); ++i) {
1071                     dst[i] = f32(src[i]) != 0.0;
1072                 }
1073                 return dst;
1074             }
1075             break;
1076 
1077         default:
1078             break;
1079     }
1080     SkDEBUGFAILF("Unsupported type conversion: %d -> %d", (int)srcKind, (int)dstKind);
1081     return {};
1082 }
1083 
writeConstructorCast(const AnyConstructor & c)1084 Value SkVMGenerator::writeConstructorCast(const AnyConstructor& c) {
1085     auto arguments = c.argumentSpan();
1086     SkASSERT(arguments.size() == 1);
1087     const Expression& argument = *arguments.front();
1088 
1089     const Type& srcType = argument.type();
1090     const Type& dstType = c.type();
1091     Type::NumberKind srcKind = base_number_kind(srcType);
1092     Type::NumberKind dstKind = base_number_kind(dstType);
1093     Value src = this->writeExpression(argument);
1094     return this->writeTypeConversion(src, srcKind, dstKind);
1095 }
1096 
writeConstructorSplat(const ConstructorSplat & c)1097 Value SkVMGenerator::writeConstructorSplat(const ConstructorSplat& c) {
1098     SkASSERT(c.type().isVector());
1099     SkASSERT(c.argument()->type().isScalar());
1100     int columns = c.type().columns();
1101 
1102     // Splat the argument across all components of a vector.
1103     Value src = this->writeExpression(*c.argument());
1104     Value dst(columns);
1105     for (int i = 0; i < columns; ++i) {
1106         dst[i] = src[0];
1107     }
1108     return dst;
1109 }
1110 
writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix & ctor)1111 Value SkVMGenerator::writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& ctor) {
1112     const Type& dstType = ctor.type();
1113     SkASSERT(dstType.isMatrix());
1114     SkASSERT(ctor.argument()->type().matches(dstType.componentType()));
1115 
1116     Value src = this->writeExpression(*ctor.argument());
1117     Value dst(dstType.rows() * dstType.columns());
1118     size_t dstIndex = 0;
1119 
1120     // Matrix-from-scalar builds a diagonal scale matrix
1121     const skvm::F32 zero = fBuilder->splat(0.0f);
1122     for (int c = 0; c < dstType.columns(); ++c) {
1123         for (int r = 0; r < dstType.rows(); ++r) {
1124             dst[dstIndex++] = (c == r ? f32(src) : zero);
1125         }
1126     }
1127 
1128     SkASSERT(dstIndex == dst.slots());
1129     return dst;
1130 }
1131 
writeConstructorMatrixResize(const ConstructorMatrixResize & ctor)1132 Value SkVMGenerator::writeConstructorMatrixResize(const ConstructorMatrixResize& ctor) {
1133     const Type& srcType = ctor.argument()->type();
1134     const Type& dstType = ctor.type();
1135     Value src = this->writeExpression(*ctor.argument());
1136     Value dst(dstType.rows() * dstType.columns());
1137 
1138     // Matrix-from-matrix uses src where it overlaps, and fills in missing fields with identity.
1139     size_t dstIndex = 0;
1140     for (int c = 0; c < dstType.columns(); ++c) {
1141         for (int r = 0; r < dstType.rows(); ++r) {
1142             if (c < srcType.columns() && r < srcType.rows()) {
1143                 dst[dstIndex++] = src[c * srcType.rows() + r];
1144             } else {
1145                 dst[dstIndex++] = fBuilder->splat(c == r ? 1.0f : 0.0f);
1146             }
1147         }
1148     }
1149 
1150     SkASSERT(dstIndex == dst.slots());
1151     return dst;
1152 }
1153 
writeFieldAccess(const FieldAccess & expr)1154 Value SkVMGenerator::writeFieldAccess(const FieldAccess& expr) {
1155     Value base = this->writeExpression(*expr.base());
1156     Value field(expr.type().slotCount());
1157     size_t offset = expr.initialSlot();
1158     for (size_t i = 0; i < field.slots(); ++i) {
1159         field[i] = base[offset + i];
1160     }
1161     return field;
1162 }
1163 
indexSlotOffset(const IndexExpression & expr)1164 size_t SkVMGenerator::indexSlotOffset(const IndexExpression& expr) {
1165     Value index = this->writeExpression(*expr.index());
1166     int indexValue = -1;
1167     SkAssertResult(fBuilder->allImm(index[0], &indexValue));
1168 
1169     // When indexing by a literal, the front-end guarantees that we don't go out of bounds.
1170     // But when indexing by a loop variable, it's possible to generate out-of-bounds access.
1171     // The GLSL spec leaves that behavior undefined - we'll just clamp everything here.
1172     indexValue = SkTPin(indexValue, 0, expr.base()->type().columns() - 1);
1173 
1174     size_t stride = expr.type().slotCount();
1175     return indexValue * stride;
1176 }
1177 
writeIndexExpression(const IndexExpression & expr)1178 Value SkVMGenerator::writeIndexExpression(const IndexExpression& expr) {
1179     Value base = this->writeExpression(*expr.base());
1180     Value element(expr.type().slotCount());
1181     size_t offset = this->indexSlotOffset(expr);
1182     for (size_t i = 0; i < element.slots(); ++i) {
1183         element[i] = base[offset + i];
1184     }
1185     return element;
1186 }
1187 
writeVariableExpression(const VariableReference & expr)1188 Value SkVMGenerator::writeVariableExpression(const VariableReference& expr) {
1189     size_t slot = this->getSlot(*expr.variable());
1190     return this->getSlotValue(slot, expr.type().slotCount());
1191 }
1192 
writeMatrixInverse2x2(const Value & m)1193 Value SkVMGenerator::writeMatrixInverse2x2(const Value& m) {
1194     SkASSERT(m.slots() == 4);
1195     skvm::F32 a = f32(m[0]),
1196               b = f32(m[1]),
1197               c = f32(m[2]),
1198               d = f32(m[3]);
1199     skvm::F32 idet = 1.0f / (a*d - b*c);
1200 
1201     Value result(m.slots());
1202     result[0] = ( d ** idet);
1203     result[1] = (-b ** idet);
1204     result[2] = (-c ** idet);
1205     result[3] = ( a ** idet);
1206     return result;
1207 }
1208 
writeMatrixInverse3x3(const Value & m)1209 Value SkVMGenerator::writeMatrixInverse3x3(const Value& m) {
1210     SkASSERT(m.slots() == 9);
1211     skvm::F32 a11 = f32(m[0]), a12 = f32(m[3]), a13 = f32(m[6]),
1212               a21 = f32(m[1]), a22 = f32(m[4]), a23 = f32(m[7]),
1213               a31 = f32(m[2]), a32 = f32(m[5]), a33 = f32(m[8]);
1214     skvm::F32 idet = 1.0f / (a11*a22*a33 + a12*a23*a31 + a13*a21*a32 -
1215                              a11*a23*a32 - a12*a21*a33 - a13*a22*a31);
1216 
1217     Value result(m.slots());
1218     result[0] = ((a22**a33 - a23**a32) ** idet);
1219     result[1] = ((a23**a31 - a21**a33) ** idet);
1220     result[2] = ((a21**a32 - a22**a31) ** idet);
1221     result[3] = ((a13**a32 - a12**a33) ** idet);
1222     result[4] = ((a11**a33 - a13**a31) ** idet);
1223     result[5] = ((a12**a31 - a11**a32) ** idet);
1224     result[6] = ((a12**a23 - a13**a22) ** idet);
1225     result[7] = ((a13**a21 - a11**a23) ** idet);
1226     result[8] = ((a11**a22 - a12**a21) ** idet);
1227     return result;
1228 }
1229 
writeMatrixInverse4x4(const Value & m)1230 Value SkVMGenerator::writeMatrixInverse4x4(const Value& m) {
1231     SkASSERT(m.slots() == 16);
1232     skvm::F32 a00 = f32(m[0]), a10 = f32(m[4]), a20 = f32(m[ 8]), a30 = f32(m[12]),
1233               a01 = f32(m[1]), a11 = f32(m[5]), a21 = f32(m[ 9]), a31 = f32(m[13]),
1234               a02 = f32(m[2]), a12 = f32(m[6]), a22 = f32(m[10]), a32 = f32(m[14]),
1235               a03 = f32(m[3]), a13 = f32(m[7]), a23 = f32(m[11]), a33 = f32(m[15]);
1236 
1237     skvm::F32 b00 = a00**a11 - a01**a10,
1238               b01 = a00**a12 - a02**a10,
1239               b02 = a00**a13 - a03**a10,
1240               b03 = a01**a12 - a02**a11,
1241               b04 = a01**a13 - a03**a11,
1242               b05 = a02**a13 - a03**a12,
1243               b06 = a20**a31 - a21**a30,
1244               b07 = a20**a32 - a22**a30,
1245               b08 = a20**a33 - a23**a30,
1246               b09 = a21**a32 - a22**a31,
1247               b10 = a21**a33 - a23**a31,
1248               b11 = a22**a33 - a23**a32;
1249 
1250     skvm::F32 idet = 1.0f / (b00**b11 - b01**b10 + b02**b09 + b03**b08 - b04**b07 + b05**b06);
1251 
1252     b00 *= idet;
1253     b01 *= idet;
1254     b02 *= idet;
1255     b03 *= idet;
1256     b04 *= idet;
1257     b05 *= idet;
1258     b06 *= idet;
1259     b07 *= idet;
1260     b08 *= idet;
1261     b09 *= idet;
1262     b10 *= idet;
1263     b11 *= idet;
1264 
1265     Value result(m.slots());
1266     result[ 0] = (a11*b11 - a12*b10 + a13*b09);
1267     result[ 1] = (a02*b10 - a01*b11 - a03*b09);
1268     result[ 2] = (a31*b05 - a32*b04 + a33*b03);
1269     result[ 3] = (a22*b04 - a21*b05 - a23*b03);
1270     result[ 4] = (a12*b08 - a10*b11 - a13*b07);
1271     result[ 5] = (a00*b11 - a02*b08 + a03*b07);
1272     result[ 6] = (a32*b02 - a30*b05 - a33*b01);
1273     result[ 7] = (a20*b05 - a22*b02 + a23*b01);
1274     result[ 8] = (a10*b10 - a11*b08 + a13*b06);
1275     result[ 9] = (a01*b08 - a00*b10 - a03*b06);
1276     result[10] = (a30*b04 - a31*b02 + a33*b00);
1277     result[11] = (a21*b02 - a20*b04 - a23*b00);
1278     result[12] = (a11*b07 - a10*b09 - a12*b06);
1279     result[13] = (a00*b09 - a01*b07 + a02*b06);
1280     result[14] = (a31*b01 - a30*b03 - a32*b00);
1281     result[15] = (a20*b03 - a21*b01 + a22*b00);
1282     return result;
1283 }
1284 
writeChildCall(const ChildCall & c)1285 Value SkVMGenerator::writeChildCall(const ChildCall& c) {
1286     size_t* childPtr = fSlotMap.find(&c.child());
1287     SkASSERT(childPtr != nullptr);
1288 
1289     const Expression* arg = c.arguments()[0].get();
1290     Value argVal = this->writeExpression(*arg);
1291     skvm::Color color;
1292 
1293     switch (c.child().type().typeKind()) {
1294         case Type::TypeKind::kShader: {
1295             SkASSERT(c.arguments().size() == 1);
1296             SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fFloat2));
1297             skvm::Coord coord = {f32(argVal[0]), f32(argVal[1])};
1298             color = fCallbacks->sampleShader(*childPtr, coord);
1299             break;
1300         }
1301         case Type::TypeKind::kColorFilter: {
1302             SkASSERT(c.arguments().size() == 1);
1303             SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fHalf4) ||
1304                      arg->type().matches(*fProgram.fContext->fTypes.fFloat4));
1305             skvm::Color inColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
1306             color = fCallbacks->sampleColorFilter(*childPtr, inColor);
1307             break;
1308         }
1309         case Type::TypeKind::kBlender: {
1310             SkASSERT(c.arguments().size() == 2);
1311             SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fHalf4) ||
1312                      arg->type().matches(*fProgram.fContext->fTypes.fFloat4));
1313             skvm::Color srcColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
1314 
1315             arg = c.arguments()[1].get();
1316             argVal = this->writeExpression(*arg);
1317             SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fHalf4) ||
1318                      arg->type().matches(*fProgram.fContext->fTypes.fFloat4));
1319             skvm::Color dstColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
1320 
1321             color = fCallbacks->sampleBlender(*childPtr, srcColor, dstColor);
1322             break;
1323         }
1324         default: {
1325             SkDEBUGFAILF("cannot sample from type '%s'", c.child().type().description().c_str());
1326         }
1327     }
1328 
1329     Value result(4);
1330     result[0] = color.r;
1331     result[1] = color.g;
1332     result[2] = color.b;
1333     result[3] = color.a;
1334     return result;
1335 }
1336 
writeIntrinsicCall(const FunctionCall & c)1337 Value SkVMGenerator::writeIntrinsicCall(const FunctionCall& c) {
1338     IntrinsicKind intrinsicKind = c.function().intrinsicKind();
1339     SkASSERT(intrinsicKind != kNotIntrinsic);
1340 
1341     const size_t nargs = c.arguments().size();
1342     const size_t kMaxArgs = 3;  // eg: clamp, mix, smoothstep
1343     Value args[kMaxArgs];
1344     SkASSERT(nargs >= 1 && nargs <= std::size(args));
1345 
1346     // All other intrinsics have at most three args, and those can all be evaluated up front:
1347     for (size_t i = 0; i < nargs; ++i) {
1348         args[i] = this->writeExpression(*c.arguments()[i]);
1349     }
1350     Type::NumberKind nk = base_number_kind(c.arguments()[0]->type());
1351 
1352     auto binary = [&](auto&& fn) {
1353         // Binary intrinsics are (vecN, vecN), (vecN, float), or (float, vecN)
1354         size_t nslots = std::max(args[0].slots(), args[1].slots());
1355         Value result(nslots);
1356         SkASSERT(args[0].slots() == nslots || args[0].slots() == 1);
1357         SkASSERT(args[1].slots() == nslots || args[1].slots() == 1);
1358 
1359         for (size_t i = 0; i < nslots; ++i) {
1360             result[i] = fn({fBuilder, args[0][args[0].slots() == 1 ? 0 : i]},
1361                            {fBuilder, args[1][args[1].slots() == 1 ? 0 : i]});
1362         }
1363         return result;
1364     };
1365 
1366     auto ternary = [&](auto&& fn) {
1367         // Ternary intrinsics are some combination of vecN and float
1368         size_t nslots = std::max({args[0].slots(), args[1].slots(), args[2].slots()});
1369         Value result(nslots);
1370         SkASSERT(args[0].slots() == nslots || args[0].slots() == 1);
1371         SkASSERT(args[1].slots() == nslots || args[1].slots() == 1);
1372         SkASSERT(args[2].slots() == nslots || args[2].slots() == 1);
1373 
1374         for (size_t i = 0; i < nslots; ++i) {
1375             result[i] = fn({fBuilder, args[0][args[0].slots() == 1 ? 0 : i]},
1376                            {fBuilder, args[1][args[1].slots() == 1 ? 0 : i]},
1377                            {fBuilder, args[2][args[2].slots() == 1 ? 0 : i]});
1378         }
1379         return result;
1380     };
1381 
1382     auto dot = [&](const Value& x, const Value& y) {
1383         SkASSERT(x.slots() == y.slots());
1384         skvm::F32 result = f32(x[0]) * f32(y[0]);
1385         for (size_t i = 1; i < x.slots(); ++i) {
1386             result += f32(x[i]) * f32(y[i]);
1387         }
1388         return result;
1389     };
1390 
1391     switch (intrinsicKind) {
1392         case k_radians_IntrinsicKind:
1393             return unary(args[0], [](skvm::F32 deg) { return deg * (SK_FloatPI / 180); });
1394         case k_degrees_IntrinsicKind:
1395             return unary(args[0], [](skvm::F32 rad) { return rad * (180 / SK_FloatPI); });
1396 
1397         case k_sin_IntrinsicKind: return unary(args[0], skvm::approx_sin);
1398         case k_cos_IntrinsicKind: return unary(args[0], skvm::approx_cos);
1399         case k_tan_IntrinsicKind: return unary(args[0], skvm::approx_tan);
1400 
1401         case k_asin_IntrinsicKind: return unary(args[0], skvm::approx_asin);
1402         case k_acos_IntrinsicKind: return unary(args[0], skvm::approx_acos);
1403 
1404         case k_atan_IntrinsicKind: return nargs == 1 ? unary(args[0], skvm::approx_atan)
1405                                                  : binary(skvm::approx_atan2);
1406 
1407         case k_pow_IntrinsicKind:
1408             return binary([](skvm::F32 x, skvm::F32 y) { return skvm::approx_powf(x, y); });
1409         case k_exp_IntrinsicKind:  return unary(args[0], skvm::approx_exp);
1410         case k_log_IntrinsicKind:  return unary(args[0], skvm::approx_log);
1411         case k_exp2_IntrinsicKind: return unary(args[0], skvm::approx_pow2);
1412         case k_log2_IntrinsicKind: return unary(args[0], skvm::approx_log2);
1413 
1414         case k_sqrt_IntrinsicKind: return unary(args[0], skvm::sqrt);
1415         case k_inversesqrt_IntrinsicKind:
1416             return unary(args[0], [](skvm::F32 x) { return 1.0f / skvm::sqrt(x); });
1417 
1418         case k_abs_IntrinsicKind: return unary(args[0], skvm::abs);
1419         case k_sign_IntrinsicKind:
1420             return unary(args[0], [](skvm::F32 x) { return select(x < 0, -1.0f,
1421                                                            select(x > 0, +1.0f, 0.0f)); });
1422         case k_floor_IntrinsicKind: return unary(args[0], skvm::floor);
1423         case k_ceil_IntrinsicKind:  return unary(args[0], skvm::ceil);
1424         case k_fract_IntrinsicKind: return unary(args[0], skvm::fract);
1425         case k_mod_IntrinsicKind:
1426             return binary([](skvm::F32 x, skvm::F32 y) { return x - y*skvm::floor(x / y); });
1427 
1428         case k_min_IntrinsicKind:
1429             return binary([](skvm::F32 x, skvm::F32 y) { return skvm::min(x, y); });
1430         case k_max_IntrinsicKind:
1431             return binary([](skvm::F32 x, skvm::F32 y) { return skvm::max(x, y); });
1432         case k_clamp_IntrinsicKind:
1433             return ternary(
1434                     [](skvm::F32 x, skvm::F32 lo, skvm::F32 hi) { return skvm::clamp(x, lo, hi); });
1435         case k_saturate_IntrinsicKind:
1436             return unary(args[0], [](skvm::F32 x) { return skvm::clamp01(x); });
1437         case k_mix_IntrinsicKind:
1438             return ternary(
1439                     [](skvm::F32 x, skvm::F32 y, skvm::F32 t) { return skvm::lerp(x, y, t); });
1440         case k_step_IntrinsicKind:
1441             return binary([](skvm::F32 edge, skvm::F32 x) { return select(x < edge, 0.0f, 1.0f); });
1442         case k_smoothstep_IntrinsicKind:
1443             return ternary([](skvm::F32 edge0, skvm::F32 edge1, skvm::F32 x) {
1444                 skvm::F32 t = skvm::clamp01((x - edge0) / (edge1 - edge0));
1445                 return t ** t ** (3 - 2 ** t);
1446             });
1447 
1448         case k_length_IntrinsicKind: return skvm::sqrt(dot(args[0], args[0]));
1449         case k_distance_IntrinsicKind: {
1450             Value vec = binary([](skvm::F32 x, skvm::F32 y) { return x - y; });
1451             return skvm::sqrt(dot(vec, vec));
1452         }
1453         case k_dot_IntrinsicKind: return dot(args[0], args[1]);
1454         case k_cross_IntrinsicKind: {
1455             skvm::F32 ax = f32(args[0][0]), ay = f32(args[0][1]), az = f32(args[0][2]),
1456                       bx = f32(args[1][0]), by = f32(args[1][1]), bz = f32(args[1][2]);
1457             Value result(3);
1458             result[0] = ay**bz - az**by;
1459             result[1] = az**bx - ax**bz;
1460             result[2] = ax**by - ay**bx;
1461             return result;
1462         }
1463         case k_normalize_IntrinsicKind: {
1464             skvm::F32 invLen = 1.0f / skvm::sqrt(dot(args[0], args[0]));
1465             return unary(args[0], [&](skvm::F32 x) { return x ** invLen; });
1466         }
1467         case k_faceforward_IntrinsicKind: {
1468             const Value &N    = args[0],
1469                         &I    = args[1],
1470                         &Nref = args[2];
1471 
1472             skvm::F32 dotNrefI = dot(Nref, I);
1473             return unary(N, [&](skvm::F32 n) { return select(dotNrefI<0, n, -n); });
1474         }
1475         case k_reflect_IntrinsicKind: {
1476             const Value &I = args[0],
1477                         &N = args[1];
1478 
1479             skvm::F32 dotNI = dot(N, I);
1480             return binary([&](skvm::F32 i, skvm::F32 n) {
1481                 return i - 2**dotNI**n;
1482             });
1483         }
1484         case k_refract_IntrinsicKind: {
1485             const Value &I  = args[0],
1486                         &N  = args[1];
1487             skvm::F32   eta = f32(args[2]);
1488 
1489             skvm::F32 dotNI = dot(N, I),
1490                       k     = 1 - eta**eta**(1 - dotNI**dotNI);
1491             return binary([&](skvm::F32 i, skvm::F32 n) {
1492                 return select(k<0, 0.0f, eta**i - (eta**dotNI + sqrt(k))**n);
1493             });
1494         }
1495 
1496         case k_matrixCompMult_IntrinsicKind:
1497             return binary([](skvm::F32 x, skvm::F32 y) { return x ** y; });
1498         case k_inverse_IntrinsicKind: {
1499             switch (args[0].slots()) {
1500                 case  4: return this->writeMatrixInverse2x2(args[0]);
1501                 case  9: return this->writeMatrixInverse3x3(args[0]);
1502                 case 16: return this->writeMatrixInverse4x4(args[0]);
1503                 default:
1504                     SkDEBUGFAIL("Invalid call to inverse");
1505                     return {};
1506             }
1507         }
1508 
1509         case k_lessThan_IntrinsicKind:
1510             return nk == Type::NumberKind::kFloat
1511                            ? binary([](skvm::F32 x, skvm::F32 y) { return x < y; })
1512                            : binary([](skvm::I32 x, skvm::I32 y) { return x < y; });
1513         case k_lessThanEqual_IntrinsicKind:
1514             return nk == Type::NumberKind::kFloat
1515                            ? binary([](skvm::F32 x, skvm::F32 y) { return x <= y; })
1516                            : binary([](skvm::I32 x, skvm::I32 y) { return x <= y; });
1517         case k_greaterThan_IntrinsicKind:
1518             return nk == Type::NumberKind::kFloat
1519                            ? binary([](skvm::F32 x, skvm::F32 y) { return x > y; })
1520                            : binary([](skvm::I32 x, skvm::I32 y) { return x > y; });
1521         case k_greaterThanEqual_IntrinsicKind:
1522             return nk == Type::NumberKind::kFloat
1523                            ? binary([](skvm::F32 x, skvm::F32 y) { return x >= y; })
1524                            : binary([](skvm::I32 x, skvm::I32 y) { return x >= y; });
1525 
1526         case k_equal_IntrinsicKind:
1527             return nk == Type::NumberKind::kFloat
1528                            ? binary([](skvm::F32 x, skvm::F32 y) { return x == y; })
1529                            : binary([](skvm::I32 x, skvm::I32 y) { return x == y; });
1530         case k_notEqual_IntrinsicKind:
1531             return nk == Type::NumberKind::kFloat
1532                            ? binary([](skvm::F32 x, skvm::F32 y) { return x != y; })
1533                            : binary([](skvm::I32 x, skvm::I32 y) { return x != y; });
1534 
1535         case k_any_IntrinsicKind: {
1536             skvm::I32 result = i32(args[0][0]);
1537             for (size_t i = 1; i < args[0].slots(); ++i) {
1538                 result |= i32(args[0][i]);
1539             }
1540             return result;
1541         }
1542         case k_all_IntrinsicKind: {
1543             skvm::I32 result = i32(args[0][0]);
1544             for (size_t i = 1; i < args[0].slots(); ++i) {
1545                 result &= i32(args[0][i]);
1546             }
1547             return result;
1548         }
1549         case k_not_IntrinsicKind: return unary(args[0], [](skvm::I32 x) { return ~x; });
1550 
1551         case k_toLinearSrgb_IntrinsicKind: {
1552             skvm::Color color = {
1553                     f32(args[0][0]), f32(args[0][1]), f32(args[0][2]), fBuilder->splat(1.0f)};
1554             color = fCallbacks->toLinearSrgb(color);
1555             Value result(3);
1556             result[0] = color.r;
1557             result[1] = color.g;
1558             result[2] = color.b;
1559             return result;
1560         }
1561         case k_fromLinearSrgb_IntrinsicKind: {
1562             skvm::Color color = {
1563                     f32(args[0][0]), f32(args[0][1]), f32(args[0][2]), fBuilder->splat(1.0f)};
1564             color = fCallbacks->fromLinearSrgb(color);
1565             Value result(3);
1566             result[0] = color.r;
1567             result[1] = color.g;
1568             result[2] = color.b;
1569             return result;
1570         }
1571 
1572         default:
1573             SkDEBUGFAILF("unsupported intrinsic %s", c.function().description().c_str());
1574             return {};
1575     }
1576     SkUNREACHABLE;
1577 }
1578 
writeFunctionCall(const FunctionCall & call)1579 Value SkVMGenerator::writeFunctionCall(const FunctionCall& call) {
1580     if (call.function().isIntrinsic() && !call.function().definition()) {
1581         return this->writeIntrinsicCall(call);
1582     }
1583 
1584     const FunctionDeclaration& decl = call.function();
1585     SkASSERTF(decl.definition(), "no definition for function '%s'", decl.description().c_str());
1586     const FunctionDefinition& funcDef = *decl.definition();
1587 
1588     // Evaluate all arguments, gather the results into a contiguous list of IDs
1589     std::vector<skvm::Val> argVals;
1590     for (const auto& arg : call.arguments()) {
1591         Value v = this->writeExpression(*arg);
1592         for (size_t i = 0; i < v.slots(); ++i) {
1593             argVals.push_back(v[i]);
1594         }
1595     }
1596 
1597     size_t returnSlot;
1598     {
1599         // This merges currentFunction().fReturned into fConditionMask. Lanes that conditionally
1600         // returned in the current function would otherwise resume execution within the child.
1601         ScopedCondition m(this, ~currentFunction().fReturned);
1602         returnSlot = this->writeFunction(call, funcDef, SkSpan(argVals));
1603     }
1604 
1605     // Propagate new values of any 'out' params back to the original arguments
1606     const std::unique_ptr<Expression>* argIter = call.arguments().begin();
1607     size_t valIdx = 0;
1608     for (const Variable* p : decl.parameters()) {
1609         size_t nslots = p->type().slotCount();
1610         if (p->modifiers().fFlags & Modifiers::kOut_Flag) {
1611             Value v(nslots);
1612             for (size_t i = 0; i < nslots; ++i) {
1613                 v[i] = argVals[valIdx + i];
1614             }
1615             const std::unique_ptr<Expression>& arg = *argIter;
1616             this->writeStore(*arg, v);
1617         }
1618         valIdx += nslots;
1619         argIter++;
1620     }
1621 
1622     // Create a result Value from the return slot
1623     return this->getSlotValue(returnSlot, call.type().slotCount());
1624 }
1625 
writeLiteral(const Literal & l)1626 Value SkVMGenerator::writeLiteral(const Literal& l) {
1627     if (l.type().isFloat()) {
1628         return fBuilder->splat(l.as<Literal>().floatValue());
1629     }
1630     if (l.type().isInteger()) {
1631         return fBuilder->splat(static_cast<int>(l.as<Literal>().intValue()));
1632     }
1633     SkASSERT(l.type().isBoolean());
1634     return fBuilder->splat(l.as<Literal>().boolValue() ? ~0 : 0);
1635 }
1636 
writePrefixExpression(const PrefixExpression & p)1637 Value SkVMGenerator::writePrefixExpression(const PrefixExpression& p) {
1638     Value val = this->writeExpression(*p.operand());
1639 
1640     switch (p.getOperator().kind()) {
1641         case Operator::Kind::PLUSPLUS:
1642         case Operator::Kind::MINUSMINUS: {
1643             bool incr = p.getOperator().kind() == Operator::Kind::PLUSPLUS;
1644 
1645             switch (base_number_kind(p.type())) {
1646                 case Type::NumberKind::kFloat:
1647                     val = f32(val) + fBuilder->splat(incr ? 1.0f : -1.0f);
1648                     break;
1649                 case Type::NumberKind::kSigned:
1650                     val = i32(val) + fBuilder->splat(incr ? 1 : -1);
1651                     break;
1652                 default:
1653                     SkASSERT(false);
1654                     return {};
1655             }
1656             return this->writeStore(*p.operand(), val);
1657         }
1658         case Operator::Kind::MINUS: {
1659             switch (base_number_kind(p.type())) {
1660                 case Type::NumberKind::kFloat:
1661                     return this->unary(val, [](skvm::F32 x) { return -x; });
1662                 case Type::NumberKind::kSigned:
1663                     return this->unary(val, [](skvm::I32 x) { return -x; });
1664                 default:
1665                     SkASSERT(false);
1666                     return {};
1667             }
1668         }
1669         case Operator::Kind::LOGICALNOT:
1670         case Operator::Kind::BITWISENOT:
1671             return this->unary(val, [](skvm::I32 x) { return ~x; });
1672         default:
1673             SkASSERT(false);
1674             return {};
1675     }
1676 }
1677 
writePostfixExpression(const PostfixExpression & p)1678 Value SkVMGenerator::writePostfixExpression(const PostfixExpression& p) {
1679     switch (p.getOperator().kind()) {
1680         case Operator::Kind::PLUSPLUS:
1681         case Operator::Kind::MINUSMINUS: {
1682             Value old = this->writeExpression(*p.operand()),
1683                   val = old;
1684             SkASSERT(val.slots() == 1);
1685             bool incr = p.getOperator().kind() == Operator::Kind::PLUSPLUS;
1686 
1687             switch (base_number_kind(p.type())) {
1688                 case Type::NumberKind::kFloat:
1689                     val = f32(val) + fBuilder->splat(incr ? 1.0f : -1.0f);
1690                     break;
1691                 case Type::NumberKind::kSigned:
1692                     val = i32(val) + fBuilder->splat(incr ? 1 : -1);
1693                     break;
1694                 default:
1695                     SkASSERT(false);
1696                     return {};
1697             }
1698             this->writeStore(*p.operand(), val);
1699             return old;
1700         }
1701         default:
1702             SkASSERT(false);
1703             return {};
1704     }
1705 }
1706 
writeSwizzle(const Swizzle & s)1707 Value SkVMGenerator::writeSwizzle(const Swizzle& s) {
1708     Value base = this->writeExpression(*s.base());
1709     Value swizzled(s.components().size());
1710     for (int i = 0; i < s.components().size(); ++i) {
1711         swizzled[i] = base[s.components()[i]];
1712     }
1713     return swizzled;
1714 }
1715 
writeTernaryExpression(const TernaryExpression & t)1716 Value SkVMGenerator::writeTernaryExpression(const TernaryExpression& t) {
1717     skvm::I32 test = i32(this->writeExpression(*t.test()));
1718     Value ifTrue, ifFalse;
1719 
1720     {
1721         ScopedCondition m(this, test);
1722         ifTrue = this->writeExpression(*t.ifTrue());
1723     }
1724     {
1725         ScopedCondition m(this, ~test);
1726         ifFalse = this->writeExpression(*t.ifFalse());
1727     }
1728 
1729     size_t nslots = ifTrue.slots();
1730     SkASSERT(nslots == ifFalse.slots());
1731 
1732     Value result(nslots);
1733     for (size_t i = 0; i < nslots; ++i) {
1734         result[i] = skvm::select(test, i32(ifTrue[i]), i32(ifFalse[i]));
1735     }
1736     return result;
1737 }
1738 
writeExpression(const Expression & e)1739 Value SkVMGenerator::writeExpression(const Expression& e) {
1740     switch (e.kind()) {
1741         case Expression::Kind::kBinary:
1742             return this->writeBinaryExpression(e.as<BinaryExpression>());
1743         case Expression::Kind::kChildCall:
1744             return this->writeChildCall(e.as<ChildCall>());
1745         case Expression::Kind::kConstructorArray:
1746         case Expression::Kind::kConstructorCompound:
1747         case Expression::Kind::kConstructorStruct:
1748             return this->writeAggregationConstructor(e.asAnyConstructor());
1749         case Expression::Kind::kConstructorArrayCast:
1750             return this->writeExpression(*e.as<ConstructorArrayCast>().argument());
1751         case Expression::Kind::kConstructorDiagonalMatrix:
1752             return this->writeConstructorDiagonalMatrix(e.as<ConstructorDiagonalMatrix>());
1753         case Expression::Kind::kConstructorMatrixResize:
1754             return this->writeConstructorMatrixResize(e.as<ConstructorMatrixResize>());
1755         case Expression::Kind::kConstructorScalarCast:
1756         case Expression::Kind::kConstructorCompoundCast:
1757             return this->writeConstructorCast(e.asAnyConstructor());
1758         case Expression::Kind::kConstructorSplat:
1759             return this->writeConstructorSplat(e.as<ConstructorSplat>());
1760         case Expression::Kind::kFieldAccess:
1761             return this->writeFieldAccess(e.as<FieldAccess>());
1762         case Expression::Kind::kIndex:
1763             return this->writeIndexExpression(e.as<IndexExpression>());
1764         case Expression::Kind::kVariableReference:
1765             return this->writeVariableExpression(e.as<VariableReference>());
1766         case Expression::Kind::kLiteral:
1767             return this->writeLiteral(e.as<Literal>());
1768         case Expression::Kind::kFunctionCall:
1769             return this->writeFunctionCall(e.as<FunctionCall>());
1770         case Expression::Kind::kPrefix:
1771             return this->writePrefixExpression(e.as<PrefixExpression>());
1772         case Expression::Kind::kPostfix:
1773             return this->writePostfixExpression(e.as<PostfixExpression>());
1774         case Expression::Kind::kSwizzle:
1775             return this->writeSwizzle(e.as<Swizzle>());
1776         case Expression::Kind::kTernary:
1777             return this->writeTernaryExpression(e.as<TernaryExpression>());
1778         default:
1779             SkDEBUGFAIL("Unsupported expression");
1780             return {};
1781     }
1782 }
1783 
writeStore(const Expression & lhs,const Value & rhs)1784 Value SkVMGenerator::writeStore(const Expression& lhs, const Value& rhs) {
1785     SkASSERTF(rhs.slots() == lhs.type().slotCount(),
1786               "lhs=%s (%s)\nrhs=%zu slot",
1787               lhs.type().description().c_str(), lhs.description().c_str(), rhs.slots());
1788 
1789     // We need to figure out the collection of slots that we're storing into. The l-value (lhs)
1790     // is always a VariableReference, possibly wrapped by one or more Swizzle, FieldAccess, or
1791     // IndexExpressions. The underlying VariableReference has a range of slots for its storage,
1792     // and each expression wrapped around that selects a sub-set of those slots (Field/Index),
1793     // or rearranges them (Swizzle).
1794     SkSTArray<4, size_t, true> slots;
1795     slots.resize(rhs.slots());
1796 
1797     // Start with the identity slot map - this basically says that the values from rhs belong in
1798     // slots [0, 1, 2 ... N] of the lhs.
1799     for (int i = 0; i < slots.size(); ++i) {
1800         slots[i] = i;
1801     }
1802 
1803     // Now, as we peel off each outer expression, adjust 'slots' to be the locations relative to
1804     // the next (inner) expression:
1805     const Expression* expr = &lhs;
1806     while (!expr->is<VariableReference>()) {
1807         switch (expr->kind()) {
1808             case Expression::Kind::kFieldAccess: {
1809                 const FieldAccess& fld = expr->as<FieldAccess>();
1810                 size_t offset = fld.initialSlot();
1811                 for (size_t& s : slots) {
1812                     s += offset;
1813                 }
1814                 expr = fld.base().get();
1815             } break;
1816             case Expression::Kind::kIndex: {
1817                 const IndexExpression& idx = expr->as<IndexExpression>();
1818                 size_t offset = this->indexSlotOffset(idx);
1819                 for (size_t& s : slots) {
1820                     s += offset;
1821                 }
1822                 expr = idx.base().get();
1823             } break;
1824             case Expression::Kind::kSwizzle: {
1825                 const Swizzle& swz = expr->as<Swizzle>();
1826                 for (size_t& s : slots) {
1827                     s = swz.components()[s];
1828                 }
1829                 expr = swz.base().get();
1830             } break;
1831             default:
1832                 // No other kinds of expressions are valid in lvalues. (see Analysis::IsAssignable)
1833                 SkDEBUGFAIL("Invalid expression type");
1834                 return {};
1835         }
1836     }
1837 
1838     // When we get here, 'slots' are all relative to the first slot holding 'var's storage
1839     const Variable& var = *expr->as<VariableReference>().variable();
1840     size_t varSlot = this->getSlot(var);
1841     for (size_t& slot : slots) {
1842         SkASSERT(slot < var.type().slotCount());
1843         slot += varSlot;
1844     }
1845 
1846     // `slots` are now absolute indices into `fSlots`.
1847     skvm::I32 mask = this->mask();
1848     for (size_t i = 0; i < rhs.slots(); ++i) {
1849         int slotNum = slots[i];
1850         skvm::Val conditionalStore = this->writeConditionalStore(fSlots[slotNum].val, rhs[i], mask);
1851         this->writeToSlot(slotNum, conditionalStore);
1852     }
1853 
1854     return rhs;
1855 }
1856 
writeConditionalStore(skvm::Val lhs,skvm::Val rhs,skvm::I32 mask)1857 skvm::Val SkVMGenerator::writeConditionalStore(skvm::Val lhs, skvm::Val rhs, skvm::I32 mask) {
1858     return select(mask, f32(rhs), f32(lhs)).id;
1859 }
1860 
writeBlock(const Block & b)1861 void SkVMGenerator::writeBlock(const Block& b) {
1862     skvm::I32 mask = this->mask();
1863     if (b.blockKind() == Block::Kind::kCompoundStatement) {
1864         this->emitTraceLine(this->getLine(b.fPosition));
1865         ++fInsideCompoundStatement;
1866     } else {
1867         this->emitTraceScope(mask, +1);
1868     }
1869 
1870     for (const std::unique_ptr<Statement>& stmt : b.children()) {
1871         this->writeStatement(*stmt);
1872     }
1873 
1874     if (b.blockKind() == Block::Kind::kCompoundStatement) {
1875         --fInsideCompoundStatement;
1876     } else {
1877         this->emitTraceScope(mask, -1);
1878     }
1879 }
1880 
writeBreakStatement()1881 void SkVMGenerator::writeBreakStatement() {
1882     // Any active lanes stop executing for the duration of the current loop
1883     fLoopMask &= ~this->mask();
1884 }
1885 
writeContinueStatement()1886 void SkVMGenerator::writeContinueStatement() {
1887     // Any active lanes stop executing for the current iteration.
1888     // Remember them in fContinueMask, to be re-enabled later.
1889     skvm::I32 mask = this->mask();
1890     fLoopMask &= ~mask;
1891     fContinueMask |= mask;
1892 }
1893 
writeForStatement(const ForStatement & f)1894 void SkVMGenerator::writeForStatement(const ForStatement& f) {
1895     // We require that all loops be ES2-compliant (unrollable), and actually unroll them here
1896     SkASSERT(f.unrollInfo());
1897     const LoopUnrollInfo& loop = *f.unrollInfo();
1898     SkASSERT(loop.fIndex->type().slotCount() == 1);
1899 
1900     size_t indexSlot = this->getSlot(*loop.fIndex);
1901     double val = loop.fStart;
1902 
1903     const skvm::I32 zero      = fBuilder->splat(0);
1904     skvm::I32 oldLoopMask     = fLoopMask,
1905               oldContinueMask = fContinueMask;
1906 
1907     const Type::NumberKind indexKind = base_number_kind(loop.fIndex->type());
1908 
1909     // We want the loop index to disappear at the end of the loop, so wrap the for statement in a
1910     // trace scope.
1911     if (loop.fCount > 0) {
1912         int line = this->getLine(f.test() ? f.test()->fPosition : f.fPosition);
1913         skvm::I32 mask = this->mask();
1914         this->emitTraceScope(mask, +1);
1915 
1916         for (int i = 0; i < loop.fCount; ++i) {
1917             this->writeToSlot(indexSlot, (indexKind == Type::NumberKind::kFloat)
1918                                             ? fBuilder->splat(static_cast<float>(val)).id
1919                                             : fBuilder->splat(static_cast<int>(val)).id);
1920 
1921             fContinueMask = zero;
1922             this->writeStatement(*f.statement());
1923             fLoopMask |= fContinueMask;
1924 
1925             this->emitTraceLine(line);
1926             val += loop.fDelta;
1927         }
1928 
1929         this->emitTraceScope(mask, -1);
1930     }
1931 
1932     fLoopMask     = oldLoopMask;
1933     fContinueMask = oldContinueMask;
1934 }
1935 
writeIfStatement(const IfStatement & i)1936 void SkVMGenerator::writeIfStatement(const IfStatement& i) {
1937     Value test = this->writeExpression(*i.test());
1938     {
1939         ScopedCondition ifTrue(this, i32(test));
1940         this->writeStatement(*i.ifTrue());
1941     }
1942     if (i.ifFalse()) {
1943         ScopedCondition ifFalse(this, ~i32(test));
1944         this->writeStatement(*i.ifFalse());
1945     }
1946 }
1947 
writeReturnStatement(const ReturnStatement & r)1948 void SkVMGenerator::writeReturnStatement(const ReturnStatement& r) {
1949     skvm::I32 returnsHere = this->mask();
1950 
1951     if (r.expression()) {
1952         Value val = this->writeExpression(*r.expression());
1953 
1954         size_t slot = currentFunction().fReturnSlot;
1955         size_t nslots = r.expression()->type().slotCount();
1956         for (size_t i = 0; i < nslots; ++i) {
1957             fSlots[slot + i].writtenTo = false;
1958             skvm::Val conditionalStore = this->writeConditionalStore(fSlots[slot + i].val, val[i],
1959                                                                      returnsHere);
1960             this->writeToSlot(slot + i, conditionalStore);
1961         }
1962     }
1963 
1964     currentFunction().fReturned |= returnsHere;
1965 }
1966 
writeSwitchStatement(const SwitchStatement & s)1967 void SkVMGenerator::writeSwitchStatement(const SwitchStatement& s) {
1968     skvm::I32 falseValue = fBuilder->splat( 0);
1969     skvm::I32 trueValue  = fBuilder->splat(~0);
1970 
1971     // Create a "switchFallthough" scratch variable, initialized to false.
1972     skvm::I32 switchFallthrough = falseValue;
1973 
1974     // Loop masks behave just like for statements. When a break is encountered, it masks off all
1975     // lanes for the rest of the body of the switch.
1976     skvm::I32 oldLoopMask       = fLoopMask;
1977     Value switchValue           = this->writeExpression(*s.value());
1978 
1979     for (const std::unique_ptr<Statement>& stmt : s.cases()) {
1980         const SwitchCase& c = stmt->as<SwitchCase>();
1981         if (!c.isDefault()) {
1982             Value caseValue = fBuilder->splat((int) c.value());
1983 
1984             // We want to execute this switch case if we're falling through from a previous case, or
1985             // if the case value matches.
1986             ScopedCondition conditionalCaseBlock(
1987                     this,
1988                     switchFallthrough | (i32(caseValue) == i32(switchValue)));
1989             this->writeStatement(*c.statement());
1990 
1991             // If we are inside the case block, we set the fallthrough flag to true (`break` still
1992             // works to stop the flow of execution regardless, since it zeroes out the loop-mask).
1993             switchFallthrough.id = this->writeConditionalStore(switchFallthrough.id, trueValue.id,
1994                                                                this->mask());
1995         } else {
1996             // This is the default case. Since it's always last, we can just dump in the code.
1997             this->writeStatement(*c.statement());
1998         }
1999     }
2000 
2001     // Restore state.
2002     fLoopMask = oldLoopMask;
2003 }
2004 
writeVarDeclaration(const VarDeclaration & decl)2005 void SkVMGenerator::writeVarDeclaration(const VarDeclaration& decl) {
2006     size_t slot   = this->getSlot(*decl.var()),
2007            nslots = decl.var()->type().slotCount();
2008 
2009     Value val = decl.value() ? this->writeExpression(*decl.value()) : Value{};
2010     for (size_t i = 0; i < nslots; ++i) {
2011         fSlots[slot + i].writtenTo = false;
2012         this->writeToSlot(slot + i, val ? val[i] : fBuilder->splat(0.0f).id);
2013     }
2014 }
2015 
emitTraceLine(int line)2016 void SkVMGenerator::emitTraceLine(int line) {
2017     if (fDebugTrace && line > 0 && fInsideCompoundStatement == 0) {
2018         fBuilder->trace_line(fTraceHookID, this->mask(), fTraceMask, line);
2019     }
2020 }
2021 
emitTraceScope(skvm::I32 executionMask,int delta)2022 void SkVMGenerator::emitTraceScope(skvm::I32 executionMask, int delta) {
2023     if (fDebugTrace) {
2024         fBuilder->trace_scope(fTraceHookID, executionMask, fTraceMask, delta);
2025     }
2026 }
2027 
writeStatement(const Statement & s)2028 void SkVMGenerator::writeStatement(const Statement& s) {
2029     // The debugger should stop on all types of statements, except for Blocks.
2030     if (!s.is<Block>()) {
2031         this->emitTraceLine(this->getLine(s.fPosition));
2032     }
2033 
2034     switch (s.kind()) {
2035         case Statement::Kind::kBlock:
2036             this->writeBlock(s.as<Block>());
2037             break;
2038         case Statement::Kind::kBreak:
2039             this->writeBreakStatement();
2040             break;
2041         case Statement::Kind::kContinue:
2042             this->writeContinueStatement();
2043             break;
2044         case Statement::Kind::kExpression:
2045             this->writeExpression(*s.as<ExpressionStatement>().expression());
2046             break;
2047         case Statement::Kind::kFor:
2048             this->writeForStatement(s.as<ForStatement>());
2049             break;
2050         case Statement::Kind::kIf:
2051             this->writeIfStatement(s.as<IfStatement>());
2052             break;
2053         case Statement::Kind::kReturn:
2054             this->writeReturnStatement(s.as<ReturnStatement>());
2055             break;
2056         case Statement::Kind::kSwitch:
2057             this->writeSwitchStatement(s.as<SwitchStatement>());
2058             break;
2059         case Statement::Kind::kVarDeclaration:
2060             this->writeVarDeclaration(s.as<VarDeclaration>());
2061             break;
2062         case Statement::Kind::kDiscard:
2063         case Statement::Kind::kDo:
2064             SkDEBUGFAIL("Unsupported control flow");
2065             break;
2066         case Statement::Kind::kNop:
2067             break;
2068         default:
2069             SkDEBUGFAIL("Unrecognized statement");
2070             break;
2071     }
2072 }
2073 
ProgramToSkVM(const Program & program,const FunctionDefinition & function,skvm::Builder * builder,SkVMDebugTrace * debugTrace,SkSpan<skvm::Val> uniforms,skvm::Coord device,skvm::Coord local,skvm::Color inputColor,skvm::Color destColor,SkVMCallbacks * callbacks)2074 skvm::Color ProgramToSkVM(const Program& program,
2075                           const FunctionDefinition& function,
2076                           skvm::Builder* builder,
2077                           SkVMDebugTrace* debugTrace,
2078                           SkSpan<skvm::Val> uniforms,
2079                           skvm::Coord device,
2080                           skvm::Coord local,
2081                           skvm::Color inputColor,
2082                           skvm::Color destColor,
2083                           SkVMCallbacks* callbacks) {
2084     skvm::Val zero = builder->splat(0.0f).id;
2085     skvm::Val result[4] = {zero,zero,zero,zero};
2086 
2087     skvm::Val args[8];  // At most 8 arguments (half4 srcColor, half4 dstColor)
2088     size_t argSlots = 0;
2089     for (const SkSL::Variable* param : function.declaration().parameters()) {
2090         switch (param->modifiers().fLayout.fBuiltin) {
2091             case SK_MAIN_COORDS_BUILTIN:
2092                 SkASSERT(param->type().slotCount() == 2);
2093                 SkASSERT((argSlots + 2) <= std::size(args));
2094                 args[argSlots++] = local.x.id;
2095                 args[argSlots++] = local.y.id;
2096                 break;
2097             case SK_INPUT_COLOR_BUILTIN:
2098                 SkASSERT(param->type().slotCount() == 4);
2099                 SkASSERT((argSlots + 4) <= std::size(args));
2100                 args[argSlots++] = inputColor.r.id;
2101                 args[argSlots++] = inputColor.g.id;
2102                 args[argSlots++] = inputColor.b.id;
2103                 args[argSlots++] = inputColor.a.id;
2104                 break;
2105             case SK_DEST_COLOR_BUILTIN:
2106                 SkASSERT(param->type().slotCount() == 4);
2107                 SkASSERT((argSlots + 4) <= std::size(args));
2108                 args[argSlots++] = destColor.r.id;
2109                 args[argSlots++] = destColor.g.id;
2110                 args[argSlots++] = destColor.b.id;
2111                 args[argSlots++] = destColor.a.id;
2112                 break;
2113             default:
2114                 SkDEBUGFAIL("Invalid parameter to main()");
2115                 return {};
2116         }
2117     }
2118     SkASSERT(argSlots <= std::size(args));
2119 
2120     // Make sure that the SkVMDebugTrace starts from a clean slate.
2121     if (debugTrace) {
2122         debugTrace->fSlotInfo.clear();
2123         debugTrace->fFuncInfo.clear();
2124         debugTrace->fTraceInfo.clear();
2125     }
2126 
2127     SkVMGenerator generator(program, builder, debugTrace, callbacks);
2128     generator.writeProgram(uniforms, device, function, {args, argSlots}, SkSpan(result));
2129 
2130     return skvm::Color{{builder, result[0]},
2131                        {builder, result[1]},
2132                        {builder, result[2]},
2133                        {builder, result[3]}};
2134 }
2135 
ProgramToSkVM(const Program & program,const FunctionDefinition & function,skvm::Builder * b,SkVMDebugTrace * debugTrace,SkSpan<skvm::Val> uniforms,SkVMSignature * outSignature)2136 bool ProgramToSkVM(const Program& program,
2137                    const FunctionDefinition& function,
2138                    skvm::Builder* b,
2139                    SkVMDebugTrace* debugTrace,
2140                    SkSpan<skvm::Val> uniforms,
2141                    SkVMSignature* outSignature) {
2142     SkVMSignature ignored,
2143                   *signature = outSignature ? outSignature : &ignored;
2144 
2145     std::vector<skvm::Ptr> argPtrs;
2146     std::vector<skvm::Val> argVals;
2147 
2148     for (const Variable* p : function.declaration().parameters()) {
2149         size_t slots = p->type().slotCount();
2150         signature->fParameterSlots += slots;
2151         for (size_t i = 0; i < slots; ++i) {
2152             argPtrs.push_back(b->varying<float>());
2153             argVals.push_back(b->loadF(argPtrs.back()).id);
2154         }
2155     }
2156 
2157     std::vector<skvm::Ptr> returnPtrs;
2158     std::vector<skvm::Val> returnVals;
2159 
2160     signature->fReturnSlots = function.declaration().returnType().slotCount();
2161     for (size_t i = 0; i < signature->fReturnSlots; ++i) {
2162         returnPtrs.push_back(b->varying<float>());
2163         returnVals.push_back(b->splat(0.0f).id);
2164     }
2165 
2166     class Callbacks : public SkVMCallbacks {
2167     public:
2168         Callbacks(skvm::Color color) : fColor(color) {}
2169 
2170         skvm::Color sampleShader(int, skvm::Coord) override {
2171             fUsedUnsupportedFeatures = true;
2172             return fColor;
2173         }
2174         skvm::Color sampleColorFilter(int, skvm::Color) override {
2175             fUsedUnsupportedFeatures = true;
2176             return fColor;
2177         }
2178         skvm::Color sampleBlender(int, skvm::Color, skvm::Color) override {
2179             fUsedUnsupportedFeatures = true;
2180             return fColor;
2181         }
2182 
2183         skvm::Color toLinearSrgb(skvm::Color) override {
2184             fUsedUnsupportedFeatures = true;
2185             return fColor;
2186         }
2187         skvm::Color fromLinearSrgb(skvm::Color) override {
2188             fUsedUnsupportedFeatures = true;
2189             return fColor;
2190         }
2191 
2192         bool fUsedUnsupportedFeatures = false;
2193         const skvm::Color fColor;
2194     };
2195 
2196     // Set up device coordinates so that the rightmost evaluated pixel will be centered on (0, 0).
2197     // (If the coordinates aren't used, dead-code elimination will optimize this away.)
2198     skvm::F32 pixelCenter = b->splat(0.5f);
2199     skvm::Coord device = {pixelCenter, pixelCenter};
2200     device.x += to_F32(b->splat(1) - b->index());
2201 
2202     skvm::F32 zero = b->splat(0.0f);
2203     skvm::Color sampledColor{zero, zero, zero, zero};
2204     Callbacks callbacks(sampledColor);
2205 
2206     SkVMGenerator generator(program, b, debugTrace, &callbacks);
2207     generator.writeProgram(uniforms, device, function, SkSpan(argVals), SkSpan(returnVals));
2208 
2209     // If the SkSL tried to use any shader, colorFilter, or blender objects - we don't have a
2210     // mechanism (yet) for binding to those.
2211     if (callbacks.fUsedUnsupportedFeatures) {
2212         return false;
2213     }
2214 
2215     // generateCode has updated the contents of 'argVals' for any 'out' or 'inout' parameters.
2216     // Propagate those changes back to our varying buffers:
2217     size_t argIdx = 0;
2218     for (const Variable* p : function.declaration().parameters()) {
2219         size_t nslots = p->type().slotCount();
2220         if (p->modifiers().fFlags & Modifiers::kOut_Flag) {
2221             for (size_t i = 0; i < nslots; ++i) {
2222                 b->storeF(argPtrs[argIdx + i], skvm::F32{b, argVals[argIdx + i]});
2223             }
2224         }
2225         argIdx += nslots;
2226     }
2227 
2228     // It's also updated the contents of 'returnVals' with the return value of the entry point.
2229     // Store that as well:
2230     for (size_t i = 0; i < signature->fReturnSlots; ++i) {
2231         b->storeF(returnPtrs[i], skvm::F32{b, returnVals[i]});
2232     }
2233 
2234     return true;
2235 }
2236 
2237 /*
2238  * Testing utility function that emits program's "main" with a minimal harness. Used to create
2239  * representative skvm op sequences for SkSL tests.
2240  */
testingOnly_ProgramToSkVMShader(const Program & program,skvm::Builder * builder,SkVMDebugTrace * debugTrace)2241 bool testingOnly_ProgramToSkVMShader(const Program& program,
2242                                      skvm::Builder* builder,
2243                                      SkVMDebugTrace* debugTrace) {
2244     const SkSL::FunctionDeclaration* main = program.getFunction("main");
2245     if (!main) {
2246         return false;
2247     }
2248 
2249     size_t uniformSlots = 0;
2250     int childSlots = 0;
2251     for (const SkSL::ProgramElement* e : program.elements()) {
2252         if (e->is<GlobalVarDeclaration>()) {
2253             const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
2254             const Variable& var = *decl.varDeclaration().var();
2255             if (var.type().isEffectChild()) {
2256                 childSlots++;
2257             } else if (is_uniform(var)) {
2258                 uniformSlots += var.type().slotCount();
2259             }
2260         }
2261     }
2262 
2263     skvm::Uniforms uniforms(builder->uniform(), 0);
2264 
2265     auto new_uni = [&]() { return builder->uniformF(uniforms.pushF(0.0f)); };
2266 
2267     // Assume identity CTM
2268     skvm::Coord device = {pun_to_F32(builder->index()), new_uni()};
2269     skvm::Coord local  = device;
2270 
2271     class Callbacks : public SkVMCallbacks {
2272     public:
2273         Callbacks(skvm::Builder* builder, skvm::Uniforms* uniforms, int numChildren) {
2274             for (int i = 0; i < numChildren; ++i) {
2275                 fChildren.push_back(
2276                         {uniforms->pushPtr(nullptr), builder->uniform32(uniforms->push(0))});
2277             }
2278         }
2279 
2280         skvm::Color sampleShader(int i, skvm::Coord coord) override {
2281             skvm::PixelFormat pixelFormat = skvm::SkColorType_to_PixelFormat(kRGBA_F32_SkColorType);
2282             skvm::I32 index  = trunc(coord.x);
2283                       index += trunc(coord.y) * fChildren[i].rowBytesAsPixels;
2284             return gather(pixelFormat, fChildren[i].addr, index);
2285         }
2286 
2287         skvm::Color sampleColorFilter(int i, skvm::Color color) override {
2288             return color;
2289         }
2290 
2291         skvm::Color sampleBlender(int i, skvm::Color src, skvm::Color dst) override {
2292             return blend(SkBlendMode::kSrcOver, src, dst);
2293         }
2294 
2295         // TODO(skia:10479): Make these actually convert to/from something like sRGB, for use in
2296         // test files.
2297         skvm::Color toLinearSrgb(skvm::Color color) override {
2298             return color;
2299         }
2300         skvm::Color fromLinearSrgb(skvm::Color color) override {
2301             return color;
2302         }
2303 
2304         struct Child {
2305             skvm::Uniform addr;
2306             skvm::I32     rowBytesAsPixels;
2307         };
2308         std::vector<Child> fChildren;
2309     };
2310     Callbacks callbacks(builder, &uniforms, childSlots);
2311 
2312     std::vector<skvm::Val> uniformVals;
2313     for (size_t i = 0; i < uniformSlots; ++i) {
2314         uniformVals.push_back(new_uni().id);
2315     }
2316 
2317     skvm::Color inColor = builder->uniformColor(SkColors::kWhite, &uniforms);
2318     skvm::Color destColor = builder->uniformColor(SkColors::kBlack, &uniforms);
2319 
2320     skvm::Color result = SkSL::ProgramToSkVM(program, *main->definition(), builder, debugTrace,
2321                                              SkSpan(uniformVals), device, local, inColor,
2322                                              destColor, &callbacks);
2323 
2324     storeF(builder->varying<float>(), result.r);
2325     storeF(builder->varying<float>(), result.g);
2326     storeF(builder->varying<float>(), result.b);
2327     storeF(builder->varying<float>(), result.a);
2328 
2329     return true;
2330 }
2331 
2332 }  // namespace SkSL
2333