Question about undefined functions' parameters during LTO

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



On 13.03.20 00:44, Richard Biener wrote:
> On Thu, Mar 12, 2020 at 5:31 PM Erick Ochoa
> <erick.ochoa@theobroma-systems.com> wrote:
>>
>> 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.
>>
>> 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:
>>
>> ```
>> 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 argument types or the actual arguments to all calls?  "hello world" sounds
> like you want actual arguments.  For those you need to look at the callgraph
> edges to the cgraph node of the external functions (node->callers) and there
> at the call stmts - which will not be available in WPA mode.
> 

I'm interested in the argument types to all calls of undefined 
functions. I have better defined what I mean by "undefined functions" in 
a sibling thread: https://gcc.gnu.org/pipermail/gcc/2020-March/231817.html

So, for example, what I'm trying to do is the following:

+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;
+    const char *name = undefined_function->name();
+    gcc_assert(name);
+    if (dump_file) fprintf(dump_file, "getting function arguments for 
%s\n", name);
+
+    gcc_assert(undefined_function->decl);
+    for (tree parm = DECL_ARGUMENTS (undefined_function->decl); parm; 
parm = DECL_CHAIN (parm))
+     {
+       tree type = TREE_TYPE(parm);
+       if (dump_file) fprintf(dump_file, "I want the type, do I have 
it? %s\n", type ? "true" : "false");
+     }
+  }
+  return 0;
+}

I have added the complete patch below, however the function iphw_execute 
encapsulates the logic I am trying at the moment.

The problem is that while this program runs, DECL_ARGUMENTS returns NULL 
and therefore the loop is never entered. This is true for functions that 
have arguments, such as puts/malloc/... and others in glibc.

I suspect this is because glibc is not compiled with -flto (I don't 
believe it is possible to compile glibc with -flto) and during the 
compilation of a simple hello world the only reference to "puts" is an 
extern declaration in stdio.h

So, do you think there's a way to obtain the argument types without 
having to iterate over gimple code and look for GIMPLE_CALL and look for 
undefined functions, then look at the arguments and their types?

Thanks!

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..5f71f3a411d
--- /dev/null
+++ b/gcc/ipa-hello-world.c
@@ -0,0 +1,90 @@
+#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;
+    const char *name = undefined_function->name();
+    gcc_assert(name);
+    if (dump_file) fprintf(dump_file, "getting function arguments for 
%s\n", name);
+
+    gcc_assert(undefined_function->decl);
+    for (tree parm = DECL_ARGUMENTS (undefined_function->decl); parm; 
parm = DECL_CHAIN (parm))
+     {
+       tree type = TREE_TYPE(parm);
+       if (dump_file) fprintf(dump_file, "I want the type, do I have 
it? %s\n", type ? "true" : "false");
+     }
+  }
+  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);

>>
>> 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