changeset 32442:c841a01aca55

Refactor bytecode interpreter to use new functions Rework how cs-lists are expanded. Use vm_dispatch_call() to get the type of dispatch needed instead of multiple is_X checks. Use get_catched_fcn() which arguments are a range of stack element pointers such that no container need to be allocated. * pt-bytecode-vm-internal.h: Expand cs-list at bytecode call Rename i to ii to avoid clang shadow warning * pt-bytecode-vm.cc: Use vm_dispatch_call (). Rework where cs-lists are expanded. Use get_cached_fcn() on vm stack objects * pt-bytecode-vm.h: Type id field for cs-list
author Petter T.
date Fri, 27 Oct 2023 18:19:45 +0200
parents 030cf510f141
children 0abebb3f41e4
files libinterp/parse-tree/pt-bytecode-vm-internal.h libinterp/parse-tree/pt-bytecode-vm.cc libinterp/parse-tree/pt-bytecode-vm.h
diffstat 3 files changed, 533 insertions(+), 533 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/parse-tree/pt-bytecode-vm-internal.h	Fri Oct 27 18:19:39 2023 +0200
+++ b/libinterp/parse-tree/pt-bytecode-vm-internal.h	Fri Oct 27 18:19:45 2023 +0200
@@ -38,9 +38,9 @@
 \
     int n_retval = std::min (static_cast<int> (ovl.length ()), static_cast<int> (nargout));\
     /* We want to push the ovl backwards */\
-    for (int i = n_retval - 1; i >= 0 && actual_nargout < nargout; i--)\
+    for (int ii = n_retval - 1; ii >= 0 && actual_nargout < nargout; ii--)\
     {\
-      octave_value &arg = ovl (i);\
+      octave_value &arg = ovl (ii);\
 \
       if (arg.is_cs_list ())\
         {\
@@ -331,55 +331,49 @@
                                                                                                   \
 /* Construct return values - note nargout */                                                      \
 /* is allready pushed as a uint64 */                                                              \
-for (int i = 1; i < n_returns_callee; i++)                                                        \
+for (int ii = 1; ii < n_returns_callee; ii++)                                                     \
   PUSH_OV ();                                                                                     \
                                                                                                   \
-int ii;                                                                                           \
 int n_args_on_callee_stack = 0;                                                                   \
+bool all_too_many_args = false;                                                                   \
 /* Move the args to the new stack */                                                              \
-if (!has_cs_list_arg) /* TODO: Kludge. Move to beginning. */                                      \
- {                                                                                                \
-    n_args_on_callee_stack = n_args_on_stack;                                                     \
-    for (ii = 0; ii < n_args_on_stack; ii++)                                                      \
+for (int ii = 0; ii < n_args_on_stack; ii++)                                                      \
+  {                                                                                               \
+    octave_value &arg = first_arg[ii].ov;                                                         \
+                                                                                                  \
+    if (arg.is_cs_list ())                                                                        \
       {                                                                                           \
-        PUSH_OV (std::move (first_arg[ii].ov));                                                   \
-        /* Destroy the args */                                                                    \
-        first_arg[ii].ov.~octave_value ();                                                        \
-      }                                                                                           \
-  }                                                                                               \
-else                                                                                              \
-  {                                                                                               \
-    for (ii = 0; ii < n_args_on_stack; ii++)                                                      \
-      {                                                                                           \
-        octave_value &arg = first_arg[ii].ov;                                                     \
-                                                                                                  \
-        if (arg.is_cs_list ())                                                                    \
+        octave_value_list args = arg.list_value ();                                               \
+        octave_idx_type n_el = args.length ();                                                    \
+        if (n_el + n_args_on_callee_stack > 512)                                                  \
           {                                                                                       \
-            /* TODO: Use opcode instead? */                                                       \
-            octave_value_list args = arg.list_value ();                                           \
-            for (int j = 0; j < args.length (); j++)                                              \
+            all_too_many_args = true;                                                             \
+          }                                                                                       \
+        else                                                                                      \
+          {                                                                                       \
+            for (int j = 0; j < n_el; j++)                                                        \
               {                                                                                   \
                 PUSH_OV (args (j));                                                               \
                 n_args_on_callee_stack++;                                                         \
               }                                                                                   \
-        }                                                                                         \
-      else                                                                                        \
-        {                                                                                         \
-          PUSH_OV (std::move (arg));                                                              \
-          n_args_on_callee_stack++;                                                               \
-        }                                                                                         \
-      /* Destroy the args */                                                                      \
-      arg.~octave_value ();                                                                       \
-    }                                                                                             \
+          }                                                                                       \
+      }                                                                                           \
+    else                                                                                          \
+      {                                                                                           \
+        PUSH_OV (std::move (arg));                                                                \
+        n_args_on_callee_stack++;                                                                 \
+      }                                                                                           \
+    /* Destroy the args */                                                                        \
+    arg.~octave_value ();                                                                         \
   }                                                                                               \
 /* Construct missing args */                                                                      \
-for (int i = n_args_on_callee_stack; i < n_args_callee; i++)                                      \
+for (int ii = n_args_on_callee_stack; ii < n_args_callee; ii++)                                   \
   PUSH_OV ();                                                                                     \
                                                                                                   \
 /* Construct locals */                                                                            \
 int n_locals_to_ctor =                                                                            \
   n_locals_callee - n_args_callee - n_returns_callee;                                             \
-for (int i = 0; i < n_locals_to_ctor; i++)                                                        \
+for (int ii = 0; ii < n_locals_to_ctor; ii++)                                                     \
   PUSH_OV ();                                                                                     \
                                                                                                   \
 try                                                                                               \
@@ -398,6 +392,15 @@
 /* "auto var" in the frame object. This is needed if nargout() etc are called */                  \
 set_nargout (nargout);                                                                            \
                                                                                                   \
+if (all_too_many_args)                                                                            \
+  {                                                                                               \
+    std::string fn_name = unwind_data->m_name;                                                    \
+    (*sp++).pee = new execution_exception {"error", "Octave:invalid-fun-call",                    \
+                                           fn_name + ": function called with over 512 inputs."    \
+                                           " Consider using varargin."};                          \
+    (*sp++).i = static_cast<int> (error_type::EXECUTION_EXC);                                     \
+    goto unwind;                                                                                  \
+  }                                                                                               \
 if (n_args_on_callee_stack > n_args_callee)                                                       \
   {                                                                                               \
     std::string fn_name = unwind_data->m_name;                                                    \
--- a/libinterp/parse-tree/pt-bytecode-vm.cc	Fri Oct 27 18:19:39 2023 +0200
+++ b/libinterp/parse-tree/pt-bytecode-vm.cc	Fri Oct 27 18:19:45 2023 +0200
@@ -43,6 +43,7 @@
 #include "pt-tm-const.h"
 #include "pt-stmt.h"
 #include "ov-classdef.h"
+#include "ov-cs-list.h"
 #include "ov-ref.h"
 #include "ov-range.h"
 #include "ov-inline.h"
@@ -674,12 +675,68 @@
     tmp.append (std::move (start[i].ov));
   for (int i = 0; i < n_elem; i++)
     start[i].ov.~octave_value ();
+
+  // Negative n_lift means we need to erase
+  for (int i = n_lift; i < 0; i++)
+    {
+      start[i].ov.~octave_value ();
+      new (start + i) octave_value;
+    }
   for (int i = 0; i < n_lift; i++)
     new (start + i) octave_value;
+
   for (int i = 0; i < n_elem; i++)
     new (start + n_lift + i) octave_value (std::move (tmp.xelem (i)));
 }
 
+static void append_cslist_to_ovl (octave_value_list &ovl, octave_value ov_cs)
+{
+  octave_value_list cslist = ov_cs.list_value (); // TODO: Wastefull copy. octave_cs_list has no const ref to m_list.
+  ovl.append (cslist);
+}
+
+#define POP_STACK_RANGE_TO_OVL(ovl, beg, end) \
+do {                                               \
+  stack_element *pbeg = beg;                       \
+  stack_element *pend = end;                       \
+  while (pbeg != pend)                             \
+    {                                              \
+      if (OCTAVE_UNLIKELY (pbeg->ov.is_cs_list ()))\
+        {                                          \
+          append_cslist_to_ovl (ovl, pbeg->ov);    \
+          pbeg->ov.~octave_value ();               \
+        }                                          \
+      else                                         \
+        {                                          \
+          ovl.append (std::move (pbeg->ov));       \
+          pbeg->ov.~octave_value ();               \
+        }                                          \
+                                                   \
+      pbeg++;                                      \
+    }                                              \
+  sp = beg;                                        \
+                                                   \
+} while (0)
+
+#define COPY_STACK_RANGE_TO_OVL(ovl, beg, end) \
+do {                                \
+  stack_element *pbeg = beg;        \
+  stack_element *pend = end;        \
+  while (pbeg != pend)                             \
+    {                                              \
+      if (OCTAVE_UNLIKELY (pbeg->ov.is_cs_list ()))\
+        {                                          \
+          append_cslist_to_ovl (ovl, pbeg->ov);    \
+        }                                          \
+      else                                         \
+        {                                          \
+          ovl.append (pbeg->ov);                   \
+        }                                          \
+                                                   \
+      pbeg++;                                      \
+    }                                              \
+} while (0)
+
 #define COMMA ,
 #define PRINT_VM_STATE(msg)                                                    \
   do {                                                                         \
@@ -1830,8 +1887,8 @@
       {
         try
           {
-            octave_fcn_cache *ovb_cache = ov.fcn_cache_value ();
-            ov = ovb_cache->get_cached_obj ({});
+            octave_fcn_cache &cache = REP (octave_fcn_cache, ov);
+            ov = cache.get_cached_obj ();
           }
         CATCH_EXECUTION_EXCEPTION
       }
@@ -1863,7 +1920,6 @@
                 // Alot of code in this define
                 PUSH_OV (ov); // Calling convention anticipates object to call on the stack.
                 int n_args_on_stack = 0;
-                bool has_cs_list_arg = false;
                 MAKE_BYTECODE_CALL
 
                 // Now dispatch to first instruction in the
@@ -2065,7 +2121,8 @@
   octave_function *fcn;
   try
     {
-      fcn = ov.get_cached_fcn ({arg});
+      octave_fcn_cache &cache = REP (octave_fcn_cache, ov);
+      fcn = cache.get_cached_fcn (&sp[-1], &sp[0]); // sp[-1] is the arg, sp[0] is the stack end
     }
   CATCH_EXECUTION_EXCEPTION // parse errors might throw in classdefs
 
@@ -2106,7 +2163,10 @@
   octave_function *fcn;
   try
     {
-      fcn = ov.get_cached_fcn ({});
+      octave_fcn_cache &cache = REP (octave_fcn_cache, ov);
+      fcn = cache.get_cached_fcn_if_fresh ();
+      if (! fcn)
+        fcn = cache.get_cached_fcn (static_cast<octave_value*> (nullptr), static_cast<octave_value*> (nullptr));
     }
   CATCH_EXECUTION_EXCEPTION // parse errors might throw in classdefs
 
@@ -2167,69 +2227,59 @@
     // The object to index is before the args on the stack
     octave_value &ov = (sp[-1 - n_args_on_stack]).ov;
 
-    // TODO: The ovl should not be needed
-    // Make an ovl with the args
-    // TODO: Should be inplace moves
-    octave_value_list ovl;
-    bool has_cs_list_arg = false;
-    // The operands are on the top of the stack
-
-    bool all_args_double = true;
-    for (int i = n_args_on_stack - 1; i >= 0; i--)
-      {
-        octave_value &arg = sp[-1 - i].ov;
-        if (arg.type_id () != m_scalar_typeid)
-          all_args_double = false;
-        // If the operand arg is a cs list we need to expand it
-        if (arg.is_cs_list ())
-          {
-            has_cs_list_arg = true;
-            ovl.append (arg.list_value ());
-          }
-        else
-          ovl.append (sp[-1 - i].ov); // TODO: copied, not moved
-      }
-
-    // If the ov is a "full matrix", i.e. based on octave_base_matrix,
-    // and the arguments are all scalar, we modify this opcode to a
-    // specialized opcode for matrix scalar indexing.
-    if (nargout == 1 && all_args_double && ov.is_full_num_matrix () && specialization_ok)
-      {
-        if (n_args_on_stack == 1)
+    switch (ov.vm_dispatch_call ())
+      {
+        case octave_base_value::vm_call_dispatch_type::SUBSREF:
           {
-            ip -= 1;
-            int wide_opcode_offset = slot < 256 ? 0 : -1; // If WIDE is used, we need to look further back
-
-            CHECK (ip[-2 + wide_opcode_offset] == static_cast<unsigned char> (INSTR::INDEX_ID_NARGOUT1));
-            ip[-2 + wide_opcode_offset] = static_cast<unsigned char> (INSTR::INDEX_ID1_MAT_1D);
-
-            goto index_id1_mat_1d;
-          }
-        else if (n_args_on_stack == 2)
-          {
-            ip -= 1;
-            int wide_opcode_offset = slot < 256 ? 0 : -1; // If WIDE is used, we need to look further back
-
-            CHECK (ip[-2 + wide_opcode_offset] == static_cast<unsigned char> (INSTR::INDEX_ID_NARGOUT1));
-            ip[-2 + wide_opcode_offset] = static_cast<unsigned char> (INSTR::INDEX_ID1_MAT_2D);
-
-            goto index_id1_mat_2d;
-          }
-      }
-
-    //TODO: Are the args really destroyed in all paths? Remember cell too
-
-    // octave_fcn_cache and some octave_fcn_handle have caches
-    bool has_function_cache = ov.has_function_cache ();
-
-    if (! has_function_cache && ov.is_defined ())
-      {
-        // It is probably a variable
-        octave_value_list retval;
-
-        if (OCTAVE_LIKELY (! ov.is_function ()
-                           || ov.is_classdef_meta ()))
-          {
+            // Make an ovl with the args
+            octave_value_list ovl;
+
+            // The operands are on the top of the stack
+            bool all_args_double = true;
+            for (int i = n_args_on_stack - 1; i >= 0; i--)
+              {
+                octave_value &arg = sp[-1 - i].ov;
+                int type = arg.type_id ();
+                if (type != m_scalar_typeid)
+                  all_args_double = false;
+
+                if (OCTAVE_UNLIKELY (type == m_cslist_typeid))
+                  ovl.append (arg.list_value ());
+                else
+                  ovl.append (arg); // TODO: copied, not moved
+              }
+
+            // If the ov is a "full matrix", i.e. based on octave_base_matrix,
+            // and the arguments are all scalar, we modify this opcode to a
+            // specialized opcode for matrix scalar indexing.
+            if (nargout == 1 && all_args_double && ov.is_full_num_matrix () && specialization_ok)
+              {
+                if (n_args_on_stack == 1)
+                  {
+                    ip -= 1;
+                    int wide_opcode_offset = slot < 256 ? 0 : -1; // If WIDE is used, we need to look further back
+
+                    CHECK (ip[-2 + wide_opcode_offset] == static_cast<unsigned char> (INSTR::INDEX_ID_NARGOUT1));
+                    ip[-2 + wide_opcode_offset] = static_cast<unsigned char> (INSTR::INDEX_ID1_MAT_1D);
+
+                    goto index_id1_mat_1d;
+                  }
+                else if (n_args_on_stack == 2)
+                  {
+                    ip -= 1;
+                    int wide_opcode_offset = slot < 256 ? 0 : -1; // If WIDE is used, we need to look further back
+
+                    CHECK (ip[-2 + wide_opcode_offset] == static_cast<unsigned char> (INSTR::INDEX_ID_NARGOUT1));
+                    ip[-2 + wide_opcode_offset] = static_cast<unsigned char> (INSTR::INDEX_ID1_MAT_2D);
+
+                    goto index_id1_mat_2d;
+                  }
+              }
+
+            octave_value_list retval;
+
+            CHECK_PANIC (! ov.is_function () || ov.is_classdef_meta ()); // TODO: Remove
+
             try
               {
                 m_tw->set_active_bytecode_ip (ip - code);
@@ -2242,76 +2292,80 @@
             CATCH_BAD_ALLOC
             CATCH_EXIT_EXCEPTION
 
-          }
-        else
-          TODO ("Silly state");
-
-        ov = octave_value ();
-
-        STACK_DESTROY (n_args_on_stack + 1);
-        EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (retval, nargout);
-      }
-    else if (has_function_cache)
-      {
-// The else clause bellow jumps to here
-querry_fcn_cache:
-
-        octave_function *fcn;
-        try
-          {
-            fcn = ov.get_cached_fcn (ovl);
+            ov = octave_value ();
+
+            STACK_DESTROY (n_args_on_stack + 1);
+            EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (retval, nargout);
           }
-        CATCH_EXECUTION_EXCEPTION // parse errors might throw in classdefs
-
-        if (! fcn)
-          {
-            (*sp++).ps = new std::string {name_data[slot]};
-            (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
-            goto unwind;
-          }
-        else if (fcn->is_compiled ())
+        break;
+
+        case octave_base_value::vm_call_dispatch_type::FN_LOOKUP:
           {
-            octave_user_code *usr_fcn = static_cast<octave_user_code *> (fcn);
-
-            // Alot of code in this define
-            MAKE_BYTECODE_CALL
-
-            // Now dispatch to first instruction in the
-            // called function
+            // It is probably a function call
+            CHECK_PANIC (ov.is_nil ()); // TODO :Remove
+
+            // Put a function cache object in the slot and in the local ov
+            ov = octave_value (new octave_fcn_cache (name_data[slot]));
+            if (OCTAVE_UNLIKELY (bsp[slot].ov.is_ref ()))
+              bsp[slot].ov.ref_rep ()->set_value (ov);
+            else
+              bsp[slot].ov = ov;
           }
-        else
+          // Fallthrough
+        case octave_base_value::vm_call_dispatch_type::CALL:
+        case octave_base_value::vm_call_dispatch_type::HANDLE:
+        case octave_base_value::vm_call_dispatch_type::OBJECT:
           {
+            CHECK_PANIC (ov.has_function_cache ()); // TODO :Remove
+
+            octave_function *fcn;
             try
               {
-                m_tw->set_active_bytecode_ip (ip - code);
-                octave_value_list ret = fcn->call (*m_tw, nargout, ovl);
-
-                STACK_DESTROY (n_args_on_stack + 1);
-                EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (ret, nargout);
+                stack_element *first_arg = &sp[-n_args_on_stack];
+                stack_element *end_arg = &sp[0];
+                fcn = ov.get_cached_fcn (first_arg, end_arg);
+              }
+            CATCH_EXECUTION_EXCEPTION // parse errors might throw in classdefs
+
+            if (! fcn)
+              {
+                (*sp++).ps = new std::string {name_data[slot]};
+                (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
+                goto unwind;
+              }
+            else if (fcn->is_compiled ())
+              {
+                octave_user_code *usr_fcn = static_cast<octave_user_code *> (fcn);
+
+                // Alot of code in this define
+                MAKE_BYTECODE_CALL
+
+                // Now dispatch to first instruction in the
+                // called function
               }
-            CATCH_INTERRUPT_EXCEPTION
-            CATCH_INDEX_EXCEPTION
-            CATCH_EXECUTION_EXCEPTION
-            CATCH_BAD_ALLOC
-            CATCH_EXIT_EXCEPTION
+            else
+              {
+                try
+                  {
+                    octave_value_list ovl;// = octave_value_list::make_ovl_from_stack_range (sp - n_args_on_stack, sp);
+                    //sp = sp - n_args_on_stack;
+                    // The operands are on the top of the stack
+                    POP_STACK_RANGE_TO_OVL (ovl, sp - n_args_on_stack, sp);
+
+                    m_tw->set_active_bytecode_ip (ip - code);
+                    octave_value_list ret = fcn->call (*m_tw, nargout, ovl);
+
+                    STACK_DESTROY (1);
+                    EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (ret, nargout);
+                  }
+                CATCH_INTERRUPT_EXCEPTION
+                CATCH_INDEX_EXCEPTION
+                CATCH_EXECUTION_EXCEPTION
+                CATCH_BAD_ALLOC
+                CATCH_EXIT_EXCEPTION
+              }
           }
-      }
-    else
-      {
-        // It is probably a function call
-        if (! ov.is_nil ())
-          {
-            PRINT_VM_STATE("err %s" COMMA name_data[slot].c_str ());
-            TODO ("Not nil object for fcn cache replacement");
-          }
-
-        // Put a function cache object in the slot and in the local ov
-        ov = octave_value (new octave_fcn_cache (name_data[slot]));
-        if (bsp[slot].ov.is_ref ())
-          bsp[slot].ov.ref_rep ()->set_value (ov);
-        else
-          bsp[slot].ov = ov;
-        goto querry_fcn_cache; // Jump into the if clause above
+        break;
       }
   }
   DISPATCH ();
@@ -3899,87 +3953,79 @@
         n_args_on_stack = *ip++;
       }
 
-
     // The object to index is before the args on the stack
     octave_value &ov = (sp[-1 - n_args_on_stack]).ov;
 
-    octave_value_list ovl;
-    // The operands are on the top of the stack
-    for (int i = n_args_on_stack - 1; i >= 0; i--)
-      ovl.append (sp[-1 - i].ov); // TODO: copied, not moved
-    // TODO: Expand cs_list? Probably not since strings?
-
-    // octave_fcn_cache and some octave_fcn_handle have caches
-    bool has_function_cache = ov.has_function_cache ();
-
-    if (! has_function_cache && ov.is_defined ())
-      TODO ("Error: word list command defined");
-
-    else if (has_function_cache)
-      {
-// The else clause bellow jumps to here
-// TODO: Should be a shared thing?
-//       Add a "register" for octave_function *fcn?
-querry_fcn_cache2:
-
-        octave_function *fcn;
-        try
+    switch (ov.vm_dispatch_call ())
+      {
+        case octave_base_value::vm_call_dispatch_type::FN_LOOKUP:
           {
-            fcn = ov.get_cached_fcn (ovl);
-          }
-        CATCH_EXECUTION_EXCEPTION
-
-        if (! fcn)
-          {
-            (*sp++).ps = new std::string {name_data[slot]};
-            (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
-            goto unwind;
+            CHECK_PANIC (ov.is_nil ()); // TODO: Remove
+
+            // Put a function cache object in the slot and in the local ov
+            ov = octave_value (new octave_fcn_cache (name_data[slot]));
+            if (bsp[slot].ov.is_ref ())
+              bsp[slot].ov.ref_rep ()->set_value (ov);
+            else
+              bsp[slot].ov = ov;
           }
-
-        if (fcn->is_compiled ())
+          // Fallthrough
+        case octave_base_value::vm_call_dispatch_type::CALL:
+        case octave_base_value::vm_call_dispatch_type::HANDLE:
+        case octave_base_value::vm_call_dispatch_type::OBJECT:
           {
-            octave_user_code *usr_fcn = static_cast<octave_user_code *> (fcn);
-            // Alot of code in this define
-            bool has_cs_list_arg = false;
-            MAKE_BYTECODE_CALL
-
-            // Now dispatch to first instruction in the
-            // called function
-          }
-        else
-          {
+            octave_function *fcn;
             try
               {
-                m_tw->set_active_bytecode_ip (ip - code);
-                octave_value_list ret = fcn->call (*m_tw, nargout, ovl);
-
-                STACK_DESTROY (n_args_on_stack + 1);
-                EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (ret, nargout);
+                stack_element *first_arg = &sp[-n_args_on_stack];
+                stack_element *end_arg = &sp[0];
+                fcn = ov.get_cached_fcn (first_arg, end_arg);
+              }
+            CATCH_EXECUTION_EXCEPTION
+
+            if (! fcn)
+              {
+                (*sp++).ps = new std::string {name_data[slot]};
+                (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
+                goto unwind;
+              }
+
+            if (fcn->is_compiled ())
+              {
+                octave_user_code *usr_fcn = static_cast<octave_user_code *> (fcn);
+                // Alot of code in this define
+                MAKE_BYTECODE_CALL
+
+                // Now dispatch to first instruction in the
+                // called function
               }
-            CATCH_INTERRUPT_EXCEPTION
-            CATCH_INDEX_EXCEPTION_WITH_NAME
-            CATCH_EXECUTION_EXCEPTION
-            CATCH_BAD_ALLOC
-            CATCH_EXIT_EXCEPTION
-
+            else
+              {
+
+                octave_value_list ovl;
+                // The operands are on the top of the stack
+                POP_STACK_RANGE_TO_OVL (ovl, sp - n_args_on_stack, sp);
+
+                try
+                  {
+                    m_tw->set_active_bytecode_ip (ip - code);
+                    octave_value_list ret = fcn->call (*m_tw, nargout, ovl);
+
+                    STACK_DESTROY (1);
+                    EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (ret, nargout);
+                  }
+                CATCH_INTERRUPT_EXCEPTION
+                CATCH_INDEX_EXCEPTION_WITH_NAME
+                CATCH_EXECUTION_EXCEPTION
+                CATCH_BAD_ALLOC
+                CATCH_EXIT_EXCEPTION
+
+              }
           }
-      }
-    else
-      {
-        // It is probably a function call
-        if (! ov.is_nil ())
-          {
-            PRINT_VM_STATE("err %s" COMMA name_data[slot].c_str ());
-            TODO ("Not nil object for fcn cache replacement");
-          }
-
-        // Put a function cache object in the slot and in the local ov
-        ov = octave_value (new octave_fcn_cache (name_data[slot]));
-        if (bsp[slot].ov.is_ref ())
-          bsp[slot].ov.ref_rep ()->set_value (ov);
-        else
-          bsp[slot].ov = ov;
-        goto querry_fcn_cache2; // Jump into the if clause above
+          break;
+
+        case octave_base_value::vm_call_dispatch_type::SUBSREF:
+          PANIC ("Invalid dispatch");
       }
   }
   DISPATCH ();
@@ -4157,47 +4203,22 @@
     // The object to index is before the args on the stack
     octave_value &ov = (sp[-1 - n_args_on_stack]).ov;
 
-    // TODO: The ovl should not be needed
-    // Make an ovl with the args
-    // TODO: Should be inplace moves
-    // TODO: cl_lists should expanded inplace on the stack instead.
-    // TODO: Cache lookup should just take pointers to the stack.
-    octave_value_list ovl;
-    bool has_cs_list_arg = false;
-    // The operands are on the top of the stack
-    for (int i = n_args_on_stack - 1; i >= 0; i--)
-      {
-        octave_value &arg = sp[-1 - i].ov;
-        // If the operand arg is a cs list we need to expand it
-        if (arg.is_cs_list ())
+    switch (ov.vm_dispatch_call ())
+      {
+        case octave_base_value::vm_call_dispatch_type::SUBSREF:
           {
-            has_cs_list_arg = true;
-            ovl.append (arg.list_value ());
-          }
-        else
-          ovl.append (sp[-1 - i].ov); // TODO: copied, not moved
-      }
-
-    // octave_fcn_cache and some octave_fcn_handle have caches
-    bool has_function_cache = ov.has_function_cache ();
-
-    if (! has_function_cache && ov.is_defined ())
-      {
-        // It is probably a variable
-
-        // TODO: subsref should take ovl instead and be chained,
-        // or something smarter
-        std::list<octave_value_list> idx; // TODO: mallocs!
-
-        idx.push_back(ovl);
-
-
-        // TODO: subsref might throw index error
-        octave_value_list retval;
-
-        if (OCTAVE_LIKELY (! ov.is_function ()
-                           || ov.is_classdef_meta ()))
-          {
+            std::list<octave_value_list> idx; // TODO: mallocs!
+
+            // Make an ovl with the args
+            octave_value_list ovl;
+            // The operands are on the top of the stack
+            POP_STACK_RANGE_TO_OVL (ovl, sp - n_args_on_stack, sp);
+
+            idx.push_back(ovl);
+
+            // TODO: subsref might throw index error
+            octave_value_list retval;
+
             try
               {
                 m_tw->set_active_bytecode_ip (ip - code);
@@ -4210,126 +4231,116 @@
             CATCH_BAD_ALLOC
             CATCH_EXIT_EXCEPTION
 
-          }
-        else
-          {
-            TODO ("Classes not implemented for cell index yet");
-            #if 0
-            retval = handle_superclass (*m_tw,
-                                        ov,
-                                        idx,
-                                        nargout);
-            #endif
+            bool is_fcn = (retval.length () ?
+                            retval(0).is_function() : false);
+
+            // "FIXME: when can the following happen?  In what case does indexing
+            //  result in a value that is a function?  Classdef method calls?
+            //  Something else?"
+
+            if (OCTAVE_LIKELY (!is_fcn))
+              {
+                idx.clear ();
+                // TODO: Necessary? I guess it might trigger dtors
+                // or something?
+                ov = octave_value ();
+              }
+            else
+              {
+                octave_value val = retval(0);
+                octave_function *fcn = val.function_value (true);
+
+                if (fcn)
+                  {
+                    octave_value_list final_args;
+
+                    if (! idx.empty ())
+                      final_args = idx.front ();
+
+                    try
+                      {
+                        m_tw->set_active_bytecode_ip (ip - code);
+                        retval = fcn->call (*m_tw, nargout, final_args);
+                      }
+                    CATCH_INTERRUPT_EXCEPTION
+                    CATCH_INDEX_EXCEPTION
+                    CATCH_EXECUTION_EXCEPTION
+                    CATCH_BAD_ALLOC
+                    CATCH_EXIT_EXCEPTION
+                  }
+
+                idx.clear ();
+                ov = octave_value ();
+                val = octave_value ();
+              }
+
+            STACK_DESTROY (1);
+            EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (retval, nargout);
           }
-
-        bool is_fcn = (retval.length () ?
-                         retval(0).is_function() : false);
-
-        // "FIXME: when can the following happen?  In what case does indexing
-        //  result in a value that is a function?  Classdef method calls?
-        //  Something else?"
-
-        if (OCTAVE_LIKELY (!is_fcn))
+          break;
+
+        case octave_base_value::vm_call_dispatch_type::FN_LOOKUP:
+          {
+            // Put a function cache object in the slot and in the local ov
+            ov = octave_value (new octave_fcn_cache (name_data[slot]));
+            if (bsp[slot].ov.is_ref ())
+              bsp[slot].ov.ref_rep ()->set_value (ov);
+            else
+              bsp[slot].ov = ov;
+          }
+          // Fallthrough
+        case octave_base_value::vm_call_dispatch_type::CALL:
+        case octave_base_value::vm_call_dispatch_type::HANDLE:
+        case octave_base_value::vm_call_dispatch_type::OBJECT:
           {
-            idx.clear ();
-            // TODO: Necessary? I guess it might trigger dtors
-            // or something?
-            ov = octave_value ();
-          }
-        else
-          {
-            octave_value val = retval(0);
-            octave_function *fcn = val.function_value (true);
-
-            if (fcn)
+            octave_function *fcn;
+            try
+              {
+                stack_element *first_arg = &sp[-n_args_on_stack];
+                stack_element *end_arg = &sp[0];
+                fcn = ov.get_cached_fcn (first_arg, end_arg);
+              }
+            CATCH_EXECUTION_EXCEPTION
+
+            if (! fcn)
               {
-                octave_value_list final_args;
-
-                if (! idx.empty ())
-                  final_args = idx.front ();
+                (*sp++).ps = new std::string {name_data[slot]};
+                (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
+                goto unwind;
+              }
+
+            if (fcn->is_compiled ())
+              {
+                octave_user_code *usr_fcn = static_cast<octave_user_code *> (fcn);
+                // Alot of code in this define
+                MAKE_BYTECODE_CALL
+
+                // Now dispatch to first instruction in the
+                // called function
+              }
+            else
+              {
+                // Make an ovl with the args
+                octave_value_list ovl;
+                // The operands are on the top of the stack
+                POP_STACK_RANGE_TO_OVL (ovl, sp - n_args_on_stack, sp);
 
                 try
                   {
                     m_tw->set_active_bytecode_ip (ip - code);
-                    retval = fcn->call (*m_tw, nargout, final_args);
+                    octave_value_list ret = fcn->call (*m_tw, nargout, ovl);
+
+                    STACK_DESTROY (1);
+                    EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (ret, nargout);
                   }
                 CATCH_INTERRUPT_EXCEPTION
-                CATCH_INDEX_EXCEPTION
+                CATCH_INDEX_EXCEPTION_WITH_NAME
                 CATCH_EXECUTION_EXCEPTION
                 CATCH_BAD_ALLOC
                 CATCH_EXIT_EXCEPTION
+
               }
-
-            idx.clear ();
-            ov = octave_value ();
-            val = octave_value ();
-          }
-
-        STACK_DESTROY (n_args_on_stack + 1);
-        EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (retval, nargout);
-      }
-    else if (has_function_cache)
-      {
-// The else clause bellow jumps to here
-querry_fcn_cache3:
-
-        octave_function *fcn;
-        try
-          {
-            fcn = ov.get_cached_fcn (ovl);
-          }
-        CATCH_EXECUTION_EXCEPTION
-
-        if (! fcn)
-          {
-            (*sp++).ps = new std::string {name_data[slot]};
-            (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
-            goto unwind;
           }
-
-        if (fcn->is_compiled ())
-          {
-            octave_user_code *usr_fcn = static_cast<octave_user_code *> (fcn);
-            // Alot of code in this define
-            MAKE_BYTECODE_CALL
-
-            // Now dispatch to first instruction in the
-            // called function
-          }
-        else
-          {
-            try
-              {
-                m_tw->set_active_bytecode_ip (ip - code);
-                octave_value_list ret = fcn->call (*m_tw, nargout, ovl);
-
-                STACK_DESTROY (n_args_on_stack + 1);
-                EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (ret, nargout);
-              }
-            CATCH_INTERRUPT_EXCEPTION
-            CATCH_INDEX_EXCEPTION_WITH_NAME
-            CATCH_EXECUTION_EXCEPTION
-            CATCH_BAD_ALLOC
-            CATCH_EXIT_EXCEPTION
-
-          }
-      }
-    else
-      {
-        // It is probably a function call
-        if (! ov.is_nil ())
-          {
-            PRINT_VM_STATE("err %s" COMMA name_data[slot].c_str ());
-            TODO ("Not nil object for fcn cache replacement");
-          }
-
-        // Put a function cache object in the slot and in the local ov
-        ov = octave_value (new octave_fcn_cache (name_data[slot]));
-        if (bsp[slot].ov.is_ref ())
-          bsp[slot].ov.ref_rep ()->set_value (ov);
-        else
-          bsp[slot].ov = ov;
-        goto querry_fcn_cache3; // Jump into the if clause above
       }
   }
   DISPATCH ();
@@ -5090,7 +5101,6 @@
               ovl.append (arg.list_value ().reverse ());
             else
               ovl.append (std::move (arg));
-
             STACK_DESTROY (1);
           }
         // args are pushed left to right to stack, so we need to reverse the ovl
@@ -5131,18 +5141,19 @@
 
               }
 
-            if (cntr == 0 && ov.has_function_cache () && has_slot)
+            if (cntr == 0 && ov.has_function_cache () && has_slot && !ov.is_classdef_meta ())
               {
                 octave_function *fcn;
                 try
                   {
                     if (step_type == "(")
                       {
-                        fcn = ov.get_cached_fcn (idx.front ());
+                        octave_value_list &ovl = idx.front ();
+                        fcn = ov.get_cached_fcn (ovl);
                       }
                     else // { or .
                       {
-                        fcn = ov.get_cached_fcn ({});
+                        fcn = ov.get_cached_fcn (static_cast<octave_value*> (nullptr), static_cast<octave_value*> (nullptr));
                         eat_args = false; // The function call has no args
                       }
                   }
@@ -5477,45 +5488,23 @@
     // The object to index is before the args on the stack
     octave_value &ov = (sp[-1 - n_args_on_stack]).ov;
 
-    // Make an ovl with the args
-    // TODO: Should be inplace moves
-    octave_value_list ovl;
-    bool has_cs_list_arg = false;
-    // The operands are on the top of the stack
-    for (int i = n_args_on_stack - 1; i >= 0; i--)
-      {
-        octave_value &arg = sp[-1 - i].ov;
-
-        // If the operand arg is a cs list we need to expand it
-        if (arg.is_cs_list ())
+    switch (ov.vm_dispatch_call ())
+      {
+        case octave_base_value::vm_call_dispatch_type::SUBSREF:
           {
-            has_cs_list_arg = true;
-            ovl.append (arg.list_value ());
-          }
-        else
-          ovl.append (sp[-1 - i].ov); // TODO: copied, not moved
-      }
-
-    // octave_fcn_cache and some octave_fcn_handle have caches
-    bool has_function_cache = ov.has_function_cache ();
-
-    if (! has_function_cache && ov.is_defined ())
-      {
-        // It is probably a variable
-
-        // TODO: subsref should take ovl instead and be chained,
-        // or something smarter
-        std::list<octave_value_list> idx; // TODO: mallocs!
-
-        idx.push_back(ovl);
-
-
-        // TODO: subsref might throw index error
-        octave_value_list retval;
-
-        if (OCTAVE_LIKELY (! ov.is_function ()
-                           || ov.is_classdef_meta ()))
-          {
+            // TODO: subsref should take ovl instead and be chained,
+            // or something smarter
+            std::list<octave_value_list> idx; // TODO: mallocs!
+
+            // Make an ovl with the args
+            octave_value_list ovl;
+            // The operands are on the top of the stack
+            POP_STACK_RANGE_TO_OVL (ovl, sp - n_args_on_stack, sp);
+
+            idx.push_back(ovl);
+
+            octave_value_list retval;
+
             try
               {
                 m_tw->set_active_bytecode_ip (ip - code);
@@ -5528,133 +5517,139 @@
             CATCH_BAD_ALLOC
             CATCH_EXIT_EXCEPTION
 
+            bool is_fcn = (retval.length () ?
+                            retval(0).is_function() : false);
+
+            // "FIXME: when can the following happen?  In what case does indexing
+            //  result in a value that is a function?  Classdef method calls?
+            //  Something else?"
+
+            if (OCTAVE_LIKELY (!is_fcn))
+              {
+                idx.clear ();
+                // TODO: Necessary? I guess it might trigger dtors
+                // or something?
+                ov = octave_value ();
+              }
+            else
+              {
+                octave_value val = retval(0);
+                octave_function *fcn = val.function_value (true);
+
+                if (fcn)
+                  {
+                    octave_value_list final_args;
+
+                    if (! idx.empty ())
+                      final_args = idx.front ();
+
+                    try
+                      {
+                        m_tw->set_active_bytecode_ip (ip - code);
+                        retval = fcn->call (*m_tw, nargout, final_args);
+                      }
+                    CATCH_INTERRUPT_EXCEPTION
+                    CATCH_INDEX_EXCEPTION_WITH_MAYBE_NAME (has_slot)
+                    CATCH_EXECUTION_EXCEPTION
+                    CATCH_BAD_ALLOC
+                    CATCH_EXIT_EXCEPTION
+                  }
+
+                idx.clear ();
+                ov = octave_value ();
+                val = octave_value ();
+              }
+
+            // Destroy the indexed variable on the stack
+            STACK_DESTROY (1);
+            EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (retval, nargout);
           }
-        else
-          PANIC ("Strange state");
-
-        bool is_fcn = (retval.length () ?
-                         retval(0).is_function() : false);
-
-        // "FIXME: when can the following happen?  In what case does indexing
-        //  result in a value that is a function?  Classdef method calls?
-        //  Something else?"
-
-        if (OCTAVE_LIKELY (!is_fcn))
+        break;
+
+        case octave_base_value::vm_call_dispatch_type::FN_LOOKUP:
           {
-            idx.clear ();
-            // TODO: Necessary? I guess it might trigger dtors
-            // or something?
-            ov = octave_value ();
+            // If the first object is not an identifier we can't look it up for
+            // a function call.
+            if (!has_slot)
+              {
+                (*sp++).ps = new std::string {"temporary object"};
+                (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
+                goto unwind;
+              }
+
+            if (! ov.is_nil ())
+              {
+                TODO ("Not nil object for fcn cache replacement");
+              }
+
+            // It is probably a function call.
+            // Put a function cache object in the slot and in the local ov
+            // and jump into the if clause above to search for some function
+            // to call.
+            ov = octave_value (new octave_fcn_cache (name_data[slot]));
+            if (bsp[slot].ov.is_ref ())
+              bsp[slot].ov.ref_rep ()->set_value (ov);
+            else
+              bsp[slot].ov = ov;
           }
-        else
+        // Fallthrough
+        case octave_base_value::vm_call_dispatch_type::CALL:
+        case octave_base_value::vm_call_dispatch_type::HANDLE:
+        case octave_base_value::vm_call_dispatch_type::OBJECT:
           {
-            octave_value val = retval(0);
-            octave_function *fcn = val.function_value (true);
-
-            if (fcn)
+            octave_function *fcn;
+            try
+              {
+                stack_element *first_arg = &sp[-n_args_on_stack];
+                stack_element *end_arg = &sp[0];
+                fcn = ov.get_cached_fcn (first_arg, end_arg);
+              }
+            CATCH_EXECUTION_EXCEPTION
+
+            if (! fcn)
               {
-                octave_value_list final_args;
-
-                if (! idx.empty ())
-                  final_args = idx.front ();
+                if (has_slot)
+                  (*sp++).ps = new std::string {name_data[slot]};
+                else
+                  (*sp++).ps = new std::string {"temporary object"};
+                (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
+                goto unwind;
+              }
+
+            if (fcn->is_compiled ())
+              {
+                octave_user_code *usr_fcn = static_cast<octave_user_code *> (fcn);
+                // Alot of code in this define
+                MAKE_BYTECODE_CALL
+
+                // Now dispatch to first instruction in the
+                // called function
+              }
+            else
+              {
+                // Make an ovl with the args
+                octave_value_list ovl;
+                // The operands are on the top of the stack
+                POP_STACK_RANGE_TO_OVL (ovl, sp - n_args_on_stack, sp);
 
                 try
                   {
                     m_tw->set_active_bytecode_ip (ip - code);
-                    retval = fcn->call (*m_tw, nargout, final_args);
+                    octave_value_list ret = fcn->call (*m_tw, nargout, ovl);
+
+                    STACK_DESTROY (1);
+                    EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (ret, nargout);
                   }
                 CATCH_INTERRUPT_EXCEPTION
-                CATCH_INDEX_EXCEPTION_WITH_MAYBE_NAME (has_slot)
+                CATCH_INDEX_EXCEPTION
                 CATCH_EXECUTION_EXCEPTION
                 CATCH_BAD_ALLOC
                 CATCH_EXIT_EXCEPTION
+
               }
-
-            idx.clear ();
-            ov = octave_value ();
-            val = octave_value ();
-          }
-
-        // TODO: Maybe the args should be destroyed before the indexed
-        // variable?
-        STACK_DESTROY (n_args_on_stack + 1);
-        EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (retval, nargout);
-      }
-    else if (has_function_cache)
-      {
-querry_fcn_cache_index_obj:
-
-        octave_function *fcn;
-        try
-          {
-            fcn = ov.get_cached_fcn (ovl);
-          }
-        CATCH_EXECUTION_EXCEPTION
-
-        if (! fcn)
-          {
-            if (has_slot)
-              (*sp++).ps = new std::string {name_data[slot]};
-            else
-              (*sp++).ps = new std::string {"temporary object"};
-            (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
-            goto unwind;
-          }
-
-        if (fcn->is_compiled ())
-          {
-            octave_user_code *usr_fcn = static_cast<octave_user_code *> (fcn);
-            // Alot of code in this define
-            MAKE_BYTECODE_CALL
-
-            // Now dispatch to first instruction in the
-            // called function
           }
-        else
-          {
-            try
-              {
-                m_tw->set_active_bytecode_ip (ip - code);
-                octave_value_list ret = fcn->call (*m_tw, nargout, ovl);
-
-                STACK_DESTROY (n_args_on_stack + 1);
-                EXPAND_CSLIST_PUSH_N_OVL_ELEMENTS_TO_STACK (ret, nargout);
-              }
-            CATCH_INTERRUPT_EXCEPTION
-            CATCH_INDEX_EXCEPTION
-            CATCH_EXECUTION_EXCEPTION
-            CATCH_BAD_ALLOC
-            CATCH_EXIT_EXCEPTION
-
-          }
-      }
-    else
-      {
-        // If the first object is not an identifier we can't look it up for
-        // a function call.
-        if (!has_slot)
-          {
-            (*sp++).ps = new std::string {"temporary object"};
-            (*sp++).i = static_cast<int>(error_type::ID_UNDEFINED);
-            goto unwind;
-          }
-
-        if (! ov.is_nil ())
-          {
-            TODO ("Not nil object for fcn cache replacement");
-          }
-
-        // It is probably a function call.
-        // Put a function cache object in the slot and in the local ov
-        // and jump into the if clause above to search for some function
-        // to call.
-        ov = octave_value (new octave_fcn_cache (name_data[slot]));
-        if (bsp[slot].ov.is_ref ())
-          bsp[slot].ov.ref_rep ()->set_value (ov);
-        else
-          bsp[slot].ov = ov;
-        goto querry_fcn_cache_index_obj;
-      }
+      }
+
   }
   DISPATCH ();
 load_far_cst:
@@ -6540,6 +6535,7 @@
   CHECK (octave_scalar::static_type_id () == m_scalar_typeid);
   CHECK (octave_bool::static_type_id () == m_bool_typeid);
   CHECK (octave_matrix::static_type_id () == m_matrix_typeid);
+  CHECK (octave_cs_list::static_type_id () == m_cslist_typeid);
 
   // Function pointer used for specialized op-codes
   m_fn_dbl_mul = m_ti->lookup_binary_op (octave_value::binary_op::op_mul, m_scalar_typeid, m_scalar_typeid);
--- a/libinterp/parse-tree/pt-bytecode-vm.h	Fri Oct 27 18:19:39 2023 +0200
+++ b/libinterp/parse-tree/pt-bytecode-vm.h	Fri Oct 27 18:19:45 2023 +0200
@@ -505,6 +505,7 @@
   static int constexpr m_scalar_typeid = 2;
   static int constexpr m_matrix_typeid = 4;
   static int constexpr m_bool_typeid = 10;
+  static int constexpr m_cslist_typeid = 36;
 
   // If there are any ignored outputs, e.g. "[x, ~] = foo ()", we need to push a separate
   // stack frame with the ignored outputs for isargout () to be able to querry for ignored