Question about undefined functions' parameters during LTO

Erick Ochoa erick.ochoa@theobroma-systems.com
Fri Mar 13 08:51:09 GMT 2020



On 12.03.20 08:48, Jan Hubicka wrote:
>> Hello,
> Hello,
>>
>> I am trying to find out the arguments of functions which are undefined
>> during LTO.
>>
>> Basically:
>>
>> gcc_assert(in_lto_p && !cnode->definition)
>> // Do we have arguments?
>> gcc_assert(DECL_ARGUMENTS(cnode->decl)) // fails
>> // No, we don't.
>>
>> As I understand it, functions which are not defined are ones which have have
>> been declared external.
> 
> LTO works in a way that function bodies are loaded from the stream on
> demand (using node->get_body or node->get_untransformed_body calls).
> Declaration of function parameters are part of function body and that is
> why they are missing.
> 
> You can get all bodies if you experiment with your pass using the call
> above. If you want real LTO pass working at whole propgram eventualy you
> will need avoid reading all bodies (or WPA stage will be slow and memory
> hungry) and store info you need into summaries.

Thanks. I did tried getting node->get_untransformed_body() and 
node->get_body() and for undefined functions which were not compiled 
with -flto (such as the ones on glibc) an epected exception is triggered.

Take for example the following simple ipa-pass that I have made only for 
this example (can be applied to
54e69cb00da0b50e4fa228a0617e4e8713bbc998 (upstream/master, gcc-master)
Author: GCC Administrator <gccadmin@gcc.gnu.org>
Date:   Fri Mar 13 00:16:15 2020 +0000)

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index fa9923bb270..92421fe500e 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1399,6 +1399,7 @@ OBJS = \
  	incpath.o \
  	init-regs.o \
  	internal-fn.o \
+	ipa-hello-world.o \
  	ipa-cp.o \
  	ipa-sra.o \
  	ipa-devirt.o \
diff --git a/gcc/common.opt b/gcc/common.opt
index fa9da505fc2..df807e4e388 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -3392,4 +3392,8 @@ fipa-ra
  Common Report Var(flag_ipa_ra) Optimization
  Use caller save register across calls if possible.

+fipa-hello-world
+Common Report Var(flag_ipa_hello_world) Optimization
+TBD
+
  ; This comment is to ensure we retain the blank line above.
diff --git a/gcc/ipa-hello-world.c b/gcc/ipa-hello-world.c
new file mode 100644
index 00000000000..402c62c6b59
--- /dev/null
+++ b/gcc/ipa-hello-world.c
@@ -0,0 +1,84 @@
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "predict.h"
+#include "alloc-pool.h"
+#include "tree-pass.h"
+#include "cgraph.h"
+#include "diagnostic.h"
+#include "fold-const.h"
+#include "gimple-fold.h"
+#include "symbol-summary.h"
+#include "tree-vrp.h"
+#include "ipa-prop.h"
+#include "tree-pretty-print.h"
+#include "tree-inline.h"
+#include "ipa-fnsummary.h"
+#include "ipa-utils.h"
+#include "tree-ssa-ccp.h"
+#include "stringpool.h"
+#include "attribs.h"
+
+
+#include <set>
+
+static unsigned int
+iphw_execute()
+{
+  cgraph_node *node = NULL;
+  std::set<cgraph_node *> functions;
+  FOR_EACH_FUNCTION(node)
+  {
+    functions.insert(node);
+  }
+
+  FOR_EACH_DEFINED_FUNCTION(node)
+  {
+    functions.erase(node);
+  }
+
+  for (auto it = functions.cbegin(); it != functions.cend(); ++it)
+  {
+    cgraph_node *undefined_function = *it;
+    if (dump_file) fprintf(dump_file, "getting function body for %s\n", 
undefined_function->name());
+
+    //undefined_function->get_untransformed_body();
+    undefined_function->get_body();
+  }
+  return 0;
+}
+
+namespace {
+const pass_data pass_data_ipa_hello_world =
+{
+  SIMPLE_IPA_PASS,
+  "hello-world",
+  OPTGROUP_NONE,
+  TV_NONE,
+  (PROP_cfg | PROP_ssa),
+  0,
+  0,
+  0,
+  0,
+};
+
+class pass_ipa_hello_world : public simple_ipa_opt_pass
+{
+public:
+  pass_ipa_hello_world (gcc::context *ctx)
+    : simple_ipa_opt_pass(pass_data_ipa_hello_world, ctx)
+  {}
+
+  virtual bool gate(function*) { return flag_ipa_hello_world; }
+  virtual unsigned execute (function*) { return iphw_execute(); }
+};
+} // anon namespace
+
+simple_ipa_opt_pass*
+make_pass_ipa_hello_world (gcc::context *ctx)
+{
+  return new pass_ipa_hello_world (ctx);
+}
diff --git a/gcc/passes.def b/gcc/passes.def
index 2bf2cb78fc5..66f333f81dc 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -149,6 +149,7 @@ along with GCC; see the file COPYING3.  If not see
    NEXT_PASS (pass_ipa_profile);
    NEXT_PASS (pass_ipa_icf);
    NEXT_PASS (pass_ipa_devirt);
+  NEXT_PASS (pass_ipa_hello_world);
    NEXT_PASS (pass_ipa_cp);
    NEXT_PASS (pass_ipa_sra);
    NEXT_PASS (pass_ipa_cdtor_merge);
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index a1207a20a3c..377dda689cc 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -501,6 +501,7 @@ extern ipa_opt_pass_d *make_pass_ipa_fn_summary 
(gcc::context *ctxt);
  extern ipa_opt_pass_d *make_pass_ipa_inline (gcc::context *ctxt);
  extern simple_ipa_opt_pass *make_pass_ipa_free_lang_data (gcc::context 
*ctxt);
  extern simple_ipa_opt_pass *make_pass_ipa_free_fn_summary 
(gcc::context *ctxt);
+extern simple_ipa_opt_pass *make_pass_ipa_hello_world (gcc::context *ctxt);
  extern ipa_opt_pass_d *make_pass_ipa_cp (gcc::context *ctxt);
  extern ipa_opt_pass_d *make_pass_ipa_sra (gcc::context *ctxt);
  extern ipa_opt_pass_d *make_pass_ipa_icf (gcc::context *ctxt);

Compile the following program:

#include <stdio.h>

int
main ()
{
         puts("hello world\n");
}

And you get the following exception while trying to get the body of 
function "puts".

[eochoa@osprey1 temp]$ $HOME/code/gcc-inst/bin/gcc -flto 
-fipa-hello-world -fdump-ipa-hello-world -o a.out a.c
during IPA pass: hello-world
lto1: internal compiler error: Segmentation fault
0xbb88d7 crash_signal
         /home/eochoa/code/gcc/gcc/toplev.c:328
0xa37830 lto_get_decl_name_mapping(lto_file_decl_data*, char const*)
         /home/eochoa/code/gcc/gcc/lto-section-in.c:372
0x6f3e37 cgraph_node::get_untransformed_body()
         /home/eochoa/code/gcc/gcc/cgraph.c:3859
0x6f40b3 cgraph_node::get_body()
         /home/eochoa/code/gcc/gcc/cgraph.c:3901
0x16007db iphw_execute
         /home/eochoa/code/gcc/gcc/ipa-hello-world.c:49
0x16007db execute
         /home/eochoa/code/gcc/gcc/ipa-hello-world.c:76
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.
lto-wrapper: fatal error: /home/eochoa/code/gcc-inst/bin/gcc returned 1 
exit status
compilation terminated.
/usr/bin/ld: error: lto-wrapper failed
collect2: error: ld returned 1 exit status



> 
> ipa-prop already stores nunber and types of arguments that may be all
> you need.
>>
>> I believe that, when building an application with -flto, the only functions
>> which are not visible during LTO **and** are declared external are functions
>> defined in libraries which have not been compiled with -flto. An example of
>> this is glibc.
>>
>> Indeed, I have implemented an analysis pass in gcc which prints out
>> undefined functions, and it prints out the following:
>>
>> undefined function __gcov_merge_add
>> undefined function fopen
>> undefined function printf
>> undefined function __builtin_putchar
>> undefined function calloc
>> undefined function __gcov_merge_topn
>> undefined function strtol
>> undefined function free
>> ... and more
>>
>> Now, I am not interested in the bodies of these. I am only interested in
>> determining the type of the arguments passed to these functions. However,
>> when I call the following function:
> 
> See ipa_get_type

Thanks for the lead, I did not know about ipa_get_type.

I looked at uses of ipa_get_type and see that it takes ipa_node_params 
as a parameter. However, IPA_NODE_REF returns NULL for functions not 
compiled with -flto (such as ones in glibc).

     //undefined_function->get_untransformed_body(); // <-- segfault puts
     //undefined_function->get_body(); // <-- segfault for function puts
     //
     class ipa_node_params *info;
     info = IPA_NODE_REF (undefined_function);
     gcc_assert(info); // <-- segfaults for function puts


Erick

> 
> Honza
>>
>> ```
>> void
>> print_parameters_undefined_functions(const cgraph_node *cnode)
>> {
>>    gcc_assert(cnode);
>>    gcc_assert(in_lto_p);
>>    gcc_assert(!cnode->definition);
>>
>>    tree function = cnode->decl;
>>    gcc_assert(function);
>>    enum tree_code code = TREE_CODE (function);
>>    bool is_function_decl = FUNCTION_DECL == code;
>>    gcc_assert (is_function_decl);
>>
>>    log("about to print decl_arguments(%s)\n", cnode->name());
>>    for (tree parm = DECL_ARGUMENTS (function); parm; parm = DECL_CHAIN(parm))
>>    {
>>      log("hello world\n");
>>    }
>> ```
>>
>> I never see "hello world" but I do see "about to print...".
>> Does anyone have any idea on how to obtain the arguments to undefined
>> functions?
>>
>> The only way I see to do this, is to walk through the gimple instructions,
>> find GIMPLE_CALL statements and look at the argument list at that moment.
>> But I was wondering if there's a more efficient way to do it.
>>
>> Thanks!



More information about the Gcc mailing list