diff libinterp/corefcn/symrec.h @ 26661:cf9e10ce3351

move variable values from symbol_record objects to stack_frame objects Apologies for the massive commit. I see no way to untangle these changes into a set of smaller incremental changes in a way that would be more useful. Previously, handling data for recursive function calls was managed by a stack of values in the symbol_record class and an auxiliary integer variable was used for managing the recursion depth (context_id). Now, values for local variables are in the stack_frame class and recursion is handled naturally by the call_stack as a new stack frame is added to the call_stack object for any call to a function or a script. Values for internal function call information (nargin, nargout, etc.) are now stored specially in the stack_frame object. Values for global variables are now stored in a map in the call_stack object. Values for persistent variables are stored in the corresponding octave_user_function object. Access to non-local variables inside nested functions is managed through pointers to stack_frame objects for the parent function scopes. The new implementation more closely resembles the techniques described in standard compiler literature. These changes should make it easier to create proper closures and finally solve bug #39257 (handles to nested functions are not yet supported). They may also make it easier to implement JIT compiler, though that is probably still a long way off. The new stack-frame.h file has some details about the new implementation of stack frames that should help in understanding how things work now. Describing each change to each file and function will probably not provide much greater understanding of the changes and would be quite tedious to write so I am omitting them.
author John W. Eaton <jwe@octave.org>
date Mon, 28 Jan 2019 18:01:46 +0000
parents 00f796120a6d
children 568565a70b58
line wrap: on
line diff
--- a/libinterp/corefcn/symrec.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/symrec.h	Mon Jan 28 18:01:46 2019 +0000
@@ -49,28 +49,12 @@
     // generic variable
     static const unsigned int local = 1;
 
-    // varargin, argn, .nargin., .nargout.
-    // (FIXME: is this really used now?)
-    static const unsigned int automatic = 2;
-
     // formal parameter
-    static const unsigned int formal = 4;
-
-    // not listed or cleared (.nargin., .nargout.)
-    static const unsigned int hidden = 8;
-
-    // inherited from parent scope; not cleared at function exit
-    static const unsigned int inherited = 16;
-
-    // global (redirects to global scope)
-    static const unsigned int global = 32;
-
-    // not cleared at function exit
-    static const unsigned int persistent = 64;
+    static const unsigned int formal = 2;
 
     // this symbol may NOT become a variable.
     // (symbol added to a static workspace)
-    static const unsigned int added_static = 128;
+    static const unsigned int added_static = 4;
 
   private:
 
@@ -78,464 +62,78 @@
     {
     public:
 
-      symbol_record_rep (const std::string& nm, const octave_value& v,
-                         unsigned int sc)
-        : m_storage_class (sc), m_name (nm), m_fwd_scope (),
-          m_fwd_rep (), m_value_stack ()
-      {
-        m_value_stack.push_back (v);
-      }
+      symbol_record_rep (const std::string& nm, unsigned int sc)
+        : m_frame_offset (0), m_data_offset (0), m_storage_class (sc),
+          m_name (nm)
+      { }
 
-      // No copying!
+      symbol_record_rep (const symbol_record_rep&) = default;
 
-      symbol_record_rep (const symbol_record_rep& ov) = delete;
-
-      symbol_record_rep& operator = (const symbol_record_rep&) = delete;
+      symbol_record_rep& operator = (const symbol_record_rep&) = default;
 
       ~symbol_record_rep (void) = default;
 
-      void assign (const octave_value& value, context_id context)
-      {
-        varref(context) = value;
-      }
-
-      void assign (octave_value::assign_op op,
-                   const std::string& type,
-                   const std::list<octave_value_list>& idx,
-                   const octave_value& value, context_id context)
-      {
-        varref(context).assign (op, type, idx, value);
-      }
-
-      void assign (octave_value::assign_op op, const octave_value& value,
-                   context_id context)
-      {
-        varref(context).assign (op, value);
-      }
-
-      void do_non_const_unary_op (octave_value::unary_op op,
-                                  context_id context)
-      {
-        varref(context).do_non_const_unary_op (op);
-      }
+      // FIXME: use special storage class instead?
+      bool is_valid (void) const { return ! m_name.empty (); }
 
-      void do_non_const_unary_op (octave_value::unary_op op,
-                                  const std::string& type,
-                                  const std::list<octave_value_list>& idx,
-                                  context_id context)
-      {
-        varref(context).do_non_const_unary_op (op, type, idx);
-      }
-
-      context_id get_fwd_scope_context (void) const;
-
-      octave_value& varref (context_id context)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->varref (get_fwd_scope_context ());
-
-        if (is_persistent ())
-          context = 0;
-
-        context_id n = m_value_stack.size ();
-        while (n++ <= context)
-          m_value_stack.push_back (octave_value ());
-
-        return m_value_stack[context];
-      }
-
-      octave_value varval (context_id context) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->varval (get_fwd_scope_context ());
-
-        if (is_persistent ())
-          context = 0;
-
-        if (context < m_value_stack.size ())
-          return m_value_stack[context];
-        else
-          return octave_value ();
-      }
+      void set_frame_offset (size_t offset) { m_frame_offset = offset; }
 
-      void push_context (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return;
-
-        if (! (is_persistent () || is_global ()))
-          m_value_stack.push_back (octave_value ());
-      }
-
-      // If pop_context returns 0, we are out of values and this element
-      // of the symbol table should be deleted.  This can happen for
-      // functions like
-      //
-      //   function foo (n)
-      //     if (n > 0)
-      //       foo (n-1);
-      //     else
-      //       eval ("x = 1");
-      //     endif
-      //   endfunction
-      //
-      // Here, X should only exist in the final stack frame.
-
-      context_id pop_context (void)
-      {
-        context_id retval = 1;
-
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return retval;
-
-        if (! (is_persistent () || is_global ()))
-          {
-            m_value_stack.pop_back ();
-            retval = m_value_stack.size ();
-          }
+      size_t frame_offset (void) const { return m_frame_offset; }
 
-        return retval;
-      }
-
-      void clear (context_id context)
-      {
-        // For globals, break the link to the global value first, then
-        // clear the local value.
-
-        if (is_global ())
-          unbind_global_rep ();
-
-        if (! (is_hidden () || is_inherited ()))
-          {
-            assign (octave_value (), context);
-
-            // For persistent values, we clear the value then unmark so
-            // that we clear the first element of the value stack.
+      void set_data_offset (size_t offset) { m_data_offset = offset; }
 
-            if (is_persistent ())
-              unmark_persistent ();
-          }
-      }
-
-      bool is_defined (context_id context) const
-      {
-        return varval (context).is_defined ();
-      }
-
-      bool is_variable (context_id context) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_variable (context);
-
-        return (! is_local () || is_defined (context));
-      }
+      size_t data_offset (void) const { return m_data_offset; }
 
       bool is_local (void) const
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_local ();
-
         return m_storage_class & local;
       }
 
-      bool is_automatic (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_automatic ();
-
-        return m_storage_class & automatic;
-      }
-
       bool is_formal (void) const
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_formal ();
-
         return m_storage_class & formal;
       }
 
-      bool is_hidden (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_hidden ();
-
-        return m_storage_class & hidden;
-      }
-
-      bool is_inherited (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_inherited ();
-
-        return m_storage_class & inherited;
-      }
-
-      bool is_forwarded (void) const
-      {
-        return ! m_fwd_rep.expired ();
-      }
-
-      bool is_global (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_global ();
-
-        return is_marked_global ();
-      }
-
-      bool is_persistent (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_persistent ();
-
-        return m_storage_class & persistent;
-      }
-
       bool is_added_static (void) const
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_added_static ();
-
         return m_storage_class & added_static;
       }
 
       void mark_local (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_local ();
-            return;
-          }
-
         m_storage_class |= local;
       }
 
-      void mark_automatic (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_automatic ();
-            return;
-          }
-
-        m_storage_class |= automatic;
-      }
-
       void mark_formal (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_formal ();
-            return;
-          }
-
         m_storage_class |= formal;
       }
 
-      void mark_hidden (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_hidden ();
-            return;
-          }
-
-        m_storage_class |= hidden;
-      }
-
-      void mark_inherited (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_inherited ();
-            return;
-          }
-
-        m_storage_class |= inherited;
-      }
-
-      // This flag should only be set for a symbol record that is
-      // actually in the global symbol_scope, and that should only
-      // happen when it is added to the global symbol_scope.
-
-      void mark_global (void)
-      {
-        m_storage_class |= global;
-      }
-
-      bool is_marked_global (void) const
-      {
-        return m_storage_class & global;
-      }
-
-      void mark_persistent (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_persistent ();
-            return;
-          }
-
-        if (is_global ())
-          error ("can't make global variable %s persistent", m_name.c_str ());
-
-        if (is_formal ())
-          error ("can't make function parameter %s persistent", m_name.c_str ());
-        m_storage_class |= persistent;
-      }
-
       void mark_added_static (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_added_static ();
-            return;
-          }
-
         m_storage_class |= added_static;
       }
 
       void unmark_local (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_local ();
-            return;
-          }
-
         m_storage_class &= ~local;
       }
 
-      void unmark_automatic (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_automatic ();
-            return;
-          }
-
-        m_storage_class &= ~automatic;
-      }
-
       void unmark_formal (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_formal ();
-            return;
-          }
-
         m_storage_class &= ~formal;
       }
 
-      void unmark_hidden (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_hidden ();
-            return;
-          }
-
-        m_storage_class &= ~hidden;
-      }
-
-      void unmark_inherited (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_inherited ();
-            return;
-          }
-
-        m_storage_class &= ~inherited;
-      }
-
-      void unmark_persistent (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_persistent ();
-            return;
-          }
-
-        m_storage_class &= ~persistent;
-      }
-
       void unmark_added_static (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_added_static ();
-            return;
-          }
-
         m_storage_class &= ~added_static;
       }
 
       unsigned int storage_class (void) const { return m_storage_class; }
 
-      void init_persistent (void);
-
-      void bind_fwd_rep (const std::shared_ptr<symbol_scope_rep>& fwd_scope,
-                         const std::shared_ptr<symbol_record_rep>& fwd_rep)
-      {
-        // If this object is already bound to another scope (for
-        // example, a variable in a script or nested function is bound
-        // to the enclosing scope), then bind that object to the next
-        // scope.  FIXME: can this happen for any other reason than we
-        // are making a variable in a script global?
-
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            // If this is the symbol in the global scope, then don't
-            // forward again!
-
-            if (t_fwd_rep->is_marked_global ())
-              return;
-
-            t_fwd_rep->bind_fwd_rep (fwd_scope, fwd_rep);
-          }
-
-        m_fwd_scope = fwd_scope;
-        m_fwd_rep = fwd_rep;
-      }
+      std::shared_ptr<symbol_record_rep> dup (void) const;
 
-      void unbind_fwd_rep (void)
-      {
-        // When unbinding variables in a script scope, we only break
-        // the immediate link.  By doing that, we ensure that any
-        // variables that are made global in a script remain linked as
-        // globals in the enclosing scope.
-
-        m_fwd_scope = std::weak_ptr<symbol_scope_rep> ();
-        m_fwd_rep.reset ();
-      }
-
-      void unbind_global_rep (void)
-      {
-        // Break the link to the global symbol_record_rep.  These must
-        // forwarded, so we don't do anything unless the forward rep
-        // points to something.
-
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            if (t_fwd_rep->is_marked_global ())
-              {
-                // The rep this object points to is in the global
-                // scope, so delete the link to it.
-
-                m_fwd_scope = std::weak_ptr<symbol_scope_rep> ();
-                m_fwd_rep.reset ();
-              }
-            else
-              t_fwd_rep->unbind_global_rep ();
-          }
-      }
-
-      std::shared_ptr<symbol_record_rep>
-      dup (const std::shared_ptr<symbol_scope_rep>& new_scope) const;
-
-      octave_value dump (context_id context) const;
+      octave_value dump (void) const;
 
       std::string name (void) const { return m_name; }
 
@@ -543,23 +141,23 @@
 
     private:
 
+      size_t m_frame_offset;
+      size_t m_data_offset;
+
       unsigned int m_storage_class;
 
       std::string m_name;
-
-      std::weak_ptr<symbol_scope_rep> m_fwd_scope;
-
-      std::weak_ptr<symbol_record_rep> m_fwd_rep;
-
-      std::deque<octave_value> m_value_stack;
     };
 
   public:
 
-    symbol_record (const std::string& nm = "",
-                   const octave_value& v = octave_value (),
+    symbol_record (const std::string& nm = "", unsigned int sc = local)
+      : m_rep (new symbol_record_rep (nm, sc))
+    { }
+
+    symbol_record (const std::string& nm, const octave_value&,
                    unsigned int sc = local)
-      : m_rep (new symbol_record_rep (nm, v, sc))
+      : m_rep (new symbol_record_rep (nm, sc))
     { }
 
     symbol_record (const symbol_record&) = default;
@@ -568,132 +166,39 @@
 
     ~symbol_record (void) = default;
 
-    symbol_record dup (const std::shared_ptr<symbol_scope_rep>& sid) const
-    {
-      return symbol_record (m_rep->dup (sid));
-    }
+    bool is_valid (void) const { return m_rep->is_valid (); }
+
+    explicit operator bool () const { return is_valid (); }
+
+    void set_frame_offset (size_t offset) { m_rep->set_frame_offset (offset); }
+
+    size_t frame_offset (void) const { return m_rep->frame_offset (); }
+
+    void set_data_offset (size_t offset) { m_rep->set_data_offset (offset); }
+
+    size_t data_offset (void) const { return m_rep->data_offset (); }
+
+    symbol_record dup (void) const { return symbol_record (m_rep->dup ()); }
 
     std::string name (void) const { return m_rep->name (); }
 
     void rename (const std::string& new_name) { m_rep->rename (new_name); }
 
-    octave_value
-    find (context_id context,
-          const octave_value_list& args = octave_value_list ()) const
-    {
-      octave_value retval = varval (context);
-
-      if (retval.is_undefined ())
-        return find_function (name (), args);
-
-      return retval;
-    }
-
-    void assign (const octave_value& value, context_id context)
-    {
-      m_rep->assign (value, context);
-    }
-
-    void assign (octave_value::assign_op op,
-                 const std::string& type,
-                 const std::list<octave_value_list>& idx,
-                 const octave_value& value, context_id context)
-    {
-      m_rep->assign (op, type, idx, value, context);
-    }
-
-    void assign (octave_value::assign_op op, const octave_value& value,
-                 context_id context)
-    {
-      m_rep->assign (op, value, context);
-    }
-
-    void do_non_const_unary_op (octave_value::unary_op op, context_id context)
-    {
-      m_rep->do_non_const_unary_op (op, context);
-    }
-
-    void do_non_const_unary_op (octave_value::unary_op op,
-                                const std::string& type,
-                                const std::list<octave_value_list>& idx,
-                                context_id context)
-    {
-      m_rep->do_non_const_unary_op (op, type, idx, context);
-    }
-
-    octave_value varval (context_id context) const
-    {
-      return m_rep->varval (context);
-    }
-
-    void push_context (void) { m_rep->push_context (); }
-
-    context_id pop_context (void) { return m_rep->pop_context (); }
-
-    void clear (context_id context) { m_rep->clear (context); }
-
-    bool is_defined (context_id context) const
-    {
-      return m_rep->is_defined (context);
-    }
-
-    bool is_undefined (context_id context) const
-    {
-      return ! m_rep->is_defined (context);
-    }
-
-    bool is_variable (context_id context) const
-    {
-      return m_rep->is_variable (context);
-    }
-
     bool is_local (void) const { return m_rep->is_local (); }
-    bool is_automatic (void) const { return m_rep->is_automatic (); }
     bool is_formal (void) const { return m_rep->is_formal (); }
-    bool is_global (void) const { return m_rep->is_global (); }
-    bool is_hidden (void) const { return m_rep->is_hidden (); }
-    bool is_inherited (void) const { return m_rep->is_inherited (); }
-    bool is_forwarded (void) const { return m_rep->is_forwarded (); }
-    bool is_persistent (void) const { return m_rep->is_persistent (); }
     bool is_added_static (void) const { return m_rep->is_added_static (); }
 
     void mark_local (void) { m_rep->mark_local (); }
-    void mark_automatic (void) { m_rep->mark_automatic (); }
     void mark_formal (void) { m_rep->mark_formal (); }
-    void mark_hidden (void) { m_rep->mark_hidden (); }
-    void mark_inherited (void) { m_rep->mark_inherited (); }
-    void mark_persistent (void) { m_rep->mark_persistent (); }
     void mark_added_static (void) { m_rep->mark_added_static (); }
 
     void unmark_local (void) { m_rep->unmark_local (); }
-    void unmark_automatic (void) { m_rep->unmark_automatic (); }
     void unmark_formal (void) { m_rep->unmark_formal (); }
-    void unmark_hidden (void) { m_rep->unmark_hidden (); }
-    void unmark_inherited (void) { m_rep->unmark_inherited (); }
-    void unmark_persistent (void) { m_rep->unmark_persistent (); }
     void unmark_added_static (void) { m_rep->unmark_added_static (); }
 
-    bool is_marked_global (void) const { return m_rep->is_marked_global (); }
-    void mark_global (void) { m_rep->mark_global (); }
-
-    void init_persistent (void) { m_rep->init_persistent (); }
-
     unsigned int storage_class (void) const { return m_rep->storage_class (); }
 
-    void bind_fwd_rep (const std::shared_ptr<symbol_scope_rep>& fwd_scope,
-                       const symbol_record& sr)
-    {
-      m_rep->bind_fwd_rep (fwd_scope, sr.m_rep);
-    }
-
-    void unbind_global_rep (void) { m_rep->unbind_global_rep (); }
-
-    void unbind_fwd_rep (void) { m_rep->unbind_fwd_rep (); }
-
-    octave_value dump (context_id context) const
-    {
-      return m_rep->dump (context);
-    }
+    octave_value dump (void) const { return m_rep->dump (); }
 
   private:
 
@@ -703,11 +208,6 @@
     symbol_record (const std::shared_ptr<symbol_record_rep>& new_rep)
       : m_rep (new_rep)
     { }
-
-    octave_value find_function (const std::string& name,
-                                const octave_value_list& args) const;
-
-    static octave_value dummy_octave_value;
   };
 }