Add new SkVM trace op 'trace_scope' to track SkSL scope depth.

This CL adds the trace op and tests the builder commands, but it is not
yet hooked up to the SkVM code generator or debugger.

Change-Id: Iaa64293dfd0973e299eb480cb2b43672b0886c32
Bug: skia:12741
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/484439
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
diff --git a/src/core/SkVM.cpp b/src/core/SkVM.cpp
index 20093e9..5aecf33 100644
--- a/src/core/SkVM.cpp
+++ b/src/core/SkVM.cpp
@@ -276,6 +276,7 @@
                                                                   VarSlot{immB}, "=", V{z}); break;
             case Op::trace_enter: write(o, op, TraceHookID{immA}, V{x}, V{y}, FnIdx{immB}); break;
             case Op::trace_exit:  write(o, op, TraceHookID{immA}, V{x}, V{y}, FnIdx{immB}); break;
+            case Op::trace_scope: write(o, op, TraceHookID{immA}, V{x}, V{y}, Shift{immB}); break;
 
             case Op::store8:   write(o, op, Ptr{immA}, V{x}               ); break;
             case Op::store16:  write(o, op, Ptr{immA}, V{x}               ); break;
@@ -402,6 +403,8 @@
                                                    R{x}, R{y}, FnIdx{immB}); break;
                 case Op::trace_exit:  write(o, op, TraceHookID{immA},
                                                    R{x}, R{y}, FnIdx{immB}); break;
+                case Op::trace_scope: write(o, op, TraceHookID{immA},
+                                                   R{x}, R{y}, Shift{immB}); break;
 
                 case Op::store8:   write(o, op, Ptr{immA}, R{x}                  ); break;
                 case Op::store16:  write(o, op, Ptr{immA}, R{x}                  ); break;
@@ -685,6 +688,15 @@
         if (this->isImm(traceMask.id,~0)) { traceMask = mask; }
         (void)push(Op::trace_exit, mask.id,traceMask.id,NA,NA, traceHookID, fnIdx);
     }
+    void Builder::trace_scope(int traceHookID, I32 mask, I32 traceMask, int delta) {
+        SkASSERT(traceHookID >= 0);
+        SkASSERT(traceHookID < (int)fTraceHooks.size());
+        if (this->isImm(mask.id,      0)) { return; }
+        if (this->isImm(traceMask.id, 0)) { return; }
+        if (this->isImm(mask.id,     ~0)) { mask = traceMask; }
+        if (this->isImm(traceMask.id,~0)) { traceMask = mask; }
+        (void)push(Op::trace_scope, mask.id,traceMask.id,NA,NA, traceHookID, delta);
+    }
 
     void Builder::store8 (Ptr ptr, I32 val) { (void)push(Op::store8 , val.id,NA,NA,NA, ptr.ix); }
     void Builder::store16(Ptr ptr, I32 val) { (void)push(Op::store16, val.id,NA,NA,NA, ptr.ix); }
@@ -2673,6 +2685,7 @@
                 case Op::trace_var:
                 case Op::trace_enter:
                 case Op::trace_exit:
+                case Op::trace_scope:
                     /* Force this program to run in the interpreter. */
                     return false;
 
@@ -3611,6 +3624,7 @@
                 case Op::trace_var:
                 case Op::trace_enter:
                 case Op::trace_exit:
+                case Op::trace_scope:
                     /* Force this program to run in the interpreter. */
                     return false;
 
@@ -3984,6 +3998,7 @@
                 case Op::trace_var:
                 case Op::trace_enter:
                 case Op::trace_exit:
+                case Op::trace_scope:
                     /* Force this program to run in the interpreter. */
                     return false;
 
diff --git a/src/core/SkVM.h b/src/core/SkVM.h
index ed499e7..c0079db 100644
--- a/src/core/SkVM.h
+++ b/src/core/SkVM.h
@@ -434,7 +434,8 @@
     // Order matters a little: Ops <=store128 are treated as having side effects.
     #define SKVM_OPS(M)                                              \
         M(assert_true)                                               \
-        M(trace_line) M(trace_var) M(trace_enter) M(trace_exit)      \
+        M(trace_line) M(trace_var)                                   \
+        M(trace_enter) M(trace_exit) M(trace_scope)                  \
         M(store8)   M(store16)   M(store32) M(store64) M(store128)   \
         M(load8)    M(load16)    M(load32)  M(load64) M(load128)     \
         M(index)                                                     \
@@ -475,7 +476,7 @@
         return Op::store8 <= op && op <= Op::index;
     }
     static inline bool is_trace(Op op) {
-        return Op::trace_line <= op && op <= Op::trace_exit;
+        return Op::trace_line <= op && op <= Op::trace_scope;
     }
 
     using Val = int;
@@ -612,6 +613,7 @@
         virtual void var(int slot, int32_t val) = 0;
         virtual void enter(int fnIdx) = 0;
         virtual void exit(int fnIdx) = 0;
+        virtual void scope(int delta) = 0;
     };
 
     class Builder {
@@ -649,6 +651,7 @@
         void trace_var  (int traceHookID, I32 mask, I32 traceMask, int slot, I32 val);
         void trace_enter(int traceHookID, I32 mask, I32 traceMask, int fnIdx);
         void trace_exit (int traceHookID, I32 mask, I32 traceMask, int fnIdx);
+        void trace_scope(int traceHookID, I32 mask, I32 traceMask, int delta);
 
         // Store {8,16,32,64,128}-bit varying.
         void store8  (Ptr ptr, I32 val);
diff --git a/src/opts/SkVM_opts.h b/src/opts/SkVM_opts.h
index 1db89cd..6038aba 100644
--- a/src/opts/SkVM_opts.h
+++ b/src/opts/SkVM_opts.h
@@ -250,6 +250,12 @@
                         }
                         break;
 
+                    CASE(Op::trace_scope):
+                        if (should_trace(immA, x, y)) {
+                            traceHooks[immA]->scope(immB);
+                        }
+                        break;
+
                     CASE(Op::index): {
                         const int iota[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
                                             16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
diff --git a/src/sksl/codegen/SkSLVMCodeGenerator.cpp b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
index cf115d0..d3ed1f1 100644
--- a/src/sksl/codegen/SkSLVMCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
@@ -82,12 +82,15 @@
             fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kExit,
                                           /*data=*/{fnIdx, 0}});
         }
+        void scope(int delta) override {
+            fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kScope,
+                                          /*data=*/{delta, 0}});
+        }
 
     private:
         SkSL::SkVMDebugTrace* fTrace;
     };
-}
-
+}  // namespace
 
 namespace SkSL {
 
diff --git a/src/sksl/tracing/SkVMDebugTrace.cpp b/src/sksl/tracing/SkVMDebugTrace.cpp
index f15ef9e..88dedc9 100644
--- a/src/sksl/tracing/SkVMDebugTrace.cpp
+++ b/src/sksl/tracing/SkVMDebugTrace.cpp
@@ -153,6 +153,19 @@
                     o->writeText("exit ");
                     o->writeText(fFuncInfo[data0].name.c_str());
                     break;
+
+                case SkSL::SkVMTraceInfo::Op::kScope:
+                    for (int delta = data0; delta < 0; ++delta) {
+                        indent.pop_back();
+                    }
+                    o->writeText(indent.c_str());
+                    o->writeText("scope ");
+                    o->writeText((data0 >= 0) ? "+" : "");
+                    o->writeDecAsText(data0);
+                    for (int delta = data0; delta > 0; --delta) {
+                        indent.push_back(' ');
+                    }
+                    break;
             }
             o->newline();
         }
diff --git a/src/sksl/tracing/SkVMDebugTrace.h b/src/sksl/tracing/SkVMDebugTrace.h
index 40bfef4..3a0792c 100644
--- a/src/sksl/tracing/SkVMDebugTrace.h
+++ b/src/sksl/tracing/SkVMDebugTrace.h
@@ -47,6 +47,7 @@
         kVar,   /** data: slot, value */
         kEnter, /** data: function index, (unused) */
         kExit,  /** data: function index, (unused) */
+        kScope, /** data: scope delta, (unused) */
     };
     Op op;
     int32_t data[2];
diff --git a/src/sksl/tracing/SkVMDebugTracePlayer.cpp b/src/sksl/tracing/SkVMDebugTracePlayer.cpp
index cdaae4e..7b76f70 100644
--- a/src/sksl/tracing/SkVMDebugTracePlayer.cpp
+++ b/src/sksl/tracing/SkVMDebugTracePlayer.cpp
@@ -193,6 +193,10 @@
             fStack.pop_back();
             return true;
         }
+        case SkVMTraceInfo::Op::kScope: { // data: scope delta, (unused)
+            // TODO(skia:12741): track scope depth of variables
+            return false;
+        }
     }
 
     return false;
diff --git a/tests/SkVMTest.cpp b/tests/SkVMTest.cpp
index 199f275..547efa4 100644
--- a/tests/SkVMTest.cpp
+++ b/tests/SkVMTest.cpp
@@ -886,6 +886,7 @@
         void var(int, int32_t) override { fBuffer.push_back(-9999999); }
         void enter(int) override        { fBuffer.push_back(-9999999); }
         void exit(int) override         { fBuffer.push_back(-9999999); }
+        void scope(int) override        { fBuffer.push_back(-9999999); }
         void line(int lineNum) override { fBuffer.push_back(lineNum); }
 
         std::vector<int> fBuffer;
@@ -911,6 +912,7 @@
         void line(int) override                  { fBuffer.push_back(-9999999); }
         void enter(int) override                 { fBuffer.push_back(-9999999); }
         void exit(int) override                  { fBuffer.push_back(-9999999); }
+        void scope(int) override                 { fBuffer.push_back(-9999999); }
         void var(int slot, int32_t val) override {
             fBuffer.push_back(slot);
             fBuffer.push_back(val);
@@ -938,6 +940,7 @@
     public:
         void line(int) override                   { fBuffer.push_back(-9999999); }
         void var(int, int32_t) override           { fBuffer.push_back(-9999999); }
+        void scope(int) override                  { fBuffer.push_back(-9999999); }
         void enter(int fnIdx) override {
             fBuffer.push_back(fnIdx);
             fBuffer.push_back(1);
@@ -965,12 +968,39 @@
     REPORTER_ASSERT(r, (testTrace.fBuffer == std::vector<int>{12, 1, 56, 0}));
 }
 
+DEF_TEST(SkVM_trace_scope, r) {
+    class TestTraceHook : public skvm::TraceHook {
+    public:
+        void var(int, int32_t) override { fBuffer.push_back(-9999999); }
+        void enter(int) override        { fBuffer.push_back(-9999999); }
+        void exit(int) override         { fBuffer.push_back(-9999999); }
+        void line(int) override         { fBuffer.push_back(-9999999); }
+        void scope(int delta) override  { fBuffer.push_back(delta); }
+
+        std::vector<int> fBuffer;
+    };
+
+    skvm::Builder b;
+    TestTraceHook testTrace;
+    int traceHookID = b.attachTraceHook(&testTrace);
+    b.trace_scope(traceHookID, b.splat(0xFFFFFFFF), b.splat(0xFFFFFFFF), 1);
+    b.trace_scope(traceHookID, b.splat(0xFFFFFFFF), b.splat(0x00000000), -2);
+    b.trace_scope(traceHookID, b.splat(0x00000000), b.splat(0x00000000), 3);
+    b.trace_scope(traceHookID, b.splat(0x00000000), b.splat(0xFFFFFFFF), 4);
+    b.trace_scope(traceHookID, b.splat(0xFFFFFFFF), b.splat(0xFFFFFFFF), -5);
+    skvm::Program p = b.done();
+    p.eval(1);
+
+    REPORTER_ASSERT(r, (testTrace.fBuffer == std::vector<int>{1, -5}));
+}
+
 DEF_TEST(SkVM_trace_multiple_hooks, r) {
     class TestTraceHook : public skvm::TraceHook {
     public:
         void var(int, int32_t) override { fBuffer.push_back(-9999999); }
         void enter(int) override        { fBuffer.push_back(-9999999); }
         void exit(int) override         { fBuffer.push_back(-9999999); }
+        void scope(int) override        { fBuffer.push_back(-9999999); }
         void line(int lineNum) override { fBuffer.push_back(lineNum); }
 
         std::vector<int> fBuffer;