| /* Callgraph based analysis of static variables. |
| Copyright (C) 2015-2017 Free Software Foundation, Inc. |
| Contributed by Martin Liska <mliska@suse.cz> |
| |
| 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/>. */ |
| |
| /* Interprocedural HSA pass is responsible for creation of HSA clones. |
| For all these HSA clones, we emit HSAIL instructions and pass processing |
| is terminated. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "is-a.h" |
| #include "hash-set.h" |
| #include "vec.h" |
| #include "tree.h" |
| #include "tree-pass.h" |
| #include "function.h" |
| #include "basic-block.h" |
| #include "gimple.h" |
| #include "dumpfile.h" |
| #include "gimple-pretty-print.h" |
| #include "tree-streamer.h" |
| #include "stringpool.h" |
| #include "cgraph.h" |
| #include "print-tree.h" |
| #include "symbol-summary.h" |
| #include "hsa-common.h" |
| |
| namespace { |
| |
| /* If NODE is not versionable, warn about not emiting HSAIL and return false. |
| Otherwise return true. */ |
| |
| static bool |
| check_warn_node_versionable (cgraph_node *node) |
| { |
| if (!node->local.versionable) |
| { |
| warning_at (EXPR_LOCATION (node->decl), OPT_Whsa, |
| "could not emit HSAIL for function %s: function cannot be " |
| "cloned", node->name ()); |
| return false; |
| } |
| return true; |
| } |
| |
| /* The function creates HSA clones for all functions that were either |
| marked as HSA kernels or are callable HSA functions. Apart from that, |
| we redirect all edges that come from an HSA clone and end in another |
| HSA clone to connect these two functions. */ |
| |
| static unsigned int |
| process_hsa_functions (void) |
| { |
| struct cgraph_node *node; |
| |
| if (hsa_summaries == NULL) |
| hsa_summaries = new hsa_summary_t (symtab); |
| |
| FOR_EACH_DEFINED_FUNCTION (node) |
| { |
| hsa_function_summary *s = hsa_summaries->get (node); |
| |
| /* A linked function is skipped. */ |
| if (s->m_bound_function != NULL) |
| continue; |
| |
| if (s->m_kind != HSA_NONE) |
| { |
| if (!check_warn_node_versionable (node)) |
| continue; |
| cgraph_node *clone |
| = node->create_virtual_clone (vec <cgraph_edge *> (), |
| NULL, NULL, "hsa"); |
| TREE_PUBLIC (clone->decl) = TREE_PUBLIC (node->decl); |
| clone->externally_visible = node->externally_visible; |
| |
| clone->force_output = true; |
| hsa_summaries->link_functions (clone, node, s->m_kind, false); |
| |
| if (dump_file) |
| fprintf (dump_file, "Created a new HSA clone: %s, type: %s\n", |
| clone->name (), |
| s->m_kind == HSA_KERNEL ? "kernel" : "function"); |
| } |
| else if (hsa_callable_function_p (node->decl) |
| /* At this point, this is enough to identify clones for |
| parallel, which for HSA would need to be kernels anyway. */ |
| && !DECL_ARTIFICIAL (node->decl)) |
| { |
| if (!check_warn_node_versionable (node)) |
| continue; |
| cgraph_node *clone |
| = node->create_virtual_clone (vec <cgraph_edge *> (), |
| NULL, NULL, "hsa"); |
| TREE_PUBLIC (clone->decl) = TREE_PUBLIC (node->decl); |
| clone->externally_visible = node->externally_visible; |
| |
| if (!cgraph_local_p (node)) |
| clone->force_output = true; |
| hsa_summaries->link_functions (clone, node, HSA_FUNCTION, false); |
| |
| if (dump_file) |
| fprintf (dump_file, "Created a new HSA function clone: %s\n", |
| clone->name ()); |
| } |
| } |
| |
| /* Redirect all edges that are between HSA clones. */ |
| FOR_EACH_DEFINED_FUNCTION (node) |
| { |
| cgraph_edge *e = node->callees; |
| |
| while (e) |
| { |
| hsa_function_summary *src = hsa_summaries->get (node); |
| if (src->m_kind != HSA_NONE && src->m_gpu_implementation_p) |
| { |
| hsa_function_summary *dst = hsa_summaries->get (e->callee); |
| if (dst->m_kind != HSA_NONE && !dst->m_gpu_implementation_p) |
| { |
| e->redirect_callee (dst->m_bound_function); |
| if (dump_file) |
| fprintf (dump_file, |
| "Redirecting edge to HSA function: %s->%s\n", |
| xstrdup_for_dump (e->caller->name ()), |
| xstrdup_for_dump (e->callee->name ())); |
| } |
| } |
| |
| e = e->next_callee; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Iterate all HSA functions and stream out HSA function summary. */ |
| |
| static void |
| ipa_hsa_write_summary (void) |
| { |
| struct bitpack_d bp; |
| struct cgraph_node *node; |
| struct output_block *ob; |
| unsigned int count = 0; |
| lto_symtab_encoder_iterator lsei; |
| lto_symtab_encoder_t encoder; |
| |
| if (!hsa_summaries) |
| return; |
| |
| ob = create_output_block (LTO_section_ipa_hsa); |
| encoder = ob->decl_state->symtab_node_encoder; |
| ob->symbol = NULL; |
| for (lsei = lsei_start_function_in_partition (encoder); !lsei_end_p (lsei); |
| lsei_next_function_in_partition (&lsei)) |
| { |
| node = lsei_cgraph_node (lsei); |
| hsa_function_summary *s = hsa_summaries->get (node); |
| |
| if (s->m_kind != HSA_NONE) |
| count++; |
| } |
| |
| streamer_write_uhwi (ob, count); |
| |
| /* Process all of the functions. */ |
| for (lsei = lsei_start_function_in_partition (encoder); !lsei_end_p (lsei); |
| lsei_next_function_in_partition (&lsei)) |
| { |
| node = lsei_cgraph_node (lsei); |
| hsa_function_summary *s = hsa_summaries->get (node); |
| |
| if (s->m_kind != HSA_NONE) |
| { |
| encoder = ob->decl_state->symtab_node_encoder; |
| int node_ref = lto_symtab_encoder_encode (encoder, node); |
| streamer_write_uhwi (ob, node_ref); |
| |
| bp = bitpack_create (ob->main_stream); |
| bp_pack_value (&bp, s->m_kind, 2); |
| bp_pack_value (&bp, s->m_gpu_implementation_p, 1); |
| bp_pack_value (&bp, s->m_bound_function != NULL, 1); |
| streamer_write_bitpack (&bp); |
| if (s->m_bound_function) |
| stream_write_tree (ob, s->m_bound_function->decl, true); |
| } |
| } |
| |
| streamer_write_char_stream (ob->main_stream, 0); |
| produce_asm (ob, NULL); |
| destroy_output_block (ob); |
| } |
| |
| /* Read section in file FILE_DATA of length LEN with data DATA. */ |
| |
| static void |
| ipa_hsa_read_section (struct lto_file_decl_data *file_data, const char *data, |
| size_t len) |
| { |
| const struct lto_function_header *header |
| = (const struct lto_function_header *) data; |
| const int cfg_offset = sizeof (struct lto_function_header); |
| const int main_offset = cfg_offset + header->cfg_size; |
| const int string_offset = main_offset + header->main_size; |
| struct data_in *data_in; |
| unsigned int i; |
| unsigned int count; |
| |
| lto_input_block ib_main ((const char *) data + main_offset, |
| header->main_size, file_data->mode_table); |
| |
| data_in |
| = lto_data_in_create (file_data, (const char *) data + string_offset, |
| header->string_size, vNULL); |
| count = streamer_read_uhwi (&ib_main); |
| |
| for (i = 0; i < count; i++) |
| { |
| unsigned int index; |
| struct cgraph_node *node; |
| lto_symtab_encoder_t encoder; |
| |
| index = streamer_read_uhwi (&ib_main); |
| encoder = file_data->symtab_node_encoder; |
| node = dyn_cast<cgraph_node *> (lto_symtab_encoder_deref (encoder, |
| index)); |
| gcc_assert (node->definition); |
| hsa_function_summary *s = hsa_summaries->get (node); |
| |
| struct bitpack_d bp = streamer_read_bitpack (&ib_main); |
| s->m_kind = (hsa_function_kind) bp_unpack_value (&bp, 2); |
| s->m_gpu_implementation_p = bp_unpack_value (&bp, 1); |
| bool has_tree = bp_unpack_value (&bp, 1); |
| |
| if (has_tree) |
| { |
| tree decl = stream_read_tree (&ib_main, data_in); |
| s->m_bound_function = cgraph_node::get_create (decl); |
| } |
| } |
| lto_free_section_data (file_data, LTO_section_ipa_hsa, NULL, data, |
| len); |
| lto_data_in_delete (data_in); |
| } |
| |
| /* Load streamed HSA functions summary and assign the summary to a function. */ |
| |
| static void |
| ipa_hsa_read_summary (void) |
| { |
| struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data (); |
| struct lto_file_decl_data *file_data; |
| unsigned int j = 0; |
| |
| if (hsa_summaries == NULL) |
| hsa_summaries = new hsa_summary_t (symtab); |
| |
| while ((file_data = file_data_vec[j++])) |
| { |
| size_t len; |
| const char *data = lto_get_section_data (file_data, LTO_section_ipa_hsa, |
| NULL, &len); |
| |
| if (data) |
| ipa_hsa_read_section (file_data, data, len); |
| } |
| } |
| |
| const pass_data pass_data_ipa_hsa = |
| { |
| IPA_PASS, /* type */ |
| "hsa", /* name */ |
| OPTGROUP_OMP, /* optinfo_flags */ |
| TV_IPA_HSA, /* tv_id */ |
| 0, /* properties_required */ |
| 0, /* properties_provided */ |
| 0, /* properties_destroyed */ |
| 0, /* todo_flags_start */ |
| TODO_dump_symtab, /* todo_flags_finish */ |
| }; |
| |
| class pass_ipa_hsa : public ipa_opt_pass_d |
| { |
| public: |
| pass_ipa_hsa (gcc::context *ctxt) |
| : ipa_opt_pass_d (pass_data_ipa_hsa, ctxt, |
| NULL, /* generate_summary */ |
| ipa_hsa_write_summary, /* write_summary */ |
| ipa_hsa_read_summary, /* read_summary */ |
| ipa_hsa_write_summary, /* write_optimization_summary */ |
| ipa_hsa_read_summary, /* read_optimization_summary */ |
| NULL, /* stmt_fixup */ |
| 0, /* function_transform_todo_flags_start */ |
| NULL, /* function_transform */ |
| NULL) /* variable_transform */ |
| {} |
| |
| /* opt_pass methods: */ |
| virtual bool gate (function *); |
| |
| virtual unsigned int execute (function *) { return process_hsa_functions (); } |
| |
| }; // class pass_ipa_reference |
| |
| bool |
| pass_ipa_hsa::gate (function *) |
| { |
| return hsa_gen_requested_p (); |
| } |
| |
| } // anon namespace |
| |
| ipa_opt_pass_d * |
| make_pass_ipa_hsa (gcc::context *ctxt) |
| { |
| return new pass_ipa_hsa (ctxt); |
| } |