protovm: insert/delete array operations

Change-Id: I2d6f25c903e6df227e454891e037913b412245dc
diff --git a/protos/perfetto/protovm/vm_program.proto b/protos/perfetto/protovm/vm_program.proto
index 292e6f5..23daabb 100644
--- a/protos/perfetto/protovm/vm_program.proto
+++ b/protos/perfetto/protovm/vm_program.proto
@@ -30,6 +30,7 @@
     VmOpMerge merge = 3;
     VmOpSet set = 4;
     VmOpDel del = 5;
+    VmOpInsert insert = 8;
   }
 
   enum AbortLevel {
@@ -56,12 +57,14 @@
   optional bool create_if_not_exist = 3;
 
   // A path component is either: (1) a field id to descend into (common case);
-  // (2) an array index (for repeated fields); (3) a map lookup operation (for
-  // repeated fields, where we snoop a sub-field as a map key)
+  // (2) an hardcoded array index (for repeated fields); (3) an array index in a
+  // register (for repeated fields) (4) a map lookup operation (for repeated
+  // fields, where we snoop a sub-field as a map key)
   message PathComponent {
     oneof field {
       uint32 field_id = 1;
       uint32 array_index = 2;
+      uint32 array_index_register = 4;
 
       // The ID of the field in the repeated submessage that we
       // use as a key (e.g. LayerState.layer_id). When setting this
@@ -117,9 +120,16 @@
 // pre-existing field.
 message VmOpSet {}
 
-// Delete the field or message pointed by DST.
+// Deletes the field or message pointed by DST.
+// If DST points to an array element (repeated field), subsequent elements are
+// shifted left by one position.
 message VmOpDel {}
 
+// Inserts SRC into DST. If DST points to an array element, the pointed element
+// (and the subsequent ones) are shited right by one position. In other cases,
+// the operation is semantically equivalent to VmOpSet.
+message VmOpInsert {}
+
 enum VmCursorEnum {
   VM_CURSOR_UNSPECIFIED = 0;
   VM_CURSOR_SRC = 1;
diff --git a/src/protovm/test/protos/incremental_trace.proto b/src/protovm/test/protos/incremental_trace.proto
index 549b939..e0a17c7 100644
--- a/src/protovm/test/protos/incremental_trace.proto
+++ b/src/protovm/test/protos/incremental_trace.proto
@@ -29,8 +29,15 @@
   repeated Element elements = 1;
 }
 
+message ElementAndIndex {
+  optional Element element = 1;
+  optional int32 index = 2;
+}
+
 message Patch {
-  repeated int32 elements_to_delete = 1;
+  repeated int32 elements_to_delete_by_id = 1;
   repeated Element elements_to_merge = 2;
   repeated Element elements_to_set = 3;
-};
+  repeated int32 elements_to_delete_by_index = 4;
+  repeated ElementAndIndex elements_to_insert_by_index = 5;
+}
diff --git a/src/protovm/test/sample_packets.h b/src/protovm/test/sample_packets.h
index c117617..15b8079 100644
--- a/src/protovm/test/sample_packets.h
+++ b/src/protovm/test/sample_packets.h
@@ -69,7 +69,7 @@
 
   static protos::Patch PatchWithDelOperation() {
     protos::Patch patch;
-    patch.add_elements_to_delete(0);
+    patch.add_elements_to_delete_by_id(0);
     return patch;
   }
 
diff --git a/src/protovm/test/sample_programs.h b/src/protovm/test/sample_programs.h
index 7aab2d2..4924513 100644
--- a/src/protovm/test/sample_programs.h
+++ b/src/protovm/test/sample_programs.h
@@ -268,14 +268,16 @@
     perfetto::protos::VmProgram program;
 
     constexpr uint32_t REGISTER_HOLDING_ELEMENT_ID = 0;
+    constexpr uint32_t REGISTER_HOLDING_ELEMENT_INDEX = 1;
 
-    // Process elements_to_delete
+    // Process elements_to_delete_by_id
     {
-      // select element to delete (src)
+      // select element to delete by id (src)
       auto* instr_src_select = program.add_instructions();
       auto* src_select = instr_src_select->mutable_select();
       auto* src_element = src_select->add_relative_path();
-      src_element->set_field_id(protos::Patch::kElementsToDeleteFieldNumber);
+      src_element->set_field_id(
+          protos::Patch::kElementsToDeleteByIdFieldNumber);
       src_element->set_is_repeated(true);
 
       {
@@ -393,6 +395,101 @@
       }
     }
 
+    // SRC: for each Patch.elements_to_delete_by_index[i]
+    {
+      auto* instr_src_select = program.add_instructions();
+      auto* src_select = instr_src_select->mutable_select();
+      auto* src_element = src_select->add_relative_path();
+      src_element->set_field_id(
+          protos::Patch::kElementsToDeleteByIndexFieldNumber);
+      src_element->set_is_repeated(true);
+
+      // SRC: load index into register
+      {
+        auto* instr_reg_load = instr_src_select->add_nested_instructions();
+        auto* reg_load = instr_reg_load->mutable_reg_load();
+        reg_load->set_dst_register(REGISTER_HOLDING_ELEMENT_INDEX);
+      }
+
+      // DST: select TraceEntry.elements[register]
+      {
+        auto* instr_dst_select = instr_src_select->add_nested_instructions();
+        auto* dst_select = instr_dst_select->mutable_select();
+        dst_select->set_cursor(perfetto::protos::VmCursorEnum::VM_CURSOR_DST);
+        dst_select->set_create_if_not_exist(false);
+        auto* component_elements = dst_select->add_relative_path();
+        component_elements->set_field_id(
+            protos::TraceEntry::kElementsFieldNumber);
+        auto* component_array_index = dst_select->add_relative_path();
+        component_array_index->set_array_index_register(
+            REGISTER_HOLDING_ELEMENT_INDEX);
+
+        // delete
+        {
+          auto* instr_del = instr_dst_select->add_nested_instructions();
+          instr_del->mutable_del();
+        }
+      }
+    }
+
+    // SRC: for each Patch.elements_to_insert_by_index[i]
+    {
+      auto* instr_select_element_and_index = program.add_instructions();
+      auto* select_elemente_and_index =
+          instr_select_element_and_index->mutable_select();
+      auto* element_and_index = select_elemente_and_index->add_relative_path();
+      element_and_index->set_field_id(
+          protos::Patch::kElementsToInsertByIndexFieldNumber);
+      element_and_index->set_is_repeated(true);
+
+      // SRC: select Patch.elements_to_insert_by_index[i].index
+      {
+        auto* instr_select_index =
+            instr_select_element_and_index->add_nested_instructions();
+        auto* select_index = instr_select_index->mutable_select();
+        auto* index = select_index->add_relative_path();
+        index->set_field_id(protos::ElementAndIndex::kIndexFieldNumber);
+
+        // SRC: load index into register
+        {
+          auto* instr_reg_load = instr_select_index->add_nested_instructions();
+          auto* reg_load = instr_reg_load->mutable_reg_load();
+          reg_load->set_dst_register(REGISTER_HOLDING_ELEMENT_INDEX);
+        }
+      }
+
+      // SRC: select Patch.elements_to_insert_by_index[i].element
+      {
+        auto* instr_select_element =
+            instr_select_element_and_index->add_nested_instructions();
+        auto* select_element = instr_select_element->mutable_select();
+        auto* element = select_element->add_relative_path();
+        element->set_field_id(protos::ElementAndIndex::kElementFieldNumber);
+
+        // DST: select TraceEntry.elements[register]
+        {
+          auto* instr_dst_select_element =
+              instr_select_element_and_index->add_nested_instructions();
+          auto* select_dst_element = instr_dst_select_element->mutable_select();
+          select_dst_element->set_cursor(
+              perfetto::protos::VmCursorEnum::VM_CURSOR_DST);
+          select_dst_element->set_create_if_not_exist(true);
+          auto* dst_element = select_dst_element->add_relative_path();
+          dst_element->set_field_id(protos::TraceEntry::kElementsFieldNumber);
+          auto* component_array_index = select_dst_element->add_relative_path();
+          component_array_index->set_array_index_register(
+              REGISTER_HOLDING_ELEMENT_INDEX);
+
+          // insert
+          {
+            auto* instr_array_insert =
+                instr_dst_select_element->add_nested_instructions();
+            instr_array_insert->mutable_insert();
+          }
+        }
+      }
+    }
+
     return program;
   }
 };