# HG changeset patch # User Gene Harvey # 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 -// 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 @@ -42,17 +40,14 @@ # include #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 (jit_value::to_llvm ()); - } - // -------------------- jit_call -------------------- bool jit_call::needs_release (void) const @@ -785,8 +769,8 @@ m_count (factory.create (acount)) { } - jit_magic_end::jit_magic_end (const std::vector& full_context) - : m_contexts (full_context) + jit_magic_end::jit_magic_end (std::vector 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 PARENT_T; jit_use (void) : m_user (0), m_index (0) { } @@ -312,7 +313,7 @@ std::list 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::max()) { PARENT_T::stash_value (avalue); m_index = aindex; @@ -493,7 +494,7 @@ std::list::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& full_context); + explicit jit_magic_end (std::vector 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 -// 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 +#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; @@ -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& aargs) - : m_module (amodule), m_result (aresult), m_args (aargs), + std::vector aargs) + : m_module (amodule), m_result (aresult), m_args (std::move (aargs)), m_call_conv (acall_conv), m_can_error (false) { llvm::SmallVector 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& aargs) + std::vector 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& in_args) const + { + if (! valid ()) + throw jit_fail_exception ("Call not implemented"); + + assert (in_args.size () == m_args.size ()); + std::vector 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_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 (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 (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 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 args; args.resize (1); - for (auto iter = m_builtins.begin (); - iter != m_builtins.end (); ++iter) + for (std::map::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(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 m_sret; + std::array m_pointer_arg; - convert_fn m_pack[jit_convention::length]; - convert_fn m_unpack[jit_convention::length]; + std::array m_pack; + std::array m_unpack; - llvm::Type *m_packed_type[jit_convention::length]; + std::array 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& aargs); + std::vector 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& aargs); + std::vector 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& in_args) const; + + template + 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 + 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 + 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 + 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 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 -// 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 (value) - // basically just a short cut type typing dyanmic_cast. + // basically just a short cut type typing dynamic_cast. template bool isa (U *value) { - return dynamic_cast (value); + return dynamic_cast (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 -#define __STDC_LIMIT_MACROS -#define __STDC_CONSTANT_MACROS - #if defined (HAVE_CONFIG_H) # include "config.h" #endif @@ -126,6 +123,9 @@ # include #endif +#include +#include + 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 (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 (var)); } @@ -195,9 +200,12 @@ jit_convert::jit_convert (octave_user_function& fcn, const std::vector& 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_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 (m_end_context)); + m_result = m_block->append ( + m_factory.create (m_end_context)); } else { @@ -643,8 +650,8 @@ m_blocks.push_back (body); jit_instruction *br = m_factory.create (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 (expr); - - do_bind_ans = (! id->is_variable (m_scope.current_context ())); + tree_identifier *t_id = dynamic_cast (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 - (expr->name ()); + jit_const_string *name + = m_factory.create (expr->name ()); m_block->append (m_factory.create (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 entry_blocks (case_blocks_num+1 - has_otherwise); + std::vector 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 ("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 ("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_typeinfo::create_undef); + jit_call *init + = m_factory.create (&jit_typeinfo::create_undef); jit_assign *assign = m_factory.create (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 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 (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 (dest)); + br->append (m_factory.create (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& 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 (*iter)) - m_argument_vec.push_back (std::make_pair (extract->name (), true)); + = dynamic_cast (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& constants, octave_user_function& fcn, const std::vector& args) @@ -1412,8 +1417,8 @@ jit_return *ret = dynamic_cast (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& constants) { - std::list::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 (*iter)) - visit (*iter); + for (jit_value *c : constants) + if (! isa (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 (*piter); ++piter) + for (jit_instruction *instr : *bl) { - jit_instruction *phi = *piter; - finish_phi (static_cast (phi)); + if (jit_phi *phi_node = dynamic_cast(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(cr.type_llvm ()); + llvm::StructType *stype = llvm::cast (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 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& 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 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 (iter->second, - dblock->use_count ()); + jit_phi *phi = m_factory.create (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 (*iter);) { jit_phi *phi = static_cast (*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 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 (ablock.terminator ())) + if (temp.empty () || ! isa (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_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 (*iter); ++iter) - simplify_phi (*static_cast (*iter)); + for (jit_instruction *instr : *block) + { + if (jit_phi *phi_node = dynamic_cast(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 ()) + .setMCJITMemoryManager (llvm::make_unique ()) .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::const_iterator it; + std::list::const_iterator it; for (it = jm_list.begin (); it != jm_list.end (); it++) { uint64_t addr = (*it)->getFunctionAddress (name); if (addr) - return reinterpret_cast (addr); + return reinterpret_cast (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::const_iterator it; + std::list::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 types) const + const std::vector& types) const { - return llvm::Intrinsic::getDeclaration - (m_module, static_cast (id), types); + return llvm::Intrinsic::getDeclaration (m_module, + static_cast (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 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 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> 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& constants, - const std::string& llvm_function_name); + llvm::Function * convert_loop (const jit_block_list& blocks, + const std::list& 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& constants, octave_user_function& fcn, const std::vector& args); // arguments to the llvm::Function for loops - const std::vector>& get_arguments(void) const - { return m_argument_vec; } + const std::vector>& 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 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 jm_list; - void do_register_jit_module (jit_module* jm); - void do_unregister_jit_module (jit_module* jm); + std::list 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 types) const; + llvm::Function * get_intrinsic_declaration (size_t id, + const std::vector& 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 - 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 (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 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 +#else +# include +#endif +#if defined (HAVE_LLVM_IR_FUNCTION_H) +# include +# include +#else +# include +# include +#endif +#include +#include + ]], [[ + 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,