# HG changeset patch
# User Petter Tomner <tomner@kth.se>
# Date 1640022319 -3600
#      Mon Dec 20 18:45:19 2021 +0100
# Node ID f40431034aef59dfeef79cc0d2bf5defca19e56c
# Parent  c3e215768aeb4b44cafce6da1d08d53c210d4091
interpreter: octave_lvalue with m_rep pointer

Moving members of octave_lvalue to "representation" pointer like in octave_value
to decrease stack footprint. Different classes for different kind of lvalues.

libinterp/
	* corefcn/stack-frame.h: New functions needed
	* parse-tree/oct-lvalue.cc: m_rep like in octave_value
	* parse-tree/oct-lvalue.h:
	* parse-tree/pt-id.cc: Make black hole

diff -r c3e215768aeb -r f40431034aef libinterp/corefcn/stack-frame.h
--- a/libinterp/corefcn/stack-frame.h	Sat Dec 18 13:42:45 2021 +0100
+++ b/libinterp/corefcn/stack-frame.h	Mon Dec 20 18:45:19 2021 +0100
@@ -461,6 +461,15 @@
     }
 
     void assign (octave_value::assign_op op, const symbol_record& sym,
+                 const octave_value& rhs)
+    {
+      if (op == octave_value::op_asn_eq)
+        assign (sym, rhs);
+      else
+        varref (sym).assign (op, rhs);
+    }
+
+    void assign (octave_value::assign_op op, const symbol_record& sym,
                  const std::string& type,
                  const std::list<octave_value_list>& idx,
                  const octave_value& rhs)
@@ -477,20 +486,31 @@
     }
 
     void non_const_unary_op (octave_value::unary_op op,
+                             const symbol_record& sym)
+    {
+      varref (sym).non_const_unary_op (op);
+    }
+
+    void non_const_unary_op (octave_value::unary_op op,
                              const symbol_record& sym,
                              const std::string& type,
                              const std::list<octave_value_list>& idx)
     {
       if (idx.empty ())
-        varref (sym).non_const_unary_op (op);
+        non_const_unary_op (op, sym);
       else
         varref (sym).non_const_unary_op (op, type, idx);
     }
 
+    octave_value value (const symbol_record& sym) const
+    {
+      return varval (sym);
+    }
+
     octave_value value (const symbol_record& sym, const std::string& type,
                         const std::list<octave_value_list>& idx) const
     {
-      octave_value retval = varval (sym);
+      octave_value retval = value (sym);
 
       if (! idx.empty ())
         {
diff -r c3e215768aeb -r f40431034aef libinterp/parse-tree/oct-lvalue.cc
--- a/libinterp/parse-tree/oct-lvalue.cc	Sat Dec 18 13:42:45 2021 +0100
+++ b/libinterp/parse-tree/oct-lvalue.cc	Mon Dec 20 18:45:19 2021 +0100
@@ -37,29 +37,10 @@
 
 namespace octave
 {
-  bool octave_lvalue::is_defined (void) const
-  {
-    return ! is_black_hole () && m_frame->is_defined (m_sym);
-  }
-
-  bool octave_lvalue::is_undefined (void) const
-  {
-    return ! is_defined ();
-  }
+  // For const empty reference returns.
+  const std::string octave_lvalue_base::m_empty_string = "";
 
-  void octave_lvalue::define (const octave_value& v)
-  {
-    m_frame->assign (m_sym, v);
-  }
-
-  void octave_lvalue::assign (octave_value::assign_op op,
-                              const octave_value& rhs)
-  {
-    if (! is_black_hole ())
-      m_frame->assign (op, m_sym, m_type, m_idx, rhs);
-  }
-
-  octave_idx_type octave_lvalue::numel (void) const
+  octave_idx_type octave_lvalue_idx::numel (void) const
   {
     // Return 1 if there is no index because without an index there
     // should be no way to have a cs-list here.  Cs-lists may be passed
@@ -168,17 +149,7 @@
       }
   }
 
-  void octave_lvalue::set_index (const std::string& t,
-                                 const std::list<octave_value_list>& i)
-  {
-    if (! m_idx.empty ())
-      error ("invalid index expression in assignment");
-
-    m_type = t;
-    m_idx = i;
-  }
-
-  bool octave_lvalue::index_is_empty (void) const
+  bool octave_lvalue_idx::index_is_empty (void) const
   {
     bool retval = false;
 
@@ -192,7 +163,7 @@
     return retval;
   }
 
-  bool octave_lvalue::index_is_colon (void) const
+  bool octave_lvalue_idx::index_is_colon (void) const
   {
     bool retval = false;
 
@@ -206,21 +177,10 @@
     return retval;
   }
 
-  void octave_lvalue::unary_op (octave_value::unary_op op)
-  {
-    if (! is_black_hole ())
-      m_frame->non_const_unary_op (op, m_sym, m_type, m_idx);
-  }
-
-  octave_value octave_lvalue::value (void) const
-  {
-    return (is_black_hole ()
-            ? octave_value () : m_frame->value (m_sym, m_type, m_idx));
-  }
-
   octave_value
-  octave_lvalue::eval_for_numel (const std::string& type,
-                                 const std::list<octave_value_list>& idx) const
+  octave_lvalue_idx::
+  eval_for_numel (const std::string& type,
+                  const std::list<octave_value_list>& idx) const
   {
     octave_value retval;
 
diff -r c3e215768aeb -r f40431034aef libinterp/parse-tree/oct-lvalue.h
--- a/libinterp/parse-tree/oct-lvalue.h	Sat Dec 18 13:42:45 2021 +0100
+++ b/libinterp/parse-tree/oct-lvalue.h	Mon Dec 20 18:45:19 2021 +0100
@@ -36,50 +36,311 @@
 
 namespace octave
 {
+  class octave_lvalue_base
+  {
+  public:
+    friend class octave_lvalue;
+
+    octave_lvalue_base (const symbol_record& sr,
+                        const std::shared_ptr<stack_frame>& frame)
+      : m_sym (sr), m_frame (frame) { }
+
+    octave_lvalue_base (symbol_record &&sr,
+                        std::shared_ptr<stack_frame> &&frame)
+      : m_sym (sr), m_frame (frame) { }
+
+    virtual ~octave_lvalue_base () = default;
+
+    virtual bool is_black_hole (void) const { return false; }
+
+    virtual bool is_defined (void) const
+    {
+      return m_frame->is_defined (m_sym);
+    }
+
+    void define (const octave_value& v)
+    {
+      // ??? Does this make sense for blackhole?
+      m_frame->assign (m_sym, v);
+    }
+
+    virtual void assign (octave_value::assign_op op,
+                         const octave_value& rhs) = 0;
+
+    virtual octave_idx_type numel (void) const { return 1; }
+
+    virtual octave_value value (void) const = 0;
+    virtual void unary_op (octave_value::unary_op op) = 0;
+
+    virtual octave_lvalue_base *
+    set_index (const std::string&,
+               const std::list<octave_value_list>&) = 0;
+
+    virtual octave_lvalue_base * clone () const = 0;
+
+    // idx has specializations of these index related functions
+
+    virtual void clear_index (void) { };
+    virtual const std::string & index_type (void) const
+    {
+      return m_empty_string;
+    }
+    // Scalars are never empty.
+    virtual bool index_is_empty (void) const { return false; }
+    virtual bool index_is_colon (void) const { return false; }
+
+    symbol_record m_sym;
+
+    std::shared_ptr<stack_frame> m_frame;
+
+    static const std::string m_empty_string;
+  };
+
+  class octave_lvalue_idx : public octave_lvalue_base
+  {
+  public:
+    octave_lvalue_idx (const symbol_record& sr,
+                       const std::shared_ptr<stack_frame>& frame,
+                       const std::string &type,
+                       const std::list<octave_value_list> &idx)
+      : octave_lvalue_base (sr, frame), m_type (type), m_idx (idx) { }
+
+    octave_lvalue_idx (symbol_record &&sr,
+                       std::shared_ptr<stack_frame> &&frame,
+                       std::string &&type,
+                       std::list<octave_value_list> &&idx)
+      : octave_lvalue_base (sr, frame), m_type (type), m_idx (idx) { }
+
+    ~octave_lvalue_idx () {};
+
+    void assign (octave_value::assign_op op,  const octave_value& rhs)
+    {
+      m_frame->assign (op, m_sym, m_type, m_idx, rhs);
+    }
+
+    octave_idx_type numel (void) const;
+
+    octave_value value (void) const
+    {
+      return m_frame->value (m_sym, m_type, m_idx);
+    }
+
+    octave_lvalue_base *
+    set_index (const std::string& t,
+               const std::list<octave_value_list>& i) final
+    {
+      if (! m_idx.empty ())
+        error ("invalid index expression in assignment");
+
+      m_type = t;
+      m_idx = i;
+
+      return this;
+    }
+
+    void clear_index (void) final { m_type = ""; m_idx.clear (); }
+
+    const std::string & index_type (void) const final { return m_type; }
+
+    bool index_is_empty (void) const final;
+
+    bool index_is_colon (void) const final;
+
+    void unary_op (octave_value::unary_op op) final
+    {
+      m_frame->non_const_unary_op (op, m_sym, m_type, m_idx);
+    }
+
+    octave_lvalue_base * clone () const
+    {
+      return new octave_lvalue_idx (m_sym, m_frame, m_type, m_idx);
+    }
+
+  private:
+
+    octave_value
+    eval_for_numel (const std::string& type,
+                    const std::list<octave_value_list>& idx) const;
+
+    std::string m_type;
+
+    std::list<octave_value_list> m_idx;
+
+    octave_idx_type m_nel;
+  };
+
+  class octave_lvalue_id : public octave_lvalue_base
+  {
+  public:
+    octave_lvalue_id (const symbol_record& sr,
+                      const std::shared_ptr<stack_frame>& frame)
+      : octave_lvalue_base (sr, frame) { }
+
+    octave_lvalue_id (symbol_record &&sr,
+                      std::shared_ptr<stack_frame> &&frame)
+      : octave_lvalue_base (sr, frame) { }
+
+    ~octave_lvalue_id () {};
+
+    void assign (octave_value::assign_op op,  const octave_value& rhs)
+    {
+      m_frame->assign (op, m_sym, rhs);
+    }
+
+    octave_value value (void) const
+    {
+      return m_frame->value (m_sym);
+    }
+
+    void unary_op (octave_value::unary_op op)
+    {
+      m_frame->non_const_unary_op (op, m_sym);
+    }
+
+    octave_lvalue_base * clone () const
+    {
+      return new octave_lvalue_id (m_sym, m_frame);
+    }
+
+    // Makes the octave_lvalue_id object invalid due to std::move
+    // of members
+    octave_lvalue_base *
+    set_index (const std::string &type,
+               const std::list<octave_value_list>& idx)
+    {
+      return new octave_lvalue_idx (std::move (m_sym),
+                                    std::move (m_frame),
+                                    type, idx);
+    }
+  };
+
+  class octave_lvalue_blackhole : public octave_lvalue_base
+  {
+  public:
+    octave_lvalue_blackhole (const symbol_record& sr,
+                             const std::shared_ptr<stack_frame>& frame)
+      : octave_lvalue_base (sr, frame) { }
+
+    ~octave_lvalue_blackhole () {};
+
+    bool is_defined (void) const { return false; }
+
+    void assign (octave_value::assign_op , const octave_value& ) { }
+
+    octave_value value (void) const { return octave_value (); }
+
+    void unary_op (octave_value::unary_op) { }
+
+    octave_lvalue_base * clone () const
+    {
+      return new octave_lvalue_blackhole (m_sym, m_frame);
+    }
+
+    bool is_black_hole (void) const { return true; }
+
+    octave_lvalue_base *
+    set_index (const std::string &,
+               const std::list<octave_value_list>&)
+    {
+      error ("can't set index on black hole");
+      return nullptr; // Unreachable
+    }
+  };
+
   class octave_lvalue
   {
   public:
 
+    static octave_lvalue
+    make_lvalue_idx (const symbol_record& sr,
+                     const std::shared_ptr<stack_frame>& frame,
+                     const std::string &type,
+                     const std::list<octave_value_list> &idx)
+    {
+      auto *m_rep = new octave_lvalue_idx (sr, frame, type, idx);
+      return octave_lvalue (m_rep);
+    }
+
+    static octave_lvalue
+    make_lvalue_id (const symbol_record& sr,
+                    const std::shared_ptr<stack_frame>& frame)
+    {
+      auto *m_rep = new octave_lvalue_id (sr, frame);
+      return octave_lvalue (m_rep);
+    }
+
+    static octave_lvalue
+    make_lvalue_blackhole (const symbol_record& sr,
+                           const std::shared_ptr<stack_frame>& frame)
+    {
+      auto *m_rep = new octave_lvalue_blackhole (sr, frame);
+      return octave_lvalue (m_rep);
+    }
+
+    // Makes an identifier lvalue
     octave_lvalue (const symbol_record& sr,
                    const std::shared_ptr<stack_frame>& frame)
-      : m_sym (sr), m_frame (frame), m_black_hole (false),
-        m_type (), m_idx ()
-    { }
+    {
+      m_rep = new octave_lvalue_id (sr, frame);
+    }
 
-    octave_lvalue (const octave_lvalue&) = default;
+    octave_lvalue (const octave_lvalue &olv)
+    {
+      m_rep = olv.m_rep->clone ();
+    }
+
+    octave_lvalue (octave_lvalue &&olv)
+    {
+      m_rep = olv.m_rep;
+      olv.m_rep = nullptr;
+    }
 
     octave_lvalue& operator = (const octave_lvalue&) = delete;
 
-    ~octave_lvalue (void) = default;
-
-    bool is_black_hole (void) const { return m_black_hole; }
+    ~octave_lvalue (void)
+    {
+      delete m_rep;
+    };
 
-    void mark_black_hole (void) { m_black_hole = true; }
+    bool is_black_hole (void) const { return m_rep->is_black_hole(); }
 
-    bool is_defined (void) const;
+    bool is_defined (void) const { return m_rep->is_defined (); }
 
-    bool is_undefined (void) const;
+    bool is_undefined (void) const { return ! m_rep->is_defined (); }
 
     bool isstruct (void) const { return value().isstruct (); }
 
-    void define (const octave_value& v);
+    void define (const octave_value& v) { m_rep->define (v); }
 
-    void assign (octave_value::assign_op, const octave_value&);
+    void assign (octave_value::assign_op op, const octave_value &ov)
+    {
+      m_rep->assign (op, ov);
+    }
 
-    octave_idx_type numel (void) const;
+    octave_idx_type numel (void) const { return m_rep->numel (); }
 
     void set_index (const std::string& t,
-                    const std::list<octave_value_list>& i);
+                    const std::list<octave_value_list>& i)
+    {
+      // id will change type to idx here, black_hole will error
+      octave_lvalue_base * tmp = m_rep;
+      m_rep = m_rep->set_index (t, i);
+      if (m_rep != tmp)
+        delete tmp;
+    }
 
-    void clear_index (void) { m_type = ""; m_idx.clear (); }
-
-    std::string index_type (void) const { return m_type; }
+    void clear_index (void) { m_rep->clear_index(); }
 
-    bool index_is_empty (void) const;
+    const std::string & index_type (void) const { return m_rep->index_type (); }
+
+    bool index_is_empty (void) const { return m_rep->index_is_empty (); }
 
-    bool index_is_colon (void) const;
+    bool index_is_colon (void) const { return m_rep->index_is_colon (); }
 
-    void unary_op (octave_value::unary_op op);
+    void unary_op (octave_value::unary_op op)
+    {
+      m_rep->unary_op(op);
+    }
 
 #if defined (OCTAVE_PROVIDE_DEPRECATED_SYMBOLS)
     OCTAVE_DEPRECATED (7, "use 'octave_lvalue::unary_op' instead")
@@ -89,25 +350,15 @@
     }
 #endif
 
-    octave_value value (void) const;
+    octave_value value (void) const { return m_rep->value ();}
 
   private:
 
-    octave_value
-    eval_for_numel (const std::string& type,
-                    const std::list<octave_value_list>& idx) const;
-
-    symbol_record m_sym;
-
-    std::shared_ptr<stack_frame> m_frame;
+    octave_lvalue_base *m_rep;
 
-    bool m_black_hole;
-
-    std::string m_type;
+    // Only for builder functions
+    octave_lvalue (octave_lvalue_base *rep) : m_rep (rep) { };
 
-    std::list<octave_value_list> m_idx;
-
-    octave_idx_type m_nel;
   };
 }
 
diff -r c3e215768aeb -r f40431034aef libinterp/parse-tree/pt-id.cc
--- a/libinterp/parse-tree/pt-id.cc	Sat Dec 18 13:42:45 2021 +0100
+++ b/libinterp/parse-tree/pt-id.cc	Mon Dec 20 18:45:19 2021 +0100
@@ -153,10 +153,7 @@
   octave_lvalue
   tree_black_hole::lvalue (tree_evaluator& tw)
   {
-    octave_lvalue retval (m_sym, tw.get_current_stack_frame ());
-
-    retval.mark_black_hole ();
-
-    return retval;
+    return octave_lvalue::make_lvalue_blackhole (m_sym,
+                                                 tw.get_current_stack_frame ());
   }
 }
