This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.

[PATCH] libgccjit.so: an embeddable JIT-compiler based on GCC

From: David Malcolm <dmalcolm at redhat dot com>

To: gcc-patches at gcc dot gnu dot org

Date: Wed, 02 Oct 2013 21:32:48 -0400

Subject: [PATCH] libgccjit.so: an embeddable JIT-compiler based on GCC

Authentication-results: sourceware.org; auth=none

This is very much a proof-of-concept/work-in-progress at this stage, but attached is a patch to GCC which aims to provide an embeddable JIT-compilation API, using GCC as the backend: libgccjit.so. This shared library can then be dynamically-linked into bytecode interpreters and other such programs that want to generate machine code "on the fly" at run-time. The idea is that GCC is configured with a special --enable-host-shared option, which leads to it being built as position-independent code. You would configure it with host==target, given that the generated machine code will be executed within the same process (the whole point of JIT). libgccjit.so is built against libbackend.a. To the rest of GCC, it looks like a "frontend" (in the "gcc/jit" subdir), but the parsing hook just runs a callback provided by client code. You can see a diagram of how it all fits together within the patch (see gcc/jit/notes.txt). The jit "frontend" requires --enable-host-shared, so it is off by default, so you need to configure with: --enable-host-shared --enable-languages=jit to get the jit (and see caveats below). The "main" function is in the client code. It uses a pure C API to call into libgccjit.so, registering a code creation hook: gcc_jit_context *ctxt; gcc_jit_result *result; ctxt = gcc_jit_context_acquire (); gcc_jit_context_set_code_factory (ctxt, some_code_making_callback, user_data); /* This actually calls into GCC and runs the build, all in a mutex for now, getting make a result object. */ result = gcc_jit_context_compile (ctxt); /* result is actually a wrapper around a DSO */ /* Now that we have result, we're done with ctxt: */ gcc_git_context_release (ctxt); /* Look up a generated function by name, getting a void* back from the result object (pointing to the machine code), and cast it to the appropriate type for the function: */ some_fn_type some_fn = (some_fn_type)gcc_jit_result_get_code (result, "some_fn"); /* We can now call the machine code: */ int val = some_fn (3, 4); /* Presumably we'd call it more than once. Once we're done with the code, this unloads the built DSO: */ gcc_jit_result_release (result); There are some major kludges in there, but it does work: it can successfully build code in-process 1000 times in a row [1], albeit with a slow memory leak, with all optimization turned off. Upon turning on optimizations I run into crashes owing to not properly purging all state within the compiler - so this is a great motivation for doing more state-cleanup work. I've also hacked timevars to run in "cumulative" mode, accumulating all timings across all iterations. The library API hides GCC's internals, and tries to be much more typesafe than GCC's, giving something rather like Andrew MacLeod's proposed changes - client code does not see "tree", instead dealing with types, rvalues, lvalues, jump labels, etc. It is pure C, given the horror stories I have heard about people dealing with C++ ABIs. FWIW I also have the beginnings of Python bindings for the library (doing the interface as pure C makes language-bindings easier), though that would probably live in a separate repository (so not part of this patch). The API deliberately uses C terminology, given that it's likely that the user will want to be plugging the JIT-generated code into a C/C++ program (or library). I've been able to successfully use this API to add JIT-compilation to a toy bytecode interpreter: https://github.com/davidmalcolm/jittest (where regvm.cc uses this API to compile a bytecode function into machine code). There's a DejaGnu-based test suite, which I can invoke via: make check-parallel-jit RUNTESTFLAGS="" (potentially with some -v verbosity options in RUNTESTFLAGS), giving # of expected passes 144 and no failures on this box. Various caveats: * Currently it only supports a small subset of C-like code. * The API is still in flux: I'm not convinced by the label-placement approach; I suspect having an explicit "block" type may be easier for users to deal with. * The patch is against r202664, which is a little out-of-date (2013-09-17), but I'm interested in feedback rather than perfection at this stage. * I'm running into configure/Makefile issues with --enable-host-shared, where CFLAGS contains -fPIC, but only on invocations of leaf Makefiles, not on recursive "make" - so it works if you cd into $builddir/gcc and make (and so on for libcpp etc), but not from the top-level builddir. Hence building the thing is currently unreliable (but again, I'm interested in feedback rather than perfection). Help with configure/Makefiles would be appreciated! * There are some grotesque kludges in internal-api.c, especially in how we go from .s assembler files to a DSO (grep for "gross hack" ;) ) * There are some changes to the rest of GCC that are needed by the JIT code. Some of this is state removal. Some of the changes are gross, some are probably reasonable. * Only tested so far on Fedora and RHEL x86_64 boxes. Hopefully this is of interest to other GCC people. Shall I get this into a "jit" branch? I greatly prefer git to svn, so I'd probably do: http://gcc.gnu.org/wiki/GitMirror#Git-only_branches assuming that this allows a sane path to (I hope) eventual merger. Thoughts? Dave Current Changelog.jit follows inline: / * configure.ac: Add --enable-host-shared * configure: Regenerate. gcc/ * Makefile.in (LIBIBERTY): Use pic build of libiberty.a if configured with --enable-host-shared. (BUILD_LIBIBERTY): Likewise. * cgraph.c (cgraph_c_finalize): New. * cgraph.h (symtab_c_finalize): New declaration. (cgraph_c_finalize): Likewise. (cgraphunit_c_finalize): Likewise. (cgraphbuild_c_finalize): Likewise. (ipa_c_finalize): Likewise. (predict_c_finalize): Likewise. (varpool_c_finalize): Likewise. * cgraphbuild.c (cgraphbuild_c_finalize): New. * cgraphunit.c (first_analyzed): Move from analyze_functions to file-scope. (first_analyzed_var): Likewise. (analyze_functions): Move static variables into file-scope. (cgraphunit_c_finalize): New. * configure.ac: Add --enable-host-shared, adding -fPIC. * configure: Regenerate. * dwarf2out.c (dwarf2out_c_finalize): New. * dwarf2out.h (dwarf2out_c_finalize): Declare. * ggc-page.c (init_ggc): Make idempotent. * ipa-pure-const.c (function_insertion_hook_holder): Move to be a field of class pass_ipa_pure_const. (node_duplication_hook_holder): Likewise. (node_removal_hook_holder): Likewise. (register_hooks): Convert to method... (pass_ipa_pure_const::register_hooks): ...here, converting static variable init_p into... (pass_ipa_pure_const::init_p): ...new field. (pure_const_generate_summary): Update invocation of register_hooks to invoke as a method of current_pass. (pure_const_read_summary): Likewise. (propagate): Convert to... (pass_ipa_pure_const::execute): ...method. * ipa.c (ipa_c_finalize): New. * main.c (main): Update usage of toplev_main. * params.c (global_init_params): Make idempotent. * passes.c (execute_ipa_summary_passes): Set current_pass. * predict.c (predict_c_finalize): New. * stringpool.c (init_stringpool): Clean up if we're called more than once. * symtab.c (symtab_c_finalize): New. * timevar.c (timevar_init): Ignore repeated calls. * timevar.def (TV_CLIENT_CALLBACK): Add. (TV_ASSEMBLE): Add. (TV_LINK): Add. (TV_LOAD): Add. * toplev.c (do_compile) Add parameter (const toplev_options *); use it to avoid starting/stopping/reporting timevar TV_TOTAL for the case where toplev_main does not emcompass all timevars. (toplev_main): Add parameter (const toplev_options *); pass it to do_compile. (toplev_finalize): New. * toplev.h (struct toplev_options): New. (toplev_main): Add parameter (const toplev_options *). (toplev_finalize): New. * varpool.c (varpool_c_finalize): New. gcc/jit/ * Make-lang.in: New. * TODO.rst: New. * config-lang.in: New. * dummy-frontend.c: New. * internal-api.c: New. * internal-api.h: New. * libgccjit.c: New. * libgccjit.h: New. * libgccjit.map: New. * notes.txt: New. gcc/testsuite/ * jit.dg: New subdirectory * jit.dg/harness.h: New. * jit.dg/jit.exp: New. * jit.dg/test-accessing-struct.c: New. * jit.dg/test-calling-external-function.c: New. * jit.dg/test-dot-product.c: New. * jit.dg/test-factorial.c: New. * jit.dg/test-failure.c: New. * jit.dg/test-fibonacci.c: New. * jit.dg/test-hello-world.c: New. * jit.dg/test-string-literal.c: New. * jit.dg/test-sum-of-squares.c: New. libbacktrace/ * configure.ac: Add --enable-host-shared. * configure: Regenerate. libcpp/ * configure.ac: Add --enable-host-shared. * configure: Regenerate. libdecnumber/ * configure.ac: Add --enable-host-shared. * configure: Regenerate. libiberty/ * configure.ac: If --enable-host-shared, use -fPIC. * configure: Regenerate. zlib/ * configure.ac: Add --enable-host-shared. * configure: Regenerate.

diff --git a/configure b/configure index a91689f..3b399cd 100755 --- a/configure +++ b/configure @@ -556,6 +556,7 @@ enable_option_checking=no ac_subst_vars='LTLIBOBJS LIBOBJS compare_exclusions +host_shared stage2_werror_flag stage1_checking stage1_cflags @@ -787,6 +788,7 @@ with_build_time_tools enable_maintainer_mode enable_stage1_checking enable_werror +enable_host_shared ' ac_precious_vars='build_alias host_alias @@ -1490,6 +1492,7 @@ Optional Features: choose additional checking for stage1 of the compiler --enable-werror enable -Werror in bootstrap stage2 and later + --enable-host-shared build host code as shared libraries Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -14570,6 +14573,16 @@ case ${enable_werror} in esac +# Enable --enable-host-shared. +# Check whether --enable-host-shared was given. +if test "${enable_host_shared+set}" = set; then : + enableval=$enable_host_shared; host_shared=$enableval +else + host_shared=no +fi + + + # Specify what files to not compare during bootstrap. compare_exclusions="gcc/cc*-checksum\$(objext) | gcc/ada/*tools/*" diff --git a/configure.ac b/configure.ac index d72b40a..1bb7c0b 100644 --- a/configure.ac +++ b/configure.ac @@ -3301,6 +3301,13 @@ case ${enable_werror} in esac AC_SUBST(stage2_werror_flag) +# Enable --enable-host-shared. +AC_ARG_ENABLE(host-shared, +[AS_HELP_STRING([--enable-host-shared], + [build host code as shared libraries])], +[host_shared=$enableval], [host_shared=no]) +AC_SUBST(host_shared) + # Specify what files to not compare during bootstrap. compare_exclusions="gcc/cc*-checksum\$(objext) | gcc/ada/*tools/*" diff --git a/gcc/Makefile.in b/gcc/Makefile.in index a2e3f7a..afcd6e4 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -341,6 +341,8 @@ PLUGINLIBS = @pluginlibs@ enable_plugin = @enable_plugin@ +enable_host_shared = @enable_host_shared@ + CPPLIB = ../libcpp/libcpp.a CPPINC = -I$(srcdir)/../libcpp/include @@ -1022,8 +1024,15 @@ ALL_COMPILERFLAGS = $(ALL_CXXFLAGS) ALL_LINKERFLAGS = $(ALL_CXXFLAGS) # Build and host support libraries. + +# Use the "pic" build of libiberty if --enable-host-shared. +ifeq ($(enable_host_shared),yes) +LIBIBERTY = ../libiberty/pic/libiberty.a +BUILD_LIBIBERTY = $(build_libobjdir)/libiberty/pic/libiberty.a +else LIBIBERTY = ../libiberty/libiberty.a BUILD_LIBIBERTY = $(build_libobjdir)/libiberty/libiberty.a +endif # Dependencies on the intl and portability libraries. LIBDEPS= libcommon.a $(CPPLIB) $(LIBIBERTY) $(LIBINTL_DEP) $(LIBICONV_DEP) \ diff --git a/gcc/cgraph.c b/gcc/cgraph.c index c8e1b9c..5a94374 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -2996,4 +2996,30 @@ cgraph_get_body (struct cgraph_node *node) return true; } +void cgraph_c_finalize (void) +{ + x_cgraph_nodes_queue = NULL; + cgraph_n_nodes = 0; + cgraph_max_uid = 0; + cgraph_edge_max_uid = 0; + cgraph_global_info_ready = false; + cgraph_state = CGRAPH_STATE_PARSING; + cgraph_function_flags_ready = false; + + cgraph_new_nodes = 0; + asm_nodes = NULL; + symtab_order = 0; + cpp_implicit_aliases_done = false; + + first_cgraph_edge_removal_hook = NULL; + first_cgraph_node_removal_hook = NULL; + first_cgraph_edge_duplicated_hook = NULL; + first_cgraph_node_duplicated_hook = NULL; + first_cgraph_function_insertion_hook = NULL; + free_nodes = NULL; + free_edges = NULL; + cgraph_fnver_htab = NULL; + version_info_node = NULL; +} + #include "gt-cgraph.h" diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 50e8743..135e200 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -597,6 +597,7 @@ extern GTY(()) int symtab_order; extern bool cpp_implicit_aliases_done; /* In symtab.c */ +void symtab_c_finalize (void); void symtab_register_node (symtab_node); void symtab_unregister_node (symtab_node); void symtab_remove_node (symtab_node); @@ -630,6 +631,7 @@ enum availability symtab_node_availability (symtab_node); bool symtab_semantically_equivalent_p (symtab_node, symtab_node); /* In cgraph.c */ +void cgraph_c_finalize (void); void dump_cgraph (FILE *); void debug_cgraph (void); void dump_cgraph_node (FILE *, struct cgraph_node *); @@ -744,6 +746,7 @@ void cgraph_speculative_call_info (struct cgraph_edge *, struct ipa_ref *&); /* In cgraphunit.c */ +void cgraphunit_c_finalize (void); struct asm_node *add_asm_node (tree); extern FILE *cgraph_dump_file; void cgraph_finalize_function (tree, bool); @@ -794,6 +797,7 @@ void tree_function_versioning (tree, tree, vec<ipa_replace_map_p, va_gc> *, struct cgraph_edge *cgraph_resolve_speculation (struct cgraph_edge *, tree); /* In cgraphbuild.c */ +void cgraphbuild_c_finalize (void); unsigned int rebuild_cgraph_edges (void); void cgraph_rebuild_references (void); int compute_call_stmt_bb_frequency (tree, basic_block bb); @@ -801,6 +805,7 @@ void record_references_in_initializer (tree, bool); void ipa_record_stmt_references (struct cgraph_node *, gimple); /* In ipa.c */ +void ipa_c_finalize (void); bool symtab_remove_unreachable_nodes (bool, FILE *); cgraph_node_set cgraph_node_set_new (void); cgraph_node_set_iterator cgraph_node_set_find (cgraph_node_set, @@ -824,10 +829,12 @@ void ipa_discover_readonly_nonaddressable_vars (void); bool varpool_externally_visible_p (struct varpool_node *); /* In predict.c */ +void predict_c_finalize (void); bool cgraph_maybe_hot_edge_p (struct cgraph_edge *e); bool cgraph_optimize_for_size_p (struct cgraph_node *); /* In varpool.c */ +void varpool_c_finalize (void); struct varpool_node *varpool_create_empty_node (void); struct varpool_node *varpool_node_for_decl (tree); struct varpool_node *varpool_node_for_asm (tree asmname); diff --git a/gcc/cgraphbuild.c b/gcc/cgraphbuild.c index 98fd12c..392874f 100644 --- a/gcc/cgraphbuild.c +++ b/gcc/cgraphbuild.c @@ -592,3 +592,7 @@ make_pass_remove_cgraph_callee_edges (gcc::context *ctxt) { return new pass_remove_cgraph_callee_edges (ctxt); } + +void cgraphbuild_c_finalize (void) +{ +} diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 1644ca9..0f9d98f 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -906,15 +906,15 @@ walk_polymorphic_call_targets (pointer_set_t *reachable_call_targets, /* Discover all functions and variables that are trivially needed, analyze them as well as all functions and variables referred by them */ +static struct cgraph_node *first_analyzed; +static struct varpool_node *first_analyzed_var; static void analyze_functions (void) { /* Keep track of already processed nodes when called multiple times for intermodule optimization. */ - static struct cgraph_node *first_analyzed; struct cgraph_node *first_handled = first_analyzed; - static struct varpool_node *first_analyzed_var; struct varpool_node *first_handled_var = first_analyzed_var; struct pointer_set_t *reachable_call_targets = pointer_set_create (); @@ -2271,5 +2271,16 @@ finalize_compilation_unit (void) timevar_pop (TV_CGRAPH); } +void cgraphunit_c_finalize (void) +{ + cgraph_new_nodes = NULL; + cgraph_dump_file = NULL; + asm_last_node = NULL; + vtable_entry_type =NULL; + first = (symtab_node)(void *)1; + + first_analyzed = NULL; + first_analyzed_var = NULL; +} #include "gt-cgraphunit.h" diff --git a/gcc/configure b/gcc/configure index c6bc3a6..996a8b2 100755 --- a/gcc/configure +++ b/gcc/configure @@ -600,6 +600,7 @@ ac_includes_default="\ ac_subst_vars='LTLIBOBJS LIBOBJS +enable_host_shared enable_plugin pluginlibs CLOOGINC @@ -920,6 +921,7 @@ enable_maintainer_mode enable_link_mutex enable_version_specific_runtime_libs enable_plugin +enable_host_shared enable_libquadmath_support with_linker_hash_style ' @@ -1635,6 +1637,7 @@ Optional Features: specify that runtime libraries should be installed in a compiler-specific directory --enable-plugin enable plugin support + --enable-host-shared build host code as shared libraries --disable-libquadmath-support disable libquadmath support for Fortran @@ -17894,7 +17897,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 17897 "configure" +#line 17900 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -18000,7 +18003,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 18003 "configure" +#line 18006 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -27695,6 +27698,17 @@ $as_echo "#define ENABLE_PLUGIN 1" >>confdefs.h fi +# Enable --enable-host-shared, adding -fPIC +# Check whether --enable-host-shared was given. +if test "${enable_host_shared+set}" = set; then : + enableval=$enable_host_shared; CFLAGS="$CFLAGS -fPIC" + CXXFLAGS="$CXXFLAGS -fPIC" + LDFLAGS="$LDFLAGS -fPIC" +fi + + + + # Check whether --enable-libquadmath-support was given. if test "${enable_libquadmath_support+set}" = set; then : enableval=$enable_libquadmath_support; ENABLE_LIBQUADMATH_SUPPORT=$enableval diff --git a/gcc/configure.ac b/gcc/configure.ac index 5d3e5ad..4eae976 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -5393,6 +5393,16 @@ if test x"$enable_plugin" = x"yes"; then fi +# Enable --enable-host-shared, adding -fPIC +AC_ARG_ENABLE(host-shared, +[AS_HELP_STRING([--enable-host-shared], + [build host code as shared libraries])], +[CFLAGS="$CFLAGS -fPIC" + CXXFLAGS="$CXXFLAGS -fPIC" + LDFLAGS="$LDFLAGS -fPIC"], []) +AC_SUBST(enable_host_shared) + + AC_ARG_ENABLE(libquadmath-support, [AS_HELP_STRING([--disable-libquadmath-support], [disable libquadmath support for Fortran])], diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 95049e4..3b6bc5b 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -24137,4 +24137,87 @@ dwarf2out_finish (const char *filename) output_indirect_strings (); } +void dwarf2out_c_finalize (void) +{ + last_var_location_insn = NULL; + cached_next_real_insn = NULL; + used_rtx_array = NULL; + incomplete_types = NULL; + decl_scope_table = NULL; + debug_info_section = NULL; + debug_skeleton_info_section = NULL; + debug_abbrev_section = NULL; + debug_skeleton_abbrev_section = NULL; + debug_aranges_section = NULL; + debug_addr_section = NULL; + debug_macinfo_section = NULL; + debug_line_section = NULL; + debug_skeleton_line_section = NULL; + debug_loc_section = NULL; + debug_pubnames_section = NULL; + debug_pubtypes_section = NULL; + debug_str_section = NULL; + debug_str_dwo_section = NULL; + debug_str_offsets_section = NULL; + debug_ranges_section = NULL; + debug_frame_section = NULL; + fde_vec = NULL; + debug_str_hash = NULL; + skeleton_debug_str_hash = NULL; + dw2_string_counter = 0; + have_multiple_function_sections = false; + text_section_used = false; + cold_text_section_used = false; + cold_text_section = NULL; + current_unit_personality = NULL; + + deferred_locations_list = NULL; + + next_die_offset = 0; + single_comp_unit_die = NULL; + comdat_type_list = NULL; + limbo_die_list = NULL; + deferred_asm_name = NULL; + file_table = NULL; + decl_die_table = NULL; + common_block_die_table = NULL; + decl_loc_table = NULL; + call_arg_locations = NULL; + call_arg_loc_last = NULL; + call_site_count = -1; + tail_call_site_count = -1; + //block_map = NULL; + cached_dw_loc_list_table = NULL; + abbrev_die_table = NULL; + abbrev_die_table_allocated = 0; + abbrev_die_table_in_use = 0; + line_info_label_num = 0; + cur_line_info_table = NULL; + text_section_line_info = NULL; + cold_text_section_line_info = NULL; + separate_line_info = NULL; + info_section_emitted = false; + pubname_table = NULL; + pubtype_table = NULL; + macinfo_table = NULL; + ranges_table = NULL; + ranges_table_allocated = 0; + ranges_table_in_use = 0; + ranges_by_label = 0; + ranges_by_label_allocated = 0; + ranges_by_label_in_use = 0; + have_location_lists = false; + loclabel_num = 0; + poc_label_num = 0; + current_function_has_inlines = 0; + last_emitted_file = NULL; + label_num = 0; + file_table_last_lookup = NULL; + tmpl_value_parm_die_table = NULL; + generic_type_instances = NULL; + frame_pointer_fb_offset = 0; + frame_pointer_fb_offset_valid = false; + //base_types = NULL; +} + #include "gt-dwarf2out.h" diff --git a/gcc/dwarf2out.h b/gcc/dwarf2out.h index ad03a34..1f39101 100644 --- a/gcc/dwarf2out.h +++ b/gcc/dwarf2out.h @@ -280,4 +280,6 @@ struct array_descr_info } dimen[10]; }; +void dwarf2out_c_finalize (void); + #endif /* GCC_DWARF2OUT_H */ diff --git a/gcc/ggc-page.c b/gcc/ggc-page.c index 487a6d4..dda3f32 100644 --- a/gcc/ggc-page.c +++ b/gcc/ggc-page.c @@ -1642,8 +1642,13 @@ compute_inverse (unsigned order) void init_ggc (void) { + static bool init_p = false; unsigned order; + if (init_p) + return; + init_p = true; + G.pagesize = getpagesize(); G.lg_pagesize = exact_log2 (G.pagesize); diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c index 55b679d..2a3a306 100644 --- a/gcc/ipa-pure-const.c +++ b/gcc/ipa-pure-const.c @@ -108,10 +108,47 @@ typedef struct funct_state_d * funct_state; static vec<funct_state> funct_state_vec; -/* Holders of ipa cgraph hooks: */ -static struct cgraph_node_hook_list *function_insertion_hook_holder; -static struct cgraph_2node_hook_list *node_duplication_hook_holder; -static struct cgraph_node_hook_list *node_removal_hook_holder; +static bool gate_pure_const (void); + +namespace { + +const pass_data pass_data_ipa_pure_const = +{ + IPA_PASS, /* type */ + "pure-const", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + true, /* has_gate */ + true, /* has_execute */ + TV_IPA_PURE_CONST, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_ipa_pure_const : public ipa_opt_pass_d +{ +public: + pass_ipa_pure_const(gcc::context *ctxt); + + /* opt_pass methods: */ + bool gate () { return gate_pure_const (); } + unsigned int execute (); + + void register_hooks (void); + +private: + bool init_p; + + /* Holders of ipa cgraph hooks: */ + struct cgraph_node_hook_list *function_insertion_hook_holder; + struct cgraph_2node_hook_list *node_duplication_hook_holder; + struct cgraph_node_hook_list *node_removal_hook_holder; + +}; // class pass_ipa_pure_const + +} // anon namespace /* Try to guess if function body will always be visible to compiler when compiling the call and whether compiler will be able @@ -884,11 +921,10 @@ remove_node_data (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED) } -static void +void +pass_ipa_pure_const:: register_hooks (void) { - static bool init_p = false; - if (init_p) return; @@ -911,7 +947,8 @@ pure_const_generate_summary (void) { struct cgraph_node *node; - register_hooks (); + pass_ipa_pure_const *pass = static_cast <pass_ipa_pure_const *> (current_pass); + pass->register_hooks (); /* There are some shared nodes, in particular the initializers on static declarations. We do not need to scan them more than once @@ -1001,7 +1038,9 @@ pure_const_read_summary (void) struct lto_file_decl_data *file_data; unsigned int j = 0; - register_hooks (); + pass_ipa_pure_const *pass = static_cast <pass_ipa_pure_const *> (current_pass); + pass->register_hooks (); + while ((file_data = file_data_vec[j++])) { const char *data; @@ -1469,8 +1508,9 @@ propagate_nothrow (void) /* Produce the global information by preforming a transitive closure on the local information that was produced by generate_summary. */ -static unsigned int -propagate (void) +unsigned int +pass_ipa_pure_const:: +execute () { struct cgraph_node *node; @@ -1500,27 +1540,7 @@ gate_pure_const (void) && !seen_error ()); } -namespace { - -const pass_data pass_data_ipa_pure_const = -{ - IPA_PASS, /* type */ - "pure-const", /* name */ - OPTGROUP_NONE, /* optinfo_flags */ - true, /* has_gate */ - true, /* has_execute */ - TV_IPA_PURE_CONST, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - 0, /* todo_flags_finish */ -}; - -class pass_ipa_pure_const : public ipa_opt_pass_d -{ -public: - pass_ipa_pure_const(gcc::context *ctxt) +pass_ipa_pure_const::pass_ipa_pure_const(gcc::context *ctxt) : ipa_opt_pass_d(pass_data_ipa_pure_const, ctxt, pure_const_generate_summary, /* generate_summary */ pure_const_write_summary, /* write_summary */ @@ -1530,16 +1550,13 @@ public: NULL, /* stmt_fixup */ 0, /* function_transform_todo_flags_start */ NULL, /* function_transform */ - NULL) /* variable_transform */ - {} - - /* opt_pass methods: */ - bool gate () { return gate_pure_const (); } - unsigned int execute () { return propagate (); } - -}; // class pass_ipa_pure_const - -} // anon namespace + NULL), /* variable_transform */ + init_p(false), + function_insertion_hook_holder(NULL), + node_duplication_hook_holder(NULL), + node_removal_hook_holder(NULL) +{ +} ipa_opt_pass_d * make_pass_ipa_pure_const (gcc::context *ctxt) diff --git a/gcc/ipa.c b/gcc/ipa.c index 67b3bc0..14d0c60 100644 --- a/gcc/ipa.c +++ b/gcc/ipa.c @@ -1585,3 +1585,7 @@ make_pass_ipa_cdtor_merge (gcc::context *ctxt) { return new pass_ipa_cdtor_merge (ctxt); } + +void ipa_c_finalize (void) +{ +} diff --git a/gcc/jit/ChangeLog b/gcc/jit/ChangeLog new file mode 100644 index 0000000..5f87293 --- /dev/null +++ b/gcc/jit/ChangeLog @@ -0,0 +1,9 @@ +2013-07-26 David Malcolm <dmalcolm@redhat.com> + + * Initial creation + +Copyright (C) 2013 Free Software Foundation, Inc. + +Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. diff --git a/gcc/jit/Make-lang.in b/gcc/jit/Make-lang.in new file mode 100644 index 0000000..8ad66f3 --- /dev/null +++ b/gcc/jit/Make-lang.in @@ -0,0 +1,143 @@ +# Top level -*- makefile -*- fragment for libgccjit.so. +# Copyright (C) 2013 Free Software Foundation, Inc. + +#This file is part of GCC. + +#GCC is free software; you can redistribute it and/or modify +#it under the terms of the GNU General Public License as published by +#the Free Software Foundation; either version 3, or (at your option) +#any later version. + +#GCC is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# This file provides the language dependent support in the main Makefile. +# Each language makefile fragment must provide the following targets: +# +# foo.all.cross, foo.start.encap, foo.rest.encap, +# foo.install-common, foo.install-man, foo.install-info, foo.install-pdf, +# foo.install-html, foo.info, foo.dvi, foo.pdf, foo.html, foo.uninstall, +# foo.mostlyclean, foo.clean, foo.distclean, +# foo.maintainer-clean, foo.stage1, foo.stage2, foo.stage3, foo.stage4 +# +# where `foo' is the name of the language. +# +# It should also provide rules for: +# +# - making any compiler driver (eg: g++) +# - the compiler proper (eg: cc1plus) +# - define the names for selecting the language in LANGUAGES. + +# +# Define the names for selecting jit in LANGUAGES. +# Note that it would be nice to move the dependency on g++ +# into the jit rule, but that needs a little bit of work +# to do the right thing within all.cross. + +jit: libgccjit.so + +# Tell GNU make to ignore these if they exist. +.PHONY: jit + +JIT_OBJS = attribs.o jit/dummy-frontend.o jit/libgccjit.o jit/internal-api.o + +# Use strict warnings for this front end. +jit-warn = $(STRICT_WARN) + +# We avoid using $(BACKEND) from Makefile.in in order to avoid pulling +# in main.o +libgccjit.so: $(JIT_OBJS) \ + libbackend.a libcommon-target.a libcommon.a \ + $(CPPLIB) $(LIBDECNUMBER) \ + $(LIBDEPS) $(srcdir)/jit/libgccjit.map + +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ -shared \ + $(JIT_OBJS) libbackend.a libcommon-target.a libcommon.a \ + $(CPPLIB) $(LIBDECNUMBER) $(LIBS) $(BACKENDLIBS) \ + -Wl,--version-script=$(srcdir)/jit/libgccjit.map + +# +# Build hooks: + +jit.all.cross: +jit.start.encap: +jit.rest.encap: +jit.info: +jit.install-info: +jit.dvi: +jit.pdf: +jit.install-pdf: +jit.install-html: +jit.html: +jit.srcinfo: +jit.srcextra: + +jit.tags: + +jit.man: + +jit.srcman: + +# +# Install hooks: +jit.install-common: + +jit.install-man: + +jit.install-plugin: + +jit.uninstall: + +# +# Clean hooks: +# A lot of the ancillary files are deleted by the main makefile. +# We just have to delete files specific to us. + +jit.mostlyclean: + +jit.clean: + +jit.distclean: + +jit.maintainer-clean: + +# +# Stage hooks: +# The main makefile has already created stage?/jit. + +jit.stage1: stage1-start + -mv jit/*$(objext) stage1/jit +jit.stage2: stage2-start + -mv jit/*$(objext) stage2/jit +jit.stage3: stage3-start + -mv jit/*$(objext) stage3/jit +jit.stage4: stage4-start + -mv jit/*$(objext) stage4/jit +jit.stageprofile: stageprofile-start + -mv jit/*$(objext) stageprofile/jit +jit.stagefeedback: stagefeedback-start + -mv jit/*$(objext) stagefeedback/jit + +# +# .o: .h dependencies. +JIT_INTERNAL_API_H = jit/libgccjit.h $(TREE_H) $(TREE_ITERATOR_H) jit/internal-api.h + +jit/dummy-frontend.o: jit/dummy-frontend.c $(CONFIG_H) $(SYSTEM_H) \ + $(CORETYPES_H) $(OPTS_H) $(TREE_H) $(DEBUG_H) langhooks.h \ + $(LANGHOOKS_DEF_H) $(CGRAPH_H) \ + $(TREE_ITERATOR_H) $(GIMPLE_H) $(GIMPLE_PRETTY_PRINT_h) \ + gt-jit-dummy-frontend.h gtype-jit.h $(JIT_INTERNAL_API_H) + +jit/libgccjit.o: jit/libgccjit.c $(CONFIG_H) $(SYSTEM_H) \ + $(CORETYPES_H) $(OPTS_H) jit/libgccjit.h $(JIT_INTERNAL_API_H) \ + +jit/internal-api.o: jit/internal-api.c $(CONFIG_H) $(SYSTEM_H) \ + $(CORETYPES_H) $(OPTS_H) $(TREE_H) $(DEBUG_H) \ + langhooks.h langhooks-def.h $(CGRAPH_H) $(TOPLEV_H) \ + $(TREE_ITERATOR_H) $(GIMPLE_H) $(GIMPLE_PRETTY_PRINT_H) \ + $(JIT_INTERNAL_API_H) diff --git a/gcc/jit/TODO.rst b/gcc/jit/TODO.rst new file mode 100644 index 0000000..65d4dc8 --- /dev/null +++ b/gcc/jit/TODO.rst @@ -0,0 +1,35 @@ +TODOs: + +* gcc_jit_context_new_local should take a function, not the context (is + it building a global?) + +* error-handling: + * have a client-provided error-handling callback for the context, and + call it, rather than asserting/crashing etc, to make the API resilient and helpful + +* more language features: + + * more types: + * unions + * function ptrs + +* demo of JIT-compilation of a bytecode interpreter: + https://github.com/davidmalcolm/jittest + (move this into tree, as an example) + +* wiring up testing into a DejaGNU testsuite, rather than testing it + by hand. + +* docs + +* fixing all the state issues + +* make the dirty dirty hacks less egregious... + +* try porting llvmpipe to gcc + +* source locations: line-numbering etc + +* pkgconfig .pc file + +etc etc diff --git a/gcc/jit/config-lang.in b/gcc/jit/config-lang.in new file mode 100644 index 0000000..a804c62 --- /dev/null +++ b/gcc/jit/config-lang.in @@ -0,0 +1,38 @@ +# Top level configure fragment for libgccjit.so. +# Copyright (C) 2013 Free Software Foundation, Inc. + +#This file is part of GCC. + +#GCC is free software; you can redistribute it and/or modify +#it under the terms of the GNU General Public License as published by +#the Free Software Foundation; either version 3, or (at your option) +#any later version. + +#GCC is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# Configure looks for the existence of this file to auto-config each language. +# We define several parameters used by configure: +# +# language - name of language as it would appear in $(LANGUAGES) +# compilers - value to add to $(COMPILERS) + +language="jit" + +compilers="libgccjit.so" + +target_libs="" + +gtfiles="\$(srcdir)/jit/dummy-frontend.c" + +# The configuration requires --enable-host-shared +# for jit to be supported. +# Hence to get the jit, one must configure with: +# --enable-host-shared --enable-languages=jit +build_by_default="no" diff --git a/gcc/jit/dummy-frontend.c b/gcc/jit/dummy-frontend.c new file mode 100644 index 0000000..b5a561d --- /dev/null +++ b/gcc/jit/dummy-frontend.c @@ -0,0 +1,238 @@ +/* jit.c -- Dummy "frontend" for use during JIT-compilation. + Copyright (C) 2013 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "ansidecl.h" +#include "coretypes.h" +#include "opts.h" +#include "tree.h" +#include "debug.h" +#include "langhooks.h" +#include "langhooks-def.h" +#include "cgraph.h" + +#include "tree-iterator.h" +#include "gimple.h" +#include "gimple-pretty-print.h" +#include "internal-api.h" + +#include <mpfr.h> + +/* Language-dependent contents of a type. */ + +struct GTY(()) lang_type +{ + char dummy; +}; + +/* Language-dependent contents of a decl. */ + +struct GTY((variable_size)) lang_decl +{ + char dummy; +}; + +/* Language-dependent contents of an identifier. This must include a + tree_identifier. */ + +struct GTY(()) lang_identifier +{ + struct tree_identifier common; +}; + +/* The resulting tree type. */ + +union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"), + chain_next ("CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN (&%h.generic)) : NULL"))) +lang_tree_node +{ + union tree_node GTY((tag ("0"), + desc ("tree_node_structure (&%h)"))) generic; + struct lang_identifier GTY((tag ("1"))) identifier; +}; + +/* We don't use language_function. */ + +struct GTY(()) language_function +{ + int dummy; +}; + +void my_walker (void *) +{ + if (gcc::jit::active_jit_ctxt) + gcc::jit::active_jit_ctxt->gt_ggc_mx (); +} + +const char *dummy; + +struct ggc_root_tab my_root_tab[] = + { + { + &dummy, 1, 0, my_walker, NULL + }, + LAST_GGC_ROOT_TAB + }; + +/* Language hooks. */ + +static bool +jit_langhook_init (void) +{ + // eventually this can be a GTY object, and can thus mark + // our state without dealing with gengtype... + //g->frontend_ = new frontend (); + static bool registered_root_tab = false; + if (!registered_root_tab) + { + ggc_register_root_tab (my_root_tab); + registered_root_tab = true; + } + + build_common_tree_nodes (false, false); + + /* I don't know why this has to be done explicitly. */ + void_list_node = build_tree_list (NULL_TREE, void_type_node); + + build_common_builtin_nodes (); + + /* The default precision for floating point numbers. This is used + for floating point constants with abstract type. This may + eventually be controllable by a command line option. */ + mpfr_set_default_prec (256); + + return true; +} + +static void +jit_langhook_parse_file (void) +{ + /* Run the IR-creation code provided by the client. */ + gcc_assert (gcc::jit::active_jit_ctxt); + gcc::jit::active_jit_ctxt->invoke_code_factory (); +} + +static tree +jit_langhook_type_for_mode (enum machine_mode mode, int unsignedp) +{ + if (mode == TYPE_MODE (float_type_node)) + return float_type_node; + + if (mode == TYPE_MODE (double_type_node)) + return double_type_node; + + if (mode == TYPE_MODE (integer_type_node)) + return unsignedp ? unsigned_type_node : integer_type_node; + + if (mode == TYPE_MODE (long_integer_type_node)) + return unsignedp ? long_unsigned_type_node : long_integer_type_node; + + if (COMPLEX_MODE_P (mode)) + { + if (mode == TYPE_MODE (complex_float_type_node)) + return complex_float_type_node; + if (mode == TYPE_MODE (complex_double_type_node)) + return complex_double_type_node; + if (mode == TYPE_MODE (complex_long_double_type_node)) + return complex_long_double_type_node; + if (mode == TYPE_MODE (complex_integer_type_node) && !unsignedp) + return complex_integer_type_node; + } + + /* gcc_unreachable */ + return NULL; +} + +static tree +jit_langhook_type_for_size (unsigned int bits ATTRIBUTE_UNUSED, + int unsignedp ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); + return NULL; +} + +/* Record a builtin function. We just ignore builtin functions. */ + +static tree +jit_langhook_builtin_function (tree decl) +{ + return decl; +} + +static bool +jit_langhook_global_bindings_p (void) +{ + gcc_unreachable (); + return true; +} + +static tree +jit_langhook_pushdecl (tree decl ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + +static tree +jit_langhook_getdecls (void) +{ + return NULL; +} + +static void +jit_langhook_write_globals (void) +{ + /* This is the hook that runs the middle and backends: */ + finalize_compilation_unit (); +} + +#undef LANG_HOOKS_NAME +#define LANG_HOOKS_NAME "libgccjit" + +#undef LANG_HOOKS_INIT +#define LANG_HOOKS_INIT jit_langhook_init + +#undef LANG_HOOKS_PARSE_FILE +#define LANG_HOOKS_PARSE_FILE jit_langhook_parse_file + +#undef LANG_HOOKS_TYPE_FOR_MODE +#define LANG_HOOKS_TYPE_FOR_MODE jit_langhook_type_for_mode + +#undef LANG_HOOKS_TYPE_FOR_SIZE +#define LANG_HOOKS_TYPE_FOR_SIZE jit_langhook_type_for_size + +#undef LANG_HOOKS_BUILTIN_FUNCTION +#define LANG_HOOKS_BUILTIN_FUNCTION jit_langhook_builtin_function + +#undef LANG_HOOKS_GLOBAL_BINDINGS_P +#define LANG_HOOKS_GLOBAL_BINDINGS_P jit_langhook_global_bindings_p + +#undef LANG_HOOKS_PUSHDECL +#define LANG_HOOKS_PUSHDECL jit_langhook_pushdecl + +#undef LANG_HOOKS_GETDECLS +#define LANG_HOOKS_GETDECLS jit_langhook_getdecls + +#undef LANG_HOOKS_WRITE_GLOBALS +#define LANG_HOOKS_WRITE_GLOBALS jit_langhook_write_globals + +struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; + +#include "gt-jit-dummy-frontend.h" +#include "gtype-jit.h" diff --git a/gcc/jit/internal-api.c b/gcc/jit/internal-api.c new file mode 100644 index 0000000..3943d05 --- /dev/null +++ b/gcc/jit/internal-api.c @@ -0,0 +1,1004 @@ +#include "config.h" +#include "system.h" +#include "ansidecl.h" +#include "coretypes.h" +#include "opts.h" +#include "tree.h" +#include "debug.h" +#include "langhooks.h" +#include "langhooks-def.h" +#include "cgraph.h" +#include "toplev.h" +#include "tree-iterator.h" +#include "gimple.h" +#include "gimple-pretty-print.h" +#include "timevar.h" + +#include <pthread.h> + +#include "internal-api.h" + +void +gcc::jit::context:: +gt_ggc_mx () +{ + int i; + function *func; + FOR_EACH_VEC_ELT (m_functions, i, func) + { + if (ggc_test_and_set_mark (func)) + func->gt_ggc_mx (); + } +} + +void +gcc::jit::context:: +set_code_factory (gcc_jit_code_callback cb, + void *user_data) +{ + m_code_factory = cb; + m_user_data = user_data; +} + +gcc::jit::type * +gcc::jit::context:: +get_void_type () +{ + return new type (void_type_node); +} + +gcc::jit::type * +gcc::jit::context:: +get_char_type () +{ + return new type (char_type_node); +} + +gcc::jit::type * +gcc::jit::context:: +get_int_type () +{ + return new type (intSI_type_node); +} + +gcc::jit::type * +gcc::jit::context:: +get_float_type () +{ + return new type (float_type_node); +} + +gcc::jit::type * +gcc::jit::context:: +get_double_type () +{ + return new type (double_type_node); +} + +gcc::jit::field * +gcc::jit::context:: +new_field (gcc::jit::location *loc, + gcc::jit::type *type, + const char *name) +{ + gcc_assert (NULL == loc); + gcc_assert (type); + gcc_assert (name); + + /* compare with c/c-decl.c:grokfield and grokdeclarator. */ + tree decl = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier (name), type->as_tree ()); + + return new field (decl); +} + +gcc::jit::type * +gcc::jit::context:: +new_struct_type (gcc::jit::location *loc, + const char *name, + int num_fields, + gcc::jit::field **fields) +{ + gcc_assert (NULL == loc); + gcc_assert (name); + gcc_assert ((num_fields == 0) || fields); + + /* Compare with c/c-decl.c: start_struct and finish_struct. */ + + tree t = make_node (RECORD_TYPE); + TYPE_NAME (t) = get_identifier (name); + TYPE_SIZE (t) = 0; + + tree fieldlist = NULL; + for (int i = 0; i < num_fields; i++) + { + field *f = fields[i]; + DECL_CONTEXT (f->as_tree ()) = t; + fieldlist = chainon (f->as_tree (), fieldlist); + } + fieldlist = nreverse (fieldlist); + TYPE_FIELDS (t) = fieldlist; + + layout_type (t); + + return new type (t); +} + + +gcc::jit::param * +gcc::jit::context:: +new_param (location *loc, + type *type, + const char *name) +{ + gcc_assert (NULL == loc); + gcc_assert (type); + gcc_assert (name); + tree inner = build_decl (UNKNOWN_LOCATION, PARM_DECL, + get_identifier (name), type->as_tree ()); + return new param (inner); +} + +gcc::jit::function * +gcc::jit::context:: +new_function (location *loc, + enum gcc_jit_function_kind kind, + type *return_type, + const char *name, + int num_params, + param **params, + int is_variadic) + { + gcc_assert (NULL == loc); + //can return_type be NULL? + gcc_assert (name); + gcc_assert ((num_params == 0) || params); + + tree *arg_types = (tree *)xcalloc(num_params, sizeof(tree*)); + for (int i = 0; i < num_params; i++) + { + arg_types[i] = TREE_TYPE (params[i]->as_tree ()); + } + tree fn_type; + if (is_variadic) + fn_type = build_varargs_function_type_array (return_type->as_tree (), + num_params, arg_types); + else + fn_type = build_function_type_array (return_type->as_tree (), + num_params, arg_types); + // FIXME: free arg_types + + /* FIXME: this uses input_location: */ + tree fndecl = build_fn_decl (name, fn_type); + + tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, + NULL_TREE, return_type->as_tree ()); + DECL_ARTIFICIAL (resdecl) = 1; + DECL_IGNORED_P (resdecl) = 1; + DECL_RESULT (fndecl) = resdecl; + + if (kind != GCC_JIT_FUNCTION_IMPORTED) + { + tree param_decl_list = NULL; + for (int i = 0; i < num_params; i++) + { + param_decl_list = chainon (params[i]->as_tree (), param_decl_list); + } + + /* The param list was created in reverse order; fix it: */ + param_decl_list = nreverse (param_decl_list); + + tree t; + for (t = param_decl_list; t; t = DECL_CHAIN (t)) + { + DECL_CONTEXT (t) = fndecl; + DECL_ARG_TYPE (t) = TREE_TYPE (t); + } + + /* Set it up on DECL_ARGUMENTS */ + DECL_ARGUMENTS(fndecl) = param_decl_list; + } + + function *func = new function (this, fndecl, kind); + m_functions.safe_push (func); + return func; +} + +gcc::jit::local * +gcc::jit::context:: +new_local (location *loc, + type *type, + const char *name) +{ + gcc_assert (NULL == loc); + gcc_assert (type); + gcc_assert (name); + tree inner = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (name), + type->as_tree ()); + return new local (inner); +} + + +gcc::jit::rvalue * +gcc::jit::context:: +new_rvalue_from_int (type *type, + int value) +{ + // FIXME: type-checking, or coercion? + tree inner_type = type->as_tree (); + if (INTEGRAL_TYPE_P (inner_type)) + { + tree inner = build_int_cst (inner_type, value); + return new rvalue (inner); + } + else + { + REAL_VALUE_TYPE real_value; + real_from_integer (&real_value, VOIDmode, value, 0, 0); + tree inner = build_real (inner_type, real_value); + return new rvalue (inner); + } +} + +gcc::jit::rvalue * +gcc::jit::context:: +new_string_literal (const char *value) +{ + tree t_str = build_string (strlen (value), value); + gcc_assert (m_char_array_type_node); + TREE_TYPE (t_str) = m_char_array_type_node; + + /* Convert to (const char*), loosely based on + c/c-typeck.c: array_to_pointer_conversion, + by taking address of start of string. */ + tree t_addr = build1 (ADDR_EXPR, m_const_char_ptr, t_str); + + return new rvalue (t_addr); +} + +gcc::jit::rvalue * +gcc::jit::context:: +new_binary_op (location *loc, + enum gcc_jit_binary_op op, + type *result_type, + rvalue *a, rvalue *b) +{ + // FIXME: type-checking, or coercion? + enum tree_code inner_op; + + gcc_assert (NULL == loc); + gcc_assert (result_type); + gcc_assert (a); + gcc_assert (b); + + switch (op) + { + default: gcc_unreachable (); + case GCC_JIT_BINARY_OP_PLUS: + inner_op = PLUS_EXPR; + break; + + case GCC_JIT_BINARY_OP_MINUS: + inner_op = MINUS_EXPR; + break; + + case GCC_JIT_BINARY_OP_MULT: + inner_op = MULT_EXPR; + break; + } + + tree inner_expr = build2 (inner_op, + result_type->as_tree (), + a->as_tree (), + b->as_tree ()); + return new rvalue (inner_expr); +} + +gcc::jit::rvalue * +gcc::jit::context:: +new_comparison (location *loc, + enum gcc_jit_comparison op, + rvalue *a, rvalue *b) +{ + // FIXME: type-checking, or coercion? + enum tree_code inner_op; + + gcc_assert (NULL == loc); + gcc_assert (a); + gcc_assert (b); + + switch (op) + { + default: gcc_unreachable (); + case GCC_JIT_COMPARISON_LT: + inner_op = LT_EXPR; + break; + case GCC_JIT_COMPARISON_GE: + inner_op = GE_EXPR; + break; + } + + tree inner_expr = build2 (inner_op, + boolean_type_node, + a->as_tree (), + b->as_tree ()); + return new rvalue (inner_expr); +} + +gcc::jit::rvalue * +gcc::jit::context:: +new_call (location *loc, + function *func, + int numargs , rvalue **args) +{ + tree fndecl; + vec<tree, va_gc> *tree_args; + + gcc_assert (NULL == loc); + gcc_assert (func); + gcc_assert (numargs >= 0); + gcc_assert ((args == 0) || (args != NULL)); + + // FIXME: type checking + // FIXME: check num args and types + + fndecl = func->as_fndecl (); + + vec_alloc (tree_args, numargs); + for (int i = 0; i < numargs; i++) + tree_args->quick_push (args[i]->as_tree ()); + + tree fntype = TREE_TYPE (fndecl); + tree fn = build1 (ADDR_EXPR, build_pointer_type (fntype), fndecl); + + return new rvalue (build_call_vec (func->get_return_type_as_tree (), + fn, tree_args)); + + /* see c-typeck.c: build_function_call + which calls build_function_call_vec + + which does lots of checking, then: + result = build_call_array_loc (loc, TREE_TYPE (fntype), + function, nargs, argarray); + which is in tree.c + (see also build_call_vec) + */ +} + +gcc::jit::rvalue * +gcc::jit::context:: +new_array_lookup (location *loc, + rvalue *ptr, + rvalue *index) +{ + gcc_assert (NULL == loc); + gcc_assert (ptr); + gcc_assert (index); + + /* For comparison, see: + c/c-typeck.c: build_array_ref + c-family/c-common.c: pointer_int_sum + */ + tree t_ptr = ptr->as_tree (); + tree t_index = index->as_tree (); + tree t_type_ptr = TREE_TYPE (t_ptr); + tree t_type_star_ptr = TREE_TYPE (t_type_ptr); + + if (TREE_CODE (t_type_ptr) == ARRAY_TYPE) + { + tree t_result = build4 (ARRAY_REF, t_type_star_ptr, t_ptr, t_index, + NULL_TREE, NULL_TREE); + return new rvalue (t_result); + } + else + { + /* Convert index to an offset in bytes. */ + tree t_sizeof = size_in_bytes (t_type_star_ptr); + t_index = fold_build1 (CONVERT_EXPR, sizetype, t_index); + tree t_offset = build2 (MULT_EXPR, sizetype, t_index, t_sizeof); + + /* Locate (ptr + offset). */ + tree t_address = build2 (POINTER_PLUS_EXPR, t_type_ptr, t_ptr, t_offset); + + tree t_indirection = build1 (INDIRECT_REF, t_type_star_ptr, t_address); + return new rvalue (t_indirection); + } +} + +static tree +get_field (tree type, tree component) +{ + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (DECL_NAME (field) == component) + return field; + return NULL; +} + +gcc::jit::lvalue * +gcc::jit::context:: +new_field_access (location *loc, + rvalue *ptr_or_struct, + const char *fieldname) +{ + gcc_assert (NULL == loc); + gcc_assert (ptr_or_struct); + gcc_assert (fieldname); + + /* Compare with c/ctypeck.c:lookup_field, build_indirect_ref, and + build_component_ref. */ + tree datum = ptr_or_struct->as_tree (); + tree type = TREE_TYPE (datum); + + if (TREE_CODE (type) == POINTER_TYPE) + { + datum = build1 (INDIRECT_REF, type, datum); + type = TREE_TYPE (type); + } + + tree component = get_identifier (fieldname); + tree field = get_field (type, component); + if (!field) + { + // FIXME: field not found + return NULL; + } + tree ref = build3 (COMPONENT_REF, TREE_TYPE (field), datum, + field, NULL_TREE); + return new lvalue (ref); +} + +void * +gcc::jit::wrapper:: +operator new (size_t sz) +{ + return ggc_internal_cleared_alloc_stat (sz MEM_STAT_INFO); +} + +gcc::jit::function:: +function(gcc::jit::context *ctxt, tree fndecl, enum gcc_jit_function_kind kind) +: m_ctxt(ctxt), + m_inner_fndecl (fndecl), + m_kind (kind) +{ + if (m_kind != GCC_JIT_FUNCTION_IMPORTED) + { + m_stmt_list = alloc_stmt_list (); + m_stmt_iter = tsi_start (m_stmt_list); + } + else + { + m_stmt_list = NULL; + } +} + +void +gcc::jit::function:: +gt_ggc_mx () +{ + gt_ggc_m_9tree_node (m_inner_fndecl); + gt_ggc_m_9tree_node (m_stmt_list); +} + +tree +gcc::jit::function:: +get_return_type_as_tree () const +{ + return TREE_TYPE (TREE_TYPE(m_inner_fndecl)); +} + +gcc::jit::label * +gcc::jit::function:: +new_forward_label (const char *name) +{ + gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED); + return new label (this, name); +} + +void +gcc::jit::function:: +postprocess () +{ + if (m_ctxt->get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE)) + debug_tree (m_stmt_list); + + /* Do we need this to force cgraphunit.c to output the function? */ + if (m_kind == GCC_JIT_FUNCTION_EXPORTED) + { + DECL_EXTERNAL (m_inner_fndecl) = 0; + DECL_PRESERVE_P (m_inner_fndecl) = 1; + } + + if (m_kind != GCC_JIT_FUNCTION_IMPORTED) + { + /* Seem to need this in gimple-low.c: */ + DECL_INITIAL (m_inner_fndecl) = make_node (BLOCK); + + /* how to add to function? the following appears to be how to + set the body of a m_inner_fndecl: */ + DECL_SAVED_TREE(m_inner_fndecl) = m_stmt_list; + //debug_tree (m_inner_fndecl); + + /* Convert to gimple: */ + //printf("about to gimplify_function_tree

"); + gimplify_function_tree (m_inner_fndecl); + //printf("finished gimplify_function_tree

"); + + current_function_decl = m_inner_fndecl; + if (m_ctxt->get_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE)) + { + gimple_seq gs = gimple_body (m_inner_fndecl); + debug_gimple_seq (gs); + debug_tree (m_inner_fndecl); + } + + //printf("about to add to cgraph

"); + /* Add to cgraph: */ + cgraph_finalize_function (m_inner_fndecl, false); + /* This can trigger a collection, so we need to have all of + the funcs as roots. */ + + current_function_decl = NULL; + } +} + +void +gcc::jit::function:: +add_eval (location *loc, + rvalue *rvalue) +{ + gcc_assert (NULL == loc); + gcc_assert (rvalue); + gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED); + + tsi_link_after (&m_stmt_iter, rvalue->as_tree (), TSI_CONTINUE_LINKING); +} + +void +gcc::jit::function:: +add_assignment (location *loc, + lvalue *lvalue, + rvalue *rvalue) +{ + gcc_assert (NULL == loc); + gcc_assert (lvalue); + gcc_assert (rvalue); + gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED); + + tree t_lvalue = lvalue->as_tree (); + tree t_rvalue = rvalue->as_tree (); + if (TREE_TYPE (t_rvalue) != TREE_TYPE (t_lvalue)) + t_rvalue = build1 (CONVERT_EXPR, + TREE_TYPE (t_lvalue), + t_rvalue); + + tree stmt = + build2 (MODIFY_EXPR, TREE_TYPE (t_lvalue), + t_lvalue, t_rvalue); + tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING); +} + +void +gcc::jit::function:: +add_conditional (location *loc, + rvalue *boolval, + label *on_true, + label *on_false) +{ + gcc_assert (NULL == loc); + gcc_assert (boolval); + gcc_assert (on_true); + /* on_false can be NULL */ + gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED); + + /* COND_EXPR wants statement lists for the true/false operands, but we + want labels. + Shim it by creating jumps to the labels */ + tree true_jump = build1 (GOTO_EXPR, void_type_node, + on_true->as_label_decl ()); + tree false_jump; + if (on_false) + false_jump = build1 (GOTO_EXPR, void_type_node, + on_false->as_label_decl ()); + else + false_jump = NULL; + + tree stmt = + build3 (COND_EXPR, void_type_node, boolval->as_tree (), + true_jump, false_jump); + tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING); +} + +gcc::jit::label * +gcc::jit::function:: +add_label (location *loc, + const char *name) +{ + gcc_assert (NULL == loc); + gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED); + + label *lab = new label (this, name); + place_forward_label (lab); + return lab; +} + +void +gcc::jit::function:: +place_forward_label (label *lab) +{ + gcc_assert (lab); + gcc_assert (NULL == lab->m_label_expr); // must not have already been placed + gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED); + + lab->m_label_expr = build1 (LABEL_EXPR, + void_type_node, + lab->as_label_decl ()); + tsi_link_after (&m_stmt_iter, lab->m_label_expr, TSI_CONTINUE_LINKING); +} + +void +gcc::jit::function:: +add_jump (location *loc, + label *target) +{ + gcc_assert (NULL == loc); + gcc_assert (target); + gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED); + + // see c_finish_loop + //tree top = build1 (LABEL_EXPR, void_type_node, NULL_TREE); + //add_stmt (top); + + //tree stmt = build_and_jump (&LABEL_EXPR_LABEL (target->label_)); + TREE_USED (target->as_label_decl ()) = 1; + tree stmt = build1 (GOTO_EXPR, void_type_node, target->as_label_decl ()); + tsi_link_after (&m_stmt_iter, stmt, TSI_CONTINUE_LINKING); + + /* + from c-typeck.c: +tree +c_finish_goto_label (location_t loc, tree label) +{ + tree decl = lookup_label_for_goto (loc, label); + if (!decl) + return NULL_TREE; + TREE_USED (decl) = 1; + { + tree t = build1 (GOTO_EXPR, void_type_node, decl); + SET_EXPR_LOCATION (t, loc); + return add_stmt (t); + } +} + */ + +} + +void +gcc::jit::function:: +add_return (location *loc, + rvalue *rvalue) +{ + gcc_assert (NULL == loc); + gcc_assert (m_kind != GCC_JIT_FUNCTION_IMPORTED); + + tree return_type = TREE_TYPE (TREE_TYPE (m_inner_fndecl)); + tree t_lvalue = DECL_RESULT (m_inner_fndecl); + tree t_rvalue = rvalue->as_tree (); + if (TREE_TYPE (t_rvalue) != TREE_TYPE (t_lvalue)) + t_rvalue = build1 (CONVERT_EXPR, + TREE_TYPE (t_lvalue), + t_rvalue); + tree modify_retval = build2 (MODIFY_EXPR, return_type, + t_lvalue, t_rvalue); + tree return_stmt = build1 (RETURN_EXPR, return_type, + modify_retval); + tsi_link_after (&m_stmt_iter, return_stmt, TSI_CONTINUE_LINKING); +} + +gcc::jit::loop * +gcc::jit::function:: +new_loop (location *loc, + rvalue *boolval) +{ + return new loop (this, loc, boolval); +} + +gcc::jit::label:: +label (function *func, + const char *name) +{ + tree identifier; + + gcc_assert (func); + // name can be NULL + if (name) + identifier = get_identifier (name); + else + identifier = NULL; + m_label_decl = build_decl (UNKNOWN_LOCATION, LABEL_DECL, + identifier, void_type_node); + DECL_CONTEXT (m_label_decl) = func->as_fndecl (); + m_label_expr = NULL; +} + + +gcc::jit::loop:: +loop (function *func, location *loc, rvalue *boolval) : + m_func(func) +{ + m_label_cond = func->add_label (loc, "loop_cond"); + m_label_body = func->new_forward_label ("loop_body"); + m_label_end = func->new_forward_label ("loop_end"); + func->add_conditional (loc, boolval, m_label_body, m_label_end); + func->place_forward_label (m_label_body); +} + +void +gcc::jit::loop:: +end (location *loc) +{ + m_func->add_jump (loc, m_label_cond); + m_func->place_forward_label (m_label_end); +} + +void +gcc::jit::context:: +set_str_option (enum gcc_jit_str_option opt, + const char *value) +{ + if (opt < 0 || opt >= GCC_JIT_NUM_STR_OPTIONS) + { + add_error ("unrecognized str option"); + return; + } + m_str_options[opt] = value; +} + +void +gcc::jit::context:: +set_int_option (enum gcc_jit_int_option opt, + int value) +{ + if (opt < 0 || opt >= GCC_JIT_NUM_INT_OPTIONS) + { + add_error ("unrecognized int option"); + return; + } + m_int_options[opt] = value; +} + +void +gcc::jit::context:: +set_bool_option (enum gcc_jit_bool_option opt, + int value) +{ + if (opt < 0 || opt >= GCC_JIT_NUM_BOOL_OPTIONS) + { + add_error ("unrecognized bool option"); + return; + } + m_bool_options[opt] = value ? true : false; +} + +namespace gcc +{ + namespace jit + { + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + context *active_jit_ctxt; + } +} + +gcc::jit::result * +gcc::jit::context:: +compile () +{ + void *handle = NULL; + result *result_obj = NULL; + + /* Acquire the big GCC mutex. */ + pthread_mutex_lock (&mutex); + + gcc_assert (NULL == active_jit_ctxt); + active_jit_ctxt = this; + + /* Call into the rest of gcc. + For now, we have to assemble command-line options to pass into + toplev_main, so that they can be parsed. */ + const char *fake_args[20]; + /* Pass in user-provided "progname", if any, so that it makes it + into GCC's "progname" global, used in various diagnostics. */ + const char *progname = m_str_options[GCC_JIT_STR_OPTION_PROGNAME]; + fake_args[0] = progname ? progname : "libgccjit.so"; + + fake_args[1] = "fake.c"; + unsigned int num_args = 2; + +#define ADD_ARG(arg) \ + do \ + { \ + gcc_assert(num_args < sizeof(fake_args)/sizeof(char*)); \ + fake_args[num_args++] = arg; \ + } \ + while (0) + + ADD_ARG ("-fPIC"); + + /* Handle int options: */ + switch (m_int_options[GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL]) + { + default: + add_error ("unrecognized optimization level"); + goto error; + + case 0: + ADD_ARG ("-O0"); + break; + + case 1: + ADD_ARG ("-O1"); + break; + + case 2: + ADD_ARG ("-O2"); + break; + + case 3: + ADD_ARG ("-O3"); + break; + } + /* What about -Os? */ + + /* Handle bool options: */ + if (m_bool_options[GCC_JIT_BOOL_OPTION_DEBUGINFO]) + ADD_ARG ("-g"); + + /* Suppress timing (and other) info. */ + if (1) + { + ADD_ARG ("-quiet"); + quiet_flag = 1; + } + + /* Aggressively garbage-collect, to shake out bugs: */ + if (0) + { + ADD_ARG ("--param"); + ADD_ARG ("ggc-min-expand=0"); + ADD_ARG ("--param"); + ADD_ARG ("ggc-min-heapsize=0"); + } + + toplev_options toplev_opts; + toplev_opts.use_TV_TOTAL = false; + + if (time_report || !quiet_flag || flag_detailed_statistics) + timevar_init (); + + timevar_start (TV_TOTAL); + + toplev_main (num_args, const_cast <char **> (fake_args), &toplev_opts); + toplev_finalize (); + + active_jit_ctxt = NULL; + + if (m_cb_result) + goto error; + + timevar_push (TV_ASSEMBLE); + + /* Gross hacks follow: + We have a .s file; we want a .so file. + We could reuse parts of gcc/gcc.c to do this. + For now, just use the /usr/bin/gcc on the system... + */ + /* FIXME: totally faking it for now, not even using pex */ + /* FIXME: don't use the same filename for everything; + also TOCTTOU issues, presumably. */ + system ("gcc -shared fake.s -o fake.so"); + + timevar_pop (TV_ASSEMBLE); + + // TODO: split out assembles vs linker + + /* dlopen the .so file. */ + { + const char *error; + + timevar_push (TV_LOAD); + + /* Clear any existing error. */ + dlerror (); + + handle = dlopen ("./fake.so", RTLD_NOW | RTLD_LOCAL); + if ((error = dlerror()) != NULL) { + fprintf(stderr, "%s

", error); + } + if (handle) + result_obj = new result (handle); + else + result_obj = NULL; + + timevar_pop (TV_LOAD); + } + + timevar_stop (TV_TOTAL); + timevar_print (stderr); + + error: + /* Release the big GCC mutex. */ + active_jit_ctxt = NULL; + pthread_mutex_unlock (&mutex); + + return result_obj; +} + +void +gcc::jit::context:: +invoke_code_factory () +{ + /* Adapted from c-common.c:c_common_nodes_and_builtins. */ + tree array_domain_type = build_index_type (size_int (200)); + m_char_array_type_node + = build_array_type (char_type_node, array_domain_type); + + m_const_char_ptr + = build_pointer_type (build_qualified_type (char_type_node, + TYPE_QUAL_CONST)); + + /* Call the client-provided code factory: */ + timevar_push (TV_CLIENT_CALLBACK); + m_within_code_factory = true; + m_cb_result = m_code_factory ((gcc_jit_context *)this, m_user_data); + m_within_code_factory = false; + timevar_pop (TV_CLIENT_CALLBACK); + + if (!m_cb_result) + { + int i; + function *func; + FOR_EACH_VEC_ELT (m_functions, i, func) + { + gcc_assert (func); + func->postprocess (); + } + } +} + +void +gcc::jit::context:: +add_error (const char */*msg*/) +{ + // TODO +} + +gcc::jit::result:: +result(void *dso_handle) + : m_dso_handle(dso_handle) +{ +} + +gcc::jit::result:: +~result() +{ + dlclose (m_dso_handle); +} + +void * +gcc::jit::result:: +get_code (const char *funcname) +{ + void *code; + const char *error; + + /* Clear any existing error. */ + dlerror (); + + code = dlsym (m_dso_handle, funcname); + + if ((error = dlerror()) != NULL) { + fprintf(stderr, "%s

", error); + } + + return code; +} diff --git a/gcc/jit/internal-api.h b/gcc/jit/internal-api.h new file mode 100644 index 0000000..8555d4b --- /dev/null +++ b/gcc/jit/internal-api.h @@ -0,0 +1,363 @@ +#include "libgccjit.h" + +#include "tree.h" +#include "tree-iterator.h" + +namespace gcc { + +namespace jit { + +class result; +class location; +class type; +class field; +class function; +class label; +class rvalue; +class lvalue; +class param; +class local; +class loop; + +/* A JIT-compilation context. */ +class context +{ +public: + void gt_ggc_mx (); + + void + set_code_factory (gcc_jit_code_callback cb, + void *user_data); + + type * + get_void_type (); + + type * + get_char_type (); + + type * + get_int_type (); + + type * + get_float_type (); + + type * + get_double_type (); + + field * + new_field (location *loc, + type *type, + const char *name); + + type * + new_struct_type (location *loc, + const char *name, + int num_fields, + field **fields); + + + param * + new_param (location *loc, + type *type, + const char *name); + + function * + new_function (location *loc, + enum gcc_jit_function_kind kind, + type *return_type, + const char *name, + int num_params, + param **params, + int is_variadic); + + local * + new_local (location *loc, + type *type, + const char *name); + + rvalue * + new_rvalue_from_int (type *type, + int value); + + rvalue * + new_string_literal (const char *value); + + rvalue * + new_binary_op (location *loc, + enum gcc_jit_binary_op op, + type *result_type, + rvalue *a, rvalue *b); + + rvalue * + new_comparison (location *loc, + enum gcc_jit_comparison op, + rvalue *a, rvalue *b); + + rvalue * + new_call (location *loc, + function *func, + int numargs , rvalue **args); + + rvalue * + new_array_lookup (location *loc, + rvalue *ptr, + rvalue *index); + + lvalue * + new_field_access (location *loc, + rvalue *ptr_or_struct, + const char *fieldname); + + void + set_str_option (enum gcc_jit_str_option opt, + const char *value); + + void + set_int_option (enum gcc_jit_int_option opt, + int value); + + void + set_bool_option (enum gcc_jit_bool_option opt, + int value); + + int + get_bool_option (enum gcc_jit_bool_option opt) const + { + return m_bool_options[opt]; + } + + result * + compile (); + + void + invoke_code_factory (); + + bool + within_code_factory () const { return m_within_code_factory; } + + void + add_error (const char *msg); + +private: + gcc_jit_code_callback m_code_factory; + bool m_within_code_factory; + void *m_user_data; + int m_cb_result; /* Result from client-provided code factory. */ + vec<function *> m_functions; + const char *m_str_options[GCC_JIT_NUM_STR_OPTIONS]; + int m_int_options[GCC_JIT_NUM_INT_OPTIONS]; + bool m_bool_options[GCC_JIT_NUM_BOOL_OPTIONS]; + tree m_char_array_type_node; + tree m_const_char_ptr; +}; + +/* The result of JIT-compilation. */ +class result +{ +public: + result(void *dso_handle); + + virtual ~result(); + + void * + get_code (const char *funcname); + +private: + void *m_dso_handle; +}; + +extern context *active_jit_ctxt; + +/* A temporary wrapper object. + These objects are (mostly) only valid within the code factory callback. + We allocate them on the GC heap, so that they will be cleaned + the next time the GC collects. + The exception is the "function" class, which is tracked and marked by + the jit::context, since it needs to stay alive during post-processing + (when the GC could run). */ +class wrapper +{ +public: + /* Allocate in the GC heap. */ + void *operator new (size_t sz); + +}; + +class location : public wrapper +{ +}; + +class type : public wrapper +{ +public: + type (tree inner) + : m_inner(inner) + {} + + tree as_tree () const { return m_inner; } + + type *get_pointer () const { return new type (build_pointer_type (m_inner)); } + + type *get_const () const + { + return new type (build_qualified_type (m_inner, TYPE_QUAL_CONST)); + } + +private: + tree m_inner; +}; + +class field : public wrapper +{ +public: + field (tree inner) + : m_inner(inner) + {} + + tree as_tree () const { return m_inner; } + +private: + tree m_inner; +}; + +class function : public wrapper +{ +public: + function(context *ctxt, tree fndecl, enum gcc_jit_function_kind kind); + + void gt_ggc_mx (); + + tree get_return_type_as_tree () const; + + tree as_fndecl () const { return m_inner_fndecl; } + + label* + new_forward_label (const char *name); + + void + add_eval (location *loc, + rvalue *rvalue); + + void + add_assignment (location *loc, + lvalue *lvalue, + rvalue *rvalue); + + void + add_conditional (location *loc, + rvalue *boolval, + label *on_true, + label *on_false); + + label * + add_label (location *loc, + const char *name); + + void + place_forward_label (label *lab); + + void + add_jump (location *loc, + label *target); + + void + add_return (location *loc, + rvalue *rvalue); + + loop * + new_loop (location *loc, + rvalue *boolval); + + void + postprocess (); + +public: + context *m_ctxt; + +private: + tree m_inner_fndecl; + enum gcc_jit_function_kind m_kind; + tree m_stmt_list; + tree_stmt_iterator m_stmt_iter; +}; + +class label : public wrapper +{ +public: + label (function *func, + const char *name); + + tree as_label_decl () const { return m_label_decl; } + +private: + tree m_label_decl; + +public: // for now + tree m_label_expr; +}; + +class rvalue : public wrapper +{ +public: + rvalue (tree inner) + : m_inner(inner) + {} + + rvalue * + as_rvalue () { return this; } + + tree as_tree () const { return m_inner; } + + type * + get_type () { return new type (TREE_TYPE (m_inner)); } + +private: + tree m_inner; +}; + +class lvalue : public rvalue +{ +public: + lvalue (tree inner) + : rvalue(inner) + {} + + lvalue * + as_lvalue () { return this; } +}; + +class param : public lvalue +{ +public: + param (tree inner) + : lvalue(inner) + {} +}; + +class local : public lvalue +{ +public: + local (tree inner) + : lvalue(inner) + {} +}; + +class loop : public wrapper +{ +public: + loop (function *func, location *loc, rvalue *boolval); + + void end (location *loc); + +private: + function *m_func; + label *m_label_cond; + label *m_label_body; + label *m_label_end; +}; + + +} // namespace gcc::jit + +} // namespace gcc + diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c new file mode 100644 index 0000000..82f1c5a --- /dev/null +++ b/gcc/jit/libgccjit.c @@ -0,0 +1,466 @@ +/* Implementation of the C API; all wrappers into a C++ API */ +#include "config.h" +#include "system.h" +#include "ansidecl.h" +#include "coretypes.h" +#include "opts.h" + +#include "libgccjit.h" +#include "internal-api.h" + +struct gcc_jit_context : public gcc::jit::context +{ +}; + +struct gcc_jit_result : public gcc::jit::result +{ +}; + +struct gcc_jit_location : public gcc::jit::location +{ +}; + +struct gcc_jit_type : public gcc::jit::type +{ +}; + +struct gcc_jit_field : public gcc::jit::field +{ +}; + +struct gcc_jit_function : public gcc::jit::function +{ +}; + +struct gcc_jit_label : public gcc::jit::label +{ +}; + +struct gcc_jit_rvalue : public gcc::jit::rvalue +{ +}; + +struct gcc_jit_lvalue : public gcc::jit::lvalue +{ +}; + +struct gcc_jit_param : public gcc::jit::param +{ +}; + +struct gcc_jit_local : public gcc::jit::local +{ +}; + +struct gcc_jit_loop : public gcc::jit::loop +{ +}; + +gcc_jit_context * +gcc_jit_context_acquire (void) +{ + return new gcc_jit_context (); +} + +void +gcc_jit_context_release (gcc_jit_context *ctxt) +{ + delete ctxt; +} + +void +gcc_jit_context_set_code_factory (gcc_jit_context *ctxt, + gcc_jit_code_callback cb, + void *user_data) +{ + gcc_assert (ctxt); + ctxt->set_code_factory (cb, user_data); +} + +/********************************************************************** + Functions for use within the code factory. + **********************************************************************/ +#define ASSERT_WITHIN_CALLBACK(CTXT) \ + do { \ + gcc_assert ((CTXT)->within_code_factory ()); \ + } while (0) + +#define ASSERT_NOT_WITHIN_CALLBACK(CTXT) \ + do { \ + gcc_assert (!(CTXT)->within_code_factory ()); \ + } while (0) + +gcc_jit_type * +gcc_jit_context_get_void_type (gcc_jit_context *ctxt) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_type *)ctxt->get_void_type (); +} + +gcc_jit_type * +gcc_jit_context_get_char_type (gcc_jit_context *ctxt) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_type *)ctxt->get_char_type (); +} + +gcc_jit_type * +gcc_jit_context_get_int_type (gcc_jit_context *ctxt) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_type *)ctxt->get_int_type (); +} + +gcc_jit_type * +gcc_jit_context_get_float_type (gcc_jit_context *ctxt) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_type *)ctxt->get_float_type (); +} + +gcc_jit_type * +gcc_jit_context_get_double_type (gcc_jit_context *ctxt) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_type *)ctxt->get_double_type (); +} + +gcc_jit_type * +gcc_jit_type_get_pointer (gcc_jit_type *type) +{ + return (gcc_jit_type *)type->get_pointer (); +} + +gcc_jit_type * +gcc_jit_type_get_const (gcc_jit_type *type) +{ + return (gcc_jit_type *)type->get_const (); +} + +gcc_jit_field * +gcc_jit_context_new_field (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_type *type, + const char *name) +{ + return (gcc_jit_field *)ctxt->new_field (loc, type, name); +} + +gcc_jit_type * +gcc_jit_context_new_struct_type (gcc_jit_context *ctxt, + gcc_jit_location *loc, + const char *name, + int num_fields, + gcc_jit_field **fields) +{ + return (gcc_jit_type *)ctxt->new_struct_type (loc, name, num_fields, + (gcc::jit::field **)fields); +} + + +/* Constructing functions. */ +gcc_jit_param * +gcc_jit_context_new_param (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_type *type, + const char *name) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_param *)ctxt->new_param (loc, type, name); +} + +gcc_jit_rvalue * +gcc_jit_param_as_rvalue (gcc_jit_param *param) +{ + return (gcc_jit_rvalue *)param->as_rvalue (); +} + +gcc_jit_function * +gcc_jit_context_new_function (gcc_jit_context *ctxt, + gcc_jit_location *loc, + enum gcc_jit_function_kind kind, + gcc_jit_type *return_type, + const char *name, + int num_params, + gcc_jit_param **params, + int is_variadic) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_function*) + ctxt->new_function (loc, kind, return_type, name, + num_params, + (gcc::jit::param **)params, + is_variadic); +} + +gcc_jit_label* +gcc_jit_function_new_forward_label (gcc_jit_function *func, + const char *name) +{ + return (gcc_jit_label *)func->new_forward_label (name); +} + +gcc_jit_local * +gcc_jit_context_new_local (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_type *type, + const char *name) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_local *)ctxt->new_local (loc, type, name); +} + +gcc_jit_lvalue * +gcc_jit_local_as_lvalue (gcc_jit_local *local) +{ + return (gcc_jit_lvalue *)local->as_lvalue (); +} + +gcc_jit_rvalue * +gcc_jit_local_as_rvalue (gcc_jit_local *local) +{ + return (gcc_jit_rvalue *)local->as_rvalue (); +} + +gcc_jit_rvalue * +gcc_jit_lvalue_as_rvalue (gcc_jit_lvalue *lvalue) +{ + return (gcc_jit_rvalue *)lvalue->as_rvalue (); +} + +gcc_jit_rvalue * +gcc_jit_context_new_rvalue_from_int (gcc_jit_context *ctxt, + gcc_jit_type *type, + int value) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_rvalue *)ctxt->new_rvalue_from_int (type, value); +} + +gcc_jit_rvalue * +gcc_jit_context_zero (gcc_jit_context *ctxt, + gcc_jit_type *type) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return gcc_jit_context_new_rvalue_from_int (ctxt, type, 0); +} + +gcc_jit_rvalue * +gcc_jit_context_one (gcc_jit_context *ctxt, + gcc_jit_type *type) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return gcc_jit_context_new_rvalue_from_int (ctxt, type, 1); +} + +gcc_jit_rvalue * +gcc_jit_context_new_string_literal (gcc_jit_context *ctxt, + const char *value) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_rvalue *)ctxt->new_string_literal (value); +} + + +gcc_jit_rvalue * +gcc_jit_context_new_binary_op (gcc_jit_context *ctxt, + gcc_jit_location *loc, + enum gcc_jit_binary_op op, + gcc_jit_type *result_type, + gcc_jit_rvalue *a, gcc_jit_rvalue *b) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_rvalue *)ctxt->new_binary_op (loc, op, result_type, a, b); +} + +gcc_jit_rvalue * +gcc_jit_context_new_comparison (gcc_jit_context *ctxt, + gcc_jit_location *loc, + enum gcc_jit_comparison op, + gcc_jit_rvalue *a, gcc_jit_rvalue *b) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_rvalue *)ctxt->new_comparison (loc, op, a, b); +} + + +gcc_jit_rvalue * +gcc_jit_context_new_call (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_function *func, + int numargs , gcc_jit_rvalue **args) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_rvalue *)ctxt->new_call (loc, + func, + numargs, + (gcc::jit::rvalue **)args); +} + +extern gcc_jit_rvalue * +gcc_jit_context_new_array_lookup (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_rvalue *ptr, + gcc_jit_rvalue *index) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_rvalue *)ctxt->new_array_lookup (loc, ptr, index); +} + +extern gcc_jit_lvalue * +gcc_jit_context_new_field_access (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_rvalue *ptr_or_struct, + const char *fieldname) +{ + ASSERT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_lvalue *)ctxt->new_field_access (loc, ptr_or_struct, fieldname); +} + +gcc_jit_label * +gcc_jit_function_add_label (gcc_jit_function *func, + gcc_jit_location *loc, + const char *name) +{ + return (gcc_jit_label *)func->add_label (loc, name); +} + +void +gcc_jit_function_place_forward_label (gcc_jit_function *func, + gcc_jit_label *lab) +{ + func->place_forward_label (lab); +} + +void +gcc_jit_function_add_eval (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_rvalue *rvalue) +{ + return func->add_eval (loc, rvalue); +} + +void +gcc_jit_function_add_assignment (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_lvalue *lvalue, + gcc_jit_rvalue *rvalue) +{ + return func->add_assignment (loc, lvalue, rvalue); +} + +void +gcc_jit_function_add_assignment_op (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_lvalue *lvalue, + enum gcc_jit_binary_op op, + gcc_jit_rvalue *rvalue) +{ + gcc_jit_type *type = (gcc_jit_type *)lvalue->get_type (); + gcc_jit_function_add_assignment ( + func, loc, + lvalue, + gcc_jit_context_new_binary_op ( + (gcc_jit_context *)func->m_ctxt, + loc, op, type, + gcc_jit_lvalue_as_rvalue (lvalue), + rvalue)); +} + +void +gcc_jit_function_add_conditional (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_rvalue *boolval, + gcc_jit_label *on_true, + gcc_jit_label *on_false) +{ + return func->add_conditional (loc, boolval, on_true, on_false); +} + +void +gcc_jit_function_add_jump (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_label *target) +{ + func->add_jump (loc, target); +} + +void +gcc_jit_function_add_return (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_rvalue *rvalue) +{ + return func->add_return (loc, rvalue); +} + +gcc_jit_loop * +gcc_jit_function_new_loop (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_rvalue *boolval) +{ + return (gcc_jit_loop *)func->new_loop (loc, boolval); +} + +void +gcc_jit_loop_end (gcc_jit_loop *loop, + gcc_jit_location *loc) +{ + loop->end (loc); +} + +/********************************************************************** + Option-management + **********************************************************************/ + +void +gcc_jit_context_set_str_option (gcc_jit_context *ctxt, + enum gcc_jit_str_option opt, + const char *value) +{ + gcc_assert (ctxt); + ASSERT_NOT_WITHIN_CALLBACK (ctxt); + ctxt->set_str_option (opt, value); +} + +void +gcc_jit_context_set_int_option (gcc_jit_context *ctxt, + enum gcc_jit_int_option opt, + int value) +{ + gcc_assert (ctxt); + ASSERT_NOT_WITHIN_CALLBACK (ctxt); + ctxt->set_int_option (opt, value); +} + +void +gcc_jit_context_set_bool_option (gcc_jit_context *ctxt, + enum gcc_jit_bool_option opt, + int value) +{ + gcc_assert (ctxt); + ASSERT_NOT_WITHIN_CALLBACK (ctxt); + ctxt->set_bool_option (opt, value); +} + +gcc_jit_result * +gcc_jit_context_compile (gcc_jit_context *ctxt) +{ + gcc_assert (ctxt); + ASSERT_NOT_WITHIN_CALLBACK (ctxt); + return (gcc_jit_result *)ctxt->compile (); +} + +void * +gcc_jit_result_get_code (gcc_jit_result *result, + const char *fnname) +{ + gcc_assert (result); + gcc_assert (fnname); + return result->get_code (fnname); +} + +void +gcc_jit_result_release (gcc_jit_result *result) +{ + delete result; +} diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h new file mode 100644 index 0000000..a1e68f8 --- /dev/null +++ b/gcc/jit/libgccjit.h @@ -0,0 +1,355 @@ +/* + A pure C API to enable client code to embed GCC as a JIT-compiler. + */ +#ifndef LIBGCCJIT_H +#define LIBGCCJIT_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Various opaque structs. */ +typedef struct gcc_jit_context gcc_jit_context; +typedef struct gcc_jit_result gcc_jit_result; +typedef struct gcc_jit_location gcc_jit_location; +typedef struct gcc_jit_type gcc_jit_type; +typedef struct gcc_jit_field gcc_jit_field; +typedef struct gcc_jit_function gcc_jit_function; +typedef struct gcc_jit_label gcc_jit_label; +typedef struct gcc_jit_rvalue gcc_jit_rvalue; +typedef struct gcc_jit_lvalue gcc_jit_lvalue; +typedef struct gcc_jit_param gcc_jit_param; +typedef struct gcc_jit_local gcc_jit_local; +typedef struct gcc_jit_loop gcc_jit_loop; + +/* + Acquire a JIT-compilation context. + + FIXME: error-handling? +*/ +extern gcc_jit_context * +gcc_jit_context_acquire (void); + +/* Release the context. After this call, it's no longer valid to use + the ctxt. */ +extern void +gcc_jit_context_release (gcc_jit_context *ctxt); + +/* A function-pointer type. + This is the client-provided hook for calling into the code-generation + API. + It should return 0 if there were no errors, or nonzero if errors + occurred (e.g. within client code that the library has no knowledge + of). */ +typedef int (*gcc_jit_code_callback) (gcc_jit_context *ctxt, + void *user_data); + +extern void +gcc_jit_context_set_code_factory (gcc_jit_context *ctxt, + gcc_jit_code_callback cb, + void *user_data); + /* clearly I'm handwaving for now about the details of what that + callback looks like, but by doing it in a callback we also + avoid dealing with lifetimes of the results: entities can + only be referenced within the lifetime of the callback - once + control returns out of the callback, you can't use them. This + avoids having to expose details of GCC's garbage-collector, + I hope. */ + +/********************************************************************** + Functions for use within the code factory. + **********************************************************************/ + +/* Access to specific types. */ +extern gcc_jit_type * +gcc_jit_context_get_void_type (gcc_jit_context *ctxt); + +extern gcc_jit_type * +gcc_jit_context_get_char_type (gcc_jit_context *ctxt); + +extern gcc_jit_type * +gcc_jit_context_get_int_type (gcc_jit_context *ctxt); + +extern gcc_jit_type * +gcc_jit_context_get_float_type (gcc_jit_context *ctxt); + +extern gcc_jit_type * +gcc_jit_context_get_double_type (gcc_jit_context *ctxt); + +/* Constructing new types. */ +extern gcc_jit_type * +gcc_jit_type_get_pointer (gcc_jit_type *type); + +extern gcc_jit_type * +gcc_jit_type_get_const (gcc_jit_type *type); + +/* Struct-handling. */ +extern gcc_jit_field * +gcc_jit_context_new_field (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_type *type, + const char *name); + +extern gcc_jit_type * +gcc_jit_context_new_struct_type (gcc_jit_context *ctxt, + gcc_jit_location *loc, + const char *name, + int num_fields, + gcc_jit_field **fields); + +/* Constructing functions. */ +extern gcc_jit_param * +gcc_jit_context_new_param (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_type *type, + const char *name); + +extern gcc_jit_rvalue * +gcc_jit_param_as_rvalue (gcc_jit_param *param); + +enum gcc_jit_function_kind +{ + /* Function is defined by the client code and visible + by name outside of the JIT. */ + GCC_JIT_FUNCTION_EXPORTED, + + /* Function is defined by the client code, but is invisible + outside of the JIT. Analogous to a "static" function. */ + GCC_JIT_FUNCTION_INTERNAL, + + /* Function is not defined by the client code; we're merely + referring to it. Analogous to using an "extern" function from a + header file. */ + GCC_JIT_FUNCTION_IMPORTED + +}; + + +extern gcc_jit_function * +gcc_jit_context_new_function (gcc_jit_context *ctxt, + gcc_jit_location *loc, + enum gcc_jit_function_kind kind, + gcc_jit_type *return_type, + const char *name, + int num_params, + gcc_jit_param **params, + int is_variadic); + +extern gcc_jit_label * +gcc_jit_function_new_forward_label (gcc_jit_function *func, + const char *name); + +extern gcc_jit_local * +gcc_jit_context_new_local (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_type *type, + const char *name); + +extern gcc_jit_lvalue * +gcc_jit_local_as_lvalue (gcc_jit_local *local); + +extern gcc_jit_rvalue * +gcc_jit_local_as_rvalue (gcc_jit_local *local); + +extern gcc_jit_rvalue * +gcc_jit_lvalue_as_rvalue (gcc_jit_lvalue *lvalue); + +/* Integer constants. */ +extern gcc_jit_rvalue * +gcc_jit_context_new_rvalue_from_int (gcc_jit_context *ctxt, + gcc_jit_type *type, + int value); + +extern gcc_jit_rvalue * +gcc_jit_context_zero (gcc_jit_context *ctxt, + gcc_jit_type *type); + +extern gcc_jit_rvalue * +gcc_jit_context_one (gcc_jit_context *ctxt, + gcc_jit_type *type); + +/* String literals. */ +extern gcc_jit_rvalue * +gcc_jit_context_new_string_literal (gcc_jit_context *ctxt, + const char *value); + + +enum gcc_jit_binary_op +{ + GCC_JIT_BINARY_OP_PLUS, + GCC_JIT_BINARY_OP_MINUS, + GCC_JIT_BINARY_OP_MULT +}; + +extern gcc_jit_rvalue * +gcc_jit_context_new_binary_op (gcc_jit_context *ctxt, + gcc_jit_location *loc, + enum gcc_jit_binary_op op, + gcc_jit_type *result_type, + gcc_jit_rvalue *a, gcc_jit_rvalue *b); + +enum gcc_jit_comparison +{ + GCC_JIT_COMPARISON_LT, + GCC_JIT_COMPARISON_GE +}; + +extern gcc_jit_rvalue * +gcc_jit_context_new_comparison (gcc_jit_context *ctxt, + gcc_jit_location *loc, + enum gcc_jit_comparison op, + gcc_jit_rvalue *a, gcc_jit_rvalue *b); + +extern gcc_jit_rvalue * +gcc_jit_context_new_call (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_function *func, + int numargs , gcc_jit_rvalue **args); + +extern gcc_jit_rvalue * +gcc_jit_context_new_array_lookup (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_rvalue *ptr, + gcc_jit_rvalue *index); + +/* Field access, either s.field or s->field. */ +extern gcc_jit_lvalue * +gcc_jit_context_new_field_access (gcc_jit_context *ctxt, + gcc_jit_location *loc, + gcc_jit_rvalue *ptr_or_struct, + const char *fieldname); + +/* Add evaluation of an rvalue, discarding the result + (e.g. a function call that "returns" void). */ +extern void +gcc_jit_function_add_eval (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_rvalue *rvalue); + +extern void +gcc_jit_function_add_assignment (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_lvalue *lvalue, + gcc_jit_rvalue *rvalue); + +/** + Modify an lvalue (analogous to "+=" and friends) + */ +extern void +gcc_jit_function_add_assignment_op (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_lvalue *lvalue, + enum gcc_jit_binary_op op, + gcc_jit_rvalue *rvalue); + +extern void +gcc_jit_function_add_conditional (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_rvalue *boolval, + gcc_jit_label *on_true, + gcc_jit_label *on_false); + +extern gcc_jit_label * +gcc_jit_function_add_label (gcc_jit_function *func, + gcc_jit_location *loc, + const char *name); + +extern void +gcc_jit_function_place_forward_label (gcc_jit_function *func, + gcc_jit_label *lab); + +extern void +gcc_jit_function_add_jump (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_label *target); + +extern void +gcc_jit_function_add_return (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_rvalue *rvalue); + +/* Helper function for creating a loop: + while (boolval) + { + BODY; + } + + Statements will be added to the body of the loop until + gcc_jit_loop_end is called. +*/ +extern gcc_jit_loop * +gcc_jit_function_new_loop (gcc_jit_function *func, + gcc_jit_location *loc, + gcc_jit_rvalue *boolval); + +extern void +gcc_jit_loop_end (gcc_jit_loop *loop, + gcc_jit_location *loc); + +/********************************************************************** + Option-management + **********************************************************************/ + +/* Options taking string values */ +enum gcc_jit_str_option +{ + GCC_JIT_STR_OPTION_PROGNAME, + GCC_JIT_NUM_STR_OPTIONS +}; + +/* Options taking int values */ +enum gcc_jit_int_option +{ + GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, + GCC_JIT_NUM_INT_OPTIONS +}; + +/* Options taking boolean values */ +enum gcc_jit_bool_option +{ + GCC_JIT_BOOL_OPTION_DEBUGINFO, + GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE, + GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, + GCC_JIT_NUM_BOOL_OPTIONS +}; + +/* The context directly stores the (const char *), so the passed string + must outlive the context. */ +extern void +gcc_jit_context_set_str_option (gcc_jit_context *ctxt, + enum gcc_jit_str_option opt, + const char *value); + +extern void +gcc_jit_context_set_int_option (gcc_jit_context *ctxt, + enum gcc_jit_int_option opt, + int value); + +extern void +gcc_jit_context_set_bool_option (gcc_jit_context *ctxt, + enum gcc_jit_bool_option opt, + int value); + +extern gcc_jit_result * +gcc_jit_context_compile (gcc_jit_context *ctxt); + /* This actually calls into GCC and runs the build, all + in a mutex for now. The result is a wrapper around a .so file. + Can we only call this once on a given context? */ + + +/* Locate a given function within the built machine code. + This will need to be cast to a function pointer of the + correct type before it can be called. */ +extern void * +gcc_jit_result_get_code (gcc_jit_result *result, + const char *funcname); + +extern void +gcc_jit_result_release (gcc_jit_result *result); + /* Once we're done with the code, this unloads the built .so file: */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBGCCJIT_H */ diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map new file mode 100644 index 0000000..f1c775a --- /dev/null +++ b/gcc/jit/libgccjit.map @@ -0,0 +1,57 @@ +# Linker script for libgccjit.so +{ + global: + gcc_jit_context_acquire; + gcc_jit_context_release; + gcc_jit_context_set_code_factory; + gcc_jit_context_get_void_type; + gcc_jit_context_get_char_type; + gcc_jit_context_get_int_type; + gcc_jit_context_get_float_type; + gcc_jit_context_get_double_type; + gcc_jit_type_get_pointer; + gcc_jit_type_get_const; + gcc_jit_context_new_field; + gcc_jit_context_new_struct_type; + gcc_jit_context_new_param; + gcc_jit_param_as_rvalue; + gcc_jit_context_new_function; + gcc_jit_function_new_forward_label; + gcc_jit_context_new_local; + gcc_jit_local_as_lvalue; + gcc_jit_local_as_rvalue; + gcc_jit_lvalue_as_rvalue; + gcc_jit_context_new_rvalue_from_int; + gcc_jit_context_zero; + gcc_jit_context_one; + gcc_jit_context_new_string_literal; + gcc_jit_context_new_binary_op; + gcc_jit_context_new_comparison; + gcc_jit_context_new_call; + gcc_jit_context_new_array_lookup; + gcc_jit_context_new_field_access; + gcc_jit_function_add_eval; + gcc_jit_function_add_assignment; + gcc_jit_function_add_assignment_op; + gcc_jit_function_add_conditional; + gcc_jit_function_add_label; + gcc_jit_function_place_forward_label; + gcc_jit_function_add_jump; + gcc_jit_function_add_return; + gcc_jit_function_new_loop; + gcc_jit_loop_end; + gcc_jit_context_set_str_option; + gcc_jit_context_set_int_option; + gcc_jit_context_set_bool_option; + gcc_jit_context_compile; + gcc_jit_result_get_code; + gcc_jit_result_release; + + # For now: + main; + + # For use by test functions: + called_function; + + local: *; +}; \ No newline at end of file diff --git a/gcc/jit/notes.txt b/gcc/jit/notes.txt new file mode 100644 index 0000000..6d1288f --- /dev/null +++ b/gcc/jit/notes.txt @@ -0,0 +1,82 @@ +Client Code . Generated . libgccjit.so + . code . + . . JIT API . JIT "Frontend". (libbackend.a) +.................................................................................... + â . . . . + â . . . . + â . . . . + V . . gcc_jit_context_compile . + ââââââââââââââââââââââââââ> . . + . . â . . + . . â ACQUIRE MUTEX . + . . â . . + . . Vâââââââââââââââââââââââ> toplev_main (for now) + . . . . â + . . . . (various code) + . . . . â + . . . . V + . . . <âââââââââââââââââ langhook:parse_file + . . . â . + . . . â (jit_langhook_parse_file) + . . . â . +..........................................â..................VVVVVVVVVVVVV... + code factory . . . â . No GC in here + callback . . . â . + <âââââââââââââââââââââââââââââââââââ . + â . . . (invoke_cb) . + â . . C API . . + ââââââââââââââââââââââ> . . + . . â Internal API . + . . âââ> . . + . . â . . + . . ââââââââââââââââââââââââ> creation of functions, + . . . . types, expression trees + . . <ââââââââââââââââââââââââ etc + . . <âââ . . + . . â . . + ââââââââââââââââââââââ . . + âetc . . . . + â . . . . + ââââââââââââââââââââââââââââââââââ> . + . . . â . No GC in here +..........................................â..................AAAAAAAAAAAAA... + . . . â for each function + . . . ââ> postprocess + . . . â . + . . . ââââââââââââ> cgraph_finalize_function + . . . <ââââââââââââ + . . . <ââ . + . . . â . + . . . ââââââââââââââââââ> (end of + . . . . â langhook_parse_file) + . . . . â + . . . . (various code) + . . . . â + . . . . â + . . . <âââââââââââââââââ langhook:write_globals + . . . â . + . . . â (jit_langhook_write_globals) + . . . â . + . . . â . + . . . ââââââââââââââââââ> finalize_compilation_unit + . . . . â + . . . . (the middleâend and backend) + . . . . â + . . <âââââââââââââââââââââââââââââ end of toplev_main + . . â RELEASE MUTEX . + . . â . . + . . â Convert assembler to DSO + . . â . . + . . â Load DSO . + <âââââââââââââââââââââââââââ . . + â . . . . + Get (void*). . . . + â . . . . + â Call it . . . . + âââââââââââââââ> . . . + . â . . . + . â . . . + <âââââââââââââââ . . . + â . . . . + â . . . . +etc diff --git a/gcc/main.c b/gcc/main.c index 82acf95..b893308 100644 --- a/gcc/main.c +++ b/gcc/main.c @@ -33,5 +33,8 @@ int main (int argc, char **argv); int main (int argc, char **argv) { - return toplev_main (argc, argv); + toplev_options toplev_opts; + toplev_opts.use_TV_TOTAL = true; + + return toplev_main (argc, argv, &toplev_opts); } diff --git a/gcc/params.c b/gcc/params.c index 2fc1d15..22c7a27 100644 --- a/gcc/params.c +++ b/gcc/params.c @@ -69,6 +69,9 @@ add_params (const param_info params[], size_t n) void global_init_params (void) { + /* Make param initialization be idempotent. */ + if (params_finished) + return; add_params (lang_independent_params, LAST_PARAM); targetm_common.option_default_params (); } diff --git a/gcc/passes.c b/gcc/passes.c index f3f85fd..4eaf0b6 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -1997,6 +1997,7 @@ execute_ipa_summary_passes (struct ipa_opt_pass_d *ipa_pass) if (pass->tv_id) timevar_push (pass->tv_id); + current_pass = pass; ipa_pass->generate_summary (); /* Stop timevar. */ diff --git a/gcc/predict.c b/gcc/predict.c index 3b275d0..fd13e15 100644 --- a/gcc/predict.c +++ b/gcc/predict.c @@ -3038,3 +3038,7 @@ rebuild_frequencies (void) gcc_unreachable (); timevar_pop (TV_REBUILD_FREQUENCIES); } + +void predict_c_finalize (void) +{ +} diff --git a/gcc/stringpool.c b/gcc/stringpool.c index f4d0dae..503dd43 100644 --- a/gcc/stringpool.c +++ b/gcc/stringpool.c @@ -61,6 +61,11 @@ stringpool_ggc_alloc (size_t x) void init_stringpool (void) { + /* Clean up if we're called more than once. + (We can't make this idempotent since identifiers contain state) */ + if (ident_hash) + ht_destroy (ident_hash); + /* Create with 16K (2^14) entries. */ ident_hash = ht_create (14); ident_hash->alloc_node = alloc_node; diff --git a/gcc/symtab.c b/gcc/symtab.c index 62fe166..af6fb13 100644 --- a/gcc/symtab.c +++ b/gcc/symtab.c @@ -1154,4 +1154,13 @@ symtab_semantically_equivalent_p (symtab_node a, bb = b; return bb == ba; } + +void symtab_c_finalize (void) +{ + symtab_hash = NULL; + assembler_name_hash = NULL; + symtab_nodes = NULL; + symtab_order = 0; +} + #include "gt-symtab.h" diff --git a/gcc/testsuite/jit.dg/harness.h b/gcc/testsuite/jit.dg/harness.h new file mode 100644 index 0000000..11537ea --- /dev/null +++ b/gcc/testsuite/jit.dg/harness.h @@ -0,0 +1,141 @@ +/* + Code shared between multiple testcases. + + This file contains "main" and support code. + Each testcase should implement the following hooks: + + extern int + code_making_callback (gcc_jit_context *ctxt, void * user_data); + + extern void + verify_code (gcc_jit_result *result); + + */ +#include <stdlib.h> +#include <stdio.h> + +#include <dejagnu.h> + +#include "libgccjit.h" + +static char test[1024]; + +#define CHECK_NON_NULL(PTR) \ + do { \ + if ((PTR) != NULL) \ + { \ + pass ("%s: %s is non-null", test, #PTR); \ + } \ + else \ + { \ + fail ("%s: %s is NULL", test, #PTR); \ + abort (); \ + } \ + } while (0) + +#define CHECK_VALUE(ACTUAL, EXPECTED) \ + do { \ + if ((ACTUAL) == (EXPECTED)) \ + { \ + pass ("%s: actual: %s == expected: %s", test, #ACTUAL, #EXPECTED); \ + } \ + else \ + { \ + fail ("%s: actual: %s != expected: %s", test, #ACTUAL, #EXPECTED); \ + fprintf (stderr, "incorrect value

"); \ + abort (); \ + } \ + } while (0) + +/* Hooks that testcases should provide. */ +extern int +code_making_callback (gcc_jit_context *ctxt, void * user_data); + +extern void +verify_code (gcc_jit_result *result); + +/* Run one iteration of the test. */ + +static void +test_jit (const char *argv0) +{ + gcc_jit_context *ctxt; + gcc_jit_result *result; + + ctxt = gcc_jit_context_acquire (); + /* FIXME: error-handling */ + + gcc_jit_context_set_code_factory (ctxt, code_making_callback, NULL); + + /* Set up options. */ + gcc_jit_context_set_str_option ( + ctxt, + GCC_JIT_STR_OPTION_PROGNAME, + argv0); + gcc_jit_context_set_int_option ( + ctxt, + GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, + 0); + gcc_jit_context_set_bool_option ( + ctxt, + GCC_JIT_BOOL_OPTION_DEBUGINFO, + 1); + gcc_jit_context_set_bool_option ( + ctxt, + GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE, + 0); + gcc_jit_context_set_bool_option ( + ctxt, + GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, + 0); + + /* This actually calls into GCC and runs the build, all + in a mutex for now. */ + result = gcc_jit_context_compile (ctxt); + + gcc_jit_context_release (ctxt); + + verify_code (result); + + /* Once we're done with the code, this unloads the built .so file: */ + gcc_jit_result_release (result); +} + +/* We want to prefix all unit test results with the test, but dejagnu.exp's + host_execute appears to get confused by the leading "./" of argv0, + leading to all tests simply reporting as a single period character ".". + + Hence strip out the final component of the path to the program name, + so that we can use that in unittest reports. */ +const char* +extract_progname (const char *argv0) +{ + const char *p; + + p = argv0 + strlen (argv0); + while (p != argv0 && p[-1] != '/') + --p; + return p; +} + +int +main (int argc, char **argv) +{ + int i; + + for (i = 1; i <= 5; i++) + { + snprintf (test, sizeof (test), + "%s iteration %d of %d", + extract_progname (argv[0]), + i, 5); + + //printf ("ITERATION %d

", i); + test_jit (argv[0]); + //printf ("

"); + } + + totals (); + + return 0; +} diff --git a/gcc/testsuite/jit.dg/jit.exp b/gcc/testsuite/jit.dg/jit.exp new file mode 100644 index 0000000..3f3f30e --- /dev/null +++ b/gcc/testsuite/jit.dg/jit.exp @@ -0,0 +1,95 @@ +# Test code for libgccjit.so +# +# We will compile each of jit.dg/test-*.c into an executable +# dynamically linked against libgccjit.so, and then run each +# such executable. +# +# These executables call into the libgccjit.so API to create +# code, compile it, and run it, verifying that the results +# are as expected. See harness.h for shared code used by all +# such executables. +# +# The executables call into DejaGnu's unit testing C API to +# report PASS/FAIL results, which this script gathers back +# up into the Tcl world, reporting a summary of all results +# across all of the executables. + +load_lib dg.exp +load_lib prune.exp +load_lib gcc-defs.exp +load_lib timeout.exp +load_lib target-libpath.exp +load_lib gcc.exp +load_lib dejagnu.exp + +# GCC_UNDER_TEST is needed by gcc_target_compile +global GCC_UNDER_TEST +if ![info exists GCC_UNDER_TEST] { + set GCC_UNDER_TEST "[find_gcc]" +} + +# Initialize dg. +dg-init + +# Gather a list of all tests. +set tests [lsort [find $srcdir/$subdir test-*.c]] +verbose "tests: $tests" + +proc jit-dg-test { prog do_what extra_tool_flags } { + verbose "within jit-dg-test..." + verbose " prog: $prog" + verbose " do_what: $do_what" + verbose " extra_tool_flags: $extra_tool_flags" + + # Determine what to name the built executable. + set output_file "[file rootname [file tail $prog]].exe" + verbose "output_file: $output_file" + + # Create the test executable: + gcc_target_compile $prog $output_file $do_what \ + "{additional_flags=$extra_tool_flags}" + + # Run the test executable, capturing the PASS/FAIL textual output + # from the C API, converting it into the Tcl API. + + # We need to set LD_LIBRARY_PATH so that the test files can find + # libgccjit.so + # Do this using set_ld_library_path_env_vars from target-libpath.exp + global ld_library_path + global base_dir + set ld_library_path "$base_dir/../../" + set_ld_library_path_env_vars + + # If running with just a "make" and not a "make install", then I was + # running into + # "fatal error: -fuse-linker-plugin, but liblto_plugin.so not found" + # emitted by the inner gcc invoked to convert the .s into .so + # This appears to be due to not installing the built compiler; + # libto_plugin is a .la at build time, with the .so becoming installed + # at install time; the "set_ld_library_path_env_vars" function from + # target-libpath.exp that I'm using to set LD_LIBRARY_PATH to find + # the library under test, libgccjit.so, was setting GCC_EXEC_PREFIX to + # the builddir, thus picking up the built-but-not-installed toolchain. + # Hacking in an "unsetenv GCC_EXEC_PREFIX" here fixes the issue, + # allowing quick running of tests