# HG changeset patch
# User Petter Tomner <tomner@kth.se>
# Date 1640962094 -3600
#      Fri Dec 31 15:48:14 2021 +0100
# Node ID b23dfec5799e42118c2a4494ca0fdcb94b34274c
# Parent  7ccd10ea2d33a13cc73f74fe30fe8c8875834a4a
interpreter: Cache function look-ups in identifiers

Uses a weak_ptr to a shared_ptr to signal when the cache need
to be refreshed.

Functions that take a classdef or class object as argument are
not cached.

Cache is invalidated on autoload or load path update.

libinterp/corefcn/
	* load-path.cc: Cache signal object
	* load-path.h:

libinterp/parse-tree/
	* pt-id.cc: Cache function object
	* pt-id:h:
	* pt-idx.cc: Retrieve or store from cache

diff -r 7ccd10ea2d33 -r b23dfec5799e libinterp/corefcn/load-path.cc
--- a/libinterp/corefcn/load-path.cc	Tue Dec 28 12:21:46 2021 +0100
+++ b/libinterp/corefcn/load-path.cc	Fri Dec 31 15:48:14 2021 +0100
@@ -233,7 +233,9 @@
       remove_hook ([=] (const std::string& dir) { this->execute_pkg_del (dir); }),
       m_interpreter (interp), m_package_map (), m_top_level_package (),
       m_dir_info_list (), m_init_dirs (), m_command_line_path ()
-  { }
+  {
+    m_n_updated = std::make_shared<int> (0);
+  }
 
   void
   load_path::initialize (bool set_initial_path)
@@ -399,6 +401,8 @@
     // preserve the correct directory ordering for new files that
     // have appeared.
 
+    signal_clear_fcn_cache ();
+
     m_top_level_package.clear ();
 
     m_package_map.clear ();
diff -r 7ccd10ea2d33 -r b23dfec5799e libinterp/corefcn/load-path.h
--- a/libinterp/corefcn/load-path.h	Tue Dec 28 12:21:46 2021 +0100
+++ b/libinterp/corefcn/load-path.h	Fri Dec 31 15:48:14 2021 +0100
@@ -215,8 +215,20 @@
     static const int OCT_FILE = 2;
     static const int MEX_FILE = 4;
 
+    std::weak_ptr<int> get_weak_n_updated () { return m_n_updated; }
+
+    void signal_clear_fcn_cache () const
+    {
+      if (m_n_updated)
+        m_n_updated = std::make_shared<int> (*m_n_updated + 1);
+      else
+        m_n_updated = std::make_shared<int> (1);
+    }
+
   private:
 
+    mutable std::shared_ptr<int> m_n_updated;
+
     class dir_info
     {
     public:
diff -r 7ccd10ea2d33 -r b23dfec5799e libinterp/parse-tree/pt-eval.cc
--- a/libinterp/parse-tree/pt-eval.cc	Tue Dec 28 12:21:46 2021 +0100
+++ b/libinterp/parse-tree/pt-eval.cc	Fri Dec 31 15:48:14 2021 +0100
@@ -4469,6 +4469,11 @@
   {
     std::string file_name = check_autoload_file (nm);
 
+    // Signal to load path that the function cache is invalid
+    const static std::string who = "tree_evaluator::add_autoload";
+    auto &lp = __get_load_path__ (who);
+    lp.signal_clear_fcn_cache ();
+
     m_autoload_map[fcn] = file_name;
   }
 
@@ -4477,6 +4482,11 @@
   {
     check_autoload_file (nm);
 
+    // Signal to load path that the function cache is invalid
+    const static std::string who = "tree_evaluator::add_autoload";
+    auto &lp = __get_load_path__ (who);
+    lp.signal_clear_fcn_cache ();
+
     // Remove function from symbol table and autoload map.
     symbol_table& symtab = m_interpreter.get_symbol_table ();
 
diff -r 7ccd10ea2d33 -r b23dfec5799e libinterp/parse-tree/pt-id.cc
--- a/libinterp/parse-tree/pt-id.cc	Tue Dec 28 12:21:46 2021 +0100
+++ b/libinterp/parse-tree/pt-id.cc	Fri Dec 31 15:48:14 2021 +0100
@@ -65,6 +65,33 @@
     error_with_id ("Octave:undefined-function", "%s", msg.c_str ());
   }
 
+  void
+  tree_identifier::set_cached_function (octave_value ov,
+                                        const octave_value_list &args)
+  {
+    clear_cached_function ();
+
+    std::vector<int> v_types;
+
+    for (int i = 0; i < args.length (); i++)
+      {
+        // FIXME: We don't cache methods or functions with class object
+        // arguments. Classes need some kind of unique simple key for this
+        // simple approach.
+        if (args(i).isobject())
+          return;
+
+        v_types.push_back (args (i).type_id ());
+      }
+
+    m_cached_args = v_types;
+    m_cached_function = ov;
+
+    const static std::string who = "tree_identifier::set_cached_function";
+    auto &lp = __get_load_path__(who);
+    m_n_updated = lp.get_weak_n_updated();
+  }
+
   octave_lvalue
   tree_identifier::lvalue (tree_evaluator& tw)
   {
diff -r 7ccd10ea2d33 -r b23dfec5799e libinterp/parse-tree/pt-id.h
--- a/libinterp/parse-tree/pt-id.h	Tue Dec 28 12:21:46 2021 +0100
+++ b/libinterp/parse-tree/pt-id.h	Fri Dec 31 15:48:14 2021 +0100
@@ -30,6 +30,7 @@
 
 #include <iosfwd>
 #include <string>
+#include <memory>
 
 class octave_value;
 class octave_value_list;
@@ -109,10 +110,42 @@
 
     symbol_record symbol (void) const { return m_sym; }
 
+    bool has_cached_function (const octave_value_list &args) const
+    {
+      auto vec_n = static_cast <octave_idx_type> (m_cached_args.size ());
+
+      if (args.length () != vec_n)
+        return false;
+
+      for (int i = 0; i < args.length (); i++)
+        {
+          if (args (i).type_id () != m_cached_args [i])
+            return false;
+        }
+
+      return m_cached_function.is_defined ();
+    }
+
+    octave_value cached_function () const { return m_cached_function; }
+    void set_cached_function (octave_value ov, const octave_value_list &args);
+
+    void clear_cached_function ()
+    {
+      m_cached_function = octave_value {};
+      m_n_updated.reset ();
+      m_cached_args.clear ();
+    }
+
+    std::weak_ptr<int> get_lp_n_updated () const { return m_n_updated; }
+
   protected:
 
     // The symbol record that this identifier references.
     symbol_record m_sym;
+
+    octave_value m_cached_function;
+    std::vector<int> m_cached_args;
+    std::weak_ptr<int> m_n_updated;
   };
 
   class tree_black_hole : public tree_identifier
diff -r 7ccd10ea2d33 -r b23dfec5799e libinterp/parse-tree/pt-idx.cc
--- a/libinterp/parse-tree/pt-idx.cc	Tue Dec 28 12:21:46 2021 +0100
+++ b/libinterp/parse-tree/pt-idx.cc	Fri Dec 31 15:48:14 2021 +0100
@@ -391,12 +391,37 @@
 
             symbol_table& symtab = interp.get_symbol_table ();
 
-            octave_value val = symtab.find_function (nm, first_args);
+            octave_value val;
 
             octave_function *fcn = nullptr;
 
-            if (val.is_function ())
-              fcn = val.function_value (true);
+            bool using_cache = false;
+            if (id->has_cached_function (first_args))
+              {
+                std::weak_ptr<int> w = id->get_lp_n_updated();
+
+                if (std::shared_ptr<int> s = w.lock ())
+                  {
+                    val = id->cached_function ();
+                    fcn = val.function_value (true);
+                    using_cache = true;
+                  }
+                else
+                  {
+                    id->clear_cached_function ();
+                  }
+              }
+
+            if (!using_cache)
+              {
+                val = symtab.find_function (nm, first_args);
+
+                if (val.is_function ())
+                  {
+                    fcn = val.function_value (true);
+                    id->set_cached_function (val, first_args);
+                  }
+              }
 
             if (fcn)
               {
