changeset 32328:dfcbe2dcd8e2

VM Fix '[]' as rhs in chained subassigns (bug #64704) Using the constant '[]' as rhs in e.g. 'a(ii) = b(ii) = []' made '[]' lose its deleting abilities on 'a' due to call to storable(). * libinterp/parse-tree/pt-bytecode-vm.cc: New opcode DUP_MOVE * libinterp/parse-tree/pt-bytecode-walk.cc: Use DUP_MOVE instead of slots and FORCE_ASSIGN * libinterp/parse-tree/pt-bytecode.h: Opcode enum * test/compile/bytecode_subsasgn.m: Update tests
author Petter T. <petter.vilhelm@gmail.com>
date Sat, 23 Sep 2023 00:45:01 +0200
parents 55ea70c8d00a
children 0c32e838e522
files libinterp/parse-tree/pt-bytecode-vm.cc libinterp/parse-tree/pt-bytecode-walk.cc libinterp/parse-tree/pt-bytecode.h test/compile/bytecode_subsasgn.m
diffstat 4 files changed, 82 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/parse-tree/pt-bytecode-vm.cc	Sat Sep 23 08:03:06 2023 -0400
+++ b/libinterp/parse-tree/pt-bytecode-vm.cc	Sat Sep 23 00:45:01 2023 +0200
@@ -279,6 +279,7 @@
           CASE_START (LOAD_CST_ALT4)  PCHAR () CASE_END ()
           CASE_START (LOAD_2_CST)     PCHAR () CASE_END ()
           CASE_START (POP_N_INTS)     PCHAR () CASE_END ()
+          CASE_START (DUP_MOVE)       PCHAR () CASE_END ()
 
           CASE_START (ASSIGN)                     PSLOT() CASE_END ()
           CASE_START (BIND_ANS)                   PSLOT() CASE_END ()
@@ -864,6 +865,7 @@
       &&anon_maybe_set_ignore_output,
       &&enter_nested_frame,
       &&install_function,
+      &&dup_move,
     };
 
   if (OCTAVE_UNLIKELY (m_profiler_enabled))
@@ -6211,6 +6213,16 @@
     goto *instr [opcode]; // Execute the next opcode
   }
 
+  dup_move:
+  {
+    // Copy the top of the stack and move it n positions down the stack.
+    int n = arg0;
+
+    octave_value ov = sp[-1].ov;
+    sp[-1 - n].ov = ov;
+  }
+  DISPATCH ();
+
   enter_script_frame:
   {
     auto fp = m_tw->get_current_stack_frame ();
--- a/libinterp/parse-tree/pt-bytecode-walk.cc	Sat Sep 23 08:03:06 2023 -0400
+++ b/libinterp/parse-tree/pt-bytecode-walk.cc	Sat Sep 23 00:45:01 2023 +0200
@@ -3589,6 +3589,11 @@
 
           tree_argument_list *arg = *arg_lists.begin ();
 
+          // rhs will be copied to this stack position if assigns are chained.
+          // (Chained as in b(1) = c(2) = 3)
+          if (DEPTH () != 1)
+            PUSH_CODE (INSTR::PUSH_NIL);
+
           int nargs = 0;
           if (arg)
             {
@@ -3612,19 +3617,19 @@
           // The value of rhs is on the operand stack now
 
           // If the assignment is not at root we want to keep the
-          // value on the stack, e.g.
+          // rhs value on the stack, e.g.
           //   a = b(1) = 3;
           //   Gives: a == 3
-          // We use a slot to store the rhs in.
-          std::string rhs_copy_nm = "%rhs_" + std::to_string (CODE_SIZE ());
-          int slot_cpy = -1;
+          //
+          // If that is the case, we pushed a nil earlier, so we
+          // copy the top of the stack (rhs) to the nils place in the stack. 
+          // The copy will then be in place to be rhs again.
           if (DEPTH () != 1)
             {
-              slot_cpy = add_id_to_table (rhs_copy_nm);
-              PUSH_CODE (INSTR::DUP);
-              MAYBE_PUSH_WIDE_OPEXT (slot_cpy);
-              PUSH_CODE (INSTR::FORCE_ASSIGN);
-              PUSH_SLOT (slot_cpy);
+              PUSH_CODE (INSTR::DUP_MOVE);
+              // There is rhs and n args on the stack, over the nil
+              // we want to copy rhs (the top of the stack) to.
+              PUSH_CODE (nargs + 1);
             }
 
           int slot = SLOT (name);
@@ -3633,13 +3638,6 @@
           PUSH_SLOT (slot);
           PUSH_CODE (nargs);
 
-          if (DEPTH () != 1)
-            {
-              MAYBE_PUSH_WIDE_OPEXT (slot_cpy);
-              PUSH_CODE (INSTR::PUSH_SLOT_INDEXED);
-              PUSH_SLOT (slot_cpy);
-            }
-
           maybe_emit_push_and_disp_id (expr, name);
         }
       else if (type == '.')
@@ -3670,15 +3668,19 @@
               rhs->accept (*this);
               // The value of rhs is on the operand stack now
 
-              std::string rhs_copy_nm = "%rhs_" + std::to_string (CODE_SIZE ());
-              int slot_cpy = -1;
-              if (DEPTH () != 1) // Chained assignments?
+              // If the assignment is not at root we want to keep the
+              // rhs value on the stack, e.g.
+              //   a = b(1) = 3;
+              //   Gives: a == 3
+              //
+              // If that is the case, dup rhs on the stack.
+              // The copy will then be in place to be rhs again.
+              if (DEPTH () != 1)
                 {
-                  slot_cpy = add_id_to_table (rhs_copy_nm);
+                  // There is 1 rhs and no args on the stack, so just do a dup,
+                  // not a DUP_MOVE like for SUBASSIGN_ID and SUBASSIGN_CELL that
+                  // got args on the stack that need to be moved around.
                   PUSH_CODE (INSTR::DUP);
-                  MAYBE_PUSH_WIDE_OPEXT (slot_cpy);
-                  PUSH_CODE (INSTR::FORCE_ASSIGN);
-                  PUSH_SLOT (slot_cpy);
                 }
 
               int slot = SLOT (name);
@@ -3687,13 +3689,6 @@
               PUSH_SLOT (slot);
               PUSH_WSLOT (slot_field);
 
-              if (DEPTH () != 1)
-                {
-                  MAYBE_PUSH_WIDE_OPEXT (slot_cpy);
-                  PUSH_CODE (INSTR::PUSH_SLOT_INDEXED);
-                  PUSH_SLOT (slot_cpy);
-                }
-
               maybe_emit_push_and_disp_id (expr, name);
             }
           else if (is_dynamic_field && is_id)
@@ -3831,6 +3826,11 @@
           CHECK (arg_lists.size ());
           tree_argument_list *arg = *arg_lists.begin ();
 
+          // rhs will be copied to this stack position if assigns are chained.
+          // (Chained as in b(1) = c(2) = 3)
+          if (DEPTH () != 1)
+            PUSH_CODE (INSTR::PUSH_NIL);
+
           int nargs = 0;
           if (arg)
             {
@@ -3854,19 +3854,19 @@
           // The value of rhs is on the operand stack now
 
           // If the assignment is not at root we want to keep the
-          // value on the stack, e.g.
-          // a = b(1) = 3;
-          // Gives: a == 3
-          // We use a slot to store the rhs in.
-          std::string rhs_copy_nm = "%rhs_" + std::to_string (CODE_SIZE ());
-          int slot_cpy = -1;
+          // rhs value on the stack, e.g.
+          //   a = b(1) = 3;
+          //   Gives: a == 3
+          //
+          // If that is the case, we pushed a nil earlier, so we
+          // copy the top of the stack (rhs) to the nils place in the stack. 
+          // The copy will then be in place to be rhs again.
           if (DEPTH () != 1)
             {
-              slot_cpy = add_id_to_table (rhs_copy_nm);
-              PUSH_CODE (INSTR::DUP);
-              MAYBE_PUSH_WIDE_OPEXT (slot_cpy);
-              PUSH_CODE (INSTR::FORCE_ASSIGN);
-              PUSH_SLOT (slot_cpy);
+              PUSH_CODE (INSTR::DUP_MOVE);
+              // There is rhs and n args on the stack, over the nil
+              // we want to copy rhs (the top of the stack) to.
+              PUSH_CODE (nargs + 1);
             }
 
           int slot = SLOT (name);
@@ -3875,13 +3875,6 @@
           PUSH_SLOT (slot);
           PUSH_CODE (nargs);
 
-          if (DEPTH () != 1)
-            {
-              MAYBE_PUSH_WIDE_OPEXT (slot_cpy);
-              PUSH_CODE (INSTR::PUSH_SLOT_INDEXED);
-              PUSH_SLOT (slot_cpy);
-            }
-
           maybe_emit_push_and_disp_id (expr, name);
         }
       else
--- a/libinterp/parse-tree/pt-bytecode.h	Sat Sep 23 08:03:06 2023 -0400
+++ b/libinterp/parse-tree/pt-bytecode.h	Sat Sep 23 00:45:01 2023 +0200
@@ -193,6 +193,7 @@
   ANON_MAYBE_SET_IGNORE_OUTPUTS,
   ENTER_NESTED_FRAME,
   INSTALL_FUNCTION,
+  DUP_MOVE,
 };
 
 enum class unwind_entry_type
--- a/test/compile/bytecode_subsasgn.m	Sat Sep 23 08:03:06 2023 -0400
+++ b/test/compile/bytecode_subsasgn.m	Sat Sep 23 00:45:01 2023 +0200
@@ -112,4 +112,32 @@
   M = ones (2,2);
   M(idx{:}) *= 3;
   __printf_assert__ ("%d ", M);
+
+  % SUBASSIGN_ID need to handle magic [] constant when chained (bug #64704)
+  M1 = [1 2 3];
+  M2 = [4 5 6];
+  M3 = [7 8 9];
+
+  M1(1) = M2(2) = M3(3) = [];
+  assert (M1 == [2 3])
+  assert (M2 == [4 6])
+  assert (M3 == [7 8])
+
+  M1 = {1 2 3};
+  M2 = {4 5 6};
+  M3 = {7 8 9};
+
+  M1(1) = M2(2) = M3(3) = [];
+  assert (cell2mat (M1) == [2 3])
+  assert (cell2mat (M2) == [4 6])
+  assert (cell2mat (M3) == [7 8])
+
+  M1 = [1 2 3; 4 5 6];
+  M2 = {4 5 6; 7 8 9};
+  S3.a = "zxc";
+
+  M1(1,2) = M2{2,1} = S3.a = 123;
+  assert (M1 == [1 123 3; 4 5 6])
+  assert (cell2mat (M2) == [4 5 6; 123 8 9])
+  assert (S3.a == 123)
 end