From: David Malcolm Date: Tue, 25 Aug 2015 20:25:05 +0000 (+0000) Subject: Support embedding the driver in-process within libgccjit X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9376dd63e6a2d94823f6faf8212c9f37bef5a656;p=gcc.git Support embedding the driver in-process within libgccjit gcc/ChangeLog: * gcc-main.c (main): Add params to driver ctor. * gcc.c (class env_manager): New. (env): New global. (env_manager::init): New. (env_manager::get): New. (env_manager::xput): New. (env_manager::restore): New. Poison getenv and putenv. (DEFAULT_TARGET_SYSTEM_ROOT): New. (target_system_root): Update initialization to use DEFAULT_TARGET_SYSTEM_ROOT. (struct spec_list): Add field "default_ptr". (INIT_STATIC_SPEC): Initialize new field "default_ptr". (init_spec): Likewise. (set_spec): Clear field "default_ptr". (read_specs): Free "spec" and "buffer". (xputenv): Reimplement in terms of env_manager. (process_command): Replace ::getenv calls with calls to the env_manager singleton. (process_brace_body): Free string in three places. (driver::driver): New. (driver::~driver): New. (used_arg): Convert from a function to... (class used_arg_t): ...this class, and... (used_arg): ...this new global instance. (used_arg_t::finalize): New function. (getenv_spec_function): Add "const" to local "value". Replace ::getenv call with call to the env_manager singleton. (path_prefix_reset): New function. (driver::finalize): New function. * gcc.h (driver::driver): New. (driver::~driver): New. (driver::finalize): New. gcc/jit/ChangeLog: * docs/cp/topics/contexts.rst (gccjit::context::set_bool_use_external_driver): New. * docs/internals/test-hello-world.exe.log.txt: Update. * docs/topics/compatibility.rst (LIBGCCJIT_ABI_5): New. * docs/topics/contexts.rst (gcc_jit_context_set_bool_use_external_driver): New. * jit-common.h (enum inner_bool_option): Add INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER. * jit-playback.c (gcc_driver_name): New global. (gcc:jit::playback::context::invoke_driver): Split out second half into... (gcc::jit::playback::context::invoke_embedded_driver): ...this new function, and... (gcc::jit::playback::context::invoke_external_driver): ...this new function. * jit-playback.h (gcc::jit::playback::context::get_inner_bool_option): New. (gcc::jit::playback::context::invoke_embedded_driver): New. (gcc::jit::playback::context::invoke_external_driver): New. * jit-recording.c (inner_bool_option_reproducer_strings): Add entry for INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER. * libgccjit++.h (gccjit::context::set_bool_use_external_driver): New. * libgccjit.c (gcc_jit_context_set_bool_use_external_driver): New. * libgccjit.h (gcc_jit_context_set_bool_use_external_driver): New. (LIBGCCJIT_HAVE_gcc_jit_context_set_bool_use_external_driver): New. * libgccjit.map (LIBGCCJIT_ABI_5): New. * notes.txt: Show invocation of embedded copy of driver. * docs/internals/test-hello-world.exe.log.txt: Update gcc/testsuite/ChangeLog: * jit.dg/test-error-pr63969-missing-driver.c: Add call to gcc_jit_context_set_bool_use_external_driver. From-SVN: r227188 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 8cb2aeb7633..c79fb4870f7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,39 @@ +2015-08-25 David Malcolm + + * gcc-main.c (main): Add params to driver ctor. + * gcc.c (class env_manager): New. + (env): New global. + (env_manager::init): New. + (env_manager::get): New. + (env_manager::xput): New. + (env_manager::restore): New. + Poison getenv and putenv. + (DEFAULT_TARGET_SYSTEM_ROOT): New. + (target_system_root): Update initialization to use + DEFAULT_TARGET_SYSTEM_ROOT. + (struct spec_list): Add field "default_ptr". + (INIT_STATIC_SPEC): Initialize new field "default_ptr". + (init_spec): Likewise. + (set_spec): Clear field "default_ptr". + (read_specs): Free "spec" and "buffer". + (xputenv): Reimplement in terms of env_manager. + (process_command): Replace ::getenv calls with calls to the + env_manager singleton. + (process_brace_body): Free string in three places. + (driver::driver): New. + (driver::~driver): New. + (used_arg): Convert from a function to... + (class used_arg_t): ...this class, and... + (used_arg): ...this new global instance. + (used_arg_t::finalize): New function. + (getenv_spec_function): Add "const" to local "value". Replace + ::getenv call with call to the env_manager singleton. + (path_prefix_reset): New function. + (driver::finalize): New function. + * gcc.h (driver::driver): New. + (driver::~driver): New. + (driver::finalize): New. + 2015-08-25 Nathan Sidwell * optabs.c (emit_indirect_jump): Don't try an emit a jump if the diff --git a/gcc/gcc-main.c b/gcc/gcc-main.c index 230ba4846cd..a0aaa3c7fc7 100644 --- a/gcc/gcc-main.c +++ b/gcc/gcc-main.c @@ -40,7 +40,8 @@ extern int main (int, char **); int main (int argc, char **argv) { - driver d; + driver d (false, /* can_finalize */ + false); /* debug */ return d.main (argc, argv); } diff --git a/gcc/gcc.c b/gcc/gcc.c index 312bf04b47e..4c37edcedcc 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -43,6 +43,131 @@ compilation is specified by a string called a "spec". */ #include "params.h" #include "filenames.h" + + +/* Manage the manipulation of env vars. + + We poison "getenv" and "putenv", so that all enviroment-handling is + done through this class. Note that poisoning happens in the + preprocessor at the identifier level, and doesn't distinguish between + env.getenv (); + and + getenv (); + Hence we need to use "get" for the accessor method, not "getenv". */ + +class env_manager +{ + public: + void init (bool can_restore, bool debug); + const char *get (const char *name); + void xput (const char *string); + void restore (); + + private: + bool m_can_restore; + bool m_debug; + struct kv + { + char *m_key; + char *m_value; + }; + vec m_keys; + +}; + +/* The singleton instance of class env_manager. */ + +static env_manager env; + +/* Initializer for class env_manager. + + We can't do this as a constructor since we have a statically + allocated instance ("env" above). */ + +void +env_manager::init (bool can_restore, bool debug) +{ + m_can_restore = can_restore; + m_debug = debug; +} + +/* Get the value of NAME within the environment. Essentially + a wrapper for ::getenv, but adding logging, and the possibility + of caching results. */ + +const char * +env_manager::get (const char *name) +{ + const char *result = ::getenv (name); + if (m_debug) + fprintf (stderr, "env_manager::getenv (%s) -> %s\n", name, result); + return result; +} + +/* Put the given KEY=VALUE entry STRING into the environment. + If the env_manager was initialized with CAN_RESTORE set, then + also record the old value of KEY within the environment, so that it + can be later restored. */ + +void +env_manager::xput (const char *string) +{ + if (m_debug) + fprintf (stderr, "env_manager::xput (%s)\n", string); + if (verbose_flag) + fnotice (stderr, "%s\n", string); + + if (m_can_restore) + { + char *equals = strchr (const_cast (string), '='); + gcc_assert (equals); + + struct kv kv; + kv.m_key = strndup (string, equals - string); + const char *cur_value = ::getenv (kv.m_key); + if (m_debug) + fprintf (stderr, "saving old value: %s\n",cur_value); + kv.m_value = cur_value ? xstrdup (cur_value) : NULL; + m_keys.safe_push (kv); + } + + ::putenv (CONST_CAST (char *, string)); +} + +/* Undo any xputenv changes made since last restore. + Can only be called if the env_manager was initialized with + CAN_RESTORE enabled. */ + +void +env_manager::restore () +{ + unsigned int i; + struct kv *item; + + gcc_assert (m_can_restore); + + FOR_EACH_VEC_ELT_REVERSE (m_keys, i, item) + { + if (m_debug) + printf ("restoring saved key: %s value: %s\n", item->m_key, item->m_value); + if (item->m_value) + ::setenv (item->m_key, item->m_value, 1); + else + ::unsetenv (item->m_key); + free (item->m_key); + free (item->m_value); + } + + m_keys.truncate (0); +} + +/* Forbid other uses of getenv and putenv. */ +#if (GCC_VERSION >= 3000) +#pragma GCC poison getenv putenv +#endif + + + /* By default there is no special suffix for target executables. */ /* FIXME: when autoconf is fixed, remove the host check - dj */ #if defined(TARGET_EXECUTABLE_SUFFIX) && defined(HOST_EXECUTABLE_SUFFIX) @@ -115,10 +240,11 @@ FILE *report_times_to_file = NULL; and library files can be found in an alternate location. */ #ifdef TARGET_SYSTEM_ROOT -static const char *target_system_root = TARGET_SYSTEM_ROOT; +#define DEFAULT_TARGET_SYSTEM_ROOT (TARGET_SYSTEM_ROOT) #else -static const char *target_system_root = 0; +#define DEFAULT_TARGET_SYSTEM_ROOT (0) #endif +static const char *target_system_root = DEFAULT_TARGET_SYSTEM_ROOT; /* Nonzero means pass the updated target_system_root to the compiler. */ @@ -235,7 +361,6 @@ static const char *validate_switches (const char *, bool); static void validate_all_switches (void); static inline void validate_switches_from_spec (const char *, bool); static void give_switch (int, int); -static int used_arg (const char *, int); static int default_arg (const char *, int); static void set_multilib_dir (void); static void print_multilib_info (void); @@ -1395,10 +1520,12 @@ struct spec_list int name_len; /* length of the name */ bool user_p; /* whether string come from file spec. */ bool alloc_p; /* whether string was allocated */ + const char *default_ptr; /* The default value of *ptr_spec. */ }; #define INIT_STATIC_SPEC(NAME,PTR) \ - { NAME, NULL, PTR, (struct spec_list *) 0, sizeof (NAME) - 1, false, false } + { NAME, NULL, PTR, (struct spec_list *) 0, sizeof (NAME) - 1, false, false, \ + *PTR } /* List of statically defined specs. */ static struct spec_list static_specs[] = @@ -1566,6 +1693,8 @@ init_spec (void) sl->next = next; sl->name_len = strlen (sl->name); sl->ptr_spec = &sl->ptr; + gcc_assert (sl->ptr_spec != NULL); + sl->default_ptr = sl->ptr; next = sl; } #endif @@ -1740,6 +1869,7 @@ set_spec (const char *name, const char *spec, bool user_p) sl->alloc_p = 0; *(sl->ptr_spec) = ""; sl->next = specs; + sl->default_ptr = NULL; specs = sl; } @@ -2132,7 +2262,10 @@ read_specs (const char *filename, bool main_p, bool user_p) if (! strcmp (suffix, "*link_command")) link_command_spec = spec; else - set_spec (suffix + 1, spec, user_p); + { + set_spec (suffix + 1, spec, user_p); + free (spec); + } } else { @@ -2152,6 +2285,8 @@ read_specs (const char *filename, bool main_p, bool user_p) if (link_command_spec == 0) fatal_error (input_location, "spec file has no spec for linking"); + + XDELETEVEC (buffer); } /* Record the names of temporary files we tell compilers to write, @@ -2494,9 +2629,7 @@ add_to_obstack (char *path, void *data) static void xputenv (const char *string) { - if (verbose_flag) - fnotice (stderr, "%s\n", string); - putenv (CONST_CAST (char *, string)); + env.xput (string); } /* Build a list of search directories from PATHS. @@ -3970,7 +4103,7 @@ process_command (unsigned int decoded_options_count, struct cl_option_handlers handlers; unsigned int j; - gcc_exec_prefix = getenv ("GCC_EXEC_PREFIX"); + gcc_exec_prefix = env.get ("GCC_EXEC_PREFIX"); n_switches = 0; n_infiles = 0; @@ -4075,7 +4208,7 @@ process_command (unsigned int decoded_options_count, /* COMPILER_PATH and LIBRARY_PATH have values that are lists of directory names with colons. */ - temp = getenv ("COMPILER_PATH"); + temp = env.get ("COMPILER_PATH"); if (temp) { const char *startp, *endp; @@ -4109,7 +4242,7 @@ process_command (unsigned int decoded_options_count, } } - temp = getenv (LIBRARY_PATH_ENV); + temp = env.get (LIBRARY_PATH_ENV); if (temp && *cross_compile == '0') { const char *startp, *endp; @@ -4142,7 +4275,7 @@ process_command (unsigned int decoded_options_count, } /* Use LPATH like LIBRARY_PATH (for the CMU build program). */ - temp = getenv ("LPATH"); + temp = env.get ("LPATH"); if (temp && *cross_compile == '0') { const char *startp, *endp; @@ -4285,7 +4418,7 @@ process_command (unsigned int decoded_options_count, if (!compare_debug) { - const char *gcd = getenv ("GCC_COMPARE_DEBUG"); + const char *gcd = env.get ("GCC_COMPARE_DEBUG"); if (gcd && gcd[0] == '-') { @@ -6217,7 +6350,10 @@ process_brace_body (const char *p, const char *atom, const char *end_atom, if (!have_subst) { if (do_spec_1 (string, 0, NULL) < 0) - return 0; + { + free (string); + return 0; + } } else { @@ -6233,12 +6369,16 @@ process_brace_body (const char *p, const char *atom, const char *end_atom, { if (do_spec_1 (string, 0, &switches[i].part1[hard_match_len]) < 0) - return 0; + { + free (string); + return 0; + } /* Pass any arguments this switch has. */ give_switch (i, 1); suffix_subst = NULL; } } + free (string); } return p; @@ -6947,6 +7087,19 @@ compare_files (char *cmpfile[]) return ret; } +driver::driver (bool can_finalize, bool debug) : + explicit_link_files (NULL), + decoded_options (NULL) +{ + env.init (can_finalize, debug); +} + +driver::~driver () +{ + XDELETEVEC (explicit_link_files); + XDELETEVEC (decoded_options); +} + /* driver::main is implemented as a series of driver:: method calls. */ int @@ -8160,9 +8313,13 @@ static int n_mdswitches; /* Check whether a particular argument was used. The first time we canonicalize the switches to keep only the ones we care about. */ -static int -used_arg (const char *p, int len) +class used_arg_t { + public: + int operator () (const char *p, int len); + void finalize (); + + private: struct mswitchstr { const char *str; @@ -8171,8 +8328,16 @@ used_arg (const char *p, int len) int rep_len; }; - static struct mswitchstr *mswitches; - static int n_mswitches; + mswitchstr *mswitches; + int n_mswitches; + +}; + +used_arg_t used_arg; + +int +used_arg_t::operator () (const char *p, int len) +{ int i, j; if (!mswitches) @@ -8301,6 +8466,14 @@ used_arg (const char *p, int len) return 0; } +void used_arg_t::finalize () +{ + XDELETEVEC (mswitches); + mswitches = NULL; + n_mswitches = 0; +} + + static int default_arg (const char *p, int len) { @@ -8855,7 +9028,7 @@ print_multilib_info (void) static const char * getenv_spec_function (int argc, const char **argv) { - char *value; + const char *value; char *result; char *ptr; size_t len; @@ -8863,7 +9036,7 @@ getenv_spec_function (int argc, const char **argv) if (argc != 2) return NULL; - value = getenv (argv[0]); + value = env.get (argv[0]); if (!value) fatal_error (input_location, "environment variable %qs not defined", argv[0]); @@ -9526,6 +9699,191 @@ convert_white_space (char *orig) return orig; } +static void +path_prefix_reset (path_prefix *prefix) +{ + struct prefix_list *iter, *next; + iter = prefix->plist; + while (iter) + { + next = iter->next; + free (const_cast (iter->prefix)); + XDELETE (iter); + iter = next; + } + prefix->plist = 0; + prefix->max_len = 0; +} + +/* Restore all state within gcc.c to the initial state, so that the driver + code can be safely re-run in-process. + + Many const char * variables are referenced by static specs (see + INIT_STATIC_SPEC above). These variables are restored to their default + values by a simple loop over the static specs. + + For other variables, we directly restore them all to their initial + values (often implicitly 0). + + Free the various obstacks in this file, along with "opts_obstack" + from opts.c. + + This function also restores any environment variables that were changed. */ + +void +driver::finalize () +{ + env.restore (); + params_c_finalize (); + diagnostic_finish (global_dc); + + is_cpp_driver = 0; + at_file_supplied = 0; + print_help_list = 0; + print_version = 0; + verbose_only_flag = 0; + print_subprocess_help = 0; + use_ld = NULL; + report_times_to_file = NULL; + target_system_root = DEFAULT_TARGET_SYSTEM_ROOT; + target_system_root_changed = 0; + target_sysroot_suffix = 0; + target_sysroot_hdrs_suffix = 0; + save_temps_flag = SAVE_TEMPS_NONE; + save_temps_prefix = 0; + save_temps_length = 0; + spec_machine = DEFAULT_TARGET_MACHINE; + greatest_status = 1; + + finalize_options_struct (&global_options); + finalize_options_struct (&global_options_set); + + obstack_free (&obstack, NULL); + obstack_free (&opts_obstack, NULL); /* in opts.c */ + obstack_free (&collect_obstack, NULL); + + link_command_spec = LINK_COMMAND_SPEC; + + obstack_free (&multilib_obstack, NULL); + + user_specs_head = NULL; + user_specs_tail = NULL; + + /* Within the "compilers" vec, the fields "suffix" and "spec" were + statically allocated for the default compilers, but dynamically + allocated for additional compilers. Delete them for the latter. */ + for (int i = n_default_compilers; i < n_compilers; i++) + { + free (const_cast (compilers[i].suffix)); + free (const_cast (compilers[i].spec)); + } + XDELETEVEC (compilers); + compilers = NULL; + n_compilers = 0; + + linker_options.truncate (0); + assembler_options.truncate (0); + preprocessor_options.truncate (0); + + path_prefix_reset (&exec_prefixes); + path_prefix_reset (&startfile_prefixes); + path_prefix_reset (&include_prefixes); + + machine_suffix = 0; + just_machine_suffix = 0; + gcc_exec_prefix = 0; + gcc_libexec_prefix = 0; + md_exec_prefix = MD_EXEC_PREFIX; + md_startfile_prefix = MD_STARTFILE_PREFIX; + md_startfile_prefix_1 = MD_STARTFILE_PREFIX_1; + multilib_dir = 0; + multilib_os_dir = 0; + multiarch_dir = 0; + + XDELETEVEC (specs); + specs = 0; + for (unsigned i = 0; i < ARRAY_SIZE (static_specs); i++) + { + spec_list *sl = &static_specs[i]; + if (sl->alloc_p) + { + if (0) + free (const_cast (*(sl->ptr_spec))); + sl->alloc_p = false; + } + *(sl->ptr_spec) = sl->default_ptr; + } + extra_specs = NULL; + + processing_spec_function = 0; + + argbuf.truncate (0); + + have_c = 0; + have_o = 0; + + temp_names = NULL; + execution_count = 0; + signal_count = 0; + + temp_filename = NULL; + temp_filename_length = 0; + always_delete_queue = NULL; + failure_delete_queue = NULL; + + XDELETEVEC (switches); + switches = NULL; + n_switches = 0; + n_switches_alloc = 0; + + compare_debug = 0; + compare_debug_second = 0; + compare_debug_opt = NULL; + for (int i = 0; i < 2; i++) + { + switches_debug_check[i] = NULL; + n_switches_debug_check[i] = 0; + n_switches_alloc_debug_check[i] = 0; + debug_check_temp_file[i] = NULL; + } + + XDELETEVEC (infiles); + infiles = NULL; + n_infiles = 0; + n_infiles_alloc = 0; + + combine_inputs = false; + added_libraries = 0; + XDELETEVEC (outfiles); + outfiles = NULL; + spec_lang = 0; + last_language_n_infiles = 0; + gcc_input_filename = NULL; + input_file_number = 0; + input_filename_length = 0; + basename_length = 0; + suffixed_basename_length = 0; + input_basename = NULL; + input_suffix = NULL; + /* We don't need to purge "input_stat", just to unset "input_stat_set". */ + input_stat_set = 0; + input_file_compiler = NULL; + arg_going = 0; + delete_this_arg = 0; + this_is_output_file = 0; + this_is_library_file = 0; + this_is_linker_script = 0; + input_from_pipe = 0; + suffix_subst = NULL; + + mdswitches = NULL; + n_mdswitches = 0; + + debug_auxbase_opt = NULL; + + used_arg.finalize (); +} + /* PR jit/64810. Targets can provide configure-time default options in OPTION_DEFAULT_SPECS. The jit needs to access these, but diff --git a/gcc/gcc.h b/gcc/gcc.h index f10a103cd5b..e1abe43b15f 100644 --- a/gcc/gcc.h +++ b/gcc/gcc.h @@ -30,7 +30,10 @@ along with GCC; see the file COPYING3. If not see class driver { public: + driver (bool can_finalize, bool debug); + ~driver (); int main (int argc, char **argv); + void finalize (); private: void set_progname (const char *argv0) const; diff --git a/gcc/jit/ChangeLog b/gcc/jit/ChangeLog index efa0d889203..fae643686f7 100644 --- a/gcc/jit/ChangeLog +++ b/gcc/jit/ChangeLog @@ -1,3 +1,36 @@ +2015-08-25 David Malcolm + + * docs/cp/topics/contexts.rst + (gccjit::context::set_bool_use_external_driver): New. + * docs/internals/test-hello-world.exe.log.txt: Update. + * docs/topics/compatibility.rst (LIBGCCJIT_ABI_5): New. + * docs/topics/contexts.rst + (gcc_jit_context_set_bool_use_external_driver): New. + * jit-common.h (enum inner_bool_option): Add + INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER. + * jit-playback.c (gcc_driver_name): New global. + (gcc:jit::playback::context::invoke_driver): Split out second + half into... + (gcc::jit::playback::context::invoke_embedded_driver): ...this new + function, and... + (gcc::jit::playback::context::invoke_external_driver): ...this new + function. + * jit-playback.h + (gcc::jit::playback::context::get_inner_bool_option): New. + (gcc::jit::playback::context::invoke_embedded_driver): New. + (gcc::jit::playback::context::invoke_external_driver): New. + * jit-recording.c (inner_bool_option_reproducer_strings): + Add entry for INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER. + * libgccjit++.h + (gccjit::context::set_bool_use_external_driver): New. + * libgccjit.c (gcc_jit_context_set_bool_use_external_driver): New. + * libgccjit.h (gcc_jit_context_set_bool_use_external_driver): New. + (LIBGCCJIT_HAVE_gcc_jit_context_set_bool_use_external_driver): + New. + * libgccjit.map (LIBGCCJIT_ABI_5): New. + * notes.txt: Show invocation of embedded copy of driver. + * docs/internals/test-hello-world.exe.log.txt: Update + 2015-08-13 David Malcolm * jit-playback.c (invoke_driver): On OS X, add diff --git a/gcc/jit/docs/cp/topics/contexts.rst b/gcc/jit/docs/cp/topics/contexts.rst index 162e4aec8f3..05ce2308624 100644 --- a/gcc/jit/docs/cp/topics/contexts.rst +++ b/gcc/jit/docs/cp/topics/contexts.rst @@ -201,6 +201,26 @@ Boolean options #ifdef LIBGCCJIT_HAVE_gcc_jit_context_set_bool_allow_unreachable_blocks +.. function:: void \ + gccjit::context::set_bool_use_external_driver (int bool_value) + + libgccjit internally generates assembler, and uses "driver" code + for converting it to other formats (e.g. shared libraries). + + By default, libgccjit will use an embedded copy of the driver + code. + + This option can be used to instead invoke an external driver executable + as a subprocess; it is a thin wrapper around the C API + :c:func:`gcc_jit_context_set_bool_use_external_driver`. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_5`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_set_bool_use_external_driver + Integer options *************** diff --git a/gcc/jit/docs/internals/test-hello-world.exe.log.txt b/gcc/jit/docs/internals/test-hello-world.exe.log.txt index d82038be864..0bab86c25d7 100644 --- a/gcc/jit/docs/internals/test-hello-world.exe.log.txt +++ b/gcc/jit/docs/internals/test-hello-world.exe.log.txt @@ -1,4 +1,4 @@ -JIT: libgccjit (GCC) version 6.0.0 20150723 (experimental) (x86_64-unknown-linux-gnu) +JIT: libgccjit (GCC) version 6.0.0 20150803 (experimental) (x86_64-pc-linux-gnu) JIT: compiled by GNU C version 4.8.3 20140911 (Red Hat 4.8.3-7), GMP version 5.1.2, MPFR version 3.1.2, MPC version 1.0.1 JIT: entering: gcc_jit_context_set_str_option JIT: GCC_JIT_STR_OPTION_PROGNAME: "./test-hello-world.c.exe" @@ -65,6 +65,7 @@ JIT: GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING: false JIT: GCC_JIT_BOOL_OPTION_SELFCHECK_GC: true JIT: GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES: false JIT: gcc_jit_context_set_bool_allow_unreachable_blocks: false +JIT: gcc_jit_context_set_bool_use_external_driver: false JIT: entering: void gcc::jit::recording::context::validate() JIT: exiting: void gcc::jit::recording::context::validate() JIT: entering: gcc::jit::playback::context::context(gcc::jit::recording::context*) @@ -133,7 +134,8 @@ JIT: argv[3]: /tmp/libgccjit-CKq1M9/fake.s JIT: argv[4]: -o JIT: argv[5]: /tmp/libgccjit-CKq1M9/fake.so JIT: argv[6]: -fno-use-linker-plugin -JIT: argv[7]: (null) +JIT: entering: void gcc::jit::playback::context::invoke_embedded_driver(const vec*) +JIT: exiting: void gcc::jit::playback::context::invoke_embedded_driver(const vec*) JIT: exiting: void gcc::jit::playback::context::invoke_driver(const char*, const char*, const char*, timevar_id_t, bool, bool) JIT: exiting: void gcc::jit::playback::context::convert_to_dso(const char*) JIT: entering: gcc::jit::result* gcc::jit::playback::context::dlopen_built_dso() diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst index 0a4b4533f7e..e947cad4320 100644 --- a/gcc/jit/docs/topics/compatibility.rst +++ b/gcc/jit/docs/topics/compatibility.rst @@ -128,3 +128,10 @@ entrypoints: * :func:`gcc_jit_timer_pop` * :func:`gcc_jit_timer_print` + +.. _LIBGCCJIT_ABI_5: + +``LIBGCCJIT_ABI_5`` +------------------- +``LIBGCCJIT_ABI_5`` covers the addition of +:func:`gcc_jit_context_set_bool_use_external_driver` diff --git a/gcc/jit/docs/topics/contexts.rst b/gcc/jit/docs/topics/contexts.rst index 1aa319a9a3f..53ceffb6ded 100644 --- a/gcc/jit/docs/topics/contexts.rst +++ b/gcc/jit/docs/topics/contexts.rst @@ -469,6 +469,26 @@ Boolean options #ifdef LIBGCCJIT_HAVE_gcc_jit_context_set_bool_allow_unreachable_blocks +.. function:: void \ + gcc_jit_context_set_bool_use_external_driver (gcc_jit_context *ctxt, \ + int bool_value) + + libgccjit internally generates assembler, and uses "driver" code + for converting it to other formats (e.g. shared libraries). + + By default, libgccjit will use an embedded copy of the driver + code. + + This option can be used to instead invoke an external driver executable + as a subprocess. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_5`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_set_bool_use_external_driver + Integer options *************** diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h index e6fc132c6c2..6400f307d4b 100644 --- a/gcc/jit/jit-common.h +++ b/gcc/jit/jit-common.h @@ -191,6 +191,7 @@ private: enum inner_bool_option { INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS, + INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER, NUM_INNER_BOOL_OPTIONS }; diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c index 01cfd4baf13..44c3ce00d98 100644 --- a/gcc/jit/jit-playback.c +++ b/gcc/jit/jit-playback.c @@ -2373,6 +2373,8 @@ convert_to_dso (const char *ctxt_progname) true);/* bool run_linker */ } +static const char * const gcc_driver_name = GCC_DRIVER_NAME; + void playback::context:: invoke_driver (const char *ctxt_progname, @@ -2383,15 +2385,15 @@ invoke_driver (const char *ctxt_progname, bool run_linker) { JIT_LOG_SCOPE (get_logger ()); + + bool embedded_driver + = !get_inner_bool_option (INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER); + /* Currently this lumps together both assembling and linking into TV_ASSEMBLE. */ auto_timevar assemble_timevar (get_timer (), tv_id); - const char *errmsg; auto_argvec argvec; #define ADD_ARG(arg) argvec.safe_push (xstrdup (arg)) - int exit_status = 0; - int err = 0; - const char *gcc_driver_name = GCC_DRIVER_NAME; ADD_ARG (gcc_driver_name); @@ -2425,8 +2427,10 @@ invoke_driver (const char *ctxt_progname, ADD_ARG ("-Wl,-undefined,dynamic_lookup"); #endif - /* pex argv arrays are NULL-terminated. */ - argvec.safe_push (NULL); + if (0) + ADD_ARG ("-v"); + +#undef ADD_ARG /* pex_one's error-handling requires pname to be non-NULL. */ gcc_assert (ctxt_progname); @@ -2435,9 +2439,42 @@ invoke_driver (const char *ctxt_progname, for (unsigned i = 0; i < argvec.length (); i++) get_logger ()->log ("argv[%i]: %s", i, argvec[i]); + if (embedded_driver) + invoke_embedded_driver (&argvec); + else + invoke_external_driver (ctxt_progname, &argvec); +} + +void +playback::context:: +invoke_embedded_driver (const vec *argvec) +{ + JIT_LOG_SCOPE (get_logger ()); + driver d (true, /* can_finalize */ + false); /* debug */ + int result = d.main (argvec->length (), + const_cast (argvec->address ())); + d.finalize (); + if (result) + add_error (NULL, "error invoking gcc driver"); +} + +void +playback::context:: +invoke_external_driver (const char *ctxt_progname, + vec *argvec) +{ + JIT_LOG_SCOPE (get_logger ()); + const char *errmsg; + int exit_status = 0; + int err = 0; + + /* pex argv arrays are NULL-terminated. */ + argvec->safe_push (NULL); + errmsg = pex_one (PEX_SEARCH, /* int flags, */ gcc_driver_name, - const_cast (argvec.address ()), + const_cast (argvec->address ()), ctxt_progname, /* const char *pname */ NULL, /* const char *outname */ NULL, /* const char *errname */ @@ -2464,7 +2501,6 @@ invoke_driver (const char *ctxt_progname, getenv ("PATH")); return; } -#undef ADD_ARG } /* Extract the target-specific MULTILIB_DEFAULTS to diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h index 52e402f9eec..d99db542021 100644 --- a/gcc/jit/jit-playback.h +++ b/gcc/jit/jit-playback.h @@ -177,6 +177,12 @@ public: return m_recording_ctxt->get_bool_option (opt); } + int + get_inner_bool_option (enum inner_bool_option opt) const + { + return m_recording_ctxt->get_inner_bool_option (opt); + } + builtins_manager *get_builtins_manager () const { return m_recording_ctxt->get_builtins_manager (); @@ -280,6 +286,14 @@ protected: result * dlopen_built_dso (); + private: + void + invoke_embedded_driver (const vec *argvec); + + void + invoke_external_driver (const char *ctxt_progname, + vec *argvec); + private: ::gcc::jit::recording::context *m_recording_ctxt; diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c index 811d7c03544..70bd171a892 100644 --- a/gcc/jit/jit-recording.c +++ b/gcc/jit/jit-recording.c @@ -1452,7 +1452,8 @@ static const char * const static const char * const inner_bool_option_reproducer_strings[NUM_INNER_BOOL_OPTIONS] = { - "gcc_jit_context_set_bool_allow_unreachable_blocks" + "gcc_jit_context_set_bool_allow_unreachable_blocks", + "gcc_jit_context_set_bool_use_external_driver" }; /* Write the current value of all options to the log file (if any). */ diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h index d7e491bbfdc..ef9bef1af47 100644 --- a/gcc/jit/libgccjit++.h +++ b/gcc/jit/libgccjit++.h @@ -124,6 +124,7 @@ namespace gccjit int value); void set_bool_allow_unreachable_blocks (int bool_value); + void set_bool_use_external_driver (int bool_value); void add_command_line_option (const char *optname); @@ -665,6 +666,13 @@ context::set_bool_allow_unreachable_blocks (int bool_value) bool_value); } +inline void +context::set_bool_use_external_driver (int bool_value) +{ + gcc_jit_context_set_bool_use_external_driver (m_inner_ctxt, + bool_value); +} + inline void context::add_command_line_option (const char *optname) { diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c index eb9200cef5f..55cda6bf890 100644 --- a/gcc/jit/libgccjit.c +++ b/gcc/jit/libgccjit.c @@ -2608,6 +2608,23 @@ gcc_jit_context_set_bool_allow_unreachable_blocks (gcc_jit_context *ctxt, bool_value); } +/* Public entrypoint. See description in libgccjit.h. + + After error-checking, the real work is done by the + gcc::jit::recording::context::set_inner_bool_option method in + jit-recording.c. */ + +extern void +gcc_jit_context_set_bool_use_external_driver (gcc_jit_context *ctxt, + int bool_value) +{ + RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context"); + JIT_LOG_FUNC (ctxt->get_logger ()); + ctxt->set_inner_bool_option ( + gcc::jit::INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER, + bool_value); +} + /* Public entrypoint. See description in libgccjit.h. After error-checking, the real work is done by the diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h index 88e4ff31a28..442ad0a62ac 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -278,6 +278,30 @@ gcc_jit_context_set_bool_allow_unreachable_blocks (gcc_jit_context *ctxt, tested for with #ifdef. */ #define LIBGCCJIT_HAVE_gcc_jit_context_set_bool_allow_unreachable_blocks +/* Implementation detail: + libgccjit internally generates assembler, and uses "driver" code + for converting it to other formats (e.g. shared libraries). + + By default, libgccjit will use an embedded copy of the driver + code. + + This option can be used to instead invoke an external driver executable + as a subprocess. + + This entrypoint was added in LIBGCCJIT_ABI_5; you can test for + its presence using + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_set_bool_use_external_driver +*/ + +extern void +gcc_jit_context_set_bool_use_external_driver (gcc_jit_context *ctxt, + int bool_value); + +/* Pre-canned feature macro to indicate the presence of + gcc_jit_context_set_bool_use_external_driver. This can be + tested for with #ifdef. */ +#define LIBGCCJIT_HAVE_gcc_jit_context_set_bool_use_external_driver + /* Add an arbitrary gcc command-line option to the context. The context takes a copy of the string, so the (const char *) optname is not needed anymore after the call diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map index e4302c6d379..a3ced26a562 100644 --- a/gcc/jit/libgccjit.map +++ b/gcc/jit/libgccjit.map @@ -140,3 +140,8 @@ LIBGCCJIT_ABI_4 { gcc_jit_timer_pop; gcc_jit_timer_print; }; + +LIBGCCJIT_ABI_5 { + global: + gcc_jit_context_set_bool_use_external_driver; +} LIBGCCJIT_ABI_4; diff --git a/gcc/jit/notes.txt b/gcc/jit/notes.txt index e92c6651f00..36e05cb7f23 100644 --- a/gcc/jit/notes.txt +++ b/gcc/jit/notes.txt @@ -78,7 +78,13 @@ Client Code . Generated . libgccjit.so . . │ . . . . │ (assuming an in-memory compile): . . │ . . - . . │ . Convert assembler to DSO ("fake.so") + . . --> Convert assembler to DSO, via embedded + . . copy of driver: + . . driver::main () + . . invocation of "as" + . . invocation of "ld" + . . driver::finalize () + . . <---- . . │ . . . . │ . Load DSO (dlopen "fake.so") . . │ . . diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 9b0f9c03f6b..7deb14776ca 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2015-08-25 David Malcolm + + * jit.dg/test-error-pr63969-missing-driver.c: Add call to + gcc_jit_context_set_bool_use_external_driver. + 2015-08-25 Nathan Sidwell * gcc.c-torture/execute/builtins/20010124-1.x: New. diff --git a/gcc/testsuite/jit.dg/test-error-pr63969-missing-driver.c b/gcc/testsuite/jit.dg/test-error-pr63969-missing-driver.c index 13f5e3b5b83..733522310de 100644 --- a/gcc/testsuite/jit.dg/test-error-pr63969-missing-driver.c +++ b/gcc/testsuite/jit.dg/test-error-pr63969-missing-driver.c @@ -21,6 +21,10 @@ create_code (gcc_jit_context *ctxt, void *user_data) Unset it. */ gcc_jit_context_set_str_option (ctxt, GCC_JIT_STR_OPTION_PROGNAME, NULL); + /* By default, we use an embedded copy of the driver. + Opt-in to using an external copy of the driver. */ + gcc_jit_context_set_bool_use_external_driver (ctxt, 1); + /* Break PATH, so that the driver can't be found by gcc::jit::playback::context::compile () within gcc_jit_context_compile. */