From patchwork Thu Aug 6 14:52:03 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 504723 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id C052F1401E7 for ; Fri, 7 Aug 2015 00:47:01 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=Sey9N9Qp; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; q=dns; s= default; b=moEiZ9y5AINhoA5qYkRH+5KD72Ae5XQya7EqYErHhoKx5QP5WDbrW rRCJAcja4Vyqh/RrPFT84lDO3mldCqa9+NejN5UvAm9UHHVWEzgfMCbdKOJtbhnr 7T5GGTQ3hPtihs24ajDoKplJ52GBrBQI0AmE+1nSG6N0G+V+qTwxOI= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references; s= default; bh=SH4DsGm0fM7++E25QNNoVYypw7c=; b=Sey9N9QpVsUkNqv+Uw+L WwfIc9Vw7Kd45+ojmTT5A0Z0K8GSQ6bXTWkoOUtPxsCKTH8Ndn9SLBBWhC0T+8/0 z+rBqLib1FyPzoDajvz4jo2C/4DHMF/VqX/DzeQlf0M7yUmrSBGG/1Tr499FRzlE e7Hz5zAqGbWEL6cmM02t9PQ= Received: (qmail 84044 invoked by alias); 6 Aug 2015 14:46:12 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 83803 invoked by uid 89); 6 Aug 2015 14:46:11 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.1 required=5.0 tests=AWL, BAYES_50, KAM_LAZY_DOMAIN_SECURITY, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=no version=3.3.2 X-Spam-User: qpsmtpd, 2 recipients X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Thu, 06 Aug 2015 14:45:58 +0000 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (Postfix) with ESMTPS id 9EB2836B4F5; Thu, 6 Aug 2015 14:45:56 +0000 (UTC) Received: from c64.redhat.com (vpn-236-51.phx2.redhat.com [10.3.236.51]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t76EbTIS031777; Thu, 6 Aug 2015 10:37:30 -0400 From: David Malcolm To: gcc-patches@gcc.gnu.org, jit@gcc.gnu.org, Joseph Myers Cc: David Malcolm Subject: [PATCH 1/2] driver: support state cleanup Date: Thu, 6 Aug 2015 10:52:03 -0400 Message-Id: <1438872724-18658-2-git-send-email-dmalcolm@redhat.com> In-Reply-To: <1438872724-18658-1-git-send-email-dmalcolm@redhat.com> References: <1438872724-18658-1-git-send-email-dmalcolm@redhat.com> X-IsSubscribed: yes This patch implements enough state cleanup with the driver to allow it to be linked within libgccjit.so and repeatedly run in-process. The state cleanup is optional and is only performed by libgccjit. When run within the driver executables, the code does the same as before. This corresponds to the first part of: https://gcc.gnu.org/ml/gcc-patches/2015-06/msg00123.html with various cleanups. In particular, I've fixed the "genenv" / "getenv" typo noted by Bert Wesarg here: https://gcc.gnu.org/ml/gcc-patches/2015-06/msg00268.html which required using "get" rather than "getenv" for the name of the env_manager method (given that poisoning happens in the preprocessor). Successfully bootstrapped and regression-tested on x86_64-pc-linux-gnu (in combination with the followup patch, which contains the jit-specific parts, which would be committed with it; I've split them up for ease of review). OK for trunk? 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/gcc-main.c | 3 +- gcc/gcc.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- gcc/gcc.h | 3 + 3 files changed, 384 insertions(+), 22 deletions(-) diff --git a/gcc/gcc-main.c b/gcc/gcc-main.c index 230ba48..a0aaa3c 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 290ec78..36f7ca1 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. @@ -3965,7 +4098,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; @@ -4070,7 +4203,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; @@ -4104,7 +4237,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; @@ -4137,7 +4270,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; @@ -4280,7 +4413,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] == '-') { @@ -6212,7 +6345,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 { @@ -6228,12 +6364,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; @@ -6942,6 +7082,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 @@ -8155,9 +8308,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; @@ -8166,8 +8323,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) @@ -8296,6 +8461,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) { @@ -8850,7 +9023,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; @@ -8858,7 +9031,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]); @@ -9521,6 +9694,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 f10a103..e1abe43 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;