Interpreter: Optimize common swizzles

Generates better code for the runtime color filter, for example.

Change-Id: Id86dfdbd93b2419cc2f72245fe33d5109b1ca59d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/215834
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index 16d43f4..28aa1cd 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -134,6 +134,27 @@
     }
 }
 
+// A "simple" Swizzle is based on a variable (or a compound variable like a struct or array), and
+// that references consecutive values, such that it can be implemented using normal load/store ops
+// with an offset. Note that all single-component swizzles (of suitable base types) are simple.
+static bool swizzle_is_simple(const Swizzle& s) {
+    switch (s.fBase->fKind) {
+        case Expression::kFieldAccess_Kind:
+        case Expression::kIndex_Kind:
+        case Expression::kVariableReference_Kind:
+            break;
+        default:
+            return false;
+    }
+
+    for (size_t i = 1; i < s.fComponents.size(); ++i) {
+        if (s.fComponents[i] != s.fComponents[i - 1] + 1) {
+            return false;
+        }
+    }
+    return true;
+}
+
 int ByteCodeGenerator::getLocation(const Variable& var) {
     // given that we seldom have more than a couple of variables, linear search is probably the most
     // efficient way to handle lookups
@@ -192,6 +213,7 @@
     }
 }
 
+// TODO: Elide Add 0 and Mul 1 sequences
 int ByteCodeGenerator::getLocation(const Expression& expr, Variable::Storage* storage) {
     switch (expr.fKind) {
         case Expression::kFieldAccess_Kind: {
@@ -237,6 +259,20 @@
             this->write(ByteCodeInstruction::kAddI);
             return -1;
         }
+        case Expression::kSwizzle_Kind: {
+            const Swizzle& s = (const Swizzle&)expr;
+            SkASSERT(swizzle_is_simple(s));
+            int baseAddr = this->getLocation(*s.fBase, storage);
+            int offset = s.fComponents[0];
+            if (baseAddr < 0) {
+                this->write(ByteCodeInstruction::kPushImmediate);
+                this->write32(offset);
+                this->write(ByteCodeInstruction::kAddI);
+                return -1;
+            } else {
+                return baseAddr + offset;
+            }
+        }
         case Expression::kVariableReference_Kind: {
             const Variable& var = ((const VariableReference&)expr).fVariable;
             *storage = var.fStorage;
@@ -619,6 +655,11 @@
 }
 
 void ByteCodeGenerator::writeSwizzle(const Swizzle& s) {
+    if (swizzle_is_simple(s)) {
+        this->writeVariableExpression(s);
+        return;
+    }
+
     switch (s.fBase->fKind) {
         case Expression::kVariableReference_Kind: {
             const Variable& var = ((VariableReference&) *s.fBase).fVariable;
@@ -825,8 +866,12 @@
         case Expression::kIndex_Kind:
         case Expression::kVariableReference_Kind:
             return std::unique_ptr<LValue>(new ByteCodeExpressionLValue(this, e));
-        case Expression::kSwizzle_Kind:
-            return std::unique_ptr<LValue>(new ByteCodeSwizzleLValue(this, (Swizzle&) e));
+        case Expression::kSwizzle_Kind: {
+            const Swizzle& s = (const Swizzle&) e;
+            return swizzle_is_simple(s)
+                    ? std::unique_ptr<LValue>(new ByteCodeExpressionLValue(this, e))
+                    : std::unique_ptr<LValue>(new ByteCodeSwizzleLValue(this, s));
+        }
         case Expression::kTernary_Kind:
         default:
             printf("unsupported lvalue %s\n", e.description().c_str());