# HG changeset patch
# User Gene Harvey <gharveymn@gmail.com>
# Date 1559336962 18000
#      Fri May 31 16:09:22 2019 -0500
# Node ID 1ccb99c08f27355591d783b5f2cd592c407c0d1a
# Parent  df9e3cb79379aa358c8a83da1caa7e21208af901
fix the JIT and add support for LLVM-8

* configure.ac: Add additional check if LLVM include directory is already in
system path and call new macro.

* jit-ir.cc: Minor modernization.

* jit-ir.h: Minor modernization and readability improvements.

* jit-typeinfo.cc (octave_jit_release_matrix): Add reference count decrement.
(octave_jit_grab_matrix): Add reference count increment.
(jit_type::jit_type): Replace loop with std::array::fill and modernize member
initializer list.
(octave_jit_paren_scalar -> octave_jit_paren_scalar_subsref): Rename.
(jit_function::jit_function): Replace const& with pass-by-value and std::move.
(jit_function::call) Two new overload implementations.
(jit_paren_subsref::generate_matrix): Create a new module on creation of each
overload (bug #55492).
(jit_paren_subsref::init_paren_scalar): Adjust external function name.
(jit_paren_subsasgn::generate_matrix): Create a new module for each overload.
(jit_paren_subsasgn::init_paren_scalar): Adjust external function name.
(jit_typeinfo::jit_typeinfo): Call llvm::ConstantStruct::get with ArrayRef.

* jit-typeinfo.h (jit_type): Use std::array rather than raw array.
(jit_function::call): Six new overloads with jit_module parameter.
(jit_typeinfo::create_internal): New overload with jit_module parameter.

* jit-typeinfo.cc, jit-typeinfo.h (jit_function::type,
jit_function::print_all_flocs, jit_function::print_all_functions):
New functions.

* jit-util.cc: Remove LLVM defines.

* jit-util.h (isa): Compare dynamic_cast explicitly with nullptr.

* pt-eval.cc (tree_evaluator::visit_simple_for_command,
tree_evaluator::execute_user_function,
tree_evaluator::visit_unwind_protect_command,
tree_evaluator::visit_do_until_command): Pass the self-reference as an argument
to tree_jit::execute.

* pt-jit.cc (jit_convert::jit_convert): Initialize stack_frame member.
(jit_convert::visit_statement): Update to stack_frame system.
(jit_convert::initialize): Remove symbol_scope initializer.
(jit_convert::visit_statement, jit_convert::get_variable, jit_info::find):
Update to stack_frame system (bug #55809).
(jit_convert_llvm::convert_function): Use member module instead of argument.
(jit_convert_llvm::convert): Improve loop readability.
(jit_convert_llvm::visit): Use ArrayRef constructor and invoke
jit_function::call using the member module.
(jit_info::simplify_phi): Improve loop readability.
(jit_memory_manager): Make deleted functions public.
(jit_memory_manager::getSymbolAddress): Comment out call to
llvm::SectionMemoryManager::getSymbolAddress which would retrieve an incorrect
address with "sin" as an argument and cause a crash.
(tree_jit::do_execute): Pass tree_evaluator as an argument to jit_info.
(jit_module::getFunction): New function implementation.
(jit_module::optimize): Use autoconf macro for llvm::WriteBitcodeToFile.
(jit_function_info::jit_function_info): Initialize tree_evaluator member and
pass self-reference to jit_function::call.
(jit_info::jit_info): Initialize tree_evaluator member.
(jit_info::compile): Pass self-reference to jit_convert_llvm constructor
instead of jit_convert_llvm::convert_loop.

pt-jit.h: Add stack-frame.h to includes.
(jit_convert::m_frame): New member variable.
(jit_convert::initialize): Remove symbol_scope argument.
(jit_convert_llvm::jit_convert_llvm): New constructor.
(jit_convert_llvm::convert_loop): Remove jit_module argument.
(jit_convert_llvm::convert_function): Remove jit_module argument.
(jit_convert_llvm::m_module): New member variable.
(tree_jit::execute): Add tree_evaluator to parameters.
(tree_jit::do_execute): Add tree_evaluator to parameters.
(jit_info::jit_info): Add tree_evaluator to parameters.
(jit_info::m_tr_eval): New member variable.
(jit_function_info::jit_function_info): Add tree_evaluator to parameters.
(jit_function_info::m_tr_eval): New member variable.

* pt-jit.cc, pt-jit.h (jit_module::getFunction,
jit_module::print_object_summary, jit_module::print_all_flocs,
jit_module::print_all_functions, jit_module::dump): New functions.

* acinclude.m4 (OCTAVE_LLVM_WRITEBITCODETOFILE_API, OCTAVE_CHECK_SEARCH_PATH):
New macros.

diff -r df9e3cb79379 -r 1ccb99c08f27 configure.ac
--- a/configure.ac	Wed May 08 18:45:34 2019 -0700
+++ b/configure.ac	Fri May 31 16:09:22 2019 -0500
@@ -1377,26 +1377,40 @@
     save_CPPFLAGS="$CPPFLAGS"
     save_LDFLAGS="$LDFLAGS"
 
+    LLVM_INCLUDE_DIR="$($LLVM_CONFIG --includedir)"
+    AC_MSG_NOTICE([using LLVM_INCLUDE_DIR=$LLVM_INCLUDE_DIR])
+
     dnl Use -isystem if available because we don't want to see warnings in LLVM
-    LLVM_INCLUDE_FLAG=-I
+    LLVM_INCLUDE_FLAG="-I $LLVM_INCLUDE_DIR"
     OCTAVE_CC_FLAG([-isystem .], [
-      LLVM_INCLUDE_FLAG=-isystem
-      AC_MSG_NOTICE([using -isystem for LLVM headers])])
-
-    dnl Use -isystem so we don't get warnings from llvm headers
-    LLVM_CPPFLAGS="$LLVM_INCLUDE_FLAG `$LLVM_CONFIG --includedir`"
-    LLVM_LDFLAGS="-L`$LLVM_CONFIG --libdir`"
-
-    LDFLAGS="$LDFLAGS $LLVM_LDFLAGS"
-    LLVM_SO=LLVM-`$LLVM_CONFIG --version`
+      OCTAVE_CHECK_SEARCH_PATH(["$LLVM_INCLUDE_DIR"],
+        [AC_MSG_NOTICE([-isystem not used because LLVM_INCLUDE_DIR was found in the system search path])],
+        [LLVM_INCLUDE_FLAG="-isystem $LLVM_INCLUDE_DIR"
+         AC_MSG_NOTICE([using -isystem for LLVM headers])])])
+
+    dnl Can't use --cppflags here because earlier versions of LLVM force NDEBUG
+    LLVM_CPPFLAGS="$LLVM_INCLUDE_FLAG -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS"
+    LLVM_LDFLAGS="$($LLVM_CONFIG --ldflags)"
+
+    dnl We can't just use -lLLVM because earlier versions do not supply the
+    dnl library by that name.
+    LLVM_SO="LLVM-$($LLVM_CONFIG --version)"
+
+    dnl Check the guessed library. If it fails fall back to --libs.
+    dnl Don't use --system-libs because earlier versions will link libedit
+    dnl which conflicts with libreadline and will cause a crash at runtime.
     AC_CHECK_LIB([$LLVM_SO], [LLVMBuildAdd], [LLVM_LIBS="-l$LLVM_SO"],
-                 [LLVM_LIBS="`$LLVM_CONFIG --libs` `$LLVM_CONFIG --system-libs`"])
-
-    dnl Define some extra flags that LLVM requires in order to include headers.
-    dnl Ideally we should get these from llvm-config, but llvm-config isn't
-    dnl very helpful.
-    CPPFLAGS="-D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS $LLVM_CPPFLAGS $CPPFLAGS"
-    AC_LANG_PUSH(C++)
+                     [LLVM_LIBS="$($LLVM_CONFIG --libs)"])
+
+    CPPFLAGS="$LLVM_CPPFLAGS $CPPFLAGS"
+    LDFLAGS="$LLVM_LDFLAGS $LDFLAGS"
+    AC_LANG_PUSH([C++])
+
+    dnl FIXME: Some of these may show warnings if LLVM is in the system
+    dnl path, but we are building with a different version of LLVM.
+    dnl The warnings don't appear to affect the final build.
+    dnl POTENTIAL FIX: Preappend the headers given to the check macros
+    dnl with $LLVM_INCLUDE_DIR/
     AC_CHECK_HEADER([llvm/Support/TargetSelect.h], [warn_llvm=""])
 
     have_function_h=no
@@ -1433,8 +1447,9 @@
     OCTAVE_LLVM_IRBUILDER_API
     OCTAVE_LLVM_HAS_CREATEALWAYSINLINERPASS
     OCTAVE_LLVM_IRBUILDER_CREATECONSTINBOUNDSGEP1_32_API
-
-    AC_LANG_POP(C++)
+    OCTAVE_LLVM_WRITEBITCODETOFILE_API
+
+    AC_LANG_POP([C++])
     CPPFLAGS="$save_CPPFLAGS"
     LDFLAGS="$save_LDFLAGS"
   fi
@@ -1642,7 +1657,7 @@
   PKG_CHECK_EXISTS([$magick++], [
     dnl Make sure we only get -I, -L, and -l flags.
     dnl Some Graphics/ImageMagick++ dnl packages add extra flags that are
-    dnl useful when building Graphics/ImageMagick++ extentions.  These extra
+    dnl useful when building Graphics/ImageMagick++ extensions.  These extra
     dnl flags break the Octave build.
     MAGICK_CPPFLAGS="$($PKG_CONFIG --cflags-only-I $magick++ | $SED -e 's/^ *$//')"
     MAGICK_LDFLAGS="$($PKG_CONFIG --libs-only-L $magick++ | $SED -e 's/^ *$//')"
diff -r df9e3cb79379 -r 1ccb99c08f27 libinterp/parse-tree/jit-ir.cc
--- a/libinterp/parse-tree/jit-ir.cc	Wed May 08 18:45:34 2019 -0700
+++ b/libinterp/parse-tree/jit-ir.cc	Fri May 31 16:09:22 2019 -0500
@@ -22,17 +22,15 @@
 
 // Author: Max Brister <max@2bass.com>
 
-// defines required by llvm
-#define __STDC_LIMIT_MACROS
-#define __STDC_CONSTANT_MACROS
-
 #if defined (HAVE_CONFIG_H)
 #  include "config.h"
 #endif
 
 #if defined (HAVE_LLVM)
 
+#include "error.h"
 #include "jit-ir.h"
+#include "oct.h"
 
 #if defined (HAVE_LLVM_IR_FUNCTION_H)
 #  include <llvm/IR/BasicBlock.h>
@@ -42,17 +40,14 @@
 #  include <llvm/Instructions.h>
 #endif
 
-#include "error.h"
-
 namespace octave
 {
 
   // -------------------- jit_factory --------------------
   jit_factory::~jit_factory (void)
   {
-    for (auto iter = m_all_values.begin ();
-         iter != m_all_values.end (); ++iter)
-      delete *iter;
+    for (jit_value *& m_all_value : m_all_values)
+      delete m_all_value;
   }
 
   void
@@ -93,7 +88,7 @@
   void
   jit_block_list::label (void)
   {
-    if (m_list.size ())
+    if (! m_list.empty ())
       {
         jit_block *block = m_list.back ();
         block->label ();
@@ -111,10 +106,10 @@
   jit_block_list::print_dom (std::ostream& os) const
   {
     os << "-------------------- dom info --------------------\n";
-    for (auto iter = begin (); iter != end (); ++iter)
+    for (const jit_block *jblock : *this)
       {
-        assert (*iter);
-        (*iter)->print_dom (os);
+        assert (jblock);
+        jblock->print_dom (os);
       }
     os << std::endl;
 
@@ -132,10 +127,10 @@
   std::ostream&
   operator<<(std::ostream& os, const jit_block_list& blocks)
   {
-    for (auto iter = blocks.begin (); iter != blocks.end (); ++iter)
+    for (const jit_block *jblock : blocks)
       {
-        assert (*iter);
-        (*iter)->print (os, 0);
+        assert (jblock);
+        jblock->print (os, 0);
       }
     return os << std::endl;
   }
@@ -148,9 +143,6 @@
   }
 
   // -------------------- jit_value --------------------
-  jit_value::~jit_value (void)
-  { }
-
   jit_block *
   jit_value::first_use_block (void)
   {
@@ -163,7 +155,7 @@
         use = use->next ();
       }
 
-    return 0;
+    return nullptr;
   }
 
   void
@@ -277,7 +269,7 @@
         return to_merge;
       }
 
-    return 0;
+    return nullptr;
   }
 
   void
@@ -404,27 +396,27 @@
     os << "  predecessors: ";
     for (jit_use *use = first_use (); use; use = use->next ())
       os << *use->user_parent () << ' ';
-    os << std::endl;
+    os << '\n';
 
     os << "  successors: ";
     for (size_t i = 0; i < successor_count (); ++i)
       os << *successor (i) << ' ';
-    os << std::endl;
+    os << '\n';
 
     os << "  m_idom: ";
     if (m_idom)
       os << *m_idom;
     else
       os << "NULL";
-    os << std::endl;
+    os << '\n';
     os << "  df: ";
     for (auto iter = df_begin (); iter != df_end (); ++iter)
       os << **iter << ' ';
-    os << std::endl;
+    os << '\n';
 
     os << "  m_dom_succ: ";
-    for (size_t i = 0; i < m_dom_succ.size (); ++i)
-      os << *m_dom_succ[i] << ' ';
+    for (const jit_block *jblock : m_dom_succ)
+      os << *jblock << ' ';
 
     return os << std::endl;
   }
@@ -504,9 +496,8 @@
   void
   jit_block::pop_all (void)
   {
-    for (auto iter = begin (); iter != end (); ++iter)
+    for (jit_instruction *instr : *this)
       {
-        jit_instruction *instr = *iter;
         instr->pop_variable ();
       }
   }
@@ -523,14 +514,13 @@
         if (use->next ())
           os << ", ";
       }
-    os << std::endl;
+    os << '\n';
 
-    for (auto iter = begin (); iter != end (); ++iter)
+    for (const jit_instruction *instr : *this)
       {
-        jit_instruction *instr = *iter;
-        instr->print (os, indent + 1) << std::endl;
+        instr->print (os, indent + 1) << '\n';
       }
-    return os;
+    return os.flush();
   }
 
   jit_block *
@@ -610,7 +600,7 @@
         if (inc->branch_alive (p))
           {
             if (unique != argument (i))
-              unique = 0;
+              unique = nullptr;
 
             if (new_idx != i)
               {
@@ -698,12 +688,6 @@
     return changed;
   }
 
-  llvm::TerminatorInst *
-  jit_terminator::to_llvm (void) const
-  {
-    return llvm::cast<llvm::TerminatorInst> (jit_value::to_llvm ());
-  }
-
   // -------------------- jit_call --------------------
   bool
   jit_call::needs_release (void) const
@@ -785,8 +769,8 @@
       m_count (factory.create<jit_const_index> (acount))
   { }
 
-  jit_magic_end::jit_magic_end (const std::vector<context>& full_context)
-    : m_contexts (full_context)
+  jit_magic_end::jit_magic_end (std::vector<context> full_context)
+    : m_contexts (std::move(full_context))
   {
     resize_arguments (m_contexts.size ());
 
diff -r df9e3cb79379 -r 1ccb99c08f27 libinterp/parse-tree/jit-ir.h
--- a/libinterp/parse-tree/jit-ir.h	Wed May 08 18:45:34 2019 -0700
+++ b/libinterp/parse-tree/jit-ir.h	Fri May 31 16:09:22 2019 -0500
@@ -184,10 +184,11 @@
   public:
 
     jit_value (void)
-      : m_llvm_value (0), m_type (0), m_last_use (0), m_in_worklist (false)
+      : m_llvm_value (nullptr), m_type (nullptr), m_last_use (nullptr),
+        m_in_worklist (false)
     { }
 
-    virtual ~jit_value (void);
+    virtual ~jit_value (void) = default;
 
     bool in_worklist (void) const
     {
@@ -245,7 +246,7 @@
 
     bool has_llvm (void) const
     {
-      return m_llvm_value;
+      return m_llvm_value != nullptr;
     }
 
     llvm::Value * to_llvm (void) const
@@ -285,8 +286,8 @@
   {
   public:
 
-    // some compilers don't allow us to use jit_internal_node without template
-    // paremeters
+    // Some compilers don't allow us to use jit_internal_node without template
+    // parameters
     typedef jit_internal_node<jit_value, jit_use> PARENT_T;
 
     jit_use (void) : m_user (0), m_index (0) { }
@@ -312,7 +313,7 @@
     std::list<jit_block *> user_parent_location (void) const;
 
     void stash_value (jit_value *avalue, jit_instruction *auser = nullptr,
-                      size_t aindex = -1)
+                      size_t aindex = std::numeric_limits<size_t>::max())
     {
       PARENT_T::stash_value (avalue);
       m_index = aindex;
@@ -493,7 +494,7 @@
     std::list<jit_instruction *>::iterator m_location;
   };
 
-  // defnie accept methods for subclasses
+  // define accept methods for subclasses
 #define JIT_VALUE_ACCEPT                        \
   virtual void accept (jit_ir_walker& walker);
 
@@ -1100,8 +1101,6 @@
 
     virtual bool infer (void);
 
-    llvm::TerminatorInst * to_llvm (void) const;
-
   protected:
 
     virtual bool check_alive (size_t) const { return true; }
@@ -1305,7 +1304,8 @@
     {
     public:
 
-      context (void) : m_value (0), m_index (0), m_count (0) { }
+      context (void) : m_value (nullptr), m_index (nullptr), m_count (nullptr)
+      { }
 
       context (jit_factory& factory, jit_value *avalue, size_t aindex,
                size_t acount);
@@ -1315,7 +1315,7 @@
       jit_const_index *m_count;
     };
 
-    jit_magic_end (const std::vector<context>& full_context);
+    explicit jit_magic_end (std::vector<context> full_context);
 
     virtual bool infer (void);
 
@@ -1373,7 +1373,7 @@
   {
   public:
 
-    jit_store_argument (jit_variable *var)
+    explicit jit_store_argument (jit_variable *var)
       : jit_instruction (var), m_dest (var)
     { }
 
diff -r df9e3cb79379 -r 1ccb99c08f27 libinterp/parse-tree/jit-typeinfo.cc
--- a/libinterp/parse-tree/jit-typeinfo.cc	Wed May 08 18:45:34 2019 -0700
+++ b/libinterp/parse-tree/jit-typeinfo.cc	Fri May 31 16:09:22 2019 -0500
@@ -22,10 +22,6 @@
 
 // Author: Max Brister <max@2bass.com>
 
-// defines required by llvm
-#define __STDC_LIMIT_MACROS
-#define __STDC_CONSTANT_MACROS
-
 #if defined (HAVE_CONFIG_H)
 #  include "config.h"
 #endif
@@ -64,15 +60,16 @@
 
 #include <llvm/Support/raw_os_ostream.h>
 
+#include "interpreter-private.h"
 #include "jit-typeinfo.h"
-#include "pt-jit.h"
 #include "jit-ir.h"
-#include "ov.h"
+#include "oct.h"
 #include "ov-builtin.h"
 #include "ov-complex.h"
 #include "ov-scalar.h"
+#include "ov.h"
 #include "pager.h"
-#include "interpreter-private.h"
+#include "pt-jit.h"
 
 namespace octave
 {
@@ -128,7 +125,8 @@
   extern "C" void
   octave_jit_release_matrix (jit_matrix *m)
   {
-    delete m->m_array;
+    if(--(*(m->m_ref_count)) == 0)
+      delete m->m_array;
   }
 
   extern "C" octave_base_value *
@@ -141,6 +139,7 @@
   extern "C" jit_matrix
   octave_jit_grab_matrix (jit_matrix *m)
   {
+    ++(*(m->m_ref_count));
     return *m->m_array;
   }
 
@@ -260,8 +259,8 @@
   }
 
   extern "C" double
-  octave_jit_paren_scalar (jit_matrix *mat, double *indicies,
-                           octave_idx_type idx_count)
+  octave_jit_paren_scalar_subsref (jit_matrix *mat, double *indicies,
+                           	   octave_idx_type idx_count)
   {
     // FIXME: Replace this with a more optimal version
     Array<idx_vector> idx;
@@ -378,7 +377,7 @@
   octave_jit_complex_mul (Complex lhs, Complex rhs)
   {
     if (lhs.imag () == 0 && rhs.imag() == 0)
-      return Complex (lhs.real () * rhs.real (), 0);
+      return {lhs.real () * rhs.real (), 0};
 
     return lhs * rhs;
   }
@@ -471,7 +470,7 @@
            || (ovl.length () == 1 && ovl.xelem (0).is_undefined ())))
       err_bad_result ();
 
-    return 0;
+    return nullptr;
   }
 
 
@@ -504,18 +503,14 @@
 
   // -------------------- jit_type --------------------
 
-  jit_type::jit_type (const std::string& aname, jit_type *aparent,
+  jit_type::jit_type (std::string aname, jit_type *aparent,
                       llvm::Type *allvm_type, bool askip_paren, int aid)
-    : m_name (aname), m_parent (aparent), m_llvm_type (allvm_type), m_id (aid),
-      m_depth (aparent ? aparent->m_depth + 1 : 0), m_skip_paren (askip_paren)
+    : m_name (std::move (aname)), m_parent (aparent), m_llvm_type (allvm_type),
+      m_id (aid), m_depth (aparent ? aparent->m_depth + 1 : 0),
+      m_skip_paren (askip_paren), m_sret {}, m_pointer_arg {}, m_pack {},
+      m_unpack {}, m_packed_type {}
   {
-    std::memset (m_sret, 0, sizeof (m_sret));
-    std::memset (m_pointer_arg, 0, sizeof (m_pointer_arg));
-    std::memset (m_pack, 0, sizeof (m_pack));
-    std::memset (m_unpack, 0, sizeof (m_unpack));
-
-    for (size_t i = 0; i < jit_convention::length; ++i)
-      m_packed_type[i] = m_llvm_type;
+    m_packed_type.fill (m_llvm_type);
   }
 
   llvm::Type *
@@ -565,8 +560,8 @@
   jit_function::jit_function (const jit_module *amodule,
                               jit_convention::type acall_conv,
                               const llvm::Twine& aname, jit_type *aresult,
-                              const std::vector<jit_type *>& aargs)
-    : m_module (amodule), m_result (aresult), m_args (aargs),
+                              std::vector<jit_type *> aargs)
+    : m_module (amodule), m_result (aresult), m_args (std::move (aargs)),
       m_call_conv (acall_conv), m_can_error (false)
   {
     llvm::SmallVector<llvm::Type *, 15> llvm_args;
@@ -582,9 +577,8 @@
           }
       }
 
-    for (auto iter = m_args.cbegin (); iter != m_args.cend (); ++iter)
+    for (jit_type *ty : m_args)
       {
-        jit_type *ty = *iter;
         assert (ty);
         llvm::Type *argty = ty->packed_type (m_call_conv);
         if (ty->pointer_arg (m_call_conv))
@@ -617,16 +611,10 @@
   }
 
   jit_function::jit_function (const jit_function& fn, jit_type *aresult,
-                              const std::vector<jit_type *>& aargs)
+                              std::vector<jit_type *> aargs)
     : m_module (fn.m_module), m_llvm_function (fn.m_llvm_function),
-      m_result (aresult), m_args (aargs), m_call_conv (fn.m_call_conv),
-      m_can_error (fn.m_can_error)
-  { }
-
-  jit_function::jit_function (const jit_function& fn)
-    : m_module (fn.m_module), m_llvm_function (fn.m_llvm_function),
-      m_result (fn.m_result), m_args (fn.m_args), m_call_conv (fn.m_call_conv),
-      m_can_error (fn.m_can_error)
+      m_result (aresult), m_args (std::move (aargs)),
+      m_call_conv (fn.m_call_conv), m_can_error (fn.m_can_error)
   { }
 
   void
@@ -636,7 +624,13 @@
       return;
 
     m_llvm_function->eraseFromParent ();
-    m_llvm_function = 0;
+    m_llvm_function = nullptr;
+  }
+
+  llvm::FunctionType *
+  jit_function::type (void) const
+  {
+    return m_llvm_function->getFunctionType();
   }
 
   std::string
@@ -739,6 +733,105 @@
   }
 
   llvm::Value *
+  jit_function::call (const jit_module& amodule, llvm::IRBuilderD& builder,
+                      const std::vector<jit_value *>& in_args) const
+  {
+    if (! valid ())
+      throw jit_fail_exception ("Call not implemented");
+
+    assert (in_args.size () == m_args.size ());
+    std::vector<llvm::Value *> llvm_args (m_args.size ());
+    for (size_t i = 0; i < in_args.size (); ++i)
+      llvm_args[i] = in_args[i]->to_llvm ();
+
+    return call (amodule, builder, llvm_args);
+  }
+
+  llvm::Value *
+  jit_function::call (const jit_module& amodule, llvm::IRBuilderD& builder,
+                      const arg_vec& in_args) const
+  {
+    if (! valid ())
+      throw jit_fail_exception ("Call not implemented");
+
+    assert (in_args.size () == m_args.size ());
+    llvm::SmallVector<llvm::Value *, 10> llvm_args;
+    llvm_args.reserve (in_args.size () + sret ());
+
+    llvm::BasicBlock *insert_block = builder.GetInsertBlock ();
+    llvm::Function *parent = insert_block->getParent ();
+    assert (parent);
+
+    // Insert allocas inside the prelude block to prevent stack overflows
+    llvm::BasicBlock& prelude = parent->getEntryBlock ();
+    llvm::IRBuilder<> pre_builder (&prelude, prelude.begin ());
+
+    llvm::AllocaInst *sret_mem = nullptr;
+    if (sret ())
+    {
+      sret_mem = pre_builder.CreateAlloca (m_result->packed_type (m_call_conv));
+      llvm_args.push_back (sret_mem);
+    }
+
+    for (size_t i = 0; i < in_args.size (); ++i)
+    {
+      llvm::Value *arg = in_args[i];
+      jit_type::convert_fn convert = m_args[i]->pack (m_call_conv);
+      if (convert)
+        arg = convert (builder, arg);
+
+      if (m_args[i]->pointer_arg (m_call_conv))
+      {
+        llvm::Type *ty = m_args[i]->packed_type (m_call_conv);
+        llvm::Value *alloca = pre_builder.CreateAlloca (ty);
+        builder.CreateStore (arg, alloca);
+        arg = alloca;
+      }
+
+      llvm_args.push_back (arg);
+    }
+
+    // Create function declaration
+    llvm::Function *f_decl;
+    if(&amodule != m_module)
+    {
+      f_decl = amodule.getFunction(name());
+      if(!f_decl)
+      {
+        f_decl = amodule.create_llvm_function(type(), name());
+      }
+    }
+    else {
+      f_decl = m_llvm_function;
+    }
+
+    llvm::CallInst *callinst = builder.CreateCall (f_decl, llvm_args);
+    llvm::Value *ret = callinst;
+
+    if (sret ())
+      {
+#if defined (CALLINST_ADDATTRIBUTE_ARG_IS_ATTRIBUTES)
+        llvm::AttrBuilder attr_builder;
+        attr_builder.addAttribute(llvm::Attributes::StructRet);
+        llvm::Attributes attrs = llvm::Attributes::get(context, attr_builder);
+        callinst->addAttribute (1, attrs);
+#else
+        callinst->addAttribute (1, llvm::Attribute::StructRet);
+#endif
+        ret = builder.CreateLoad (sret_mem);
+      }
+
+    if (m_result)
+      {
+        jit_type::convert_fn unpack = m_result->unpack (m_call_conv);
+        if (unpack)
+          ret = unpack (builder, ret);
+      }
+
+    return ret;
+  }
+
+  llvm::Value *
   jit_function::argument (llvm::IRBuilderD& builder, size_t idx) const
   {
     assert (idx < m_args.size ());
@@ -799,10 +892,10 @@
   // -------------------- jit_operation --------------------
   jit_operation::~jit_operation (void)
   {
-    for (auto iter = m_generated.begin (); iter != m_generated.end (); ++iter)
+    for (generated_map::value_type& iter : m_generated)
       {
-        delete iter->first;
-        delete iter->second;
+        delete iter.first;
+        delete iter.second;
       }
   }
 
@@ -880,7 +973,7 @@
          ++i)
       idx(i) = types[i]->type_id ();
 
-    if (types.size () == 0)
+    if (types.empty ())
       idx(0) = idx(1) = 0;
     if (types.size () == 1)
       {
@@ -912,7 +1005,7 @@
   jit_function *
   jit_operation::generate (const signature_vec&) const
   {
-    return 0;
+    return nullptr;
   }
 
   bool
@@ -948,12 +1041,12 @@
         jit_type *scalar = jit_typeinfo::get_scalar ();
         for (size_t i = 1; i < types.size (); ++i)
           if (types[i] != scalar)
-            return 0;
+            return nullptr;
 
         return generate_matrix (types);
       }
 
-    return 0;
+    return nullptr;
   }
 
   llvm::Value *
@@ -994,12 +1087,18 @@
     if (m_paren_scalar == nullptr)
       panic_impossible ();
 
-    std::stringstream ss;
-    ss << "jit_paren_subsref_matrix_scalar" << (types.size () - 1);
+    std::string fname =
+        "jit_paren_subsref_matrix_scalar" + std::to_string(types.size () - 1);
+
+    // Cannot add this overload to the base module because this may run after
+    // it has already been finalized
+    // FIXME: This is just a hack to get the system working. We should refactor
+    // the module system to just have jm_list handle everything.
+    jit_module *module = new jit_module (fname + "_module");
 
     // FIXME: Where will this be deleted?
     jit_function *fn = new jit_function
-      (m_typeinfo.create_internal (ss.str (), m_typeinfo.m_scalar, types));
+      (m_typeinfo.create_internal (module, fname, m_typeinfo.m_scalar, types));
 
     fn->mark_can_error ();
     llvm::BasicBlock *body = fn->new_block ();
@@ -1011,6 +1110,8 @@
     llvm::Value *mat = fn->argument (builder, 0);
     llvm::Value *ret = m_paren_scalar->call (builder, mat, array, nelem);
     fn->do_return (builder, ret);
+    module->finalizeObject();
+
     return fn;
   }
 
@@ -1023,8 +1124,8 @@
     types[2] = m_typeinfo.m_index;
 
     m_paren_scalar = new jit_function
-      (m_typeinfo.create_external (&octave_jit_paren_scalar,
-                                   "octave_jit_paren_scalar",
+      (m_typeinfo.create_external (&octave_jit_paren_scalar_subsref,
+                                   "octave_jit_paren_scalar_subsref",
                                    m_typeinfo.m_scalar, types));
 
     m_paren_scalar->mark_can_error ();
@@ -1047,12 +1148,17 @@
     if (m_paren_scalar == nullptr)
       panic_impossible ();
 
-    std::stringstream ss;
-    ss << "jit_paren_subsasgn_matrix_scalar" << (types.size () - 2);
+    std::string fname =
+        "jit_paren_subsasgn_matrix_scalar" + std::to_string (types.size () - 2);
+
+    // Cannot add this overload to the base module because this may run after
+    // it has already been finalized
+    // FIXME: See jit_paren_subsref::generate_matrix
+    jit_module * module = new jit_module(fname + "_module");
 
     // FIXME: Where will this be deleted?
     jit_function *fn = new jit_function
-      (m_typeinfo.create_internal (ss.str (), m_typeinfo.m_matrix, types));
+      (m_typeinfo.create_internal (module, fname, m_typeinfo.m_matrix, types));
 
     fn->mark_can_error ();
     llvm::BasicBlock *body = fn->new_block ();
@@ -1066,6 +1172,7 @@
     llvm::Value *value = fn->argument (builder, types.size () - 1);
     llvm::Value *ret = m_paren_scalar->call (builder, mat, array, nelem, value);
     fn->do_return (builder, ret);
+    module->finalizeObject();
 
     return fn;
   }
@@ -1081,7 +1188,7 @@
 
     m_paren_scalar = new jit_function
       (m_typeinfo.create_external (&octave_jit_paren_scalar_subsasgn,
-                                   "octave_jit_paren_scalar",
+                                   "octave_jit_paren_scalar_subsasgn",
                                    m_typeinfo.m_matrix, types));
 
     m_paren_scalar->mark_can_error ();
@@ -1118,7 +1225,7 @@
   }
 
   // wrap function names to simplify jit_typeinfo::create_external
-#define JIT_FN(fn) &fn, #fn
+#define JIT_FN(fn) &(fn), #fn
 
   jit_typeinfo::jit_typeinfo (void)
     : paren_subsref_fn (*this),
@@ -1281,19 +1388,19 @@
       {
         octave_value::binary_op op = static_cast<octave_value::binary_op> (i);
         std::string op_name = octave_value::binary_op_as_string (op);
-        m_binary_ops.push_back (jit_operation ("binary" + op_name));
+        m_binary_ops.emplace_back("binary" + op_name);
       }
 
     for (size_t i = 0; i < octave_value::num_unary_ops; ++i)
       {
         octave_value::unary_op op = static_cast<octave_value::unary_op> (i);
         std::string op_name = octave_value::unary_op_as_string (op);
-        m_unary_ops.push_back (jit_operation ("unary" + op_name));
+        m_unary_ops.emplace_back("unary" + op_name);
       }
 
     for (int op = 0; op < octave_value::num_binary_ops; ++op)
       {
-        const llvm::Twine &fn_name =
+        const llvm::Twine& fn_name =
           "octave_jit_binary_any_any_" + llvm::Twine (op);
 
         fn = create_internal (fn_name, m_any, m_any, m_any);
@@ -1661,13 +1768,13 @@
       llvm::Value *inc = fn.argument (m_builder, 2);
       llvm::Value *nelem = compute_nelem.call (m_builder, base, limit, inc);
 
-      llvm::Value *dzero = llvm::ConstantFP::get (m_scalar_t, 0);
-      llvm::Value *izero = llvm::ConstantInt::get (m_index_t, 0);
-      llvm::Value *rng = llvm::ConstantStruct::get (m_range_t, dzero, dzero,
-                                                    dzero, izero, NULL);
-      rng = m_builder.CreateInsertValue (rng, base, 0);
+      llvm::Constant *dzero = llvm::ConstantFP::get (m_scalar_t, 0);
+      llvm::Constant *izero = llvm::ConstantInt::get (m_index_t, 0);
+      llvm::ArrayRef<llvm::Constant *> store {dzero, dzero, dzero, izero};
+      llvm::Value *rng = llvm::ConstantStruct::get (m_range_t, store);
+      rng = m_builder.CreateInsertValue (rng, base,  0);
       rng = m_builder.CreateInsertValue (rng, limit, 1);
-      rng = m_builder.CreateInsertValue (rng, inc, 2);
+      rng = m_builder.CreateInsertValue (rng, inc,   2);
       rng = m_builder.CreateInsertValue (rng, nelem, 3);
       fn.do_return (m_builder, rng);
     }
@@ -1964,14 +2071,13 @@
     std::vector<jit_type *> args;
     args.resize (1);
 
-    for (auto iter = m_builtins.begin ();
-         iter != m_builtins.end (); ++iter)
+    for (std::map<std::string, jit_type *>::value_type& builtin : m_builtins)
       {
-        jit_type *btype = iter->second;
+        jit_type *btype = builtin.second;
         args[0] = btype;
 
         m_grab_fn.add_overload (jit_function (grab_any, btype, args));
-        m_release_fn.add_overload (jit_function (release_any, 0, args));
+        m_release_fn.add_overload (jit_function (release_any, nullptr, args));
         m_casts[m_any->type_id ()].add_overload (jit_function (any_id, m_any,
                                                                args));
 
@@ -2015,8 +2121,8 @@
                                   m_next_id++);
     m_id_to_type.push_back (ret);
 
-    m_casts.push_back (jit_operation ("(" + name + ")"));
-    m_identities.push_back (jit_function ());
+    m_casts.emplace_back("(" + name + ")");
+    m_identities.emplace_back();
 
     return ret;
   }
@@ -2115,7 +2221,7 @@
   jit_function
   jit_typeinfo::create_identity (jit_type *type)
   {
-    size_t id = type->type_id ();
+    size_t id = static_cast<size_t>(type->type_id ());
     if (id >= m_identities.size ())
       m_identities.resize (id + 1);
 
@@ -2372,6 +2478,19 @@
 
     return m_any;
   }
+
+  void
+  jit_typeinfo::print_all_flocs ()
+  {
+    instance ().m_base_jit_module->print_all_flocs ();
+  }
+
+  void
+  jit_typeinfo::print_all_functions ()
+  {
+    instance ().m_base_jit_module->print_all_functions ();
+  }
+
 }
 
 #endif
diff -r df9e3cb79379 -r 1ccb99c08f27 libinterp/parse-tree/jit-typeinfo.h
--- a/libinterp/parse-tree/jit-typeinfo.h	Wed May 08 18:45:34 2019 -0700
+++ b/libinterp/parse-tree/jit-typeinfo.h	Fri May 31 16:09:22 2019 -0500
@@ -143,7 +143,7 @@
 
     typedef llvm::Value *(*convert_fn) (llvm::IRBuilderD&, llvm::Value *);
 
-    jit_type (const std::string& aname, jit_type *aparent, llvm::Type *allvm_type,
+    jit_type (std::string aname, jit_type *aparent, llvm::Type *allvm_type,
               bool askip_paren, int aid);
 
     // a user readable type name
@@ -213,13 +213,14 @@
     size_t m_depth;
     bool m_skip_paren;
 
-    bool m_sret[jit_convention::length];
-    bool m_pointer_arg[jit_convention::length];
+    std::array<bool, jit_convention::length> m_sret;
+    std::array<bool, jit_convention::length> m_pointer_arg;
 
-    convert_fn m_pack[jit_convention::length];
-    convert_fn m_unpack[jit_convention::length];
+    std::array<convert_fn, jit_convention::length> m_pack;
+    std::array<convert_fn, jit_convention::length> m_unpack;
 
-    llvm::Type *m_packed_type[jit_convention::length];
+    std::array<llvm::Type *, jit_convention::length> m_packed_type;
+
   };
 
   // seperate print function to allow easy printing if type is null
@@ -246,20 +247,22 @@
 
     jit_function (const jit_module *amodule, jit_convention::type acall_conv,
                   const llvm::Twine& aname, jit_type *aresult,
-                  const std::vector<jit_type *>& aargs);
+                  std::vector<jit_type *> aargs);
 
     // Use an existing function, but change the argument types.  The new argument
     // types must behave the same for the current calling convention.
     jit_function (const jit_function& fn, jit_type *aresult,
-                  const std::vector<jit_type *>& aargs);
+                  std::vector<jit_type *> aargs);
 
-    jit_function (const jit_function& fn);
+    jit_function (const jit_function& fn) = default;
 
-    // erase the interal LLVM function (if it exists).  Will become invalid.
+    // erase the internal LLVM function (if it exists).  Will become invalid.
     void erase (void);
 
     bool valid (void) const { return m_llvm_function; }
 
+    llvm::FunctionType* type (void) const;
+
     std::string name (void) const;
 
     llvm::BasicBlock * new_block (const std::string& aname = "body",
@@ -309,6 +312,50 @@
       return call (builder, in_args, other_args...);
     }
 
+    llvm::Value * call (const jit_module& amodule,
+                        llvm::IRBuilderD& builder,
+                        const arg_vec &in_args = arg_vec ()) const;
+
+    llvm::Value * call (const jit_module& amodule, llvm::IRBuilderD& builder,
+                        const std::vector<jit_value *>& in_args) const;
+
+    template <typename ...Args>
+    llvm::Value * call (const jit_module& amodule, llvm::IRBuilderD& builder,
+                        arg_vec& in_args, llvm::Value * arg1,
+                        Args... other_args) const
+    {
+      in_args.push_back (arg1);
+      return call (amodule, builder, in_args, other_args...);
+    }
+
+    template <typename T, typename ...Args>
+    llvm::Value * call (const jit_module& amodule, llvm::IRBuilderD& builder,
+                        arg_vec& in_args, T * arg1, Args... other_args) const
+    {
+      in_args.push_back (arg1->to_llvm ());
+      return call (amodule, builder, in_args, other_args...);
+    }
+
+    template <typename ...Args>
+    llvm::Value * call (const jit_module& amodule, llvm::IRBuilderD& builder,
+                        llvm::Value * arg1, Args... other_args) const
+    {
+      arg_vec in_args;
+      in_args.reserve (1 + sizeof... (other_args));
+      in_args.push_back (arg1);
+      return call (amodule, builder, in_args, other_args...);
+    }
+
+    template <typename T, typename ...Args>
+    llvm::Value * call (const jit_module& amodule, llvm::IRBuilderD& builder,
+                        T * arg1, Args... other_args) const
+    {
+      arg_vec in_args;
+      in_args.reserve (1 + sizeof... (other_args));
+      in_args.push_back (arg1->to_llvm ());
+      return call (amodule, builder, in_args, other_args...);
+    }
+
     llvm::Value * argument (llvm::IRBuilderD& builder, size_t idx) const;
 
     void do_return (llvm::IRBuilderD& builder, llvm::Value *rval = nullptr,
@@ -748,6 +795,12 @@
     static llvm::Value *unpack_complex (llvm::IRBuilderD& bld,
                                         llvm::Value *result);
 
+    // Print all function locations in the base module.
+    static void print_all_flocs ();
+
+    // Print all functions in the base module.
+    static void print_all_functions ();
+
   private:
 
     jit_type * do_type_of (const octave_value& ov) const;
@@ -830,6 +883,14 @@
                            name, ret, args);
     }
 
+    jit_function create_internal (jit_module *module, const llvm::Twine& name,
+                                  jit_type *ret, const signature_vec& args
+                                  = signature_vec ()) const
+    {
+      return jit_function (module, jit_convention::internal,
+                           name, ret, args);
+    }
+
     template <typename ...Args>
     jit_function create_internal (const llvm::Twine& name, jit_type *ret,
                                   signature_vec& args,
diff -r df9e3cb79379 -r 1ccb99c08f27 libinterp/parse-tree/jit-util.cc
--- a/libinterp/parse-tree/jit-util.cc	Wed May 08 18:45:34 2019 -0700
+++ b/libinterp/parse-tree/jit-util.cc	Fri May 31 16:09:22 2019 -0500
@@ -22,10 +22,6 @@
 
 // Author: Max Brister <max@2bass.com>
 
-// defines required by llvm
-#define __STDC_LIMIT_MACROS
-#define __STDC_CONSTANT_MACROS
-
 #if defined (HAVE_CONFIG_H)
 #  include "config.h"
 #endif
diff -r df9e3cb79379 -r 1ccb99c08f27 libinterp/parse-tree/jit-util.h
--- a/libinterp/parse-tree/jit-util.h	Wed May 08 18:45:34 2019 -0700
+++ b/libinterp/parse-tree/jit-util.h	Fri May 31 16:09:22 2019 -0500
@@ -54,13 +54,12 @@
   class Twine;
   class GlobalValue;
   class GlobalVariable;
-  class TerminatorInst;
   class PHINode;
   class TargetMachine;
 
   class ConstantFolder;
 
-#if defined  LLVM_IRBUILDER_HAS_TWO_TEMPLATE_ARGS
+#if defined (LLVM_IRBUILDER_HAS_TWO_TEMPLATE_ARGS)
 
   class IRBuilderDefaultInserter;
 
@@ -227,11 +226,11 @@
   };
 
   // Use like: isa<jit_phi> (value)
-  // basically just a short cut type typing dyanmic_cast.
+  // basically just a short cut type typing dynamic_cast.
   template <typename T, typename U>
   bool isa (U *value)
   {
-    return dynamic_cast<T *> (value);
+    return dynamic_cast<T *> (value) != nullptr;
   }
 
 }
diff -r df9e3cb79379 -r 1ccb99c08f27 libinterp/parse-tree/pt-eval.cc
--- a/libinterp/parse-tree/pt-eval.cc	Wed May 08 18:45:34 2019 -0700
+++ b/libinterp/parse-tree/pt-eval.cc	Fri May 31 16:09:22 2019 -0500
@@ -2178,7 +2178,7 @@
     octave_value rhs = evaluate (expr);
 
 #if defined (HAVE_LLVM)
-    if (tree_jit::execute (cmd, rhs))
+    if (tree_jit::execute (*this, cmd, rhs))
       return;
 #endif
 
@@ -2465,7 +2465,7 @@
 
 #if defined (HAVE_LLVM)
     if (user_function.is_special_expr ()
-        && tree_jit::execute (user_function, args, retval))
+        && tree_jit::execute (*this, user_function, args, retval))
       return retval;
 #endif
 
@@ -4018,7 +4018,7 @@
       }
 
 #if defined (HAVE_LLVM)
-    if (tree_jit::execute (cmd))
+    if (tree_jit::execute (*this, cmd))
       return;
 #endif
 
@@ -4068,7 +4068,7 @@
       }
 
 #if defined (HAVE_LLVM)
-    if (tree_jit::execute (cmd))
+    if (tree_jit::execute (*this, cmd))
       return;
 #endif
 
diff -r df9e3cb79379 -r 1ccb99c08f27 libinterp/parse-tree/pt-jit.cc
--- a/libinterp/parse-tree/pt-jit.cc	Wed May 08 18:45:34 2019 -0700
+++ b/libinterp/parse-tree/pt-jit.cc	Fri May 31 16:09:22 2019 -0500
@@ -22,9 +22,6 @@
 
 // Author: Max Brister <max@2bass.com>
 
-#define __STDC_LIMIT_MACROS
-#define __STDC_CONSTANT_MACROS
-
 #if defined (HAVE_CONFIG_H)
 #  include "config.h"
 #endif
@@ -126,6 +123,9 @@
 #  include <llvm/Transforms/Scalar/GVN.h>
 #endif
 
+#include <llvm/Transforms/InstCombine/InstCombine.h>
+#include <llvm/Transforms/Utils.h>
+
 static bool Vdebug_jit = false;
 
 static bool Vjit_enable = false;
@@ -153,6 +153,8 @@
 
   // -------------------- jit_break_exception --------------------
 
+  // FIXME: Get rid of this. We should not be throwing for expected behavior.
+
   // jit_break is thrown whenever a branch we are converting has only breaks or
   // continues.  This is because all code that follows a break or continue
   // is dead.
@@ -161,9 +163,12 @@
 
   // -------------------- jit_convert --------------------
   jit_convert::jit_convert (tree& tee, jit_type *for_bounds)
-    : m_converting_function (false)
+      : m_frame (interpreter::the_interpreter ()
+                 ->get_call_stack ()
+                 .get_current_stack_frame ()),
+        m_converting_function (false)
   {
-    initialize (__get_current_scope__ ("jit_convert::jit_convert"));
+    initialize ();
 
     if (for_bounds)
       create_variable (next_for_bounds (false), for_bounds);
@@ -182,11 +187,11 @@
     m_block->append (m_factory.create<jit_branch> (m_final_block));
     m_blocks.push_back (m_final_block);
 
-    for (auto iter = m_vmap.begin (); iter != m_vmap.end (); ++iter)
+    for (variable_map::value_type& vpair : m_vmap)
       {
-        jit_variable *var = iter->second;
+        jit_variable *var = vpair.second;
         const std::string& name = var->name ();
-        if (name.size () && name[0] != '#')
+        if (! name.empty () && name[0] != '#')
           m_final_block->append (m_factory.create<jit_store_argument> (var));
       }
 
@@ -195,9 +200,12 @@
 
   jit_convert::jit_convert (octave_user_function& fcn,
                             const std::vector<jit_type *>& args)
-    : m_converting_function (true)
+      : m_frame (interpreter::the_interpreter ()
+                 ->get_call_stack ()
+                 .get_current_stack_frame ()),
+        m_converting_function (true)
   {
-    initialize (fcn.scope ());
+    initialize ();
 
     tree_parameter_list *plist = fcn.parameter_list ();
     tree_parameter_list *rlist = fcn.return_list ();
@@ -205,7 +213,7 @@
       throw jit_fail_exception ("varags not supported");
 
     if (rlist && (rlist->size () > 1 || rlist->takes_varargs ()))
-      throw jit_fail_exception ("multiple returns not supported");
+      throw jit_fail_exception ("Multiple returns not supported");
 
     if (plist)
       {
@@ -213,7 +221,7 @@
         for (size_t i = 0; i < args.size (); ++i, ++piter)
           {
             if (piter == plist->end ())
-              throw jit_fail_exception ("Too many parameter to function");
+              throw jit_fail_exception ("Too many parameters to function");
 
             tree_decl_elt *elt = *piter;
             std::string name = elt->name ();
@@ -237,7 +245,7 @@
             catch (const jit_break_exception&)
               { }
 
-            if (m_breaks.size () || m_continues.size ())
+            if (! m_breaks.empty () || ! m_continues.empty())
               throw jit_fail_exception ("break/continue not supported in "
                                         "anonymous functions");
 
@@ -275,15 +283,13 @@
 
     // FIXME: We should use live range analysis to delete variables where
     // needed.  For now we just delete everything at the end of the function.
-    for (auto iter = m_vmap.begin ();
-         iter != m_vmap.end ();
-         ++iter)
+    for (variable_map::value_type& iter : m_vmap)
       {
-        if (iter->second != return_value)
+        if (iter.second != return_value)
           {
             jit_call *call;
             call = m_factory.create<jit_call> (&jit_typeinfo::destroy,
-                                               iter->second);
+                                               iter.second);
             m_final_block->append (call);
           }
       }
@@ -418,7 +424,7 @@
   void
   jit_convert::visit_simple_for_command (tree_simple_for_command& cmd)
   {
-    // Note we do an initial check to see if the loop will run atleast once.
+    // Note we do an initial check to see if the loop will run at least once.
     // This allows us to get better type inference bounds on variables defined
     // and used only inside the for loop (e.g., the index variable)
 
@@ -560,9 +566,10 @@
   {
     if (ti.has_magic_end ())
       {
-        if (! m_end_context.size ())
+        if (m_end_context.empty ())
           throw jit_fail_exception ("Illegal end");
-        m_result = m_block->append (m_factory.create<jit_magic_end> (m_end_context));
+        m_result = m_block->append (
+            m_factory.create<jit_magic_end> (m_end_context));
       }
     else
       {
@@ -643,8 +650,8 @@
             m_blocks.push_back (body);
 
             jit_instruction *br = m_factory.create<jit_cond_branch> (check,
-                                                                     body,
-                                                                     entry_blocks[i + 1]);
+                                                           body,
+                                                           entry_blocks[i + 1]);
             m_block->append (br);
             m_block = body;
           }
@@ -820,13 +827,12 @@
     else
       {
         // stolen from tree_evaluator::visit_statement
-        bool do_bind_ans = false;
+        bool do_bind_ans;
 
         if (expr->is_identifier ())
           {
-            tree_identifier *id = dynamic_cast<tree_identifier *> (expr);
-
-            do_bind_ans = (! id->is_variable (m_scope.current_context ()));
+            tree_identifier *t_id = dynamic_cast<tree_identifier *> (expr);
+            do_bind_ans = ! m_frame.is_variable (t_id->symbol ());
           }
         else
           do_bind_ans = (! expr->is_assignment_expression ());
@@ -840,8 +846,8 @@
             // FIXME: ugly hack, we need to come up with a way to pass
             // nargout to visit_identifier
             const jit_operation& fn = jit_typeinfo::print_value ();
-            jit_const_string *name = m_factory.create<jit_const_string>
-              (expr->name ());
+            jit_const_string *name
+                = m_factory.create<jit_const_string> (expr->name ());
             m_block->append (m_factory.create<jit_call> (fn, name,
                                                          expr_result));
           }
@@ -851,9 +857,8 @@
   void
   jit_convert::visit_statement_list (tree_statement_list& lst)
   {
-    for (auto iter = lst.begin (); iter != lst.end(); ++iter)
+    for (tree_statement *elt : lst)
       {
-        tree_statement *elt = *iter;
         // jwe: Can this ever be null?
         assert (elt);
         elt->accept (*this);
@@ -894,7 +899,7 @@
     if (last->is_default_case ())
       has_otherwise = 1;
 
-    std::vector<jit_block *> entry_blocks (case_blocks_num+1 - has_otherwise);
+    std::vector<jit_block *> entry_blocks (case_blocks_num + 1 - has_otherwise);
 
     // the first entry point is always the actual block.  Afterward, new blocks
     // are created for every case and the otherwise branch
@@ -906,8 +911,8 @@
 
     // if there's no otherwise branch, the 'else' of the last branch
     // has to point to the tail
-    if (! has_otherwise)
-      entry_blocks[entry_blocks.size()-1] = tail;
+    if (!has_otherwise)
+      entry_blocks[entry_blocks.size () - 1] = tail;
 
     // each branch in the case statement will have different breaks/continues
     block_list current_breaks = m_breaks;
@@ -925,18 +930,19 @@
         assert (m_block);
 
         if (i)
-          m_blocks.push_back (entry_blocks[i]);  // first block already pushed
-
-        if (! twc->is_default_case ())
+          m_blocks.push_back (entry_blocks[i]); // first block already pushed
+
+        if (!twc->is_default_case ())
           {
             // compare result of switch expression with actual case label
             tree_expression *te = twc->case_label ();
             jit_value *label = visit (te);
-            assert(label);
-
-            const jit_operation& fn = jit_typeinfo::binary_op (octave_value::op_eq);
+            assert (label);
+
+            const jit_operation& fn
+                = jit_typeinfo::binary_op (octave_value::op_eq);
             jit_value *cond = create_checked (fn, value, label);
-            assert(cond);
+            assert (cond);
 
             jit_call *check = create_checked (&jit_typeinfo::logically_true,
                                               cond);
@@ -950,7 +956,7 @@
           }
 
         tree_statement_list *stmt_lst = twc->commands ();
-        assert(stmt_lst);
+        assert (stmt_lst);
 
         try
           {
@@ -971,13 +977,13 @@
     m_breaks.splice (m_breaks.end (), current_breaks);
     m_continues.splice (m_continues.end (), current_continues);
 
-    if (num_incoming || ! has_otherwise)
+    if (num_incoming || !has_otherwise)
       {
         m_blocks.push_back (tail);
         m_block = tail; // switch_tail
       }
     else
-      throw jit_break_exception ();   // every branch broke
+      throw jit_break_exception (); // every branch broke
   }
 
   void
@@ -1034,7 +1040,7 @@
 
     finish_breaks (tail, m_breaks);
 
-    if (! all_breaking || m_continues.size ())
+    if (!all_breaking || ! m_continues.empty())
       {
         jit_block *interrupt_check
           = m_factory.create<jit_block> ("interrupt_check");
@@ -1087,7 +1093,7 @@
 
     finish_breaks (tail, m_breaks);
 
-    if (! all_breaking || m_continues.size ())
+    if (! all_breaking || ! m_continues.empty ())
       {
         jit_block *interrupt_check
           = m_factory.create<jit_block> ("interrupt_check");
@@ -1119,9 +1125,8 @@
   }
 
   void
-  jit_convert::initialize (const symbol_scope& s)
+  jit_convert::initialize ()
   {
-    m_scope = s;
     m_iterator_count = 0;
     m_for_bounds_count = 0;
     m_short_count = 0;
@@ -1165,17 +1170,19 @@
     if (ret)
       return ret;
 
+    // FIXME: there is a better way of doing this...
     symbol_table& symtab = __get_symbol_table__ ("jit_convert::find_variable");
 
-    symbol_record record = m_scope.find_symbol (vname);
-    if (record.is_persistent () || record.is_global ())
+    symbol_record record = m_frame.lookup_symbol (vname);
+
+    if (m_frame.is_persistent (record) || m_frame.is_global (record))
       throw jit_fail_exception ("Persistent and global not yet supported");
 
     if (m_converting_function)
       return create_variable (vname, jit_typeinfo::get_any (), false);
     else
       {
-        octave_value val = record.varval (m_scope.current_context ());
+        octave_value val = m_frame.varval (record);
         if (val.is_undefined ())
           val = symtab.find_function (vname);
 
@@ -1200,7 +1207,8 @@
       }
     else
       {
-        jit_call *init = m_factory.create<jit_call> (&jit_typeinfo::create_undef);
+        jit_call *init
+            = m_factory.create<jit_call> (&jit_typeinfo::create_undef);
         jit_assign *assign = m_factory.create<jit_assign> (var, init);
         m_entry_block->prepend (assign);
         m_entry_block->prepend (init);
@@ -1236,7 +1244,7 @@
     if (! arg_list)
       throw jit_fail_exception ("null argument list");
 
-    if (arg_list->size () < 1)
+    if (arg_list->empty ())
       throw jit_fail_exception ("Empty arg_list");
 
     tree_expression *tree_object = exp.expression ();
@@ -1253,7 +1261,7 @@
 
     size_t narg = arg_list->size ();
     auto iter = arg_list->begin ();
-    bool have_extra = extra_arg;
+    bool have_extra = extra_arg != nullptr;
     std::vector<jit_value *> call_args (narg + 1 + have_extra);
     call_args[0] = object;
 
@@ -1272,14 +1280,14 @@
       call_args[call_args.size () - 1] = extra_arg;
 
     const jit_operation& fres = (lhs ? jit_typeinfo::paren_subsasgn ()
-                                 : jit_typeinfo::paren_subsref ());
+                                     : jit_typeinfo::paren_subsref ());
 
     return create_checked (fres, call_args);
   }
 
   jit_value *
-  jit_convert::do_assign (tree_expression *exp, jit_value *rhs, bool
-                          artificial)
+  jit_convert::do_assign (tree_expression *exp, jit_value *rhs,
+                          bool artificial)
   {
     if (! exp)
       throw jit_fail_exception ("NULL lhs in assign");
@@ -1300,8 +1308,8 @@
   }
 
   jit_value *
-  jit_convert::do_assign (const std::string& lhs, jit_value *rhs,
-                          bool print, bool artificial)
+  jit_convert::do_assign (const std::string& lhs, jit_value *rhs, bool print,
+                          bool artificial)
   {
     jit_variable *var = get_variable (lhs);
     jit_assign *assign = m_block->append (m_factory.create<jit_assign> (var,
@@ -1331,19 +1339,17 @@
   }
 
   void
-  jit_convert::finish_breaks (jit_block *dest, const block_list& lst)
+  jit_convert::finish_breaks (jit_block *dest, block_list& lst)
   {
-    for (auto iter = lst.begin (); iter != lst.end (); ++iter)
+    for (jit_block* br : lst)
       {
-        jit_block *b = *iter;
-        b->append (m_factory.create<jit_branch> (dest));
+        br->append (m_factory.create<jit_branch> (dest));
       }
   }
 
   // -------------------- jit_convert_llvm --------------------
   llvm::Function *
-  jit_convert_llvm::convert_loop (const jit_module& module,
-                                  const jit_block_list& blocks,
+  jit_convert_llvm::convert_loop (const jit_block_list& blocks,
                                   const std::list<jit_value *>& constants,
                                   const std::string& llvm_function_name)
   {
@@ -1352,11 +1358,10 @@
     // for now just init arguments from entry, later we will have to do
     // something more interesting
     jit_block *m_entry_block = blocks.front ();
-    for (auto iter = m_entry_block->begin ();
-         iter != m_entry_block->end (); ++iter)
+    for (jit_instruction *instr : *m_entry_block)
       if (jit_extract_argument *extract
-          = dynamic_cast<jit_extract_argument *> (*iter))
-        m_argument_vec.push_back (std::make_pair (extract->name (), true));
+          = dynamic_cast<jit_extract_argument *> (instr))
+        m_argument_vec.emplace_back (extract->name (), true);
 
     jit_type *any = jit_typeinfo::get_any ();
 
@@ -1366,7 +1371,7 @@
     ft = llvm::FunctionType::get (llvm::Type::getVoidTy (context),
                                   arg_type->getPointerTo (), false);
 
-    m_function = module.create_llvm_function (ft, llvm_function_name);
+    m_function = m_module.create_llvm_function (ft, llvm_function_name);
     try
       {
         m_prelude = llvm::BasicBlock::Create (context, "prelude", m_function);
@@ -1376,14 +1381,16 @@
         // octave_base_value** type
         llvm::Value *arg = &*(m_function->arg_begin ());
 
-        for (size_t i = 0; i < m_argument_vec.size (); ++i)
+        for (unsigned i = 0; i < m_argument_vec.size (); ++i)
           {
-#if defined (LLVM_IRBUILDER_CREATECONSTINBOUNDSGEP1_32_REQUIRES_TYPE)
+#if defined(LLVM_IRBUILDER_CREATECONSTINBOUNDSGEP1_32_REQUIRES_TYPE)
             // LLVM >= 3.7
-            llvm::Value *loaded_arg = builder.CreateConstInBoundsGEP1_32 (arg_type, arg, i);
+            llvm::Value *loaded_arg
+                = builder.CreateConstInBoundsGEP1_32 (arg_type, arg, i);
 #else
             // LLVM <= 3.6
-            llvm::Value *loaded_arg = builder.CreateConstInBoundsGEP1_32 (arg, i);
+            llvm::Value *loaded_arg
+                = builder.CreateConstInBoundsGEP1_32 (arg, i);
 #endif
             m_arguments[m_argument_vec[i].first] = loaded_arg;
           }
@@ -1395,13 +1402,11 @@
         m_function->eraseFromParent ();
         throw;
       }
-
     return m_function;
   }
 
   jit_function
-  jit_convert_llvm::convert_function (const jit_module& module,
-                                      const jit_block_list& blocks,
+  jit_convert_llvm::convert_function (const jit_block_list& blocks,
                                       const std::list<jit_value *>& constants,
                                       octave_user_function& fcn,
                                       const std::vector<jit_type *>& args)
@@ -1412,8 +1417,8 @@
     jit_return *ret = dynamic_cast<jit_return *> (m_final_block->back ());
     assert (ret);
 
-    m_creating = jit_function (&module, jit_convention::internal,
-                               "foobar", ret->result_type (), args);
+    m_creating = jit_function (&m_module, jit_convention::internal, "foobar",
+                               ret->result_type (), args);
     m_function = m_creating.to_llvm ();
 
     try
@@ -1449,37 +1454,34 @@
   jit_convert_llvm::convert (const jit_block_list& blocks,
                              const std::list<jit_value *>& constants)
   {
-    std::list<jit_block *>::const_iterator biter;
-    for (biter = blocks.begin (); biter != blocks.end (); ++biter)
+    for (jit_block *bl : blocks)
       {
-        jit_block *jblock = *biter;
-        llvm::BasicBlock *m_block = llvm::BasicBlock::Create (context,
-                                                            jblock->name (),
-                                                            m_function);
-        jblock->stash_llvm (m_block);
+        llvm::BasicBlock *m_block
+            = llvm::BasicBlock::Create (context, bl->name (), m_function);
+        bl->stash_llvm (m_block);
       }
 
     jit_block *first = *blocks.begin ();
     builder.CreateBr (first->to_llvm ());
 
     // constants aren't in the IR, we visit those first
-    for (auto iter = constants.begin (); iter != constants.end (); ++iter)
-      if (! isa<jit_instruction> (*iter))
-        visit (*iter);
+    for (jit_value *c : constants)
+      if (! isa<jit_instruction> (c))
+        visit (c);
 
     // convert all instructions
-    for (biter = blocks.begin (); biter != blocks.end (); ++biter)
-      visit (*biter);
+    for(jit_block *bl : blocks)
+      visit (bl);
 
     // now finish phi nodes
-    for (biter = blocks.begin (); biter != blocks.end (); ++biter)
+    for (jit_block *bl : blocks)
       {
-        jit_block& m_block = **biter;
-        for (auto piter = m_block.begin ();
-             piter != m_block.end () && isa<jit_phi> (*piter); ++piter)
+        for (jit_instruction *instr : *bl)
           {
-            jit_instruction *phi = *piter;
-            finish_phi (static_cast<jit_phi *> (phi));
+            if (jit_phi *phi_node = dynamic_cast<jit_phi *>(instr))
+              finish_phi (phi_node);
+            else
+              break;
           }
       }
   }
@@ -1523,7 +1525,8 @@
     cc.stash_llvm (jit_typeinfo::create_complex (real, imag));
   }
 
-  void jit_convert_llvm::visit (jit_const_index& ci)
+  void
+  jit_convert_llvm::visit (jit_const_index& ci)
   {
     ci.stash_llvm (llvm::ConstantInt::get (ci.type_llvm (), ci.value ()));
   }
@@ -1531,20 +1534,19 @@
   void
   jit_convert_llvm::visit (jit_const_range& cr)
   {
-    llvm::StructType *stype = llvm::cast<llvm::StructType>(cr.type_llvm ());
+    llvm::StructType *stype = llvm::cast<llvm::StructType> (cr.type_llvm ());
     llvm::Type *scalar_t = jit_typeinfo::get_scalar_llvm ();
     llvm::Type *idx = jit_typeinfo::get_index_llvm ();
     const jit_range& rng = cr.value ();
 
-    llvm::Constant *constants[4];
-    constants[0] = llvm::ConstantFP::get (scalar_t, rng.m_base);
-    constants[1] = llvm::ConstantFP::get (scalar_t, rng.m_limit);
-    constants[2] = llvm::ConstantFP::get (scalar_t, rng.m_inc);
-    constants[3] = llvm::ConstantInt::get (idx, rng.m_nelem);
-
-    llvm::Value *as_llvm;
-    as_llvm = llvm::ConstantStruct::get (stype,
-                                         llvm::makeArrayRef (constants, 4));
+    llvm::ArrayRef<llvm::Constant *> constants{
+      llvm::ConstantFP::get (scalar_t, rng.m_base),
+      llvm::ConstantFP::get (scalar_t, rng.m_limit),
+      llvm::ConstantFP::get (scalar_t, rng.m_inc),
+      llvm::ConstantInt::get (idx, rng.m_nelem)
+    };
+
+    llvm::Value *as_llvm = llvm::ConstantStruct::get (stype, constants);
     cr.stash_llvm (as_llvm);
   }
 
@@ -1553,8 +1555,8 @@
   {
     llvm::BasicBlock *m_block = b.to_llvm ();
     builder.SetInsertPoint (m_block);
-    for (auto iter = b.begin (); iter != b.end (); ++iter)
-      visit (*iter);
+    for (jit_instruction *instr : b)
+      visit (instr);
   }
 
   void
@@ -1582,7 +1584,7 @@
     for (size_t i = 0; i < args.size (); ++i)
       args[i] = call.argument (i);
 
-    llvm::Value *ret = ol.call (builder, args);
+    llvm::Value *ret = ol.call (m_module, builder, args);
     call.stash_llvm (ret);
   }
 
@@ -1599,7 +1601,7 @@
         arg = builder.CreateLoad (arg);
 
         const jit_function& ol = extract.overload ();
-        extract.stash_llvm (ol.call (builder, arg));
+        extract.stash_llvm (ol.call (m_module, builder, arg));
       }
   }
 
@@ -1607,7 +1609,7 @@
   jit_convert_llvm::visit (jit_store_argument& store)
   {
     const jit_function& ol = store.overload ();
-    llvm::Value *arg_value = ol.call (builder, store.result ());
+    llvm::Value *arg_value = ol.call (m_module, builder, store.result ());
     llvm::Value *arg = m_arguments[store.name ()];
     store.stash_llvm (builder.CreateStore (arg_value, arg));
   }
@@ -1681,7 +1683,7 @@
       {
         const jit_function& ol = jit_typeinfo::get_release (overwrite->type ());
         if (ol.valid ())
-          ol.call (builder, overwrite);
+          ol.call (m_module, builder, overwrite);
       }
   }
 
@@ -1695,7 +1697,7 @@
     const jit_function& ol = me.overload ();
 
     jit_magic_end::context ctx = me.resolve_context ();
-    llvm::Value *ret = ol.call (builder, ctx.m_value, ctx.m_index,
+    llvm::Value *ret = ol.call (m_module, builder, ctx.m_value, ctx.m_index,
                                 ctx.m_count);
     me.stash_llvm (ret);
   }
@@ -1712,15 +1714,15 @@
 
     // initialize the worklist to instructions derived from constants
     const std::list<jit_value *>& constants = m_factory.constants ();
-    for (auto iter = constants.begin (); iter != constants.end (); ++iter)
-      append_users (*iter);
+    for (jit_value *c : constants)
+      append_users (c);
 
     // the entry block terminator may be a regular branch statement
     if (entry_block ().terminator ())
       push_worklist (entry_block ().terminator ());
 
     // FIXME: Describe algorithm here
-    while (m_worklist.size ())
+    while (! m_worklist.empty ())
       {
         jit_instruction *next = m_worklist.front ();
         m_worklist.pop_front ();
@@ -1777,26 +1779,26 @@
     entry_block ().create_dom_tree ();
 
     // insert phi nodes where needed, this is done on a per variable basis
-    for (auto iter = m_vmap.cbegin (); iter != m_vmap.cend (); ++iter)
+    for (const variable_map::value_type& vpair : m_vmap)
       {
         jit_block::df_set visited, added_phi;
         std::list<jit_block *> ssa_worklist;
-        iter->second->use_blocks (visited);
+        vpair.second->use_blocks (visited);
         ssa_worklist.insert (ssa_worklist.begin (), visited.begin (),
                              visited.end ());
 
-        while (ssa_worklist.size ())
+        while (! ssa_worklist.empty ())
           {
-            jit_block *b = ssa_worklist.front ();
+            jit_block *bl = ssa_worklist.front ();
             ssa_worklist.pop_front ();
 
-            for (auto diter = b->df_begin (); diter != b->df_end (); ++diter)
+            for (auto diter = bl->df_begin (); diter != bl->df_end (); ++diter)
               {
                 jit_block *dblock = *diter;
                 if (! added_phi.count (dblock))
                   {
-                    jit_phi *phi = m_factory.create<jit_phi> (iter->second,
-                                                          dblock->use_count ());
+                    jit_phi *phi = m_factory.create<jit_phi> (vpair.second,
+                                                         dblock->use_count ());
                     dblock->prepend (phi);
                     added_phi.insert (dblock);
                   }
@@ -1820,9 +1822,8 @@
       return;
 
     // replace variables with their current SSA value
-    for (auto iter = ablock.begin (); iter != ablock.end (); ++iter)
+    for (jit_instruction *instr : ablock)
       {
-        jit_instruction *instr = *iter;
         instr->construct_ssa ();
         instr->push_variable ();
       }
@@ -1832,7 +1833,7 @@
       {
         jit_block *finish = ablock.successor (i);
 
-        for (auto  iter = finish->begin ();
+        for (auto iter = finish->begin ();
              iter != finish->end () && isa<jit_phi> (*iter);)
           {
             jit_phi *phi = static_cast<jit_phi *> (*iter);
@@ -1845,8 +1846,8 @@
               {
                 // temporaries may have extraneous phi nodes which can be
                 // removed
-                assert (! phi->use_count ());
-                assert (var->name ().size () && var->name ()[0] == '#');
+                assert (!phi->use_count ());
+                assert (! var->name ().empty () && var->name ()[0] == '#');
                 phi->remove ();
               }
           }
@@ -1862,13 +1863,12 @@
   jit_infer::place_releases (void)
   {
     std::set<jit_value *> temporaries;
-    for (auto iter = m_blocks.begin (); iter != m_blocks.end (); ++iter)
+    for (jit_block *block : m_blocks)
       {
-        jit_block& ablock = **iter;
-        if (ablock.id () != jit_block::NO_ID)
+        if (block->id () != jit_block::NO_ID)
           {
-            release_temp (ablock, temporaries);
-            release_dead_phi (ablock);
+            release_temp (*block, temporaries);
+            release_dead_phi (*block);
           }
       }
   }
@@ -1876,7 +1876,7 @@
   void
   jit_infer::push_worklist (jit_instruction *instr)
   {
-    if (! instr->in_worklist ())
+    if (!instr->in_worklist ())
       {
         instr->stash_in_worklist (true);
         m_worklist.push_back (instr);
@@ -1963,7 +1963,7 @@
                 split->insert_before (term, release);
               }
 
-            phi->replace_with (0);
+            phi->replace_with (nullptr);
             phi->remove ();
           }
       }
@@ -2004,7 +2004,7 @@
           }
       }
 
-    if (! temp.size () || ! isa<jit_error_check> (ablock.terminator ()))
+    if (temp.empty () || ! isa<jit_error_check> (ablock.terminator ()))
       return;
 
     // FIXME: If we support try/catch or unwind_protect final_block
@@ -2012,9 +2012,8 @@
     jit_block *split = ablock.maybe_split (m_factory, m_blocks,
                                            final_block ());
     jit_terminator *term = split->terminator ();
-    for (auto iter = temp.cbegin (); iter != temp.cend (); ++iter)
+    for (jit_value *value : temp)
       {
-        jit_value *value = *iter;
         jit_call *release
           = m_factory.create<jit_call> (&jit_typeinfo::release, value);
         split->insert_before (term, release);
@@ -2025,12 +2024,15 @@
   void
   jit_infer::simplify_phi (void)
   {
-    for (auto biter = m_blocks.begin (); biter != m_blocks.end (); ++biter)
+    for (jit_block *block : m_blocks)
       {
-        jit_block &ablock = **biter;
-        for (auto iter = ablock.begin ();
-             iter != ablock.end () && isa<jit_phi> (*iter); ++iter)
-          simplify_phi (*static_cast<jit_phi *> (*iter));
+        for (jit_instruction *instr : *block)
+          {
+            if (jit_phi *phi_node = dynamic_cast<jit_phi *>(instr))
+              simplify_phi (*phi_node);
+            else
+              break;
+          }
       }
   }
 
@@ -2060,7 +2062,6 @@
       }
   }
 
-
   // ---------------- jit_memory_manager ------------------
 
   // A simple memory manager for our LLVM engines,
@@ -2068,25 +2069,26 @@
 
   class jit_memory_manager : public llvm::SectionMemoryManager
   {
+  public:
+    jit_memory_manager () = default;
+    virtual ~jit_memory_manager () = default;
+
     jit_memory_manager (const jit_memory_manager&) = delete;
     void operator= (const jit_memory_manager&) = delete;
-  public:
-    jit_memory_manager () { }
-    virtual ~jit_memory_manager () { }
 
     // The Kaleidoscope example in LLVM 3.8 indicates that
     // getPointerToNamedFunction has to be overloaded, but actually it is
     // getSymbolAddress that must be overloaded.
-    virtual uint64_t getSymbolAddress (const std::string &name);
+    virtual uint64_t getSymbolAddress (const std::string& name);
 
     // Is it still useful to overload getPointerToNamedFunction to support
     // some older version of LLVM?  Are there others virtual functions
     // that must be overloaded?
-    virtual void* getPointerToNamedFunction (const std::string& name, bool
-                                             abort_on_failure);
+    virtual void *getPointerToNamedFunction (const std::string& name,
+                                             bool abort_on_failure);
   };
 
-  void*
+  void *
   jit_memory_manager::getPointerToNamedFunction (const std::string& name,
                                                  bool abort_on_failure)
   {
@@ -2098,27 +2100,32 @@
 
     pfn = tree_jit::getPointerToNamedFunction (name);
     if ((pfn == nullptr) && abort_on_failure)
-      llvm::report_fatal_error ("Program used external function '" + name +
-                                "' which could not be resolved!");
+      llvm::report_fatal_error ("Program used external function '" + name
+                                + "' which could not be resolved!");
     return pfn;
   }
 
   uint64_t
-  jit_memory_manager::getSymbolAddress (const std::string &name)
+  jit_memory_manager::getSymbolAddress (const std::string& name)
   {
-    uint64_t addr = llvm::SectionMemoryManager::getSymbolAddress (name);
-    if (addr)
-      return addr;
-
+    uint64_t addr;
+
+    // FIXME: Not sure why this doesn't work... It retrieves an incorrect
+    // address when trying to resolve `sin`. My best guess is that it's
+    // actually resolving to an llvm internal function named `sin`.
+    //    uint64_t addr = llvm::SectionMemoryManager::getSymbolAddress (name);
+    //    if (addr)
+    //      return addr;
+
+    // Less magic if we do it this way anyway
     addr = tree_jit::getSymbolAddress (name);
     if (addr == 0)
-      llvm::report_fatal_error ("Program used extern function '" + name +
-                                "' which could not be resolved!");
+      llvm::report_fatal_error ("Program used external function '" + name
+                                + "' which could not be resolved!");
 
     return addr;
   }
 
-
   // -------------------- tree_jit --------------------
 
   bool tree_jit::initialized = false;
@@ -2129,8 +2136,7 @@
   int tree_jit::next_function_number = 0;
   int tree_jit::next_module_number = 0;
 
-  tree_jit::tree_jit (void)
-    : target_machine (nullptr)
+  tree_jit::tree_jit (void) : target_machine (nullptr)
   {
     // target_machine will be truly initialized by tree_jit::do_initialize ()
   }
@@ -2143,7 +2149,7 @@
   tree_jit&
   tree_jit::instance (void)
   {
-    static tree_jit ret;  // singleton instance of tree_jit
+    static tree_jit ret; // singleton instance of tree_jit
 
     if (! initialized)
       // Try to initialize the singleton instance
@@ -2159,12 +2165,12 @@
 
     llvm::ExecutionEngine *e = llvm::EngineBuilder (std::move (module_owner))
       .setErrorStr (&err)
-      .setMCJITMemoryManager(llvm::make_unique<jit_memory_manager> ())
+      .setMCJITMemoryManager (llvm::make_unique<jit_memory_manager> ())
       .create ();
 
     // Note: in some versions of LLVM, we should call .setUseMCJIT (true)
     // before .create () ?
-    // FIXME: autconf this
+    // FIXME: autoconf this
 
     if (e == nullptr)
       error ("failed to create JIT engine: %s", err.c_str ());
@@ -2173,37 +2179,37 @@
   }
 
   void
-  tree_jit::do_register_jit_module (jit_module* jm)
+  tree_jit::do_register_jit_module (jit_module *jm)
   {
     jm_list.push_back (jm);
   }
 
   void
-  tree_jit::do_unregister_jit_module (jit_module* jm)
+  tree_jit::do_unregister_jit_module (jit_module *jm)
   {
     jm_list.remove (jm);
   }
 
-  void*
-  tree_jit::do_getPointerToNamedFunction (const std::string &name) const
+  void *
+  tree_jit::do_getPointerToNamedFunction (const std::string& name) const
   {
-    std::list<jit_module*>::const_iterator it;
+    std::list<jit_module *>::const_iterator it;
 
     for (it = jm_list.begin (); it != jm_list.end (); it++)
       {
         uint64_t addr = (*it)->getFunctionAddress (name);
 
         if (addr)
-          return reinterpret_cast<void*> (addr);
+          return reinterpret_cast<void *> (addr);
       }
 
     return nullptr;
   }
 
   uint64_t
-  tree_jit::do_getSymbolAddress(const std::string &name) const
+  tree_jit::do_getSymbolAddress (const std::string& name) const
   {
-    std::list<jit_module*>::const_iterator it;
+    std::list<jit_module *>::const_iterator it;
 
     for (it = jm_list.begin (); it != jm_list.end (); it++)
       {
@@ -2251,7 +2257,6 @@
 
     jit::ModuleOwner m (new llvm::Module (module_name, context));
 
-
     if (m != nullptr)
       m->setDataLayout (target_machine->createDataLayout ());
 
@@ -2259,7 +2264,7 @@
   }
 
   bool
-  tree_jit::do_execute (tree_simple_for_command& cmd,
+  tree_jit::do_execute (tree_evaluator& tr_eval, tree_simple_for_command& cmd,
                         const octave_value& bounds)
   {
     size_t tc = trip_count (bounds);
@@ -2278,7 +2283,7 @@
 
         delete info;
 
-        info = new jit_info (cmd, bounds);
+        info = new jit_info (tr_eval, cmd, bounds);
 
         cmd.stash_info (info);
       }
@@ -2287,7 +2292,7 @@
   }
 
   bool
-  tree_jit::do_execute (tree_while_command& cmd)
+  tree_jit::do_execute (tree_evaluator& tr_eval, tree_while_command& cmd)
   {
     if (! initialized || ! enabled ())
       return false;
@@ -2296,7 +2301,7 @@
     if (! info || ! info->match ())
       {
         delete info;
-        info = new jit_info (cmd);
+        info = new jit_info (tr_eval, cmd);
         cmd.stash_info (info);
       }
 
@@ -2304,7 +2309,7 @@
   }
 
   bool
-  tree_jit::do_execute (octave_user_function& fcn,
+  tree_jit::do_execute (tree_evaluator& tr_eval, octave_user_function& fcn,
                         const octave_value_list& args,
                         octave_value_list& retval)
   {
@@ -2315,7 +2320,7 @@
     if (! info || ! info->match (args))
       {
         delete info;
-        info = new jit_function_info (fcn, args);
+        info = new jit_function_info (tr_eval, fcn, args);
         fcn.stash_info (info);
       }
 
@@ -2347,7 +2352,6 @@
     return 0;
   }
 
-
   // -------------------- jit_module --------------------
 
   jit_module::jit_module (const std::string& module_name)
@@ -2366,8 +2370,7 @@
     // TODO?: Consider creating the engine just before jitting
 
     // We take responsibility for deleting the engine
-    m_engine = engine_owner.get ();
-    engine_owner.release ();
+    m_engine = engine_owner.release ();
 
     tree_jit::register_jit_module (this);
   }
@@ -2380,11 +2383,11 @@
   }
 
   // Create an LLVM function in the module, with external linkage
-  llvm::Function*
+  llvm::Function *
   jit_module::create_llvm_function (llvm::FunctionType *ftype,
-                                    const llvm::Twine &name) const
+                                    const llvm::Twine& name) const
   {
-    // we mark all functinos as external linkage because this prevents
+    // we mark all functions as external linkage because this prevents
     // llvm from getting rid of always inline functions
 
     return llvm::Function::Create (ftype, llvm::Function::ExternalLinkage,
@@ -2393,16 +2396,16 @@
 
   // Create or insert an LLVM Function declaration for an intrinsic and return
   // it
-  llvm::Function*
+  llvm::Function *
   jit_module::get_intrinsic_declaration (size_t id,
-                                         std::vector<llvm::Type*> types) const
+                                         const std::vector<llvm::Type *>& types) const
   {
-    return llvm::Intrinsic::getDeclaration
-      (m_module, static_cast<llvm::Intrinsic::ID> (id), types);
+    return llvm::Intrinsic::getDeclaration (m_module,
+                                  static_cast<llvm::Intrinsic::ID> (id), types);
   }
 
   // Create a global in the module
-  llvm::GlobalVariable*
+  llvm::GlobalVariable *
   jit_module::create_global_variable (llvm::Type *type, bool is_constant,
                                       const llvm::Twine& name) const
   {
@@ -2412,7 +2415,7 @@
   }
 
   void
-  jit_module::do_add_global_mapping (const llvm::GlobalValue* gv, void* p) const
+  jit_module::do_add_global_mapping (const llvm::GlobalValue *gv, void *p) const
   {
     assert (gv);
     m_engine->addGlobalMapping (gv, p);
@@ -2420,11 +2423,17 @@
 
   // Return the address of the specified function.
   uint64_t
-  jit_module::getFunctionAddress (const std::string &name) const
+  jit_module::getFunctionAddress (const std::string& name) const
   {
     return m_engine->getFunctionAddress (name);
   }
 
+  llvm::Function *
+  jit_module::getFunction (const std::string& name) const
+  {
+    return m_module->getFunction (name);
+  }
+
   void
   jit_module::optimize (llvm::Function *fn) const
   {
@@ -2434,9 +2443,10 @@
     // DOCUMENT-ME: Why do we need two separate pass managers?
 
     jit::PassManager *module_pass_manager = new jit::PassManager ();
-    jit::FunctionPassManager *pass_manager = new jit::FunctionPassManager (m_module);
-
-#if defined (LLVM_HAS_CREATEALWAYSINLINERPASS)
+    jit::FunctionPassManager *pass_manager
+        = new jit::FunctionPassManager (m_module);
+
+#if defined(LLVM_HAS_CREATEALWAYSINLINERPASS)
     // This pass has been removed from LLVM's C++ API after 3.9.0
     // FIXME: Provide a meaningful replacement instead of simply skipping it?
     module_pass_manager->add (llvm::createAlwaysInlinerPass ());
@@ -2491,24 +2501,94 @@
         //#else
         //      llvm::raw_fd_ostream fout ("test.bc", error, llvm::raw_fd_ostream::F_Binary);
         //#endif
-
+#if defined(LLVM_WRITEBITCODETOFILE_HAS_PTR_ARG)
         llvm::WriteBitcodeToFile (m_module, fout);
+#else
+        llvm::WriteBitcodeToFile (*m_module, fout);
+#endif
       }
   }
 
   void
+  jit_module::print_object_summary (void)
+  {
+    octave_stdout << "---------------------------------------------\n";
+    octave_stdout << "Summary for " << m_module->getName ().str () << ":\n\n";
+    m_module->print (llvm::outs (), nullptr);
+    octave_stdout << std::endl;
+
+    std::string errstring;
+    llvm::raw_string_ostream ros (errstring);
+    if (llvm::verifyModule (*m_module, &ros))
+      {
+        octave_stdout << "Errors: \n\n";
+        octave_stdout << ros.str () << std::endl;
+      }
+    octave_stdout << "---------------------------------------------\n";
+  }
+
+  void
+  jit_module::print_all_flocs (void) const
+  {
+    octave_stdout << "ALL FUNCTION LOCATIONS FOR MODULE "
+                  << m_module->getName ().str () << '\n';
+    octave_stdout << "--------------------------------------------------------"
+                  << '\n';
+    for (const llvm::Function& f : *m_module)
+      {
+        octave_stdout << f.getName ().str () << ": ";
+        octave_stdout << std::hex << std::showbase << std::nouppercase
+                  << m_engine->getFunctionAddress (f.getName ());
+        octave_stdout << '\n';
+      }
+    octave_stdout << std::endl;
+  }
+
+  // TESTING
+  void
+  jit_module::print_all_functions () const
+  {
+    octave_stdout << "ALL FUNCTIONS FOR MODULE " << m_module->getName ().str ()
+                  << '\n';
+    octave_stdout << "--------------------------------------------------------"
+                  << '\n';
+    for (const llvm::Function& f : *m_module)
+      {
+        octave_stdout << "Location: ";
+        octave_stdout << std::hex << std::showbase << std::nouppercase
+                  << m_engine->getFunctionAddress (f.getName ());
+        octave_stdout.flush ();
+        f.print (llvm::outs ());
+        octave_stdout << '\n';
+      }
+    octave_stdout << "--------------------------------------------------------"
+                  << std::endl;
+  }
+
+  void
+  jit_module::dump () const
+  {
+    octave_stdout << "DUMP FOR MODULE " << m_module->getName ().str ()
+                  << '\n';
+    octave_stdout << "-------------------------------" << std::endl;
+    m_module->print (llvm::outs (), nullptr);
+    octave_stdout << "-------------------------------" << '\n';
+    octave_stdout << std::endl;
+  }
+
+  void
   jit_module::finalizeObject (void)
   {
+    // print_object_summary ();
     m_engine->finalizeObject ();
   }
 
-
   // -------------------- jit_function_info --------------------
-  jit_function_info::jit_function_info (octave_user_function& fcn,
+  jit_function_info::jit_function_info (tree_evaluator& tr_eval,
+                                        octave_user_function& fcn,
                                         const octave_value_list& ov_args)
-    : m_llvm_function_name (""),
-      m_function (nullptr),
-      m_argument_types (ov_args.length ())
+      : m_tr_eval (tr_eval), m_llvm_function_name (""), m_function (nullptr),
+        m_argument_types (ov_args.length ())
   {
     size_t nargs = ov_args.length ();
     for (size_t i = 0; i < nargs; ++i)
@@ -2539,10 +2619,10 @@
           }
 
         jit_factory& factory = conv.get_factory ();
-        jit_convert_llvm to_llvm;
-        raw_fn = to_llvm.convert_function (*this, infer.get_blocks (),
-                                           factory.constants (), fcn,
-                                           m_argument_types);
+        jit_convert_llvm to_llvm (*this);
+        raw_fn = to_llvm.convert_function (infer.get_blocks (),
+                                           factory.constants (),
+                                           fcn, m_argument_types);
 
         if (Vdebug_jit)
           {
@@ -2563,7 +2643,7 @@
 
         llvm::Value *wrapper_arg = wrapper.argument (builder, 0);
         std::vector<llvm::Value *> raw_args (nargs);
-        for (size_t i = 0; i < nargs; ++i)
+        for (unsigned i = 0; i < nargs; ++i)
           {
             llvm::Value *arg;
             // LLVM <= 3.6
@@ -2576,10 +2656,10 @@
 
             jit_type *arg_type = m_argument_types[i];
             const jit_function& cast = jit_typeinfo::cast (arg_type, any_t);
-            raw_args[i] = cast.call (builder, arg);
+            raw_args[i] = cast.call (*this, builder, arg);
           }
 
-        llvm::Value *result = raw_fn.call (builder, raw_args);
+        llvm::Value *result = raw_fn.call (*this, builder, raw_args);
         if (raw_fn.result ())
           {
             jit_type *raw_result_t = raw_fn.result ();
@@ -2682,26 +2762,29 @@
     return true;
   }
 
-
   // -------------------- jit_info --------------------
-  jit_info::jit_info (tree& tee)
-    : m_llvm_function_name (tree_jit::generate_unique_function_name ()),
-      m_function (nullptr)
+  jit_info::jit_info (tree_evaluator& tr_eval, tree& tee)
+      : m_tr_eval (tr_eval),
+        m_llvm_function_name (tree_jit::generate_unique_function_name ()),
+        m_function (nullptr)
   {
     compile (tee);
   }
 
-  jit_info::jit_info (tree& tee, const octave_value& for_bounds)
-    : m_llvm_function_name (tree_jit::generate_unique_function_name ()),
-      m_function (nullptr)
+  jit_info::jit_info (tree_evaluator& tr_eval, tree& tee,
+                      const octave_value& for_bounds)
+      : m_tr_eval (tr_eval),
+        m_llvm_function_name (tree_jit::generate_unique_function_name ()),
+        m_function (nullptr)
   {
     compile (tee, jit_typeinfo::type_of (for_bounds));
   }
 
-  jit_info::jit_info (tree_simple_for_command& tee,
+  jit_info::jit_info (tree_evaluator& tr_eval, tree_simple_for_command& tee,
                       const octave_value& for_bounds)
-    : m_llvm_function_name (tree_jit::generate_unique_forloop_name ()),
-      m_function (nullptr)
+      : m_tr_eval (tr_eval),
+        m_llvm_function_name (tree_jit::generate_unique_forloop_name ()),
+        m_function (nullptr)
   {
     compile (tee, jit_typeinfo::type_of (for_bounds));
   }
@@ -2709,7 +2792,7 @@
   bool
   jit_info::execute (const vmap& extra_vars) const
   {
-    if (! m_function)
+    if (!m_function)
       return false;
 
     std::vector<octave_base_value *> real_arguments (m_arguments.size ());
@@ -2728,15 +2811,13 @@
 
     m_function (&real_arguments[0]);
 
-    symbol_scope scope = __require_current_scope__ ("jit_info::execute");
-
     for (size_t i = 0; i < m_arguments.size (); ++i)
       {
         const std::string& name = m_arguments[i].first;
 
         // do not store for loop bounds temporary
-        if (name.size () && name[0] != '#')
-          scope.assign (m_arguments[i].first, real_arguments[i]);
+        if (! name.empty () && name[0] != '#')
+          m_tr_eval.assign (m_arguments[i].first, real_arguments[i]);
       }
 
     octave_quit ();
@@ -2750,14 +2831,14 @@
     if (! m_function)
       return true;
 
-    for (size_t i = 0; i < m_bounds.size (); ++i)
+    for (const type_bound_vector::value_type& bound : m_bounds)
       {
-        const std::string& arg_name = m_bounds[i].second;
+        const std::string& arg_name = bound.second;
         octave_value value = find (extra_vars, arg_name);
         jit_type *type = jit_typeinfo::type_of (value);
 
         // FIXME: Check for a parent relationship
-        if (type != m_bounds[i].first)
+        if (type != bound.first)
           return false;
       }
 
@@ -2767,7 +2848,7 @@
   void
   jit_info::compile (tree& tee, jit_type *for_bounds)
   {
-    llvm::Function * llvm_function = nullptr;
+    llvm::Function *llvm_function = nullptr;
 
     try
       {
@@ -2787,12 +2868,14 @@
           }
 
         jit_factory& factory = conv.get_factory ();
-        jit_convert_llvm to_llvm;
-
-        llvm_function = to_llvm.convert_loop (*this, infer.get_blocks (),
+        jit_convert_llvm to_llvm (*this);
+
+        llvm_function = to_llvm.convert_loop (infer.get_blocks (),
                                               factory.constants (),
                                               m_llvm_function_name);
 
+        // llvm_function->print(llvm::outs());
+
         m_arguments = to_llvm.get_arguments ();
 
         m_bounds = conv.get_bounds ();
@@ -2829,8 +2912,10 @@
 
         finalizeObject ();
 
+        //        jit_typeinfo::print_all_flocs();
+        //        print_all_flocs ();
+
         uint64_t void_fn = getFunctionAddress (m_llvm_function_name);
-
         if (void_fn == 0)
           {
             llvm_function->eraseFromParent ();
@@ -2851,9 +2936,7 @@
 
     if (iter == extra_vars.end ())
       {
-        symbol_scope scope = __require_current_scope__ ("jit_convert::find");
-
-        return scope.varval (vname);
+        return m_tr_eval.varval (vname);
       }
     else
       return *iter->second;
diff -r df9e3cb79379 -r 1ccb99c08f27 libinterp/parse-tree/pt-jit.h
--- a/libinterp/parse-tree/pt-jit.h	Wed May 08 18:45:34 2019 -0700
+++ b/libinterp/parse-tree/pt-jit.h	Fri May 31 16:09:22 2019 -0500
@@ -34,6 +34,7 @@
 #include "jit-ir.h"
 #include "pt-walk.h"
 #include "symscope.h"
+#include "stack-frame.h"
 
 // octave_value_list is not (yet) in the octave namespace
 class octave_value_list;
@@ -47,8 +48,7 @@
   }
 
   // Convert from the parse tree (AST) to the low level Octave IR.
-  class
-  jit_convert : public tree_walker
+  class jit_convert : public tree_walker
   {
   public:
 
@@ -67,15 +67,30 @@
       return create_checked_impl (ret);
     }
 
-    jit_block_list& get_blocks (void) { return m_blocks; }
+    jit_block_list& get_blocks (void)
+    {
+      return m_blocks;
+    }
 
-    const type_bound_vector& get_bounds (void) const { return m_bounds; }
+    const type_bound_vector& get_bounds (void) const
+    {
+      return m_bounds;
+    }
 
-    jit_factory& get_factory (void) { return m_factory; }
+    jit_factory& get_factory (void)
+    {
+      return m_factory;
+    }
 
-    llvm::Function *get_function (void) const { return m_function; }
+    llvm::Function * get_function (void) const
+    {
+      return m_function;
+    }
 
-    const variable_map& get_variable_map (void) const { return m_vmap; }
+    const variable_map& get_variable_map (void) const
+    {
+      return m_vmap;
+    }
 
     void visit_anon_fcn_handle (tree_anon_fcn_handle&);
 
@@ -168,10 +183,9 @@
     std::vector<std::pair<std::string, bool>> m_arguments;
     type_bound_vector m_bounds;
 
-    bool m_converting_function;
+    stack_frame& m_frame;
 
-    // the scope of the function we are converting, or the current scope
-    symbol_scope m_scope;
+    bool m_converting_function;
 
     jit_factory m_factory;
 
@@ -196,7 +210,7 @@
 
     variable_map m_vmap;
 
-    void initialize (const symbol_scope& s);
+    void initialize ();
 
     jit_call * create_checked_impl (jit_call *ret);
 
@@ -216,26 +230,36 @@
     // The name of the next for loop iterator.  If inc is false, then the
     // iterator counter will not be incremented.
     std::string next_iterator (bool inc = true)
-    { return next_name ("#iter", m_iterator_count, inc); }
+    {
+      return next_name ("#iter", m_iterator_count, inc);
+    }
 
     std::string next_for_bounds (bool inc = true)
-    { return next_name ("#for_bounds", m_for_bounds_count, inc); }
+    {
+      return next_name ("#for_bounds", m_for_bounds_count, inc);
+    }
 
     std::string next_shortcircut_result (bool inc = true)
-    { return next_name ("#shortcircut_result", m_short_count, inc); }
+    {
+      return next_name ("#shortcircut_result", m_short_count, inc);
+    }
 
     std::string next_name (const char *prefix, size_t& count, bool inc);
 
-    jit_instruction * resolve (tree_index_expression& exp,
-                               jit_value *extra_arg = nullptr, bool lhs = false);
+    jit_instruction *resolve (tree_index_expression& exp,
+                              jit_value *extra_arg = nullptr,
+                              bool lhs = false);
+
+    jit_value *do_assign (tree_expression *exp, jit_value *rhs,
+                          bool artificial = false);
 
-    jit_value * do_assign (tree_expression *exp, jit_value *rhs,
-                           bool artificial = false);
+    jit_value *do_assign (const std::string& lhs, jit_value *rhs, bool print,
+                          bool artificial = false);
 
-    jit_value * do_assign (const std::string& lhs, jit_value *rhs, bool print,
-                           bool artificial = false);
-
-    jit_value * visit (tree *tee) { return visit (*tee); }
+    jit_value * visit (tree *tee)
+    {
+      return visit (*tee);
+    }
 
     jit_value * visit (tree& tee);
 
@@ -243,38 +267,39 @@
     block_list m_breaks;
     block_list m_continues;
 
-    void finish_breaks (jit_block *dest, const block_list& lst);
+    void finish_breaks (jit_block *dest, block_list& lst);
   };
 
   // Convert from the low level Octave IR to LLVM
-  class
-  jit_convert_llvm : public jit_ir_walker
+  class jit_convert_llvm : public jit_ir_walker
   {
   public:
+    jit_convert_llvm (const jit_module& amodule)
+        : m_module (amodule) { }
 
-    llvm::Function * convert_loop (const jit_module& module,
-                                   const jit_block_list& blocks,
-                                   const std::list<jit_value *>& constants,
-                                   const std::string& llvm_function_name);
+    llvm::Function * convert_loop (const jit_block_list& blocks,
+                                  const std::list<jit_value *>& constants,
+                                  const std::string& llvm_function_name);
 
-    jit_function convert_function (const jit_module& module,
-                                   const jit_block_list& blocks,
+    jit_function convert_function (const jit_block_list& blocks,
                                    const std::list<jit_value *>& constants,
                                    octave_user_function& fcn,
                                    const std::vector<jit_type *>& args);
 
     // arguments to the llvm::Function for loops
-    const std::vector<std::pair<std::string, bool>>& get_arguments(void) const
-    { return m_argument_vec; }
+    const std::vector<std::pair<std::string, bool>>& get_arguments (void) const
+    {
+      return m_argument_vec;
+    }
 
-#define JIT_METH(clname)                        \
-    virtual void visit (jit_ ## clname&);
+#define JIT_METH(clname) virtual void visit (jit_##clname&);
 
     JIT_VISIT_IR_CLASSES;
 
 #undef JIT_METH
 
   private:
+    const jit_module& m_module;
 
     // name -> argument index (used for compiling functions)
     std::map<std::string, int> m_argument_index;
@@ -302,6 +327,7 @@
       return visit (*jvalue);
     }
 
+    // Is this really necessary? It's only called by the above function.
     void visit (jit_value& jvalue)
     {
       jvalue.accept (*this);
@@ -309,24 +335,27 @@
   };
 
   // type inference and SSA construction on the low level Octave IR
-  class
-  jit_infer
+  class jit_infer
   {
   public:
-
     typedef jit_convert::variable_map variable_map;
 
     jit_infer (jit_factory& afactory, jit_block_list& ablocks,
                const variable_map& avmap);
 
-    jit_block_list& get_blocks (void) const { return m_blocks; }
+    jit_block_list& get_blocks (void) const
+    {
+      return m_blocks;
+    }
 
-    jit_factory& get_factory (void) const { return m_factory; }
+    jit_factory& get_factory (void) const
+    {
+      return m_factory;
+    }
 
     void infer (void);
 
   private:
-
     jit_block_list& m_blocks;
     jit_factory& m_factory;
     const variable_map& m_vmap;
@@ -340,9 +369,15 @@
 
     void do_construct_ssa (jit_block& block, size_t avisit_count);
 
-    jit_block& entry_block (void) { return *m_blocks.front (); }
+    jit_block& entry_block (void)
+    {
+      return *m_blocks.front ();
+    }
 
-    jit_block& final_block (void) { return *m_blocks.back (); }
+    jit_block& final_block (void)
+    {
+      return *m_blocks.back ();
+    }
 
     void place_releases (void);
 
@@ -359,42 +394,37 @@
     void simplify_phi (jit_phi& phi);
   };
 
-
   class jit_module;
 
-  class
-  tree_jit
+  class tree_jit
   {
     // ----- Constructor/destructor (singleton pattern) -----
 
   public:
-
     ~tree_jit (void);
 
   private:
-
     tree_jit (void);
     static tree_jit& instance (void);
 
     // ----- Initialization -----
 
   private:
-
     static bool initialized;
     bool do_initialize (void);
 
     // ----- LLVM context -----
 
   public:
-
     static llvm::LLVMContext llvm_context;
 
     // ----- Target machine ----
 
   public:
-
-    static const llvm::TargetMachine* get_target_machine (void)
-    { return instance ().target_machine; }
+    static const llvm::TargetMachine * get_target_machine (void)
+    {
+      return instance ().target_machine;
+    }
 
   private:
 
@@ -403,79 +433,77 @@
     // ----- Create LLVM modules and engines -----
 
   public:
+    static jit::ModuleOwner open_new_module (const std::string& module_name
+                                             = generate_unique_module_name ());
 
-    static jit::ModuleOwner
-    open_new_module (const std::string& module_name = generate_unique_module_name ());
-
-    static jit::EngineOwner
-    create_new_engine (jit::ModuleOwner module_owner);
+    static jit::EngineOwner create_new_engine (jit::ModuleOwner module_owner);
 
   private:
-
-    jit::ModuleOwner
-    do_open_new_module (const std::string& module_name) const;
+    jit::ModuleOwner do_open_new_module (const std::string& module_name) const;
 
     // ----- Registering JIT modules (module+engine pairs) -----
 
   public:
+    static void register_jit_module (jit_module *jm)
+    {
+      instance ().do_register_jit_module (jm);
+    }
 
-    static void register_jit_module (jit_module* jm)
-    { instance ().do_register_jit_module (jm); }
-
-    static void unregister_jit_module (jit_module* jm)
-    { instance ().do_unregister_jit_module (jm); }
+    static void unregister_jit_module (jit_module *jm)
+    {
+      instance ().do_unregister_jit_module (jm);
+    }
 
   private:
-
     // List of all currently registered jit modules
-    std::list<jit_module*> jm_list;
-    void do_register_jit_module (jit_module* jm);
-    void do_unregister_jit_module (jit_module* jm);
+    std::list<jit_module *> jm_list;
+    void do_register_jit_module (jit_module *jm);
+    void do_unregister_jit_module (jit_module *jm);
     void do_dump_all_modules (void) const;
 
     // ----- Symbol resolution -----
 
   public:
+    static void * getPointerToNamedFunction (const std::string& name)
+    {
+      return instance ().do_getPointerToNamedFunction (name);
+    }
 
-    static void* getPointerToNamedFunction (const std::string &name)
-    { return instance ().do_getPointerToNamedFunction (name); }
-
-    static uint64_t getSymbolAddress (const std::string &name)
-    { return instance ().do_getSymbolAddress (name); }
+    static uint64_t getSymbolAddress (const std::string& name)
+    {
+      return instance ().do_getSymbolAddress (name);
+    }
 
   private:
+    void *do_getPointerToNamedFunction (const std::string& Name) const;
 
-    void* do_getPointerToNamedFunction (const std::string &Name) const;
-
-    uint64_t do_getSymbolAddress (const std::string &name) const;
+    uint64_t do_getSymbolAddress (const std::string& name) const;
 
     // ----- Generate unique identifiers -----
 
   public:
-
     static std::string generate_unique_forloop_name (void)
     {
       return std::string ("jittedForLoop")
-             + std::to_string (next_forloop_number ++);
+             + std::to_string (next_forloop_number++);
     }
     // FIXME: Check that the identifier does not exist
 
     static std::string generate_unique_function_name (void)
     {
       return std::string ("jittedFunction")
-             + std::to_string (next_function_number ++);
+             + std::to_string (next_function_number++);
     }
     // FIXME: Check that the identifier does not exist
 
     static std::string generate_unique_module_name (void)
     {
       return std::string ("octaveJITModule")
-             + std::to_string (next_module_number ++);
+             + std::to_string (next_module_number++);
     }
     // FIXME: Check that the identifier does not exist
 
   private:
-
     static int next_forloop_number;
     static int next_function_number;
     static int next_module_number;
@@ -483,29 +511,31 @@
     // ----- JIT and execute ASTs -----
 
   public:
-
-    static bool execute (tree_simple_for_command& cmd,
-                         const octave_value& bounds)
-    { return instance ().do_execute (cmd, bounds); }
+    static bool execute (tree_evaluator& tr_eval, tree_simple_for_command& cmd,
+             const octave_value& bounds)
+    {
+      return instance ().do_execute (tr_eval, cmd, bounds);
+    }
 
-    static bool execute (tree_while_command& cmd)
-    { return instance ().do_execute (cmd); }
+    static bool execute (tree_evaluator& tr_eval, tree_while_command& cmd)
+    {
+      return instance ().do_execute (tr_eval, cmd);
+    }
 
-    static bool execute (octave_user_function& fcn,
-                         const octave_value_list& args,
-                         octave_value_list& retval)
-    { return instance ().do_execute (fcn, args, retval); }
+    static bool execute (tree_evaluator& tr_eval, octave_user_function& fcn,
+             const octave_value_list& args, octave_value_list& retval)
+    {
+      return instance ().do_execute (tr_eval, fcn, args, retval);
+    }
 
   private:
-
-    bool do_execute (tree_simple_for_command& cmd,
+    bool do_execute (tree_evaluator& tr_eval, tree_simple_for_command& cmd,
                      const octave_value& bounds);
 
-    bool do_execute (tree_while_command& cmd);
+    bool do_execute (tree_evaluator& tr_eval, tree_while_command& cmd);
 
-    bool do_execute (octave_user_function& fcn,
-                     const octave_value_list& args,
-                     octave_value_list& retval);
+    bool do_execute (tree_evaluator& tr_eval, octave_user_function& fcn,
+                     const octave_value_list& args, octave_value_list& retval);
 
     // ----- Miscellaneous -----
 
@@ -514,9 +544,7 @@
     size_t trip_count (const octave_value& bounds) const;
   };
 
-
-  class
-  jit_module
+  class jit_module
   {
     // TODO: Encapsulate all operations that can modify the module,
     //       and prevent them if the module has been finalized
@@ -525,7 +553,6 @@
     //       I have read somewhere that this is more efficient (nor sure)
 
   public:
-
     // Create an open module and associated JIT engine
     jit_module (const std::string& module_name
                 = tree_jit::generate_unique_module_name ());
@@ -534,34 +561,34 @@
     ~jit_module ();
 
     // Create an LLVM function in the module, with external linkage
-    llvm::Function*
-    create_llvm_function (llvm::FunctionType *ftype,
-                          const llvm::Twine &name) const;
+    llvm::Function * create_llvm_function (llvm::FunctionType *ftype,
+                                           const llvm::Twine& name) const;
 
     // Create a global in the module, with external linkage
-    llvm::GlobalVariable*
-    create_global_variable (llvm::Type *type, bool is_constant,
-                            const llvm::Twine& name) const;
+    llvm::GlobalVariable * create_global_variable (llvm::Type *type,
+                                                   bool is_constant,
+                                                 const llvm::Twine& name) const;
 
     // Create or insert an LLVM Function declaration for an intrinsic,
     // and return it
-    llvm::Function*
-    get_intrinsic_declaration (size_t id,
-                               std::vector<llvm::Type*> types) const;
+    llvm::Function * get_intrinsic_declaration (size_t id,
+                                  const std::vector<llvm::Type *>& types) const;
 
     // Underlying type of enums defined in yet-inconplete types
-    typedef unsigned llvm_gv_linkage;  // FIXME: autoconf this
+    typedef unsigned llvm_gv_linkage; // FIXME: autoconf this
 
     // add_global_mapping tells the execution engine where a specified
     // global value (variable or function) is
     template <typename ptr_type>
-    void add_global_mapping (const llvm::GlobalValue* gv, ptr_type p) const
+    void add_global_mapping (const llvm::GlobalValue *gv, ptr_type p) const
     {
       do_add_global_mapping (gv, reinterpret_cast<void *> (p));
     }
 
     // Return the address of the specified function.
-    uint64_t getFunctionAddress (const std::string &name) const;
+    uint64_t getFunctionAddress (const std::string& name) const;
+
+    llvm::Function * getFunction (const std::string& name) const;
 
     // Optimize a function in the LLVM module
     void optimize (llvm::Function *fn) const;
@@ -570,43 +597,54 @@
     // to change anything in the module...
     void finalizeObject (void);
 
-  private:
+    // Print a summary for the object. For stdout-based debugging.
+    void print_object_summary (void);
+
+    // Print all function locations. For stdout-based debugging.
+    void print_all_flocs (void) const;
 
-    void do_add_global_mapping (const llvm::GlobalValue* gv, void* p) const;
+    // Print the IR for all functions. For stdout-based debugging.
+    void print_all_functions (void) const;
+
+    // Dump a representation of the module.
+    void dump (void) const;
+
+  private:
+    void do_add_global_mapping (const llvm::GlobalValue *gv, void *p) const;
 
     llvm::Module *m_module;
     llvm::ExecutionEngine *m_engine;
   };
 
-
-  class
-  jit_info : public jit_module
+  class jit_info : public jit_module
   {
   public:
-
     // we use a pointer here so we don't have to include ov.h
     typedef std::map<std::string, const octave_value *> vmap;
 
-    jit_info (tree& tee);
+    jit_info (tree_evaluator& tr_eval, tree& tee);
 
-    jit_info (tree& tee, const octave_value& for_bounds);
+    jit_info (tree_evaluator& tr_eval, tree& tee,
+              const octave_value& for_bounds);
 
-    jit_info (tree_simple_for_command& tee, const octave_value& for_bounds);
+    jit_info (tree_evaluator& tr_eval, tree_simple_for_command& tee,
+              const octave_value& for_bounds);
 
     bool execute (const vmap& extra_vars = vmap ()) const;
 
     bool match (const vmap& extra_vars = vmap ()) const;
 
   private:
-
     typedef jit_convert::type_bound type_bound;
     typedef jit_convert::type_bound_vector type_bound_vector;
-    typedef void (*jited_function)(octave_base_value**);
+    typedef void (*jited_function) (octave_base_value **);
 
     void compile (tree& tee, jit_type *for_bounds = 0);
 
     octave_value find (const vmap& extra_vars, const std::string& vname) const;
 
+    tree_evaluator& m_tr_eval;
+
     // LLVM function associated to this jit_info object
     std::string m_llvm_function_name;
     jited_function m_function;
@@ -615,13 +653,10 @@
     type_bound_vector m_bounds;
   };
 
-
-  class
-  jit_function_info : public jit_module
+  class jit_function_info : public jit_module
   {
   public:
-
-    jit_function_info (octave_user_function& fcn,
+    jit_function_info (tree_evaluator& tr_eval, octave_user_function& fcn,
                        const octave_value_list& ov_args);
 
     bool execute (const octave_value_list& ov_args,
@@ -630,8 +665,9 @@
     bool match (const octave_value_list& ov_args) const;
 
   private:
+    typedef octave_base_value *(*jited_function) (octave_base_value **);
 
-    typedef octave_base_value *(*jited_function)(octave_base_value**);
+    tree_evaluator& m_tr_eval;
 
     // LLVM function associated to this jit_info object
     std::string m_llvm_function_name;
diff -r df9e3cb79379 -r 1ccb99c08f27 m4/acinclude.m4
--- a/m4/acinclude.m4	Wed May 08 18:45:34 2019 -0700
+++ b/m4/acinclude.m4	Fri May 31 16:09:22 2019 -0500
@@ -2884,7 +2884,7 @@
 ])
 dnl
 dnl Check llvm::IRBuilder::CreateConstInBoundsGEP1_32 API
-dbl
+dnl
 AC_DEFUN([OCTAVE_LLVM_IRBUILDER_CREATECONSTINBOUNDSGEP1_32_API], [
   AC_CACHE_CHECK([if llvm::IRBuilder::CreateConstInBoundsGEP1_32 requires a type argument],
     [octave_cv_llvm_irbuilder_createconstinboundsgep1_32_requires_type],
@@ -2914,6 +2914,56 @@
   fi
 ])
 dnl
+dnl Check llvm::WriteBitcodeToFile API
+dnl
+AC_DEFUN([OCTAVE_LLVM_WRITEBITCODETOFILE_API], [
+  AC_CACHE_CHECK([if llvm::WriteBitcodeToFile requires a pointer or a reference argument],
+    [octave_cv_llvm_writebitcodetofile_has_ptr_arg],
+    [AC_LANG_PUSH(C++)
+      AC_COMPILE_IFELSE(
+        [AC_LANG_PROGRAM([[
+#if defined (HAVE_LLVM_BITCODE_READERWRITER_H)
+#  include <llvm/Bitcode/ReaderWriter.h>
+#else
+#  include <llvm/Bitcode/BitcodeWriter.h>
+#endif
+#if defined (HAVE_LLVM_IR_FUNCTION_H)
+#  include <llvm/IR/LLVMContext.h>
+#  include <llvm/IR/Module.h>
+#else
+#  include <llvm/LLVMContext.h>
+#  include <llvm/Module.h>
+#endif
+#include <llvm/Support/FileSystem.h>
+#include <llvm/Support/raw_ostream.h>
+          ]], [[
+          std::error_code ec;
+          llvm::LLVMContext c;
+          llvm::Module* m = new llvm::Module ("test", c);
+          llvm::raw_fd_ostream fout ("test.bc", ec, llvm::sys::fs::F_None);
+          llvm::WriteBitcodeToFile (m, fout);
+        ]])],
+        octave_cv_llvm_writebitcodetofile_has_ptr_arg=pointer,
+        octave_cv_llvm_writebitcodetofile_has_ptr_arg=reference)
+    AC_LANG_POP(C++)
+  ])
+  if test $octave_cv_llvm_writebitcodetofile_has_ptr_arg = pointer; then
+    AC_DEFINE(LLVM_WRITEBITCODETOFILE_HAS_PTR_ARG, 1,
+      [Define to 1 if llvm::WriteBitcodeToFile requires a pointer argument.])
+  fi
+])
+dnl
+dnl OCTAVE_CHECK_SEARCH_PATH
+dnl
+dnl Check if the specified path is already in the system search path
+dnl used by $CXX using a regex match. Executes the second argument if
+dnl a match is found. Otherwise, the third argument is executed.
+AC_DEFUN([OCTAVE_CHECK_SEARCH_PATH], [
+  AS_IF([eval "$CXX -E -Wp,-v -xc++ /dev/null 2>&1 | grep -q \"^\\s*[$1][$]\""],
+        ifelse([$2], , , [$2]),
+        ifelse([$3], , , [$3]))
+])
+dnl
 dnl OCTAVE_CHECK_FORTRAN_SYMBOL_AND_CALLING_CONVENTIONS
 dnl
 dnl Set variables related to Fortran symbol names (append underscore,
