# HG changeset patch # User Stefan Mahr # Date 1425052776 -3600 # Node ID b65888ec820ed9bc7fc7949e81fa03376d490633 # Parent 9cef0a1207e4a0d2fff2d7a54ff2b6baca20006e dmalcom gcc jit import diff --git a/build-aux/common.mk b/build-aux/common.mk --- a/build-aux/common.mk +++ b/build-aux/common.mk @@ -258,6 +258,10 @@ LLVM_LDFLAGS = @LLVM_LDFLAGS@ LLVM_LIBS = @LLVM_LIBS@ +LIBGCCJIT_CPPFLAGS = @LIBGCCJIT_CPPFLAGS@ +LIBGCCJIT_LDFLAGS = @LIBGCCJIT_LDFLAGS@ +LIBGCCJIT_LIBS = @LIBGCCJIT_LIBS@ + MAGICK_CPPFLAGS = @MAGICK_CPPFLAGS@ MAGICK_LDFLAGS = @MAGICK_LDFLAGS@ MAGICK_LIBS = @MAGICK_LIBS@ @@ -591,6 +595,9 @@ -e "s|%OCTAVE_CONF_LLVM_CPPFLAGS%|\"${LLVM_CPPFLAGS}\"|" \ -e "s|%OCTAVE_CONF_LLVM_LDFLAGS%|\"${LLVM_LDFLAGS}\"|" \ -e "s|%OCTAVE_CONF_LLVM_LIBS%|\"${LLVM_LIBS}\"|" \ + -e "s|%OCTAVE_CONF_LIBGCCJIT_CPPFLAGS%|\"${LIBGCCJIT_CPPFLAGS}\"|" \ + -e "s|%OCTAVE_CONF_LIBGCCJIT_LDFLAGS%|\"${LIBGCCJIT_LDFLAGS}\"|" \ + -e "s|%OCTAVE_CONF_LIBGCCJIT_LIBS%|\"${LIBGCCJIT_LIBS}\"|" \ -e "s|%OCTAVE_CONF_LN_S%|\"${LN_S}\"|" \ -e "s|%OCTAVE_CONF_MAGICK_CPPFLAGS%|\"${MAGICK_CPPFLAGS}\"|" \ -e "s|%OCTAVE_CONF_MAGICK_LDFLAGS%|\"${MAGICK_LDFLAGS}\"|" \ diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -863,7 +863,6 @@ if test -z "$warn_llvm"; then AC_DEFINE(HAVE_LLVM, 1, [Define to 1 if LLVM is available.]) else - build_jit=no LLVM_CPPFLAGS= LLVM_CXXFLAGS= LLVM_LDFLAGS= @@ -883,6 +882,73 @@ AC_SUBST(LLVM_LIBS) AM_CONDITIONAL([AMCOND_HAVE_LLVM], [test -z "$warn_llvm"]) + +LIBGCCJIT_CPPFLAGS= +LIBGCCJIT_CXXFLAGS= +LIBGCCJIT_LDFLAGS= +LIBGCCJIT_LIBS= + +if test $build_jit = yes; then + + AC_ARG_VAR([LIBGCCJIT_PREFIX], [path to libgccjit build directory]) + + save_CPPFLAGS="$CPPFLAGS" + save_CXXFLAGS="$CXXFLAGS" + save_LDFLAGS="$LDFLAGS" + + if test -z "$LIBGCCJIT_PREFIX"; then + warn_libgccjit="LIBGCCJIT prefix path not set. trying defaul" + else + LIBGCCJIT_CPPFLAGS="-I${LIBGCCJIT_PREFIX}/include" + LIBGCCJIT_CXXFLAGS="" + LIBGCCJIT_LDFLAGS="-L${LIBGCCJIT_PREFIX}/lib" + + CPPFLAGS="$LIBGCCJIT_CPPFLAGS $CPPFLAGS" + CXXFLAGS="$LIBGCCJIT_CXXFLAGS $CXXFLAGS" + LDFLAGS="$LIBGCCJIT_LDFLAGS $LDFLAGS" + fi + + AC_LANG_PUSH(C++) + + AC_CHECK_HEADER([libgccjit++.h], [], + [warn_libgccjit="LIBGCCJIT header file missing"]) + + AC_CHECK_LIB(gccjit, gcc_jit_context_zero, + [LIBGCCJIT_LIBS=-lgccjit], + [warn_libgccjit="LIBGCCJIT library missing"]) + + AC_LANG_POP(C++) + CPPFLAGS="$save_CPPFLAGS" + CXXFLAGS="$save_CXXFLAGS" + LDFLAGS="$save_LDFLAGS" + + if test -z "$warn_libgccjit"; then + AC_DEFINE(HAVE_GCCJIT, 1, [Define to 1 if libgccjit is available).]) + else + LIBGCCJIT_CPPFLAGS= + LIBGCCJIT_CXXFLAGS= + LIBGCCJIT_LDFLAGS= + LIBGCCJIT_LIBS= + OCTAVE_CONFIGURE_WARNING([warn_libgccjit]) + fi + +fi + +AC_SUBST(LIBGCCJIT_CPPFLAGS) +AC_SUBST(LIBGCCJIT_CXXFLAGS) +AC_SUBST(LIBGCCJIT_LDFLAGS) +AC_SUBST(LIBGCCJIT_LIBS) +AM_CONDITIONAL([AMCOND_HAVE_LIBGCCJIT], [test -z "$warn_libgccjit"]) + +###if test -z "$warn_libgccjit" -o -z "$warn_llvm"; then +if test -z "$warn_llvm"; then + AC_DEFINE(HAVE_JIT, 1, [Define to 1 if using a JIT (LLVM or LLVM and libgccjit).]) +else + build_jit=no +fi + + + ### Check for HDF5 library. save_CPPFLAGS="$CPPFLAGS" @@ -3074,6 +3140,10 @@ Java CPPFLAGS: $JAVA_CPPFLAGS Java libraries: $JAVA_LIBS LAPACK libraries: $LAPACK_LIBS + libgccjit CPPFLAGS: $LIBGCCJIT_CPPFLAGS + libgccjit CXXFLAGS: $LIBGCCJIT_CXXFLAGS + libgccjit LDFLAGS: $LIBGCCJIT_LDFLAGS + libgccjit libraries: $LIBGCCJIT_LIBS LLVM CPPFLAGS: $LLVM_CPPFLAGS LLVM LDFLAGS: $LLVM_LDFLAGS LLVM libraries: $LLVM_LIBS diff --git a/libinterp/corefcn/gripes.h b/libinterp/corefcn/gripes.h --- a/libinterp/corefcn/gripes.h +++ b/libinterp/corefcn/gripes.h @@ -107,8 +107,10 @@ gripe_implicit_conversion (const std::string& id, const std::string& from, const std::string& to); +extern "C" { extern OCTINTERP_API void gripe_divide_by_zero (void); +} extern OCTINTERP_API void gripe_logical_conversion (void); diff --git a/libinterp/corefcn/jit-ir.cc b/libinterp/corefcn/jit-ir.cc --- a/libinterp/corefcn/jit-ir.cc +++ b/libinterp/corefcn/jit-ir.cc @@ -30,10 +30,11 @@ #include #endif -#ifdef HAVE_LLVM +#ifdef HAVE_JIT #include "jit-ir.h" +#if HAVE_LLVM #ifdef HAVE_LLVM_IR_FUNCTION_H #include #include @@ -41,6 +42,7 @@ #include #include #endif +#endif #include "error.h" @@ -208,11 +210,13 @@ resize_arguments (0); } +#if HAVE_LLVM llvm::BasicBlock * jit_instruction::parent_llvm (void) const { return mparent->to_llvm (); } +#endif std::ostream& jit_instruction::short_print (std::ostream& os) const @@ -388,11 +392,13 @@ return term ? term->successor_count () : 0; } +#if HAVE_LLVM llvm::BasicBlock * jit_block::to_llvm (void) const { return llvm::cast (llvm_value); } +#endif std::ostream& jit_block::print_dom (std::ostream& os) const @@ -661,11 +667,13 @@ return false; } +#if HAVE_LLVM llvm::PHINode * jit_phi::to_llvm (void) const { return llvm::cast (jit_value::to_llvm ()); } +#endif // -------------------- jit_terminator -------------------- size_t @@ -697,11 +705,13 @@ return changed; } +#if HAVE_LLVM llvm::TerminatorInst * jit_terminator::to_llvm (void) const { return llvm::cast (jit_value::to_llvm ()); } +#endif // -------------------- jit_call -------------------- bool @@ -842,4 +852,4 @@ return jit_typeinfo::end (ctx.value, ctx.index, ctx.count); } -#endif +#endif /* ifdef HAVE_JIT */ diff --git a/libinterp/corefcn/jit-ir.h b/libinterp/corefcn/jit-ir.h --- a/libinterp/corefcn/jit-ir.h +++ b/libinterp/corefcn/jit-ir.h @@ -25,7 +25,7 @@ #if !defined (octave_jit_ir_h) #define octave_jit_ir_h 1 -#ifdef HAVE_LLVM +#ifdef HAVE_JIT #include #include @@ -188,7 +188,11 @@ jit_value : public jit_internal_list { public: - jit_value (void) : llvm_value (0), ty (0), mlast_use (0), + jit_value (void) : llvm_value (0), +#ifdef HAVE_GCCJIT + m_gcc_lvalue (), m_gcc_rvalue (), +#endif + ty (0), mlast_use (0), min_worklist (false) {} virtual ~jit_value (void); @@ -212,10 +216,19 @@ jit_type *type (void) const { return ty; } +#if HAVE_LLVM llvm::Type *type_llvm (void) const { return ty ? ty->to_llvm () : 0; } +#endif + +#if HAVE_GCCJIT + gccjit::type type_gccjit (void) const + { + return ty ? ty->to_gccjit () : gccjit::type (); + } +#endif const std::string& type_name (void) const { @@ -247,6 +260,7 @@ virtual void accept (jit_ir_walker& walker) = 0; +#if HAVE_LLVM bool has_llvm (void) const { return llvm_value; @@ -262,6 +276,34 @@ { llvm_value = compiled; } +#endif + +#if HAVE_GCCJIT + bool has_lvalue (void) const + { + return m_gcc_lvalue.get_inner_lvalue (); + } + bool has_rvalue (void) const + { + return m_gcc_rvalue.get_inner_rvalue (); + } + gccjit::lvalue as_lvalue (void) const + { + return m_gcc_lvalue; + } + gccjit::rvalue as_rvalue (void) const + { + return m_gcc_rvalue; + } + void stash_lvalue (gccjit::lvalue lvalue) + { + m_gcc_lvalue = lvalue; + } + void stash_rvalue (gccjit::rvalue rvalue) + { + m_gcc_rvalue = rvalue; + } +#endif protected: std::ostream& print_indent (std::ostream& os, size_t indent = 0) const @@ -271,7 +313,13 @@ return os; } +#if HAVE_LLVM llvm::Value *llvm_value; +#endif +#if HAVE_GCCJIT + gccjit::lvalue m_gcc_lvalue; + gccjit::rvalue m_gcc_rvalue; +#endif private: jit_type *ty; jit_instruction *mlast_use; @@ -371,22 +419,26 @@ return marguments[i].value (); } +#if HAVE_LLVM llvm::Value *argument_llvm (size_t i) const { assert (argument (i)); return argument (i)->to_llvm (); } +#endif jit_type *argument_type (size_t i) const { return argument (i)->type (); } +#if HAVE_LLVM llvm::Type *argument_type_llvm (size_t i) const { assert (argument (i)); return argument_type (i)->to_llvm (); } +#endif std::ostream& print_argument (std::ostream& os, size_t i) const { @@ -452,7 +504,9 @@ return mlocation; } +#if HAVE_LLVM llvm::BasicBlock *parent_llvm (void) const; +#endif void stash_parent (jit_block *aparent, std::list::iterator alocation) @@ -494,10 +548,10 @@ jit_argument : public jit_value { public: - jit_argument (jit_type *atype, llvm::Value *avalue) + jit_argument (jit_type *atype/*, llvm::Value *avalue*/) { stash_type (atype); - stash_llvm (avalue); + //stash_llvm (avalue); } virtual std::ostream& print (std::ostream& os, size_t indent = 0) const @@ -584,6 +638,13 @@ const std::string& name (void) const { return mname; } + std::string name_and_id (void) const + { + std::ostringstream os; + short_print (os); + return os.str (); + } + jit_instruction *prepend (jit_instruction *instr); jit_instruction *prepend_after_phi (jit_instruction *instr); @@ -719,7 +780,9 @@ return os; } +#if HAVE_LLVM llvm::BasicBlock *to_llvm (void) const; +#endif std::list::iterator location (void) const { return mlocation; } @@ -975,10 +1038,12 @@ return mincomming[i].value (); } +#if HAVE_LLVM llvm::BasicBlock *incomming_llvm (size_t i) const { return incomming (i)->to_llvm (); } +#endif virtual void construct_ssa (void) { } @@ -1009,7 +1074,9 @@ return os; } +#if HAVE_LLVM llvm::PHINode *to_llvm (void) const; +#endif JIT_VALUE_ACCEPT; private: @@ -1037,10 +1104,12 @@ return static_cast (argument (idx)); } +#if HAVE_LLVM llvm::BasicBlock *successor_llvm (size_t idx = 0) const { return successor (idx)->to_llvm (); } +#endif size_t successor_index (const jit_block *asuccessor) const; @@ -1068,7 +1137,9 @@ virtual bool infer (void); +#if HAVE_LLVM llvm::TerminatorInst *to_llvm (void) const; +#endif protected: virtual bool check_alive (size_t) const { return true; } private: @@ -1106,10 +1177,12 @@ return cond ()->short_print (os); } +#if HAVE_LLVM llvm::Value *cond_llvm (void) const { return cond ()->to_llvm (); } +#endif virtual size_t successor_count (void) const { return 2; } @@ -1351,10 +1424,12 @@ return result ()->type (); } +#if HAVE_LLVM llvm::Value *result_llvm (void) const { return result ()->to_llvm (); } +#endif virtual std::ostream& print (std::ostream& os, size_t indent = 0) const { @@ -1431,5 +1506,5 @@ #undef JIT_VALUE_ACCEPT +#endif /* ifdef HAVE_JIT */ #endif -#endif diff --git a/libinterp/corefcn/jit-typeinfo.cc b/libinterp/corefcn/jit-typeinfo.cc --- a/libinterp/corefcn/jit-typeinfo.cc +++ b/libinterp/corefcn/jit-typeinfo.cc @@ -504,9 +504,22 @@ } // -------------------- jit_type -------------------- -jit_type::jit_type (const std::string& aname, jit_type *aparent, - llvm::Type *allvm_type, bool askip_paren, int aid) : - mname (aname), mparent (aparent), llvm_type (allvm_type), mid (aid), +jit_type::jit_type (const std::string& aname, jit_type *aparent +#ifdef HAVE_LLVM + , llvm::Type *allvm_type +#endif +#ifdef HAVE_GCCJIT + , gccjit::type agcc_type +#endif + , bool askip_paren, int aid) : + mname (aname), mparent (aparent) +#ifdef HAVE_LLVM + , llvm_type (allvm_type) +#endif +#ifdef HAVE_GCCJIT + , gccjit_type (agcc_type) +#endif + , mid (aid), mdepth (aparent ? aparent->mdepth + 1 : 0), mskip_paren (askip_paren) { std::memset (msret, 0, sizeof (msret)); @@ -525,14 +538,21 @@ } // -------------------- jit_function -------------------- -jit_function::jit_function () : module (0), llvm_function (0), mresult (0), +jit_function::jit_function () : module (0), llvm_function (0), +#ifdef HAVE_GCCJIT + gccjit_function (), +#endif + mresult (0), call_conv (jit_convention::length), mcan_error (false) {} jit_function::jit_function (llvm::Module *amodule, +#ifdef HAVE_GCCJIT + gccjit::context gccjit_ctxt, +#endif jit_convention::type acall_conv, - const llvm::Twine& aname, jit_type *aresult, + std::string aname, jit_type *aresult, const std::vector& aargs) : module (amodule), mresult (aresult), args (aargs), call_conv (acall_conv), mcan_error (false) @@ -586,17 +606,75 @@ #else llvm_function->addFnAttr (llvm::Attribute::AlwaysInline); #endif + +#ifdef HAVE_GCCJIT + if (gccjit_ctxt.get_inner_context ()) + { + std::vector gccjit_params; + for (int i = 0; i < args.size (); i++) + { + jit_type *ty = args[i]; + assert (ty); + + gccjit::type argty = ty->to_gccjit (); + if (ty->pointer_arg (call_conv)) + argty = argty.get_pointer (); + + std::stringstream paramname; + paramname << "arg" << i; + + gccjit::param param = gccjit_ctxt.new_param (argty, + paramname.str ()); + gccjit_params.push_back (param); + } + gccjit::type gccjit_return_type; + if (aresult) + gccjit_return_type = aresult->to_gccjit (); + else + gccjit_return_type = gccjit_ctxt.get_type (GCC_JIT_TYPE_VOID); + + enum gcc_jit_function_kind kind; + if (acall_conv == jit_convention::external) + kind = GCC_JIT_FUNCTION_IMPORTED; + else + { + if (0) + /* Doing this is correct, but makes the dump a little harder + to read. + Also, not fully implemented yet in libgccjit. */ + kind = GCC_JIT_FUNCTION_ALWAYS_INLINE; + else + kind = GCC_JIT_FUNCTION_EXPORTED; + } + gccjit_function = + gccjit_ctxt.new_function (kind, + gccjit_return_type, + aname, + gccjit_params, + 0); + } +#endif } jit_function::jit_function (const jit_function& fn, jit_type *aresult, const std::vector& aargs) - : module (fn.module), llvm_function (fn.llvm_function), mresult (aresult), + : module (fn.module), + llvm_function (fn.llvm_function), +#ifdef HAVE_GCCJIT + gccjit_function (fn.gccjit_function), +#endif + mresult (aresult), args (aargs), call_conv (fn.call_conv), mcan_error (fn.mcan_error) { } jit_function::jit_function (const jit_function& fn) - : module (fn.module), llvm_function (fn.llvm_function), mresult (fn.mresult), + : module (fn.module), + llvm_function (fn.llvm_function), +#ifdef HAVE_GCCJIT + gccjit_function (fn.gccjit_function), +#endif + mresult (fn.mresult), args (fn.args), call_conv (fn.call_conv), mcan_error (fn.mcan_error) {} @@ -754,8 +832,76 @@ if (verify) llvm::verifyFunction (*llvm_function); + + if (0) + { + std::cout << "-------------------- llvm ir (at do_return) --------------------"; + std::cout << *llvm_function << std::endl; + } } +#ifdef HAVE_GCCJIT +#if 1 +gccjit::rvalue +jit_function::call (gccjit::context ctxt, + gccjit::block block, + const std::vector& in_args) const +{ + assert (in_args.size () == args.size ()); + std::vector gccjit_args (args.size ()); + for (size_t i = 0; i < in_args.size (); ++i) + gccjit_args[i] = in_args[i]->as_rvalue (); + return call (ctxt, block, gccjit_args); +} + +gccjit::rvalue +jit_function::call (gccjit::context ctxt, + gccjit::block block, + std::vector& in_args) const +{ + assert (in_args.size () == args.size ()); + +#if 1 + std::vector packed_args (in_args.size ()); + + for (size_t i = 0; i < in_args.size (); ++i) + { + gccjit::rvalue arg = in_args[i]; +#if 0 + jit_type::convert_fn convert = args[i]->pack (call_conv); + if (convert) + arg = convert (builder, arg); +#endif + + if (args[i]->pointer_arg (call_conv)) + { + // The LLVM implementation takes a copy using alloca, passing + // a ptr to the copy to the fn. Emulate this behavior. + gccjit::lvalue tmp = + block.get_function ().new_local (arg.get_type (), "tmp"); + block.add_assignment (tmp, arg); + arg = tmp.get_address (); + } + + packed_args[i] = arg; + } + + return ctxt.new_call (gccjit_function, + packed_args); +#else + return ctxt.new_call (gccjit_function, + in_args); +#endif +} +#endif + +gccjit::lvalue +jit_function::argument (size_t idx) const +{ + return gccjit_function.get_param (idx); +} +#endif + void jit_function::do_add_mapping (llvm::ExecutionEngine *engine, void *fn) { @@ -928,6 +1074,7 @@ return 0; } +#ifdef HAVE_LLVM llvm::Value * jit_index_operation::create_arg_array (llvm::IRBuilderD& builder, const jit_function &fn, size_t start_idx, @@ -947,6 +1094,28 @@ builder.CreateStore (array, array_mem); return builder.CreateBitCast (array_mem, scalar_t->getPointerTo ()); } +#endif // #ifdef HAVE_LLVM + +#ifdef HAVE_GCCJIT +gccjit::rvalue +jit_index_operation::create_arg_array (const jit_function &fn, + gccjit::block block, + size_t start_idx, + size_t end_idx) const +{ + size_t n = end_idx - start_idx; + gccjit::type scalar_t = jit_typeinfo::get_scalar_gccjit (); + gccjit::type array_t = block.get_context ().new_array_type (scalar_t, n); + gccjit::lvalue array = + block.get_function ().new_local (array_t, "tmp_array"); + for (size_t i = start_idx; i < end_idx; ++i) + { + gccjit::rvalue idx = fn.argument (i); + block.add_assignment (array[i - start_idx], idx); + } + return array.get_address ().cast_to (scalar_t.get_pointer ()); +} +#endif // #ifdef HAVE_GCCJIT // -------------------- jit_paren_subsref -------------------- jit_function * @@ -956,7 +1125,11 @@ ss << "jit_paren_subsref_matrix_scalar" << (types.size () - 1); jit_type *scalar = jit_typeinfo::get_scalar (); - jit_function *fn = new jit_function (module, jit_convention::internal, + jit_function *fn = new jit_function (module, +#ifdef HAVE_GCCJIT + gccjit_ctxt, +#endif + jit_convention::internal, ss.str (), scalar, types); fn->mark_can_error (); llvm::BasicBlock *body = fn->new_block (); @@ -969,6 +1142,26 @@ llvm::Value *mat = fn->argument (builder, 0); llvm::Value *ret = paren_scalar.call (builder, mat, array, nelem); fn->do_return (builder, ret); + +#ifdef HAVE_GCCJIT + // gcc implementation + { + gccjit::function gf = fn->gccjit_function; + gccjit::block body = gf.new_block (); + gccjit::rvalue array = create_arg_array (*fn, body, 1, types.size ()); + jit_type *index = jit_typeinfo::get_index (); + gccjit::rvalue nelem = gccjit_ctxt.new_rvalue (index->to_gccjit (), + (int)types.size () - 1); + gccjit::rvalue mat = gf.get_param (0); + std::vector args (3); + args[0] = mat; + args[1] = array; + args[2] = nelem; + gccjit::rvalue ret = paren_scalar.call (gccjit_ctxt, body, args); + body.end_with_return (ret); + } +#endif + return fn; } @@ -981,7 +1174,11 @@ types[2] = jit_typeinfo::get_index (); jit_type *scalar = jit_typeinfo::get_scalar (); - paren_scalar = jit_function (module, jit_convention::external, + paren_scalar = jit_function (module, +#ifdef HAVE_GCCJIT + gccjit_ctxt, +#endif + jit_convention::external, "octave_jit_paren_scalar", scalar, types); paren_scalar.add_mapping (engine, &octave_jit_paren_scalar); paren_scalar.mark_can_error (); @@ -995,7 +1192,11 @@ ss << "jit_paren_subsasgn_matrix_scalar" << (types.size () - 2); jit_type *matrix = jit_typeinfo::get_matrix (); - jit_function *fn = new jit_function (module, jit_convention::internal, + jit_function *fn = new jit_function (module, +#ifdef HAVE_GCCJIT + gccjit_ctxt, +#endif + jit_convention::internal, ss.str (), matrix, types); fn->mark_can_error (); llvm::BasicBlock *body = fn->new_block (); @@ -1010,6 +1211,28 @@ llvm::Value *value = fn->argument (builder, types.size () - 1); llvm::Value *ret = paren_scalar.call (builder, mat, array, nelem, value); fn->do_return (builder, ret); + +#ifdef HAVE_GCCJIT + { + // FIXME: TODO + gccjit::function gf = fn->gccjit_function; + gccjit::block body = gf.new_block (); + gccjit::rvalue array = create_arg_array (*fn, body, 1, types.size () - 1); + jit_type *index = jit_typeinfo::get_index (); + gccjit::rvalue nelem = gccjit_ctxt.new_rvalue (index->to_gccjit (), + (int)types.size () - 2); + gccjit::rvalue mat = gf.get_param (0); + gccjit::rvalue value = gf.get_param (types.size () - 1); + std::vector args(4); + args[0] = mat; + args[1] = array; + args[2] = nelem; + args[3] = value; + gccjit::rvalue ret = paren_scalar.call (gccjit_ctxt, body, args); + body.end_with_return (ret); + } +#endif + return fn; } @@ -1026,7 +1249,11 @@ types[2] = jit_typeinfo::get_index (); types[3] = jit_typeinfo::get_scalar (); - paren_scalar = jit_function (module, jit_convention::external, + paren_scalar = jit_function (module, +#ifdef HAVE_GCCJIT + gccjit_ctxt, +#endif + jit_convention::external, "octave_jit_paren_scalar", matrix, types); paren_scalar.add_mapping (engine, &octave_jit_paren_scalar_subsasgn); paren_scalar.mark_can_error (); @@ -1048,7 +1275,12 @@ { instance = this; +#ifdef HAVE_GCCJIT + gccjit_ctxt = gccjit::context::acquire (); +#endif + // FIXME: We should be registering types like in octave_value_typeinfo +#ifdef HAVE_LLVM llvm::Type *any_t = llvm::StructType::create (context, "octave_base_value"); any_t = any_t->getPointerTo (); @@ -1058,14 +1290,54 @@ string_t = string_t->getPointerTo (); llvm::Type *index_t = llvm::Type::getIntNTy (context, sizeof(octave_idx_type) * 8); - +#endif + +#ifdef HAVE_GCCJIT + gccjit::type any_t_gcc = + gccjit_ctxt.new_opaque_struct_type ("octave_base_value"); + any_t_gcc = any_t_gcc.get_pointer (); + gccjit::type scalar_t_gcc = gccjit_ctxt.get_type (GCC_JIT_TYPE_DOUBLE); + gccjit::type bool_t_gcc = gccjit_ctxt.get_type (GCC_JIT_TYPE_BOOL); + gccjit::type string_t_gcc = + gccjit_ctxt.get_type (GCC_JIT_TYPE_CHAR).get_pointer (); + gccjit::type index_t_gcc = gccjit_ctxt.get_int_type (); + gccjit::type int_t_gcc = gccjit_ctxt.get_type (GCC_JIT_TYPE_INT); +#endif + +#ifdef HAVE_LLVM llvm::StructType *range_t = llvm::StructType::create (context, "range"); std::vector range_contents (4, scalar_t); range_contents[3] = index_t; range_t->setBody (range_contents); - +#endif +#ifdef HAVE_GCCJIT + field_rng_base = gccjit_ctxt.new_field (scalar_t_gcc, "rng_base"); + field_rng_limit = gccjit_ctxt.new_field (scalar_t_gcc, "rng_limit"); + field_rng_inc = gccjit_ctxt.new_field (scalar_t_gcc, "rng_inc"); + field_rng_nelem = gccjit_ctxt.new_field (index_t_gcc, "rng_nelem"); + /* FIXME: what about the "mutable Matrix cache;" */ + + std::vector range_fields (4); + range_fields [0] = field_rng_base; + range_fields [1] = field_rng_limit; + range_fields [2] = field_rng_inc; + range_fields [3] = field_rng_nelem; + + gccjit::type range_t_gcc = + gccjit_ctxt.new_struct_type ( + "range", + range_fields, + gccjit_ctxt.new_location ("liboctave/array/Range.h", 33, 0)); +#endif + +#ifdef HAVE_LLVM llvm::Type *refcount_t = llvm::Type::getIntNTy (context, sizeof(int) * 8); - +#endif +#ifdef HAVE_GCCJIT + gccjit::type refcount_t_gcc = gccjit_ctxt.get_type (GCC_JIT_TYPE_INT); +#endif + +#ifdef HAVE_LLVM llvm::StructType *matrix_t = llvm::StructType::create (context, "matrix"); llvm::Type *matrix_contents[5]; matrix_contents[0] = refcount_t->getPointerTo (); @@ -1074,8 +1346,58 @@ matrix_contents[3] = index_t->getPointerTo (); matrix_contents[4] = string_t; matrix_t->setBody (llvm::makeArrayRef (matrix_contents, 5)); - +#endif +#ifdef HAVE_GCCJIT + /* + jit-typeinfo.h has, somewhat ominously: + // jit_array is compatable with the llvm array/matrix structures + typedef jit_array jit_matrix; + */ + gccjit::type matrix_t_gcc; + { + /* typedef jit_array jit_matrix; */ + /* template struct jit_array {...}; */ + gccjit::type T = string_t_gcc; /* NDArray */ + gccjit::type U = scalar_t_gcc; /* double */ + + /* int *ref_count; */ + field_ref_count = + gccjit_ctxt.new_field (refcount_t_gcc.get_pointer (), "ref_count"); + + /* U *slice_data; */ + field_slice_data = + gccjit_ctxt.new_field (U.get_pointer (), "slice_data"); + + /* octave_idx_type slice_len; */ + field_slice_len = gccjit_ctxt.new_field (index_t_gcc, "slice_len"); + + /* octave_idx_type *dimensions; */ + field_dimensions = + gccjit_ctxt.new_field (index_t_gcc.get_pointer (), "dimensions"); + + /* T *array; */ + field_array = gccjit_ctxt.new_field (T.get_pointer (), "array"); + + std::vector matrix_fields (5); + matrix_fields[0] = field_ref_count; + matrix_fields[1] = field_slice_data; + matrix_fields[2] = field_slice_len; + matrix_fields[3] = field_dimensions; + matrix_fields[4] = field_array; + + matrix_t_gcc = gccjit_ctxt.new_struct_type ( + "jit_matrix", + matrix_fields, + gccjit_ctxt.new_location ("jit-typeinfo.h", 106, 0)); + } +#endif + +#ifdef HAVE_LLVM llvm::Type *complex_t = llvm::ArrayType::get (scalar_t, 2); +#endif +#ifdef HAVE_GCCJIT + gccjit::type complex_t_gcc = gccjit_ctxt.new_array_type (scalar_t_gcc, 2); +#endif // complex_ret is what is passed to C functions in order to get calling // convention right @@ -1089,6 +1411,20 @@ } // create types +#ifdef HAVE_GCCJIT + any = new_type ("any", 0, any_t, any_t_gcc); + matrix = new_type ("matrix", any, matrix_t, matrix_t_gcc); + complex = new_type ("complex", any, complex_t, complex_t_gcc); + scalar = new_type ("scalar", complex, scalar_t, scalar_t_gcc); + scalar_ptr = new_type ("scalar_ptr", 0, scalar_t->getPointerTo (), + scalar_t_gcc.get_pointer ()); + any_ptr = new_type ("any_ptr", 0, any_t->getPointerTo (), + any_t_gcc.get_pointer()); + range = new_type ("range", any, range_t, range_t_gcc); + string = new_type ("string", any, string_t, string_t_gcc); + boolean = new_type ("bool", any, bool_t, bool_t_gcc); + index = new_type ("index", any, index_t, index_t_gcc); +#else any = new_type ("any", 0, any_t); matrix = new_type ("matrix", any, matrix_t); complex = new_type ("complex", any, complex_t); @@ -1099,6 +1435,7 @@ string = new_type ("string", any, string_t); boolean = new_type ("bool", any, bool_t); index = new_type ("index", any, index_t); +#endif create_int (8); create_int (16); @@ -1124,10 +1461,16 @@ if (sizeof (void *) == 4) complex->mark_sret (jit_convention::external); +#ifdef HAVE_GCCJIT + paren_subsref_fn.initialize (module, engine, gccjit_ctxt); + paren_subsasgn_fn.initialize (module, engine, gccjit_ctxt); +#else paren_subsref_fn.initialize (module, engine); paren_subsasgn_fn.initialize (module, engine); +#endif // bind global variables +#ifdef HAVE_LLVM lerror_state = new llvm::GlobalVariable (*module, bool_t, false, llvm::GlobalValue::ExternalLinkage, 0, "error_state"); @@ -1142,6 +1485,20 @@ "octave_interrupt_state"); engine->addGlobalMapping (loctave_interrupt_state, reinterpret_cast (&octave_interrupt_state)); +#endif + +#ifdef HAVE_GCCJIT + // Access "error_state" (actually declared as an "int"), by + // taking its address and dereferencing: + error_state_gccjit = + *gccjit_ctxt.new_rvalue (int_t_gcc.get_pointer (), + &error_state); + sig_atomic_type_gccjit = gccjit_ctxt.get_int_type (); + octave_interrupt_state_gccjit = + *gccjit_ctxt.new_rvalue ( + sig_atomic_type_gccjit.get_volatile ().get_pointer (), + &octave_interrupt_state); +#endif // generic call function { @@ -1175,8 +1532,10 @@ for (int op = 0; op < octave_value::num_binary_ops; ++op) { - llvm::Twine fn_name ("octave_jit_binary_any_any_"); - fn_name = fn_name + llvm::Twine (op); + std::string fn_name ("octave_jit_binary_any_any_"); + char buf[64]; + snprintf (buf, sizeof(buf), "%i", op); + fn_name = fn_name + std::string (buf); fn = create_internal (fn_name, any, any, any); fn.mark_can_error (); @@ -1189,6 +1548,20 @@ fn.argument (builder, 0), fn.argument (builder, 1)); fn.do_return (builder, ret); + +#ifdef HAVE_GCCJIT + // gccjit implementation of octave_jit_binary_any_any_*: + gccjit::function gf = fn.gccjit_function; + gccjit::block gccjit_block = gf.new_block (); + gccjit_block.end_with_return ( + gccjit_block.add_call ( + any_binary.gccjit_function, + gccjit_ctxt.new_rvalue (binary_op_type->to_gccjit (), + op), + gf.get_param (0), + gf.get_param (1))); +#endif + binary_ops[op].add_overload (fn); } @@ -1220,9 +1593,40 @@ destroy_fn.add_overload (create_identity(index)); destroy_fn.add_overload (create_identity(complex)); - // -------------------- scalar related operations -------------------- - // now for binary scalar operations +#ifdef HAVE_GCCJIT + add_binary_op (scalar, octave_value::op_add, + llvm::Instruction::FAdd, + GCC_JIT_BINARY_OP_PLUS); + add_binary_op (scalar, octave_value::op_sub, + llvm::Instruction::FSub, + GCC_JIT_BINARY_OP_MINUS); + add_binary_op (scalar, octave_value::op_mul, + llvm::Instruction::FMul, + GCC_JIT_BINARY_OP_MULT); + add_binary_op (scalar, octave_value::op_el_mul, + llvm::Instruction::FMul, + GCC_JIT_BINARY_OP_MULT); + + add_binary_fcmp (scalar, octave_value::op_lt, + llvm::CmpInst::FCMP_ULT, + GCC_JIT_COMPARISON_LT); + add_binary_fcmp (scalar, octave_value::op_le, + llvm::CmpInst::FCMP_ULE, + GCC_JIT_COMPARISON_LE); + add_binary_fcmp (scalar, octave_value::op_eq, + llvm::CmpInst::FCMP_UEQ, + GCC_JIT_COMPARISON_EQ); + add_binary_fcmp (scalar, octave_value::op_ge, + llvm::CmpInst::FCMP_UGE, + GCC_JIT_COMPARISON_GE); + add_binary_fcmp (scalar, octave_value::op_gt, + llvm::CmpInst::FCMP_UGT, + GCC_JIT_COMPARISON_GT); + add_binary_fcmp (scalar, octave_value::op_ne, + llvm::CmpInst::FCMP_UNE, + GCC_JIT_COMPARISON_NE); +#else add_binary_op (scalar, octave_value::op_add, llvm::Instruction::FAdd); add_binary_op (scalar, octave_value::op_sub, llvm::Instruction::FSub); add_binary_op (scalar, octave_value::op_mul, llvm::Instruction::FMul); @@ -1234,6 +1638,7 @@ add_binary_fcmp (scalar, octave_value::op_ge, llvm::CmpInst::FCMP_UGE); add_binary_fcmp (scalar, octave_value::op_gt, llvm::CmpInst::FCMP_UGT); add_binary_fcmp (scalar, octave_value::op_ne, llvm::CmpInst::FCMP_UNE); +#endif jit_function gripe_div0 = create_external (JIT_FN (gripe_divide_by_zero), 0); gripe_div0.mark_can_error (); @@ -1261,6 +1666,25 @@ fn.argument (builder, 1)); fn.do_return (builder, ret); } + + // gccjit implementation of octave_jit_div_scalar_scalar: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block initial_block = gf.new_block ("initial"); + gccjit::block warn_block = gf.new_block ("warn"); + gccjit::block normal_block = gf.new_block ("normal"); + initial_block.end_with_conditional ( + gf.get_param (1) != scalar_t_gcc.zero (), + normal_block, // on_true + warn_block); // on_false + + warn_block.add_call (gripe_div0.gccjit_function); + warn_block.end_with_jump (normal_block); + + normal_block.end_with_return (gf.get_param (0) / gf.get_param (1)); + } +#endif binary_ops[octave_value::op_div].add_overload (fn); binary_ops[octave_value::op_el_div].add_overload (fn); @@ -1279,7 +1703,7 @@ // now for unary scalar operations // FIXME: Impelment not - fn = create_internal ("octave_jit_++", scalar, scalar); + fn = create_internal ("octave_jit_plusplus", scalar, scalar); body = fn.new_block (); builder.SetInsertPoint (body); { @@ -1288,9 +1712,17 @@ val = builder.CreateFAdd (val, one); fn.do_return (builder, val); } + // gccjit implementation of octave_jit_plusplus: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + b.end_with_return (gf.get_param (0) + scalar_t_gcc.one ()); + } +#endif unary_ops[octave_value::op_incr].add_overload (fn); - fn = create_internal ("octave_jit_--", scalar, scalar); + fn = create_internal ("octave_jit_minusminus", scalar, scalar); body = fn.new_block (); builder.SetInsertPoint (body); { @@ -1298,7 +1730,15 @@ llvm::Value *val = fn.argument (builder, 0); val = builder.CreateFSub (val, one); fn.do_return (builder, val); + + // gccjit implementation of octave_jit_minusminus: +#ifdef HAVE_GCCJIT + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + b.end_with_return (gf.get_param (0) - scalar_t_gcc.one ()); +#endif } + unary_ops[octave_value::op_decr].add_overload (fn); fn = create_internal ("octave_jit_uminus", scalar, scalar); @@ -1310,7 +1750,14 @@ val = builder.CreateFMul (val, mone); fn.do_return (builder, val); } - unary_ops[octave_value::op_uminus].add_overload (fn); + // gccjit implementation of octave_jit_uminus: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + b.end_with_return (-gf.get_param (0)); + } +#endif fn = create_identity (scalar); unary_ops[octave_value::op_uplus].add_overload (fn); @@ -1318,7 +1765,7 @@ unary_ops[octave_value::op_hermitian].add_overload (fn); // now for binary complex operations - fn = create_internal ("octave_jit_+_complex_complex", complex, complex, + fn = create_internal ("octave_jit_plus_complex_complex", complex, complex, complex); body = fn.new_block (); builder.SetInsertPoint (body); @@ -1331,9 +1778,21 @@ complex_imag (rhs)); fn.do_return (builder, complex_new (real, imag)); } + // gccjit implementation of octave_jit_plus_complex_complex: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + gccjit::rvalue lhs = gf.get_param (0); + gccjit::rvalue rhs = gf.get_param (1); + gccjit::rvalue real = complex_real (lhs) + complex_real (rhs); + gccjit::rvalue imag = complex_imag (lhs) + complex_imag (rhs); + b.end_with_return (complex_new (b, real, imag)); + } +#endif binary_ops[octave_value::op_add].add_overload (fn); - fn = create_internal ("octave_jit_-_complex_complex", complex, complex, + fn = create_internal ("octave_jit_minus_complex_complex", complex, complex, complex); body = fn.new_block (); builder.SetInsertPoint (body); @@ -1346,6 +1805,18 @@ complex_imag (rhs)); fn.do_return (builder, complex_new (real, imag)); } + // gccjit implementation of octave_jit_minus_complex_complex: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + gccjit::rvalue lhs = gf.get_param (0); + gccjit::rvalue rhs = gf.get_param (1); + gccjit::rvalue real = complex_real (lhs) - complex_real (rhs); + gccjit::rvalue imag = complex_imag (lhs) - complex_imag (rhs); + b.end_with_return (complex_new (b, real, imag)); + } +#endif binary_ops[octave_value::op_sub].add_overload (fn); fn = create_external (JIT_FN (octave_jit_complex_mul), @@ -1364,7 +1835,7 @@ binary_ops[octave_value::op_pow].add_overload (fn); binary_ops[octave_value::op_el_pow].add_overload (fn); - fn = create_internal ("octave_jit_*_scalar_complex", complex, scalar, + fn = create_internal ("octave_jit_mult_scalar_complex", complex, scalar, complex); jit_function mul_scalar_complex = fn; body = fn.new_block (); @@ -1391,6 +1862,31 @@ builder.CreateFMul (lhs, complex_imag (rhs))); fn.do_return (builder, temp); } + // gccjit implementation of octave_jit_mult_scalar_complex: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + + gccjit::rvalue fzero = scalar_t_gcc.zero (); + gccjit::rvalue lhs = gf.get_param (0); + gccjit::rvalue rhs = gf.get_param (1); + + gccjit::block initial = gf.new_block ("initial"); + gccjit::block complex_mul = gf.new_block ("complex_mul"); + gccjit::block scalar_mul = gf.new_block ("scalar_mul"); + + initial.end_with_conditional (complex_imag (rhs) == fzero, + scalar_mul, + complex_mul); + + scalar_mul.end_with_return (complex_new (scalar_mul, + lhs * complex_real (rhs), + fzero)); + complex_mul.end_with_return (complex_new (complex_mul, + lhs * complex_real (rhs), + lhs * complex_imag (rhs))); + } +#endif binary_ops[octave_value::op_mul].add_overload (fn); binary_ops[octave_value::op_el_mul].add_overload (fn); @@ -1399,7 +1895,7 @@ binary_ops[octave_value::op_mul].add_overload (fn); binary_ops[octave_value::op_el_mul].add_overload (fn); - fn = create_internal ("octave_jit_+_scalar_complex", complex, scalar, + fn = create_internal ("octave_jit_plus_scalar_complex", complex, scalar, complex); body = fn.new_block (); builder.SetInsertPoint (body); @@ -1409,12 +1905,23 @@ llvm::Value *real = builder.CreateFAdd (lhs, complex_real (rhs)); fn.do_return (builder, complex_real (rhs, real)); } + // gccjit implementation of octave_jit_plus_scalar_complex: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + gccjit::rvalue lhs = gf.get_param (0); + gccjit::lvalue rhs = gf.get_param (1); + gccjit::rvalue real = lhs + complex_real (rhs); + b.end_with_return (complex_real (b, rhs, real)); + } +#endif binary_ops[octave_value::op_add].add_overload (fn); fn = mirror_binary (fn); binary_ops[octave_value::op_add].add_overload (fn); - fn = create_internal ("octave_jit_-_complex_scalar", complex, complex, + fn = create_internal ("octave_jit_minus_complex_scalar", complex, complex, scalar); body = fn.new_block (); builder.SetInsertPoint (body); @@ -1424,9 +1931,20 @@ llvm::Value *real = builder.CreateFSub (complex_real (lhs), rhs); fn.do_return (builder, complex_real (lhs, real)); } + // gccjit implementation of octave_jit_minus_complex_scalar: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + gccjit::lvalue lhs = gf.get_param (0); + gccjit::rvalue rhs = gf.get_param (1); + gccjit::rvalue real = complex_real (lhs) - rhs; + b.end_with_return (complex_real (b, lhs, real)); + } +#endif binary_ops[octave_value::op_sub].add_overload (fn); - fn = create_internal ("octave_jit_-_scalar_complex", complex, scalar, + fn = create_internal ("octave_jit_minus_scalar_complex", complex, scalar, complex); body = fn.new_block (); builder.SetInsertPoint (body); @@ -1436,6 +1954,17 @@ llvm::Value *real = builder.CreateFSub (lhs, complex_real (rhs)); fn.do_return (builder, complex_real (rhs, real)); } + // gccjit implementation of octave_jit_minus_scalar_complex: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + gccjit::rvalue lhs = gf.get_param (0); + gccjit::lvalue rhs = gf.get_param (1); + gccjit::rvalue real = lhs - complex_real (rhs); + b.end_with_return (complex_real (b, rhs, real)); + } +#endif binary_ops[octave_value::op_sub].add_overload (fn); fn = create_external (JIT_FN (octave_jit_pow_scalar_complex), complex, scalar, @@ -1449,11 +1978,25 @@ binary_ops[octave_value::op_el_pow].add_overload (fn); // now for binary index operators +#ifdef HAVE_GCCJIT + add_binary_op (index, octave_value::op_add, + llvm::Instruction::Add, + GCC_JIT_BINARY_OP_PLUS); + + // and binary bool operators + add_binary_op (boolean, octave_value::op_el_or, + llvm::Instruction::Or, + GCC_JIT_BINARY_OP_LOGICAL_OR); + add_binary_op (boolean, octave_value::op_el_and, + llvm::Instruction::And, + GCC_JIT_BINARY_OP_LOGICAL_AND); +#else add_binary_op (index, octave_value::op_add, llvm::Instruction::Add); // and binary bool operators add_binary_op (boolean, octave_value::op_el_or, llvm::Instruction::Or); add_binary_op (boolean, octave_value::op_el_and, llvm::Instruction::And); +#endif // now for printing functions print_fn.stash_name ("print"); @@ -1470,6 +2013,13 @@ llvm::Value *zero = llvm::ConstantInt::get (index_t, 0); fn.do_return (builder, zero); } +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + b.end_with_return (gccjit_ctxt.zero (index_t_gcc)); + } +#endif for_init_fn.add_overload (fn); // bounds check for for loop @@ -1484,7 +2034,20 @@ llvm::Value *idx = fn.argument (builder, 1); llvm::Value *ret = builder.CreateICmpULT (idx, nelem); fn.do_return (builder, ret); + } +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + // FIXME: is "range" a (struct range) or a (struct range *) ? + gccjit::rvalue nelem + = gf.get_param (0).access_field (field_rng_nelem); + gccjit::rvalue idx = gf.get_param (1); + gccjit::rvalue ret = idx < nelem; + gccjit::block b = gf.new_block (); + b.end_with_return (ret); + } +#endif for_check_fn.add_overload (fn); // index variabe for for loop @@ -1504,6 +2067,20 @@ ret = builder.CreateFAdd (base, ret); fn.do_return (builder, ret); } +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + gccjit::rvalue idx = gf.get_param (1); + gccjit::rvalue didx = idx.cast_to (scalar_t_gcc); + gccjit::rvalue rng = gf.get_param (0); + gccjit::rvalue base = rng.access_field (field_rng_base); + gccjit::rvalue inc = rng.access_field (field_rng_inc); + gccjit::rvalue ret = didx * inc; + ret = base + ret; + b.end_with_return (ret); + } +#endif for_index_fn.add_overload (fn); // logically true @@ -1535,6 +2112,22 @@ llvm::Value *ret = builder.CreateFCmpONE (fn.argument (builder, 0), zero); fn.do_return (builder, ret); } +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block initial = gf.new_block ("initial"); + gccjit::block error = gf.new_block ("error"); + gccjit::block normal = gf.new_block ("normal"); + initial.add_comment ("check for NaN"); + initial.end_with_conditional (gf.get_param (0) != gf.get_param (0), + error, normal); + + error.add_call (gripe_nantl.gccjit_function); + error.end_with_jump (normal); + + normal.end_with_return ( gf.get_param (0) != scalar_t_gcc.zero ()); + } +#endif logically_true_fn.add_overload (fn); // logically_true boolean @@ -1568,6 +2161,22 @@ rng = builder.CreateInsertValue (rng, nelem, 3); fn.do_return (builder, rng); } +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::rvalue base = gf.get_param (0); + gccjit::rvalue limit = gf.get_param (1); + gccjit::rvalue inc = gf.get_param (2); + gccjit::block b = gf.new_block (); + gccjit::rvalue nelem = compute_nelem.gccjit_function (base, limit, inc); + gccjit::lvalue rng = gf.new_local (range_t_gcc, "rng"); + b.add_assignment (rng.access_field (field_rng_base), base); + b.add_assignment (rng.access_field (field_rng_limit), limit); + b.add_assignment (rng.access_field (field_rng_inc), inc); + b.add_assignment (rng.access_field (field_rng_nelem), nelem); + b.end_with_return (rng); + } +#endif make_range_fn.add_overload (fn); // paren_subsref @@ -1579,7 +2188,7 @@ 0, jit_int, jit_int, index, index); - fn = create_internal ("()subsref", scalar, matrix, scalar); + fn = create_internal ("subsref", scalar, matrix, scalar); fn.mark_can_error (); body = fn.new_block (); @@ -1638,10 +2247,55 @@ merge->addIncoming (ret, success); fn.do_return (builder, merge); } + // gccjit implementation of subsref: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block initial = gf.new_block ("initial"); + + gccjit::rvalue one_idx = index_t_gcc.one (); + gccjit::rvalue one_int = jit_int->to_gccjit ().one (); + + gccjit::rvalue mat = gf.get_param (0); + gccjit::rvalue idx = gf.get_param (1); // scalar + + // convert index to scalar to integer, and check index >= 1 + gccjit::rvalue int_idx = idx.cast_to (index_t_gcc); + gccjit::rvalue check_idx = int_idx.cast_to (scalar_t_gcc); + gccjit::rvalue cond0 = (idx != check_idx); + gccjit::rvalue cond1 = (int_idx < one_idx); + + gccjit::block conv_error = gf.new_block ("conv_error"); + gccjit::block normal = gf.new_block ("normal"); + initial.end_with_conditional (cond0 || cond1, conv_error, normal); + + // "conv_error" block: + conv_error.add_call (ginvalid_index.gccjit_function); + conv_error.end_with_return (scalar_t_gcc.zero ()); // dummy value + + // "normal" block: + gccjit::rvalue len = mat.access_field (field_slice_len); + gccjit::rvalue cond = (int_idx > len); + gccjit::block bounds_error = gf.new_block ("bounds_error"); + gccjit::block success = gf.new_block ("success"); + normal.end_with_conditional (cond, bounds_error, success); + + // "bounds_error" block: + bounds_error.add_call (gindex_range.gccjit_function, + one_int, one_int, int_idx, len); + bounds_error.end_with_return (scalar_t_gcc.zero ()); // dummy value + + // "success" block: + gccjit::rvalue data = mat.access_field (field_slice_data); + gccjit::rvalue gep = data[int_idx]; + gccjit::rvalue ret = gep; + success.end_with_return (ret); + } +#endif paren_subsref_fn.add_overload (fn); // paren subsasgn - paren_subsasgn_fn.stash_name ("()subsasgn"); + paren_subsasgn_fn.stash_name ("subsasgn"); jit_function resize_paren_subsasgn = create_external (JIT_FN (octave_jit_paren_subsasgn_impl), matrix, matrix, @@ -1710,6 +2364,61 @@ merge->addIncoming (mat, success); fn.do_return (builder, merge); } + // gccjit implementation of octave_jit_paren_subsasgn: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block initial = gf.new_block ("initial"); + gccjit::rvalue one_idx = index_t_gcc.one (); + gccjit::rvalue one_int = jit_int->to_gccjit ().one (); + + gccjit::lvalue mat = gf.get_param (0); + gccjit::rvalue idx = gf.get_param (1); + gccjit::rvalue value = gf.get_param (2); + gccjit::rvalue int_idx = idx.cast_to (index_t_gcc); + gccjit::rvalue check_idx = int_idx.cast_to (scalar_t_gcc); + gccjit::rvalue cond0 = (idx != check_idx); + gccjit::rvalue cond1 = (int_idx < one_idx); + gccjit::rvalue cond = (cond0 || cond1); + + gccjit::block conv_error = gf.new_block ("conv_error"); + gccjit::block normal = gf.new_block ("normal"); + initial.end_with_conditional (cond, conv_error, normal); + + // block: conv_error + conv_error.add_call (ginvalid_index.gccjit_function); + conv_error.end_with_return (mat); + + // block: normal + gccjit::rvalue len = mat.access_field (field_slice_len); + cond0 = (int_idx > len); + + gccjit::rvalue rcount = mat.access_field (field_ref_count); + rcount = rcount.dereference (); + cond1 = rcount > one_int; + cond = (cond0 || cond1); + + gccjit::block bounds_error = gf.new_block ("bounds_error"); + gccjit::block success = gf.new_block ("success"); + normal.end_with_conditional (cond, bounds_error, success); + + // block: bounds_error + // resize on out of bounds access + std::vector args (3); + args[0] = mat; + args[1] = int_idx; + args[2] = value; + gccjit::rvalue resize_result = + resize_paren_subsasgn.call (gccjit_ctxt, bounds_error, args); + bounds_error.end_with_return (resize_result); + + // block: success + gccjit::rvalue data = mat.access_field (field_slice_data); + gccjit::lvalue gep = data[int_idx]; + success.add_assignment (gep, value); + success.end_with_return (mat); + } +#endif paren_subsasgn_fn.add_overload (fn); fn = create_external (JIT_FN (octave_jit_paren_subsasgn_matrix_range), matrix, @@ -1726,6 +2435,16 @@ llvm::Value *ret = builder.CreateExtractValue (mat, 2); fn.do_return (builder, builder.CreateSIToFP (ret, scalar_t)); } +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::rvalue mat = gf.get_param (0); + // FIXME: is this the right behavior? + gccjit::rvalue ret = mat.access_field (field_slice_len); + gccjit::block b = gf.new_block (); + b.end_with_return (ret.cast_to (scalar_t_gcc)); + } +#endif end1_fn.add_overload (fn); end_fn.stash_name ("end"); @@ -1784,6 +2503,15 @@ llvm::Value *zero = llvm::ConstantFP::get (scalar_t, 0); fn.do_return (builder, complex_new (fn.argument (builder, 0), zero)); } + // gccjit implementation of octave_jit_cast_complex_scalar: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + gccjit::rvalue zero = scalar_t_gcc.zero (); + b.end_with_return (complex_new (b, gf.get_param (0), zero)); + } +#endif casts[complex->type_id ()].add_overload (fn); // cast scalar <- complex @@ -1791,6 +2519,13 @@ body = fn.new_block (); builder.SetInsertPoint (body); fn.do_return (builder, complex_real (fn.argument (builder, 0))); +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + b.end_with_return (complex_real (gf.get_param (0))); + } +#endif casts[scalar->type_id ()].add_overload (fn); // cast any <- any @@ -1818,7 +2553,9 @@ register_generic ("cos", matrix, matrix); add_builtin ("exp"); - register_intrinsic ("exp", llvm::Intrinsic::exp, scalar, scalar); + // FIXME: looks like a typo: "cos" here should be "exp": + // filed as http://savannah.gnu.org/bugs/index.php?41560 + register_intrinsic ("exp", llvm::Intrinsic::cos, scalar, scalar); register_generic ("exp", matrix, matrix); add_builtin ("balance"); @@ -1833,10 +2570,13 @@ add_builtin ("norm"); register_generic ("norm", scalar, matrix); + //FIXME: gccjit can't yet cope with duplicate names +#if !defined (HAVE_GCCJIT) add_builtin ("rand"); register_generic ("rand", matrix, scalar); register_generic ("rand", matrix, std::vector (2, scalar)); + add_builtin ("magic"); register_generic ("magic", matrix, scalar); register_generic ("magic", matrix, std::vector (2, scalar)); @@ -1844,6 +2584,7 @@ add_builtin ("eye"); register_generic ("eye", matrix, scalar); register_generic ("eye", matrix, std::vector (2, scalar)); +#endif add_builtin ("mod"); register_generic ("mod", scalar, std::vector (2, scalar)); @@ -1870,6 +2611,9 @@ casts[btype->type_id ()].add_overload (jit_function (any_id, btype, args)); } +#ifdef HAVE_GCCJIT + gccjit_ctxt.dump_to_file ("/tmp/jit-typeinfo-dump.c", true); +#endif } const jit_function& @@ -1883,10 +2627,23 @@ } jit_type* -jit_typeinfo::new_type (const std::string& name, jit_type *parent, - llvm::Type *llvm_type, bool skip_paren) +jit_typeinfo::new_type (const std::string& name, jit_type *parent +#ifdef HAVE_LLVM + , llvm::Type *llvm_type +#endif +#ifdef HAVE_GCCJIT + , gccjit::type gccjit_type +#endif + , bool skip_paren) { - jit_type *ret = new jit_type (name, parent, llvm_type, skip_paren, next_id++); + jit_type *ret = new jit_type (name, parent +#ifdef HAVE_LLVM + , llvm_type +#endif +#ifdef HAVE_GCCJIT + , gccjit_type +#endif + , skip_paren, next_id++); id_to_type.push_back (ret); return ret; } @@ -1903,14 +2660,21 @@ // FIXME: cp between add_binary_op, add_binary_icmp, and add_binary_fcmp void -jit_typeinfo::add_binary_op (jit_type *ty, int op, int llvm_op) +jit_typeinfo::add_binary_op (jit_type *ty, int op + , int llvm_op +#ifdef HAVE_GCCJIT + , enum gcc_jit_binary_op gccjit_op +#endif + ) { std::stringstream fname; octave_value::binary_op ov_op = static_cast(op); - fname << "octave_jit_" << octave_value::binary_op_as_string (ov_op) + fname << "octave_jit_" << octave_value::binary_op_fcn_name (ov_op) << "_" << ty->name (); jit_function fn = create_internal (fname.str (), ty, ty, ty); + + // LLVM implementation: llvm::BasicBlock *block = fn.new_block (); builder.SetInsertPoint (block); llvm::Instruction::BinaryOps temp @@ -1919,18 +2683,37 @@ llvm::Value *ret = builder.CreateBinOp (temp, fn.argument (builder, 0), fn.argument (builder, 1)); fn.do_return (builder, ret); + + // gccjit implementation: +#ifdef HAVE_GCCJIT + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + b.end_with_return ( + gf.get_context ().new_binary_op ( + gccjit_op, + ty->to_gccjit (), + gf.get_param (0), + gf.get_param (1))); +#endif binary_ops[op].add_overload (fn); } void -jit_typeinfo::add_binary_icmp (jit_type *ty, int op, int llvm_op) +jit_typeinfo::add_binary_icmp (jit_type *ty, int op + , int llvm_op +#ifdef HAVE_GCCJIT + , enum gcc_jit_comparison gccjit_op +#endif + ) { std::stringstream fname; octave_value::binary_op ov_op = static_cast(op); - fname << "octave_jit" << octave_value::binary_op_as_string (ov_op) + fname << "octave_jit_" << octave_value::binary_op_fcn_name (ov_op) << "_" << ty->name (); jit_function fn = create_internal (fname.str (), boolean, ty, ty); + + // LLVM implementation: llvm::BasicBlock *block = fn.new_block (); builder.SetInsertPoint (block); llvm::CmpInst::Predicate temp @@ -1938,18 +2721,36 @@ llvm::Value *ret = builder.CreateICmp (temp, fn.argument (builder, 0), fn.argument (builder, 1)); fn.do_return (builder, ret); + + // gccjit implementation: +#ifdef HAVE_GCCJIT + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + b.end_with_return ( + gf.get_context ().new_comparison ( + gccjit_op, + gf.get_param (0), + gf.get_param (1))); +#endif binary_ops[op].add_overload (fn); } void -jit_typeinfo::add_binary_fcmp (jit_type *ty, int op, int llvm_op) +jit_typeinfo::add_binary_fcmp (jit_type *ty, int op + , int llvm_op +#ifdef HAVE_GCCJIT + , enum gcc_jit_comparison gccjit_op +#endif + ) { std::stringstream fname; octave_value::binary_op ov_op = static_cast(op); - fname << "octave_jit" << octave_value::binary_op_as_string (ov_op) + fname << "octave_jit_" << octave_value::binary_op_fcn_name (ov_op) << "_" << ty->name (); jit_function fn = create_internal (fname.str (), boolean, ty, ty); + + // LLVM implementation: llvm::BasicBlock *block = fn.new_block (); builder.SetInsertPoint (block); llvm::CmpInst::Predicate temp @@ -1957,15 +2758,30 @@ llvm::Value *ret = builder.CreateFCmp (temp, fn.argument (builder, 0), fn.argument (builder, 1)); fn.do_return (builder, ret); + + // gccjit implementation: +#ifdef HAVE_GCCJIT + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + b.end_with_return ( + gf.get_context ().new_comparison ( + gccjit_op, + gf.get_param (0), + gf.get_param (1))); +#endif binary_ops[op].add_overload (fn); } jit_function -jit_typeinfo::create_function (jit_convention::type cc, const llvm::Twine& name, +jit_typeinfo::create_function (jit_convention::type cc, std::string name, jit_type *ret, const std::vector& args) { - jit_function result (module, cc, name, ret, args); + jit_function result (module, +#ifdef HAVE_GCCJIT + gccjit_ctxt, +#endif + cc, name, ret, args); return result; } @@ -1982,15 +2798,26 @@ name << "id_" << type->name (); jit_function fn = create_internal (name.str (), type, type); + + // LLVM implementation: llvm::BasicBlock *body = fn.new_block (); builder.SetInsertPoint (body); fn.do_return (builder, fn.argument (builder, 0)); + + // gccjit implementation: +#ifdef HAVE_GCCJIT + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block (); + b.end_with_return (gf.get_param (0)); +#endif + return identities[id] = fn; } return identities[id]; } +#ifdef HAVE_LLVM llvm::Value * jit_typeinfo::do_insert_error_check (llvm::IRBuilderD& abuilder) { @@ -2004,11 +2831,31 @@ val->setVolatile (true); return abuilder.CreateICmpSGT (val, abuilder.getInt32 (0)); } +#endif /* #ifdef HAVE_LLVM */ + +#ifdef HAVE_GCCJIT +gccjit::rvalue +jit_typeinfo::do_insert_error_check (gccjit::function func) +{ + return error_state_gccjit.cast_to (gccjit_ctxt.get_type (GCC_JIT_TYPE_BOOL)); +} + +gccjit::rvalue +jit_typeinfo::do_insert_interrupt_check (gccjit::function func) +{ + return octave_interrupt_state_gccjit > sig_atomic_type_gccjit.zero (); +} +#endif /* #ifdef HAVE_GCCJIT */ + void jit_typeinfo::add_builtin (const std::string& name) { - jit_type *btype = new_type (name, any, any->to_llvm (), true); + jit_type *btype = new_type (name, any, any->to_llvm (), +#ifdef HAVE_GCCJIT + any->to_gccjit (), +#endif + true); builtins[name] = btype; octave_builtin *ov_builtin = find_builtin (name); @@ -2050,6 +2897,21 @@ llvm::Value *ret = builder.CreateCall (ifun, fargs); fn.do_return (builder, ret); + + // gcc implementation +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::function builtin = gccjit_ctxt.get_builtin_function (name); + std::vector gccjit_args (nargs); + for (size_t i = 0; i < nargs; ++i) + gccjit_args[i] = gf.get_param (i + 1); + gccjit::rvalue ret = gccjit_ctxt.new_call (builtin, gccjit_args); + gccjit::block b = gf.new_block (); + b.end_with_return (ret); + } +#endif + paren_subsref_fn.add_overload (fn); } @@ -2075,6 +2937,8 @@ std::copy (args.begin (), args.end (), fn_args.begin () + 1); jit_function fn = create_internal (name, result, fn_args); fn.mark_can_error (); + + // LLVM implementation: llvm::BasicBlock *block = fn.new_block (); builder.SetInsertPoint (block); llvm::Type *any_t = any->to_llvm (); @@ -2105,6 +2969,55 @@ jit_function cast_result = cast (result, any); fn.do_return (builder, cast_result.call (builder, ret)); + + // gccjit implementation: +#ifdef HAVE_GCCJIT + { + gccjit::function gf = fn.gccjit_function; + gccjit::block b = gf.new_block ("body of register_generic"); + b.add_comment ("TODO: register_generic"); + + gccjit::type array_t = gccjit_ctxt.new_array_type (any->to_gccjit (), + args.size ()); +#if 1 + gccjit::lvalue array = gf.new_local (array_t, "tmp_array"); + for (size_t i = 0; i < args.size (); ++i) + { + gccjit::lvalue arg = gf.get_param (i + 1); + jit_function agrab = get_grab (args[i]); + if (agrab.valid ()) + { + std::vector grab_args (1, arg); + b.add_assignment (arg, agrab.call (gccjit_ctxt, b, grab_args)); + } + jit_function acast = cast (any, args[i]); + std::vector cast_args (1, arg); + b.add_assignment (array[i], + acast.call (gccjit_ctxt, b, cast_args)); + } +#endif + + gccjit::type int_t = intN (sizeof (octave_builtin::fcn) * 8)->to_gccjit (); + size_t fcn_int = reinterpret_cast (builtin->function ()); + gccjit::rvalue fcn = gccjit_ctxt.new_rvalue (int_t, (int)fcn_int); + gccjit::rvalue nargin = gccjit_ctxt.new_rvalue (int_t, (int)args.size ()); + size_t result_int = reinterpret_cast (result); + gccjit::rvalue res_gcc = gccjit_ctxt.new_rvalue (int_t, (int)result_int); + std::vector call_args (4); + call_args[0] = fcn; + call_args[1] = nargin; + call_args[2] = array; + call_args[3] = res_gcc; + gccjit::rvalue ret = any_call.call (gccjit_ctxt, b, call_args); + + jit_function cast_result = cast (result, any); + std::vector final_cast_args (1); + final_cast_args[0] = ret; + gccjit::rvalue final_result = cast_result.call (gccjit_ctxt, b, final_cast_args); + b.end_with_return (final_result); + } +#endif + paren_subsref_fn.add_overload (fn); } @@ -2117,6 +3030,7 @@ if (fn.can_error ()) ret.mark_can_error (); + // LLVM implementation: llvm::BasicBlock *body = ret.new_block (); builder.SetInsertPoint (body); llvm::Value *result = fn.call (builder, ret.argument (builder, 1), @@ -2126,9 +3040,27 @@ else ret.do_return (builder); + // gccjit implementation: +#ifdef HAVE_GCCJIT + gccjit::function gf = ret.gccjit_function; + gccjit::block b = gf.new_block (); + b.add_comment ("built by mirror_binary"); + //std::vector gccjit_args (2); + //gccjit_args[0] = gf.get_param (1); + //gccjit_args[1] = gf.get_param (0); + if (ret.result ()) + b.end_with_return ( + gccjit_ctxt.new_call (fn.gccjit_function, + gf.get_param (1), + gf.get_param (0))); + else + b.end_with_return (); +#endif + return ret; } +#ifdef HAVE_LLVM llvm::Value * jit_typeinfo::pack_complex (llvm::IRBuilderD& bld, llvm::Value *cplx) { @@ -2189,14 +3121,65 @@ ret = complex_real (ret, real); return complex_imag (ret, imag); } +#endif // #ifdef HAVE_LLVM + +#ifdef HAVE_GCCJIT +gccjit::rvalue +jit_typeinfo::complex_real (gccjit::rvalue cx) +{ + return cx[0]; +} + +gccjit::rvalue +jit_typeinfo::complex_real (gccjit::block block, + gccjit::lvalue cx, + gccjit::rvalue real) +{ + block.add_assignment (cx[0], real); + return cx; +} + +gccjit::rvalue +jit_typeinfo::complex_imag (gccjit::rvalue cx) +{ + return cx[1]; +} + +gccjit::rvalue +jit_typeinfo::complex_imag (gccjit::block block, + gccjit::lvalue cx, + gccjit::rvalue imag) +{ + block.add_assignment (cx[1], imag); + return cx; +} + +gccjit::rvalue +jit_typeinfo::complex_new (gccjit::block block, + gccjit::rvalue real, + gccjit::rvalue imag) +{ + gccjit::rvalue tmp = + block.get_function ().new_local (complex->to_gccjit (), + "complex_new"); + block.add_assignment (tmp[0], real); + block.add_assignment (tmp[1], imag); + return tmp; +} +#endif // #ifdef HAVE_GCCJIT void jit_typeinfo::create_int (size_t nbits) { std::stringstream tname; tname << "int" << nbits; - ints[nbits] = new_type (tname.str (), any, llvm::Type::getIntNTy (context, - nbits)); + ints[nbits] = new_type (tname.str (), any + , llvm::Type::getIntNTy (context, + nbits) +#ifdef HAVE_GCCJIT + , gccjit_ctxt.get_int_type (nbits / 8, 1) +#endif + ); } jit_type * diff --git a/libinterp/corefcn/jit-typeinfo.h b/libinterp/corefcn/jit-typeinfo.h --- a/libinterp/corefcn/jit-typeinfo.h +++ b/libinterp/corefcn/jit-typeinfo.h @@ -25,7 +25,7 @@ #if !defined (octave_jit_typeinfo_h) #define octave_jit_typeinfo_h 1 -#ifdef HAVE_LLVM +#ifdef HAVE_JIT #include #include @@ -133,10 +133,18 @@ jit_type { public: +#ifdef HAVE_LLVM typedef llvm::Value *(*convert_fn) (llvm::IRBuilderD&, llvm::Value *); +#endif - jit_type (const std::string& aname, jit_type *aparent, llvm::Type *allvm_type, - bool askip_paren, int aid); + jit_type (const std::string& aname, jit_type *aparent +#ifdef HAVE_LLVM + , llvm::Type *allvm_type +#endif +#ifdef HAVE_GCCJIT + , gccjit::type agcc_type +#endif + , bool askip_paren, int aid); // a user readable type name const std::string& name (void) const { return mname; } @@ -147,11 +155,17 @@ // An abstract base type, may be null jit_type *parent (void) const { return mparent; } +#ifdef HAVE_LLVM // convert to an llvm type llvm::Type *to_llvm (void) const { return llvm_type; } // how this type gets passed as a function argument llvm::Type *to_llvm_arg (void) const; +#endif + +#ifdef HAVE_GCCJIT + gccjit::type to_gccjit (void) const { return gccjit_type; } +#endif size_t depth (void) const { return mdepth; } @@ -179,6 +193,7 @@ // Convert into an equivalent form before calling. For example, complex is // represented as two values llvm vector, but we need to pass it as a two // valued llvm structure to C functions. +#ifdef HAVE_LLVM convert_fn pack (jit_convention::type cc) { return mpack[cc]; } void set_pack (jit_convention::type cc, convert_fn fn) { mpack[cc] = fn; } @@ -195,10 +210,17 @@ void set_packed_type (jit_convention::type cc, llvm::Type *ty) { mpacked_type[cc] = ty; } +#endif + private: std::string mname; jit_type *mparent; +#ifdef HAVE_LLVM llvm::Type *llvm_type; +#endif +#ifdef HAVE_GCCJIT + gccjit::type gccjit_type; +#endif int mid; size_t mdepth; bool mskip_paren; @@ -206,12 +228,13 @@ bool msret[jit_convention::length]; bool mpointer_arg[jit_convention::length]; +#ifdef HAVE_LLVM convert_fn mpack[jit_convention::length]; convert_fn munpack[jit_convention::length]; llvm::Type *mpacked_type[jit_convention::length]; +#endif }; - // seperate print function to allow easy printing if type is null std::ostream& jit_print (std::ostream& os, jit_type *atype); @@ -227,8 +250,16 @@ // create a function in an invalid state jit_function (); - jit_function (llvm::Module *amodule, jit_convention::type acall_conv, - const llvm::Twine& aname, jit_type *aresult, + jit_function ( +#ifdef HAVE_LLVM + llvm::Module *amodule, +#endif +#ifdef HAVE_GCCJIT + gccjit::context gccjit_ctxt, +#endif + jit_convention::type acall_conv, + std::string aname, + jit_type *aresult, const std::vector& aargs); // Use an existing function, but change the argument types. The new argument @@ -241,6 +272,7 @@ // erase the interal LLVM function (if it exists). Will become invalid. void erase (void); +#ifdef HAVE_LLVM template void add_mapping (llvm::ExecutionEngine *engine, T fn) { @@ -248,9 +280,13 @@ } bool valid (void) const { return llvm_function; } +#else + bool valid (void) const; +#endif std::string name (void) const; +#ifdef HAVE_LLVM llvm::BasicBlock *new_block (const std::string& aname = "body", llvm::BasicBlock *insert_before = 0); @@ -289,6 +325,17 @@ bool verify = true); llvm::Function *to_llvm (void) const { return llvm_function; } +#endif + +#ifdef HAVE_GCCJIT + gccjit::rvalue call (gccjit::context ctxt, + gccjit::block block, + const std::vector& in_args) const; + gccjit::rvalue call (gccjit::context ctxt, + gccjit::block block, + std::vector& in_args) const; + gccjit::lvalue argument (size_t idx) const; +#endif // If true, then the return value is passed as a pointer in the first argument bool sret (void) const { return mresult && mresult->sret (call_conv); } @@ -307,10 +354,17 @@ const std::vector& arguments (void) const { return args; } private: +#ifdef HAVE_LLVM void do_add_mapping (llvm::ExecutionEngine *engine, void *fn); llvm::Module *module; llvm::Function *llvm_function; +#endif +#ifdef HAVE_GCCJIT +public: + gccjit::function gccjit_function; +private: +#endif jit_type *mresult; std::vector args; jit_convention::type call_conv; @@ -389,14 +443,25 @@ jit_index_operation : public jit_operation { public: - jit_index_operation (void) : module (0), engine (0) { } + jit_index_operation (void) {} //: module (0), engine (0) { } - void initialize (llvm::Module *amodule, llvm::ExecutionEngine *aengine) +#ifdef HAVE_LLVM + void initialize (llvm::Module *amodule, llvm::ExecutionEngine *aengine +#ifdef HAVE_GCCJIT + , gccjit::context agccjit_ctxt +#endif + ) + { module = amodule; engine = aengine; +#ifdef HAVE_GCCJIT + gccjit_ctxt = agccjit_ctxt; +#endif do_initialize (); } +#endif + protected: virtual jit_function *generate (const signature_vec& types) const; @@ -406,12 +471,22 @@ // helper functions // [start_idx, end_idx). +#ifdef HAVE_LLVM llvm::Value *create_arg_array (llvm::IRBuilderD& builder, const jit_function &fn, size_t start_idx, size_t end_idx) const; llvm::Module *module; llvm::ExecutionEngine *engine; +#endif +#ifdef HAVE_GCCJIT + gccjit::rvalue create_arg_array (const jit_function &fn, + gccjit::block block, + size_t start_idx, + size_t end_idx) const; + + gccjit::context gccjit_ctxt; +#endif }; class @@ -442,7 +517,11 @@ jit_typeinfo { public: - static void initialize (llvm::Module *m, llvm::ExecutionEngine *e); + static void initialize ( +#ifdef HAVE_LLVM + llvm::Module *m, llvm::ExecutionEngine *e +#endif + ); static jit_type *join (jit_type *lhs, jit_type *rhs) { @@ -454,9 +533,14 @@ static jit_type *get_matrix (void) { return instance->matrix; } static jit_type *get_scalar (void) { return instance->scalar; } - +#ifdef HAVE_LLVM static llvm::Type *get_scalar_llvm (void) { return instance->scalar->to_llvm (); } +#endif +#ifdef HAVE_GCCJIT + static gccjit::type get_scalar_gccjit (void) + { return instance->scalar->to_gccjit (); } +#endif static jit_type *get_scalar_ptr (void) { return instance->scalar_ptr; } @@ -470,8 +554,10 @@ static jit_type *get_index (void) { return instance->index; } +#ifdef HAVE_LLVM static llvm::Type *get_index_llvm (void) { return instance->index->to_llvm (); } +#endif static jit_type *get_complex (void) { return instance->complex; } @@ -563,6 +649,7 @@ return instance->do_cast (to, from); } +#ifdef HAVE_LLVM static llvm::Value *insert_error_check (llvm::IRBuilderD& bld) { return instance->do_insert_error_check (bld); @@ -572,6 +659,18 @@ { return instance->do_insert_interrupt_check (bld); } +#endif +#ifdef HAVE_GCCJIT + static gccjit::rvalue insert_error_check (gccjit::function func) + { + return instance->do_insert_error_check (func); + } + + static gccjit::rvalue insert_interrupt_check (gccjit::function func) + { + return instance->do_insert_interrupt_check (func); + } +#endif static const jit_operation& end (void) { @@ -589,12 +688,25 @@ return instance->create_undef_fn; } +#ifdef HAVE_LLVM static llvm::Value *create_complex (llvm::Value *real, llvm::Value *imag) { return instance->complex_new (real, imag); } +#endif + +#ifdef HAVE_GCCJIT + static gccjit::context create_gccjit_child_context () + { + return instance->gccjit_ctxt.new_child_context (); + } +#endif + + private: +#ifdef HAVE_LLVM jit_typeinfo (llvm::Module *m, llvm::ExecutionEngine *e); +#endif // FIXME: Do these methods really need to be in jit_typeinfo? jit_type *do_join (jit_type *lhs, jit_type *rhs) @@ -668,35 +780,67 @@ const jit_function& do_end (jit_value *value, jit_value *index, jit_value *count); - jit_type *new_type (const std::string& name, jit_type *parent, - llvm::Type *llvm_type, bool skip_paren = false); - + jit_type *new_type (const std::string& name, jit_type *parent +#ifdef HAVE_LLVM + , llvm::Type *llvm_type +#endif +#ifdef HAVE_GCCJIT + , gccjit::type gccjit_type +#endif + , bool skip_paren = false); void add_print (jit_type *ty, void *fptr); - void add_binary_op (jit_type *ty, int op, int llvm_op); + void add_binary_op (jit_type *ty, int op + , int llvm_op +#ifdef HAVE_GCCJIT + , enum gcc_jit_binary_op gccjit_op +#endif + ); - void add_binary_icmp (jit_type *ty, int op, int llvm_op); + void add_binary_icmp (jit_type *ty, int op + , int llvm_op +#ifdef HAVE_GCCJIT + , enum gcc_jit_comparison gccjit_op +#endif + ); - void add_binary_fcmp (jit_type *ty, int op, int llvm_op); + void add_binary_fcmp (jit_type *ty, int op + , int llvm_op +#ifdef HAVE_GCCJIT + , enum gcc_jit_comparison gccjit_op +#endif + ); // create a function with an external calling convention // forces the function pointer to be specified template - jit_function create_external (llvm::ExecutionEngine *ee, T fn, - const llvm::Twine& name, jit_type *ret, + jit_function create_external ( +#ifdef HAVE_LLVM + llvm::ExecutionEngine *ee, +#endif + T fn, + std::string name, jit_type *ret, const std::vector& args = std::vector ()) { jit_function retval = create_function (jit_convention::external, name, ret, args); +#ifdef HAVE_LLVM retval.add_mapping (ee, fn); +#endif return retval; } +#ifdef HAVE_LLVM #define JIT_PARAM_ARGS llvm::ExecutionEngine *ee, T fn, \ - const llvm::Twine& name, jit_type *ret, + std::string name, jit_type *ret, #define JIT_PARAMS ee, fn, name, ret, +#else +#define JIT_PARAM_ARGS T fn, \ + std::string name, jit_type *ret, +#define JIT_PARAMS fn, name, ret, +#endif #define CREATE_FUNCTION(N) JIT_EXPAND(template jit_function, \ create_external, \ jit_type *, /* empty */, N) @@ -712,19 +856,19 @@ // use create_external or create_internal directly jit_function create_function (jit_convention::type cc, - const llvm::Twine& name, jit_type *ret, + std::string name, jit_type *ret, const std::vector& args = std::vector ()); // create an internal calling convention (a function defined in llvm) - jit_function create_internal (const llvm::Twine& name, jit_type *ret, + jit_function create_internal (std::string name, jit_type *ret, const std::vector& args = std::vector ()) { return create_function (jit_convention::internal, name, ret, args); } -#define JIT_PARAM_ARGS const llvm::Twine& name, jit_type *ret, +#define JIT_PARAM_ARGS std::string name, jit_type *ret, #define JIT_PARAMS name, ret, #define CREATE_FUNCTION(N) JIT_EXPAND(jit_function, create_internal, \ jit_type *, /* empty */, N) @@ -740,9 +884,17 @@ jit_function create_identity (jit_type *type); +#ifdef HAVE_LLVM llvm::Value *do_insert_error_check (llvm::IRBuilderD& bld); llvm::Value *do_insert_interrupt_check (llvm::IRBuilderD& bld); +#endif + +#ifdef HAVE_GCCJIT + gccjit::rvalue do_insert_error_check (gccjit::function func); + + gccjit::rvalue do_insert_interrupt_check (gccjit::function func); +#endif void add_builtin (const std::string& name); @@ -770,6 +922,7 @@ jit_function mirror_binary (const jit_function& fn); +#ifdef HAVE_LLVM llvm::Function *wrap_complex (llvm::Function *wrap); static llvm::Value *pack_complex (llvm::IRBuilderD& bld, @@ -787,6 +940,25 @@ llvm::Value *complex_imag (llvm::Value *cx, llvm::Value *imag); llvm::Value *complex_new (llvm::Value *real, llvm::Value *imag); +#endif + +#ifdef HAVE_GCCJIT + gccjit::rvalue complex_real (gccjit::rvalue cx); + + gccjit::rvalue complex_real (gccjit::block block, + gccjit::lvalue cx, + gccjit::rvalue real); + + gccjit::rvalue complex_imag (gccjit::rvalue cx); + + gccjit::rvalue complex_imag (gccjit::block block, + gccjit::lvalue cx, + gccjit::rvalue imag); + + gccjit::rvalue complex_new (gccjit::block block, + gccjit::rvalue real, + gccjit::rvalue imag); +#endif void create_int (size_t nbits); @@ -794,22 +966,45 @@ static jit_typeinfo *instance; +#ifdef HAVE_LLVM llvm::Module *module; llvm::ExecutionEngine *engine; +#endif int next_id; +#ifdef HAVE_LLVM llvm::GlobalVariable *lerror_state; llvm::GlobalVariable *loctave_interrupt_state; llvm::Type *sig_atomic_type; +#endif + +#ifdef HAVE_GCCJIT + gccjit::rvalue error_state_gccjit; + gccjit::rvalue octave_interrupt_state_gccjit; + gccjit::type sig_atomic_type_gccjit; +#endif std::vector id_to_type; jit_type *any; jit_type *matrix; +#ifdef HAVE_GCCJIT + gccjit::field field_ref_count; + gccjit::field field_slice_data; + gccjit::field field_slice_len; + gccjit::field field_dimensions; + gccjit::field field_array; +#endif jit_type *scalar; jit_type *scalar_ptr; // a fake type for interfacing with C++ jit_type *any_ptr; // a fake type for interfacing with C++ jit_type *range; +#ifdef HAVE_GCCJIT + gccjit::field field_rng_base; + gccjit::field field_rng_limit; + gccjit::field field_rng_inc; + gccjit::field field_rng_nelem; +#endif jit_type *string; jit_type *boolean; jit_type *index; @@ -818,7 +1013,9 @@ std::map ints; std::map builtins; +#ifdef HAVE_LLVM llvm::StructType *complex_ret; +#endif std::vector binary_ops; std::vector unary_ops; @@ -845,8 +1042,14 @@ // type id -> identity function std::vector identities; +#ifdef HAVE_LLVM llvm::IRBuilderD& builder; +#endif + +#ifdef HAVE_GCCJIT + gccjit::context gccjit_ctxt; +#endif }; +#endif /* ifdef HAVE_JIT */ #endif -#endif diff --git a/libinterp/corefcn/jit-util.h b/libinterp/corefcn/jit-util.h --- a/libinterp/corefcn/jit-util.h +++ b/libinterp/corefcn/jit-util.h @@ -27,10 +27,15 @@ #if !defined (octave_jit_util_h) #define octave_jit_util_h 1 -#ifdef HAVE_LLVM +#ifdef HAVE_JIT #include +#ifdef HAVE_GCCJIT +#include "libgccjit++.h" +#endif + +#if HAVE_LLVM #if defined(HAVE_LLVM_IR_DATALAYOUT_H) || defined(HAVE_LLVM_DATALAYOUT_H) #define HAVE_LLVM_DATALAYOUT #endif @@ -73,6 +78,7 @@ typedef IRBuilder > IRBuilderD; } +#endif /* if HAVE_LLVM */ class octave_base_value; class octave_builtin; @@ -95,7 +101,9 @@ }; // llvm doesn't provide this, and it's really useful for debugging +#if HAVE_LLVM std::ostream& operator<< (std::ostream& os, const llvm::Value& v); +#endif template class jit_internal_node; @@ -212,5 +220,5 @@ return fname (JIT_PARAMS the_args); \ } +#endif /* ifdef HAVE_JIT */ #endif -#endif diff --git a/libinterp/corefcn/module.mk b/libinterp/corefcn/module.mk --- a/libinterp/corefcn/module.mk +++ b/libinterp/corefcn/module.mk @@ -343,11 +343,12 @@ $(FT2_CPPFLAGS) \ $(HDF5_CPPFLAGS) \ $(LLVM_CPPFLAGS) \ + $(LIBGCCJIT_CPPFLAGS) \ $(Z_CPPFLAGS) libinterp_corefcn_libcorefcn_la_CFLAGS = $(AM_CFLAGS) $(WARN_CFLAGS) -libinterp_corefcn_libcorefcn_la_CXXFLAGS = $(AM_CXXFLAGS) $(WARN_CXXFLAGS) $(LLVM_CXXFLAGS) +libinterp_corefcn_libcorefcn_la_CXXFLAGS = $(AM_CXXFLAGS) $(WARN_CXXFLAGS) $(LLVM_CXXFLAGS) $(LIBGCCJIT_CXXFLAGS) libinterp_corefcn_libtex_parser_la_SOURCES = $(TEX_PARSER_SRC) diff --git a/libinterp/corefcn/pt-jit.cc b/libinterp/corefcn/pt-jit.cc --- a/libinterp/corefcn/pt-jit.cc +++ b/libinterp/corefcn/pt-jit.cc @@ -38,7 +38,7 @@ #include "symtab.h" #include "variables.h" -#ifdef HAVE_LLVM +#ifdef HAVE_JIT static bool Vdebug_jit = false; @@ -48,6 +48,7 @@ static int Vjit_failcnt = 0; +#ifdef HAVE_LLVM #include #include @@ -101,6 +102,8 @@ static llvm::LLVMContext& context = llvm::getGlobalContext (); +#endif /* ifdef HAVE_LLVM */ + // -------------------- jit_break_exception -------------------- // jit_break is thrown whenever a branch we are converting has only breaks or @@ -1295,7 +1298,565 @@ } } +// -------------------- jit_convert_gcc -------------------- +#ifdef HAVE_GCCJIT +jit_convert_gcc::jit_convert_gcc () + : m_blocks (NULL), + m_constants (NULL), + m_ctxt (jit_typeinfo::create_gccjit_child_context ()), + m_func (), + m_extracted_params (), + m_block_map (), + m_locals_for_insns (), + m_block_stack (), + m_log_visits (false) +{ + // FIXME: error-handling + + m_ctxt.set_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, + 3); + + // Diagnostics, debugging etc: + if (1) + { + m_ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO, + 1); + m_ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE, + 0); + m_ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, + 1); + m_ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_SUMMARY, + 1); + m_ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, + 0); + m_ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, + 1); + m_ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_SELFCHECK_GC, + 0); + m_ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, + 1); + } +} + +jit_convert_gcc::~jit_convert_gcc () +{ + m_ctxt.release (); +} + +gcc_jit_result * +jit_convert_gcc::convert_loop (const jit_block_list& blocks, + const std::list& constants) +{ + converting_function = false; + + m_blocks = &blocks; + m_constants = &constants; + + jit_type *any = jit_typeinfo::get_any (); + // argument is an array of octave_base_value*, or octave_base_value** + gccjit::type arg_type = any->to_gccjit (); // this is octave_base_value* + arg_type = arg_type.get_pointer (); + + // for now just init arguments from entry, later we will have to do something + // more interesting + jit_block *entry_block = m_blocks->front (); + for (jit_block::iterator iter = entry_block->begin (); + iter != entry_block->end (); ++iter) + if (jit_extract_argument *extract + = dynamic_cast (*iter)) + { + m_argument_index[extract->name ()] = m_argument_vec.size (); + m_argument_vec.push_back (std::make_pair (extract->name (), true)); + } + + /* We have a single param, confusingly named "params" (an array). */ + params = m_ctxt.new_param (arg_type, "params"); + + std::vector params_of_fn (1, params); + + m_func = m_ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, + m_ctxt.get_type (GCC_JIT_TYPE_VOID), + "loop", + params_of_fn, + 0); + + gccjit::block initial_block = m_func.new_block ("initial"); + + /* Create gccjit::blocks for each jit_block, and for each edge. + + We're converting from an SSA representation back to a non-SSA + representation, hence we need to convert phi nodes into regular + assignments. To do this, introduce a gccjit::block for each edge, to + handle the phis along each edges. Jumps to blocks in the JIT-IR + will become jumps to edge-handling gccjit::blocks in our CFG; each + such "edge" block will end with a jump to the "real" gccjit::block. */ + std::list::const_iterator block_iter; + for (block_iter = m_blocks->begin (); + block_iter != m_blocks->end (); + ++block_iter) + { + jit_block *jblock = *block_iter; + + // Find predecessor blocks + for (jit_use *use = jblock->first_use (); use; use = use->next ()) + { + jit_block *pred = use->user_parent (); + + /* Create edge-handling gccjit::block. */ + m_blocks_for_edges[std::make_pair(pred, jblock)] = + m_func.new_block (std::string ("edge_from_") + + pred->name_and_id () + + std::string ("_to_") + + jblock->name_and_id ()); + } + + /* The "real" gccjit::block. */ + m_block_map[jblock] = + m_func.new_block (jblock->name_and_id ()); + } + + /* It's an SSA representation, so we have another pass here that + creates a local for every statement. */ + for (block_iter = m_blocks->begin (); + block_iter != m_blocks->end (); + ++block_iter) + for (jit_block::iterator insn_iter = (*block_iter)->begin (); + insn_iter != (*block_iter)->end (); + ++insn_iter) + { + jit_instruction *insn = *insn_iter; + + /* Some insns have NULL type e.g. conditional branches; it only + makes sense to create a local for those with actual types. */ + if (insn->type ()) + { + std::stringstream ss; + // FIXME: perhaps make these identifiers look less weird? + // that said, the short_print vfunc conveys more information + ss << "("; + insn->short_print (ss); + ss << ")"; + + m_locals_for_insns[insn] = + m_func.new_local (insn->type_gccjit (), + ss.str ().c_str ()); + + insn->stash_rvalue (m_locals_for_insns[insn]); + } + } + + // constants aren't in the IR, we visit those first + for (std::list::const_iterator iter = m_constants->begin (); + iter != m_constants->end (); ++iter) + if (! isa (*iter)) + visit (*iter); + + /* Prelude: extract params from the "params" array. */ + if (m_with_comments) + initial_block.add_comment ("prelude"); + for (int i = 0; i < m_argument_vec.size (); ++i) + { + gccjit::lvalue extracted_param = + m_func.new_local (any->to_gccjit (), + "extracted_param_" + m_argument_vec[i].first); + m_extracted_params.push_back (extracted_param); + initial_block.add_assignment ( + extracted_param, + m_ctxt.new_array_access ( + params, + m_ctxt.new_rvalue (m_ctxt.get_type (GCC_JIT_TYPE_INT), + i))); + } + initial_block.end_with_jump (m_block_map[*m_blocks->begin ()]); + + // convert all instructions + for (block_iter = m_blocks->begin (); block_iter != m_blocks->end (); ++block_iter) + visit (**block_iter); + + m_ctxt.dump_to_file ("/tmp/loop-dump.c", true); + + m_func.dump_to_dot ("/tmp/loop.dot"); + + return m_ctxt.compile (); +} + +#if 0 +gccjit::lvalue +jit_convert_gcc::as_lvalue (jit_value *jval) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::as_lvalue (jit_value *jval): " << *jval << "\n"; + + if (!jval->has_lvalue ()) + { + gccjit::lvalue local = + m_func.new_local (/* FIXME: */ + m_ctxt.get_type (GCC_JIT_TYPE_INT), + jval->print_string ().c_str ()); + jval->stash_lvalue (local); + } + + return jval->as_lvalue (); +} +#endif + +gccjit::rvalue +jit_convert_gcc::as_rvalue (jit_value *jval) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::as_rvalue (jit_value *jval): " << *jval << "\n"; + //std::map m_rvalues; + + if (jit_instruction *insn = dynamic_cast (jval)) + { + return m_locals_for_insns[insn]; + } + + // return gcc_jit_lvalue_as_rvalue (as_lvalue (jval)); + return jval->as_rvalue (); +} + +void +jit_convert_gcc::add_assignment_for_insn (jit_instruction &insn, + gccjit::rvalue val) +{ + //insn.stash_rvalue (val); + if (insn.type ()) + get_current_block ().add_assignment (m_locals_for_insns[&insn], + val); + //TODO: use location of insn +} + +gccjit::block +jit_convert_gcc::get_block_for_edge (jit_block *src, jit_block *dest) +{ + return m_blocks_for_edges[std::make_pair (src, dest)]; +} + +gccjit::block +jit_convert_gcc::get_current_block () +{ + assert (!m_block_stack.empty ()); + return m_block_stack.top (); +} + +void +jit_convert_gcc::push_block (gccjit::block block) +{ + m_block_stack.push (block); +} + +void +jit_convert_gcc::pop_block () +{ + m_block_stack.pop (); +} + + //std::vector m_current_block_stack; + + +void +jit_convert_gcc::visit (jit_const_string& cs) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_const_string& cs): " << cs << "\n"; + gccjit::rvalue rvalue = m_ctxt.new_rvalue (cs.value ()); + cs.stash_rvalue (rvalue); +} + +void +jit_convert_gcc::visit (jit_const_bool& cb) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_const_bool& cb): " << cb << "\n"; + gccjit::rvalue rvalue = m_ctxt.new_rvalue (cb.type_gccjit (), + cb.value ()); + cb.stash_rvalue (rvalue); +} + +void +jit_convert_gcc::visit (jit_const_scalar& cs) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_const_scalar& cs): " << cs << "\n"; + gccjit::rvalue rvalue = m_ctxt.new_rvalue (cs.type_gccjit (), + cs.value ()); + cs.stash_rvalue (rvalue); +} + +void +jit_convert_gcc::visit (jit_const_complex& cc) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_const_complex& cc): " << cc << "\n"; +#if 0 + gccjit::rvalue rvalue = m_ctxt.new_rvalue (cc.type_gccjit (), + cc.value ()); + cc.stash_rvalue (rvalue); +#else + throw jit_fail_exception ("TODO"); +#endif +} + +void jit_convert_gcc::visit (jit_const_index& ci) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_const_index& ci): " << ci << "\n"; + gccjit::rvalue rvalue = m_ctxt.new_rvalue (ci.type_gccjit (), + ci.value ()); + ci.stash_rvalue (rvalue); +} + +void +jit_convert_gcc::visit (jit_const_range& cr) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_const_range& cr): " << cr << "\n"; +#if 0 + gccjit::rvalue rvalue = m_ctxt.new_rvalue (cr.type_gccjit (), + cr.value ()); + cr.stash_rvalue (rvalue); +#else + throw jit_fail_exception ("TODO"); +#endif +} + +void +jit_convert_gcc::visit (jit_block& b) +{ + /* First, handle the phi nodes by populating the edge-handling blocks + with assignments that set up the phi nodes's lvalues based on the incoming + edge, then jumping to the "real" block for this block. */ + for (jit_block::iterator insn_iter = b.begin (); + insn_iter != b.end (); + ++insn_iter) + if (jit_phi *phi = dynamic_cast (*insn_iter)) + { + for (size_t i = 0; i < phi->argument_count (); ++i) + { + jit_block *pred = phi->incomming (i); + push_block (m_blocks_for_edges[std::make_pair(pred, &b)]); + if (m_with_comments) + { + std::ostringstream os; + os << "incoming edge " << i << " for: "; + phi->print (os); + get_current_block ().add_comment (os.str ()); + } + get_current_block ().add_assignment ( + m_locals_for_insns[phi], + m_locals_for_insns[static_cast (phi->argument (i))]); + pop_block (); + } + } + + /* Terminate the blocks for edges leading to this block. */ + jit_use *use; + for (use = b.first_use (); use; use = use->next ()) + { + jit_block *pred = use->user_parent (); + gccjit::block block_for_edge = + m_blocks_for_edges[std::make_pair(pred, &b)]; + block_for_edge.end_with_jump (m_block_map[&b]); + } + + /* Now generate a block for all of the non-phi instructions. */ + push_block (m_block_map[&b]); + for (jit_block::iterator insn_iter = b.begin (); + insn_iter != b.end (); + ++insn_iter) + { + // We've handled phi nodes above; skip them: + if (dynamic_cast (*insn_iter)) + continue; + + if (m_with_comments) + get_current_block ().add_comment (std::string ("jit_instruction: ") + + (*insn_iter)->print_string ()); + visit (*insn_iter); + } + pop_block (); +} + +void +jit_convert_gcc::visit (jit_branch& b) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_branch& b): " << b << "\n"; + + jit_block *this_block = b.parent (); + jit_block *next = b.successor (); + + get_current_block ().end_with_jump (get_block_for_edge (this_block, next)); +} + +void +jit_convert_gcc::visit (jit_cond_branch& cb) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_cond_branch& cb): " << cb << "\n"; + + gccjit::rvalue boolval = as_rvalue (cb.cond ()); + + jit_block *this_block = cb.parent (); + get_current_block ().end_with_conditional ( + boolval, + get_block_for_edge (this_block, cb.successor (0)), + get_block_for_edge (this_block, cb.successor (1))); +} + +void +jit_convert_gcc::visit (jit_call& call) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_call& call): " << call << "\n"; + + const jit_function& ol = call.overload (); + + std::vector args (call.arguments ().size ()); + for (size_t i = 0; i < args.size (); ++i) + args[i] = call.argument (i); + + gccjit::rvalue ret = ol.call (m_ctxt, get_current_block (), args); + add_assignment_for_insn (call, ret); +} + +void +jit_convert_gcc::visit (jit_extract_argument& extract) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_extract_argument& extract): " << extract << "\n"; + + const std::string& argname = extract.name (); + + // Get the stored (octave_base_value *) + gccjit::lvalue param = m_extracted_params[m_argument_index[argname]]; + + // Convert it to a more specialized type: + const jit_function& ol = extract.overload (); + std::vector args (1, param); + gccjit::rvalue extracted = ol.call (m_ctxt, get_current_block (), args); + add_assignment_for_insn (extract, + extracted); +} + +void +jit_convert_gcc::visit (jit_store_argument& store) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_store_argument& store): " << store << "\n"; + + const jit_function& ol = store.overload (); + + std::vector args (1); + args[0] = store.result (); + + gccjit::rvalue ret = ol.call (m_ctxt, get_current_block (), args); + int index = m_argument_index[store.name ()]; + get_current_block ().add_assignment ( + // "params[index] = ..." + m_ctxt.new_array_access ( + params, + m_ctxt.new_rvalue (m_ctxt.get_type (GCC_JIT_TYPE_INT), + index)), + // "... = ret;" + ret); +} + +void +jit_convert_gcc::visit (jit_return& ret) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_return& ret): " << ret << "\n"; + jit_value *res = ret.result (); + + if (converting_function) + throw jit_fail_exception ("TODO"); + else + { + if (res) + get_current_block ().end_with_return (res->as_rvalue ()); + else + get_current_block ().end_with_return (); + } +} + +void +jit_convert_gcc::visit (jit_phi& phi) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_phi& phi): " << phi << "\n"; + throw jit_fail_exception ("TODO"); +} + +void +jit_convert_gcc::visit (jit_variable& var) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_variable&): " << var << "\n"; + throw jit_fail_exception ("TODO"); +} + +void +jit_convert_gcc::visit (jit_error_check& check) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_error_check& check): " << check << "\n"; + + gccjit::rvalue cond; + + switch (check.check_variable ()) + { + case jit_error_check::var_error_state: + cond = jit_typeinfo::insert_error_check (m_func); + break; + case jit_error_check::var_interrupt: + cond = jit_typeinfo::insert_interrupt_check (m_func); + break; + default: + panic_impossible (); + } + + jit_block *this_block = check.parent (); + get_current_block ().end_with_conditional ( + cond, + get_block_for_edge (this_block, check.successor (0)), + get_block_for_edge (this_block, check.successor (1))); +} + +void +jit_convert_gcc::visit (jit_assign& assign) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_assign& assign): " << assign << "\n"; + + jit_value *jsrc = assign.src (); + + add_assignment_for_insn (assign, + as_rvalue (jsrc)); +} + +void +jit_convert_gcc::visit (jit_argument& arg) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_argument&): " << arg << "\n"; + throw jit_fail_exception ("TODO"); +} + +void +jit_convert_gcc::visit (jit_magic_end& me) +{ + if (m_log_visits) + std::cerr << "jit_convert_gcc::visit (jit_magic_end& me): " << me << "\n"; + throw jit_fail_exception ("TODO"); +} +#endif /* HAVE_GCCJIT */ + // -------------------- jit_convert_llvm -------------------- + +#ifdef HAVE_LLVM + llvm::Function * jit_convert_llvm::convert_loop (llvm::Module *module, const jit_block_list& blocks, @@ -1361,7 +1922,11 @@ jit_return *ret = dynamic_cast (final_block->back ()); assert (ret); - creating = jit_function (module, jit_convention::internal, + creating = jit_function (module, +#ifdef HAVE_GCCJIT + gccjit::context (), // FIXME? +#endif + jit_convention::internal, "foobar", ret->result_type (), args); function = creating.to_llvm (); @@ -1649,6 +2214,8 @@ me.stash_llvm (ret); } +#endif /* ifdef HAVE_LLVM */ + // -------------------- jit_infer -------------------- jit_infer::jit_infer (jit_factory& afactory, jit_block_list& ablocks, const variable_map& avmap) @@ -2052,6 +2619,7 @@ bool tree_jit::initialize (void) { +#ifdef HAVE_LLVM if (engine) return true; @@ -2089,8 +2657,13 @@ pass_manager->add (llvm::createGVNPass ()); pass_manager->add (llvm::createCFGSimplificationPass ()); pass_manager->doInitialization (); - - jit_typeinfo::initialize (module, engine); +#endif /* ifdef HAVE_LLVM */ + + jit_typeinfo::initialize ( +#ifdef HAVE_LLVM + module, engine +#endif +); return true; } @@ -2177,7 +2750,7 @@ return 0; } - +#ifdef HAVE_LLVM void tree_jit::optimize (llvm::Function *fn) { @@ -2200,8 +2773,10 @@ llvm::WriteBitcodeToFile (module, fout); } } +#endif /* ifdef HAVE_LLVM */ // -------------------- jit_function_info -------------------- +#ifdef HAVE_JIT jit_function_info::jit_function_info (tree_jit& tjit, octave_user_function& fcn, const octave_value_list& ov_args) @@ -2217,6 +2792,21 @@ try { jit_convert conv (fcn, argument_types); + + if (Vdebug_jit) + { + jit_block_list& blocks = conv.get_blocks (); + blocks.label (); + std::cout << "-------------------- Compiling function "; + std::cout << "--------------------\n"; + + tree_print_code tpc (std::cout); + tpc.visit_octave_user_function_header (fcn); + tpc.visit_statement_list (*fcn.body ()); + tpc.visit_octave_user_function_trailer (fcn); + blocks.print (std::cout, "octave jit ir"); + } + jit_infer infer (conv.get_factory (), conv.get_blocks (), conv.get_variable_map ()); infer.infer (); @@ -2253,7 +2843,11 @@ std::string wrapper_name = fcn.name () + "_wrapper"; jit_type *any_t = jit_typeinfo::get_any (); std::vector wrapper_args (1, jit_typeinfo::get_any_ptr ()); - wrapper = jit_function (module, jit_convention::internal, wrapper_name, + wrapper = jit_function (module, +#ifdef HAVE_GCCJIT + gccjit::context (), // FIXME +#endif + jit_convention::internal, wrapper_name, any_t, wrapper_args); llvm::BasicBlock *wrapper_body = wrapper.new_block (); @@ -2318,6 +2912,7 @@ raw_fn.erase (); } } +#endif /* ifdef HAVE_JIT */ bool jit_function_info::execute (const octave_value_list& ov_args, @@ -2363,14 +2958,21 @@ } // -------------------- jit_info -------------------- +#ifdef HAVE_LLVM jit_info::jit_info (tree_jit& tjit, tree& tee) : engine (tjit.get_engine ()), function (0), llvm_function (0) +#ifdef HAVE_GCCJIT + , gccjit_result (NULL) +#endif { compile (tjit, tee); } jit_info::jit_info (tree_jit& tjit, tree& tee, const octave_value& for_bounds) : engine (tjit.get_engine ()), function (0), llvm_function (0) +#ifdef HAVE_GCCJIT + , gccjit_result (NULL) +#endif { compile (tjit, tee, jit_typeinfo::type_of (for_bounds)); } @@ -2379,7 +2981,12 @@ { if (llvm_function) llvm_function->eraseFromParent (); +#ifdef HAVE_GCCJIT + if (gccjit_result) + gcc_jit_result_release (gccjit_result); +#endif } +#endif bool jit_info::execute (const vmap& extra_vars) const @@ -2435,12 +3042,23 @@ return true; } +#ifdef HAVE_LLVM void jit_info::compile (tree_jit& tjit, tree& tee, jit_type *for_bounds) { try { jit_convert conv (tee, for_bounds); + + if (Vdebug_jit) + { + jit_block_list& blocks = conv.get_blocks (); + blocks.label (); + std::cout << "-------------------- Compiling tree --------------------\n"; + std::cout << tee.str_print_code () << std::endl; + blocks.print (std::cout, "octave jit ir"); + } + jit_infer infer (conv.get_factory (), conv.get_blocks (), conv.get_variable_map ()); @@ -2456,6 +3074,13 @@ } jit_factory& factory = conv.get_factory (); + +#ifdef HAVE_GCCJIT + jit_convert_gcc to_gcc; + gccjit_result = to_gcc.convert_loop (infer.get_blocks (), + factory.constants ()); +#endif + jit_convert_llvm to_llvm; llvm_function = to_llvm.convert_loop (tjit.get_module (), infer.get_blocks (), @@ -2474,7 +3099,19 @@ Vjit_failcnt++; } - +#ifdef HAVE_GCCJIT + if (gccjit_result) + { + if (Vdebug_jit) + { + std::cout << "-------------------- gccjit --------------------"; + void *void_fn = gcc_jit_result_get_code (gccjit_result, "loop"); + function = reinterpret_cast (void_fn); + } + printf ("using gccjit function\n"); + } + else +#endif if (llvm_function) { if (Vdebug_jit) @@ -2497,6 +3134,7 @@ function = reinterpret_cast (void_fn); } } +#endif octave_value jit_info::find (const vmap& extra_vars, const std::string& vname) const @@ -2506,16 +3144,16 @@ : *iter->second; } -#endif - -#if defined (HAVE_LLVM) -#define UNUSED_WITHOUT_LLVM(x) x +#endif /* if HAVE_JIT */ + +#if defined (HAVE_JIT) +#define UNUSED_WITHOUT_JIT(x) x #else -#define UNUSED_WITHOUT_LLVM(x) x GCC_ATTR_UNUSED +#define UNUSED_WITHOUT_JIT(x) x GCC_ATTR_UNUSED #endif -DEFUN (jit_failcnt, UNUSED_WITHOUT_LLVM (args), - UNUSED_WITHOUT_LLVM (nargout), +DEFUN (jit_failcnt, UNUSED_WITHOUT_JIT (args), + UNUSED_WITHOUT_JIT (nargout), "-*- texinfo -*-\n\ @deftypefn {Built-in Function} {@var{val} =} jit_failcnt ()\n\ @deftypefnx {Built-in Function} {@var{old_val} =} jit_failcnt (@var{new_val})\n\ @@ -2529,7 +3167,7 @@ @seealso{jit_enable, jit_startcnt, debug_jit}\n\ @end deftypefn") { -#if defined (HAVE_LLVM) +#if defined (HAVE_JIT) return SET_INTERNAL_VARIABLE (jit_failcnt); #else warning ("jit_failcnt: JIT compiling not available in this version of Octave"); @@ -2537,8 +3175,8 @@ #endif } -DEFUN (debug_jit, UNUSED_WITHOUT_LLVM (args), - UNUSED_WITHOUT_LLVM (nargout), +DEFUN (debug_jit, UNUSED_WITHOUT_JIT (args), + UNUSED_WITHOUT_JIT (nargout), "-*- texinfo -*-\n\ @deftypefn {Built-in Function} {@var{val} =} debug_jit ()\n\ @deftypefnx {Built-in Function} {@var{old_val} =} debug_jit (@var{new_val})\n\ @@ -2552,7 +3190,7 @@ @seealso{jit_enable, jit_startcnt}\n\ @end deftypefn") { -#if defined (HAVE_LLVM) +#if defined (HAVE_JIT) return SET_INTERNAL_VARIABLE (debug_jit); #else warning ("debug_jit: JIT compiling not available in this version of Octave"); @@ -2560,8 +3198,8 @@ #endif } -DEFUN (jit_enable, UNUSED_WITHOUT_LLVM (args), - UNUSED_WITHOUT_LLVM (nargout), +DEFUN (jit_enable, UNUSED_WITHOUT_JIT (args), + UNUSED_WITHOUT_JIT (nargout), "-*- texinfo -*-\n\ @deftypefn {Built-in Function} {@var{val} =} jit_enable ()\n\ @deftypefnx {Built-in Function} {@var{old_val} =} jit_enable (@var{new_val})\n\ @@ -2574,7 +3212,7 @@ @seealso{jit_startcnt, debug_jit}\n\ @end deftypefn") { -#if defined (HAVE_LLVM) +#if defined (HAVE_JIT) return SET_INTERNAL_VARIABLE (jit_enable); #else warning ("jit_enable: JIT compiling not available in this version of Octave"); @@ -2582,8 +3220,8 @@ #endif } -DEFUN (jit_startcnt, UNUSED_WITHOUT_LLVM (args), - UNUSED_WITHOUT_LLVM (nargout), +DEFUN (jit_startcnt, UNUSED_WITHOUT_JIT (args), + UNUSED_WITHOUT_JIT (nargout), "-*- texinfo -*-\n\ @deftypefn {Built-in Function} {@var{val} =} jit_startcnt ()\n\ @deftypefnx {Built-in Function} {@var{old_val} =} jit_startcnt (@var{new_val})\n\ @@ -2601,7 +3239,7 @@ @seealso{jit_enable, jit_failcnt, debug_jit}\n\ @end deftypefn") { -#if defined (HAVE_LLVM) +#if defined (HAVE_JIT) return SET_INTERNAL_VARIABLE_WITH_LIMITS (jit_startcnt, 1, std::numeric_limits::max ()); #else diff --git a/libinterp/corefcn/pt-jit.h b/libinterp/corefcn/pt-jit.h --- a/libinterp/corefcn/pt-jit.h +++ b/libinterp/corefcn/pt-jit.h @@ -25,12 +25,13 @@ #if !defined (octave_pt_jit_h) #define octave_pt_jit_h 1 -#ifdef HAVE_LLVM +#ifdef HAVE_JIT #include "jit-ir.h" #include "pt-walk.h" #include "symtab.h" + class octave_value_list; // Convert from the parse tree (AST) to the low level Octave IR. @@ -69,7 +70,9 @@ jit_factory& get_factory (void) { return factory; } +#ifdef HAVE_LLVM llvm::Function *get_function (void) const { return function; } +#endif const variable_map &get_variable_map (void) const { return vmap; } @@ -180,7 +183,9 @@ jit_block *block; +#ifdef HAVE_LLVM llvm::Function *function; +#endif jit_block_list blocks; @@ -242,7 +247,81 @@ void finish_breaks (jit_block *dest, const block_list& lst); }; +// Convert from the low level Octave IR to libgccjit +#ifdef HAVE_GCCJIT +class +jit_convert_gcc : public jit_ir_walker +{ +public: + jit_convert_gcc (); + ~jit_convert_gcc (); + + gcc_jit_result *convert_loop (const jit_block_list& blocks, + const std::list& constants); + +#define JIT_METH(clname) \ + virtual void visit (jit_ ## clname&); + + JIT_VISIT_IR_CLASSES; + +private: + void visit (jit_value *jvalue) + { + return visit (*jvalue); + } + + void visit (jit_value &jvalue) + { + jvalue.accept (*this); + } + +#if 0 + gccjit::lvalue as_lvalue (jit_value *); +#endif + gccjit::rvalue as_rvalue (jit_value *); + + void add_assignment_for_insn (jit_instruction &insn, + gccjit::rvalue val); + + gccjit::block get_block_for_edge (jit_block *src, jit_block *dest); + + gccjit::block get_current_block (); + void push_block (gccjit::block block); + void pop_block (); + + bool converting_function; + + // Inputs (used internally by callback): + const jit_block_list *m_blocks; + const std::list *m_constants; + + // Internal state: + gccjit::context m_ctxt; + gccjit::function m_func; + + /* We have a single param, confusingly named "params" (an array). */ + gccjit::param params; + + // name -> argument index (used for compiling functions) + std::map m_argument_index; + std::vector > m_argument_vec; + + std::vector m_extracted_params; + std::map m_block_map; + std::map, gccjit::block> m_blocks_for_edges; + std::map m_locals_for_insns; + + std::map m_rvalues; + + std::stack m_block_stack; + + bool m_with_comments; + bool m_log_visits; +}; +#endif // #ifdef HAVE_GCCJIT + // Convert from the low level Octave IR to LLVM +#if HAVE_LLVM class jit_convert_llvm : public jit_ir_walker { @@ -299,6 +378,7 @@ jvalue.accept (*this); } }; +#endif /* HAVE_LLVM */ // type inference and SSA construction on the low level Octave IR class @@ -362,11 +442,13 @@ static bool execute (octave_user_function& fcn, const octave_value_list& args, octave_value_list& retval); +#ifdef HAVE_LLVM llvm::ExecutionEngine *get_engine (void) const { return engine; } llvm::Module *get_module (void) const { return module; } void optimize (llvm::Function *fn); +#endif private: tree_jit (void); @@ -385,6 +467,7 @@ size_t trip_count (const octave_value& bounds) const; +#ifdef HAVE_LLVM llvm::Module *module; #ifdef LEGACY_PASSMANAGER llvm::legacy::PassManager *module_pass_manager; @@ -394,6 +477,7 @@ llvm::FunctionPassManager *pass_manager; #endif llvm::ExecutionEngine *engine; +#endif }; class @@ -439,13 +523,19 @@ octave_value find (const vmap& extra_vars, const std::string& vname) const; +#ifdef HAVE_LLVM llvm::ExecutionEngine *engine; +#endif jited_function function; +#ifdef HAVE_LLVM llvm::Function *llvm_function; - +#endif +#ifdef HAVE_GCCJIT + gcc_jit_result *gccjit_result; +#endif std::vector > arguments; type_bound_vector bounds; }; +#endif /* ifdef HAVE_JIT */ #endif -#endif diff --git a/libinterp/link-deps.mk b/libinterp/link-deps.mk --- a/libinterp/link-deps.mk +++ b/libinterp/link-deps.mk @@ -13,6 +13,7 @@ $(X11_LIBS) \ $(CARBON_LIBS) \ $(GL2PS_LIBS) \ + $(LIBGCCJIT_LIBS) \ $(LLVM_LIBS) \ $(JAVA_LIBS) \ $(LAPACK_LIBS) @@ -22,6 +23,7 @@ $(HDF5_LDFLAGS) \ $(Z_LDFLAGS) \ $(FFTW_XLDFLAGS) \ + $(LIBGCCJIT_LDFLAGS) \ $(LLVM_LDFLAGS) OCT_LINK_DEPS = diff --git a/libinterp/module.mk b/libinterp/module.mk --- a/libinterp/module.mk +++ b/libinterp/module.mk @@ -20,6 +20,7 @@ -Ilibinterp/parse-tree -I$(srcdir)/libinterp/parse-tree \ -Ilibinterp/corefcn -I$(srcdir)/libinterp/corefcn \ -I$(top_builddir)/libgnu -I$(top_srcdir)/libgnu \ + $(LIBGCCJIT_CPPFLAGS) \ $(HDF5_CPPFLAGS) libinterp_liboctinterp_la_CFLAGS = $(AM_CFLAGS) $(WARN_CFLAGS) @@ -223,7 +224,7 @@ %.df: %.cc $(GENERATED_MAKE_BUILTINS_INCS) $(AM_V_GEN)rm -f $@-t $@-t1 $@ && \ $(CXXCPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(libinterp_liboctinterp_la_CPPFLAGS) $(LLVM_CPPFLAGS) $(CPPFLAGS) \ + $(libinterp_liboctinterp_la_CPPFLAGS) $(LLVM_CPPFLAGS) $(LIBGCCJIT_CPPFLAGS) $(CPPFLAGS) \ $(libinterp_liboctinterp_la_CXXFLAGS) $(CXXFLAGS) \ -DMAKE_BUILTINS $< > $@-t1 && \ $(srcdir)/libinterp/mkdefs $(srcdir)/libinterp $< < $@-t1 > $@-t && \ diff --git a/libinterp/oct-conf.in.h b/libinterp/oct-conf.in.h --- a/libinterp/oct-conf.in.h +++ b/libinterp/oct-conf.in.h @@ -410,6 +410,18 @@ #define OCTAVE_CONF_MAGICK_LIBS %OCTAVE_CONF_MAGICK_LIBS% #endif +#ifndef OCTAVE_CONF_LIBGCCJIT_CPPFLAGS +#define OCTAVE_CONF_LIBGCCJIT_CPPFLAGS %OCTAVE_CONF_LIBGCCJIT_CPPFLAGS% +#endif + +#ifndef OCTAVE_CONF_LIBGCCJIT_LDFLAGS +#define OCTAVE_CONF_LIBGCCJIT_LDFLAGS %OCTAVE_CONF_LIBGCCJIT_LDFLAGS% +#endif + +#ifndef OCTAVE_CONF_LIBGCCJIT_LIBS +#define OCTAVE_CONF_LIBGCCJIT_LIBS %OCTAVE_CONF_LIBGCCJIT_LIBS% +#endif + #ifndef OCTAVE_CONF_LLVM_CPPFLAGS #define OCTAVE_CONF_LLVM_CPPFLAGS %OCTAVE_CONF_LLVM_CPPFLAGS% #endif diff --git a/libinterp/octave-value/ov-usr-fcn.h b/libinterp/octave-value/ov-usr-fcn.h --- a/libinterp/octave-value/ov-usr-fcn.h +++ b/libinterp/octave-value/ov-usr-fcn.h @@ -44,7 +44,7 @@ class tree_expression; class tree_walker; -#ifdef HAVE_LLVM +#ifdef HAVE_JIT class jit_function_info; #endif @@ -403,11 +403,11 @@ return false; } -#ifdef HAVE_LLVM + #ifdef HAVE_JIT jit_function_info *get_info (void) { return jit_info; } void stash_info (jit_function_info *info) { jit_info = info; } -#endif + #endif #if 0 void print_symtab_info (std::ostream& os) const; @@ -498,9 +498,9 @@ // pointer to the current unwind_protect frame of this function. unwind_protect *curr_unwind_protect_frame; -#ifdef HAVE_LLVM + //#ifdef HAVE_LLVM jit_function_info *jit_info; -#endif + //#endif void maybe_relocate_end_internal (void); diff --git a/libinterp/parse-tree/pt-eval.cc b/libinterp/parse-tree/pt-eval.cc --- a/libinterp/parse-tree/pt-eval.cc +++ b/libinterp/parse-tree/pt-eval.cc @@ -298,7 +298,7 @@ octave_value rhs = expr->rvalue1 (); -#if HAVE_LLVM +#if HAVE_JIT if (tree_jit::execute (cmd, rhs)) return; #endif @@ -1028,7 +1028,7 @@ void tree_evaluator::visit_while_command (tree_while_command& cmd) { -#if HAVE_LLVM +#if HAVE_JIT if (tree_jit::execute (cmd)) return; #endif diff --git a/libinterp/parse-tree/pt-loop.cc b/libinterp/parse-tree/pt-loop.cc --- a/libinterp/parse-tree/pt-loop.cc +++ b/libinterp/parse-tree/pt-loop.cc @@ -50,7 +50,7 @@ delete list; delete lead_comm; delete trail_comm; -#ifdef HAVE_LLVM +#ifdef HAVE_JIT delete compiled; #endif } @@ -101,7 +101,7 @@ delete list; delete lead_comm; delete trail_comm; -#ifdef HAVE_LLVM +#ifdef HAVE_JIT delete compiled; #endif } diff --git a/libinterp/parse-tree/pt-loop.h b/libinterp/parse-tree/pt-loop.h --- a/libinterp/parse-tree/pt-loop.h +++ b/libinterp/parse-tree/pt-loop.h @@ -48,7 +48,7 @@ tree_while_command (int l = -1, int c = -1) : tree_command (l, c), expr (0), list (0), lead_comm (0), trail_comm (0) -#ifdef HAVE_LLVM +#ifdef HAVE_JIT , compiled (0) #endif { } @@ -59,7 +59,7 @@ int l = -1, int c = -1) : tree_command (l, c), expr (e), list (0), lead_comm (lc), trail_comm (tc) -#ifdef HAVE_LLVM +#ifdef HAVE_JIT , compiled (0) #endif { } @@ -70,7 +70,7 @@ int l = -1, int c = -1) : tree_command (l, c), expr (e), list (lst), lead_comm (lc), trail_comm (tc) -#ifdef HAVE_LLVM +#ifdef HAVE_JIT , compiled (0) #endif { } @@ -90,7 +90,7 @@ void accept (tree_walker& tw); -#ifdef HAVE_LLVM +#ifdef HAVE_JIT // some functions use by tree_jit jit_info *get_info (void) const { @@ -119,7 +119,7 @@ private: -#ifdef HAVE_LLVM +#ifdef HAVE_JIT // compiled version of the loop jit_info *compiled; #endif @@ -179,7 +179,7 @@ tree_simple_for_command (int l = -1, int c = -1) : tree_command (l, c), parallel (false), lhs (0), expr (0), maxproc (0), list (0), lead_comm (0), trail_comm (0) -#ifdef HAVE_LLVM +#ifdef HAVE_JIT , compiled (0) #endif { } @@ -194,7 +194,7 @@ : tree_command (l, c), parallel (parallel_arg), lhs (le), expr (re), maxproc (maxproc_arg), list (lst), lead_comm (lc), trail_comm (tc) -#ifdef HAVE_LLVM +#ifdef HAVE_JIT , compiled (0) #endif { } @@ -220,7 +220,7 @@ void accept (tree_walker& tw); -#ifdef HAVE_LLVM +#ifdef HAVE_JIT // some functions use by tree_jit jit_info *get_info (void) const { diff --git a/run-octave.in b/run-octave.in --- a/run-octave.in +++ b/run-octave.in @@ -78,7 +78,11 @@ elif [ "x$1" = "x-strace" ]; then driver="strace -o octave.trace" shift - elif [ "x$1" = "x-cli" ]; then + fi +fi + +if [ $# -gt 0 ]; then + if [ "x$1" = "x-cli" ]; then octave_executable="$builddir/src/octave-cli" shift fi diff --git a/test/jit.tst b/test/jit.tst --- a/test/jit.tst +++ b/test/jit.tst @@ -19,14 +19,14 @@ ## Author: Max Brister ## Turn on JIT and set defaults before running tests -%!testif HAVE_LLVM +%!testif HAVE_JIT %! global __old_jit_enable__; %! global __old_jit_startcnt__; %! __old_jit_enable__ = jit_enable (true); %! __old_jit_startcnt__ = jit_startcnt (1000); ## Test some simple cases that compile. -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! for i=1:1e6 %! if (i < 5) @@ -38,7 +38,7 @@ %! assert (i, 1); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! while (1) %! if (1) @@ -49,14 +49,14 @@ %! endwhile %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! do %! break; %! until (0) %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! do %! if (1) @@ -65,7 +65,7 @@ %! until (0) %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! i=1; %! do @@ -75,7 +75,7 @@ %! assert (i, 1); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! for i=1:1e6 %! if (i == 100) @@ -86,7 +86,7 @@ %! assert (jit_failcnt, 0); ## Also test parfor keyword -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! parfor i=1:1e6 %! if (i == 100) @@ -96,7 +96,7 @@ %! assert (i, 100); %! assert (jit_failcnt, 0); ## Test some switch statements -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! do %! switch (1) @@ -104,7 +104,7 @@ %! until(1) %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! do %! switch (1) @@ -114,7 +114,7 @@ %! until(1) %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! do %! switch (1) @@ -124,7 +124,7 @@ %! until(1) %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! do %! switch (1) @@ -136,7 +136,7 @@ %! until(1) %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! i=0; %! a=0; @@ -162,7 +162,7 @@ %! assert (jit_failcnt, 0); ## Some more complex calculations -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! inc = 1e-5; %! result = 0; @@ -172,7 +172,7 @@ %! assert (abs (result - 1/9) < 1e-5); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! inc = 1e-5; %! result = 0; @@ -183,7 +183,7 @@ %! assert (abs (result - 1/9) < 1e-5); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! temp = 1+1i; %! nan = NaN; @@ -195,7 +195,7 @@ %! assert (imag (temp), 0); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! temp = 1+1i; %! nan = NaN+1i; @@ -208,7 +208,7 @@ %! assert (imag (temp), 0); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! temp = 1+1i; %! while (1) @@ -218,7 +218,7 @@ %! assert (temp, 5+5i); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! nr = 1001; %! mat = zeros (1, nr); @@ -228,7 +228,7 @@ %! assert (mat == 1:nr); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! nr = 1001; %! mat = 1:nr; @@ -240,7 +240,7 @@ %! assert (sum (mat) == total); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! nr = 1001; %! mat = [3 1 5]; @@ -290,13 +290,13 @@ %! endif %!endfunction -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! test_set = gen_test (10000); %! assert (all (vectorized (test_set, 3) == loopy (test_set, 3))); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! niter = 1001; %! i = 0; @@ -306,7 +306,7 @@ %! assert (i == niter); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! niter = 1001; %! result = 0; @@ -317,7 +317,7 @@ %! assert (result == m(end) * niter); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! ndim = 100; %! result = 0; @@ -333,7 +333,7 @@ %! assert (result == sum (sum (m))); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! ndim = 100; %! m = zeros (ndim); @@ -349,7 +349,7 @@ %! assert (all (m == m2)); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! ndim = 2; %! m = zeros (ndim, ndim, ndim, ndim); @@ -385,7 +385,7 @@ %! end_unwind_protect %!endfunction -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! lasterr (""); %! try @@ -394,7 +394,7 @@ %! assert (strcmp (lasterr (), "division by zero")); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! while (1) %! a = 0; @@ -404,7 +404,7 @@ %! assert (result, 0); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! m = zeros (2, 1001); %! for i=1:1001 @@ -417,7 +417,7 @@ %! assert (m, m2); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! m = [1 2 3]; %! for i=1:1001 @@ -427,7 +427,7 @@ %! assert (m == sin ([1 2 3])); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! i = 0; %! while i < 10 @@ -436,7 +436,7 @@ %! assert (i == 10); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! i = 0; %! while i < 10 @@ -446,7 +446,7 @@ %! assert (a == 10); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! i = 0; %! while i < 10 @@ -456,7 +456,7 @@ %! assert (a == 9); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! num = 2; %! a = zeros (1, num); @@ -480,7 +480,7 @@ %! endif; %! endwhile -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! lasterr (""); %! try @@ -496,7 +496,7 @@ %! endwhile %!endfunction -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! assert (test_overload (1), 1); %! assert (test_overload ([1 2]), [1 2]); @@ -518,12 +518,12 @@ %! endwhile %!endfunction -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! assert (bubble (), [1 2 3]); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! a = 0; %! b = 1; @@ -538,7 +538,7 @@ %!xtest %! ## FIXME: No support for functions with complex input prototypes -%! ## testif HAVE_LLVM +%! ## testif HAVE_JIT %! jit_failcnt (0) %! a = [1+1i 1+2i]; %! b = 0; @@ -555,7 +555,7 @@ %! endfor %!endfunction -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! lasterr (""); %! try @@ -569,14 +569,14 @@ %!xtest %! ## FIXME: No support for functions with complex input prototypes -%! ## testif HAVE_LLVM +%! ## testif HAVE_JIT %! jit_failcnt (0) %! assert (id (1), 1); %! assert (id (1+1i), 1+1i); %! assert (id (1, 2), 1); %! assert (jit_failcnt, 0); -%!testif HAVE_LLVM +%!testif HAVE_JIT %! jit_failcnt (0) %! lasterr (""); %! try @@ -586,7 +586,7 @@ %! assert (jit_failcnt, 0); ## Restore JIT settings -%!testif HAVE_LLVM +%!testif HAVE_JIT %! global __old_jit_enable__; %! global __old_jit_startcnt__; %! jit_enable (__old_jit_enable__);