GCC pluginを試してみる

京都マイクロコンピュータさんのブログを拝見していたら、GCC pluginについ ての記載があった。gcc-4.5からGCC pluginというものが作成できるようになっ ているらしい。もしかして構文チェックや静的解析等が可能なのかなという期 待を込めて試してみる。

 

1. GCC pluginビルド環境のインストール

Ubuntuではgcc-x.x-plugin-devというパッケージが必要。インストールすると pluginディレクトリが作成される。

$ sudo apt-get install gcc-4.7-plugin-dev
$ dpkg -L gcc-4.7-plugin-dev
<snip>
/usr/lib/gcc/x86_64-linux-gnu/4.7/plugin/
<snip>

2. 引数の内容を表示するだけのplugin

無意味だが、Hello, Wolrd的なコード。

2.1. コードの内容

fprintfで引数の内容を表示する。

#include <gcc-plugin.h>
#include <stdio.h>
 
int plugin_is_GPL_compatible;
 
#define __pr_dbg(...)                           \
  do {                                          \
    fprintf(stderr, __VA_ARGS__);               \
  } while (0)
 
#define pr_dbg(...)                             \
  do {                                          \
    __pr_dbg("%s: ", __FUNCTION__);             \
    __pr_dbg(__VA_ARGS__);                      \
  } while (0)
 
#define pr_dbg_args(args)                                            \
  do {                                                               \
    int i;                                                           \
    pr_dbg("plugin_name_args\n");                                    \
    __pr_dbg("  base_name     = %s\n", args->base_name);             \
    __pr_dbg("  full_name     = %s\n", args->full_name);             \
    __pr_dbg("  argc          = %d\n", args->argc);                  \
    __pr_dbg("  argv          = %p\n", args->argv);                  \
    for (i = 0; i < args->argc; i++)                                 \
      {                                                              \
        __pr_dbg("  argv[%d].key   = %s\n", i, args->argv[i].key);   \
        __pr_dbg("  argv[%d].value = %s\n", i, args->argv[i].value); \
      }                                                              \
    __pr_dbg("  version       = %s\n", args->version);               \
    __pr_dbg("  help          = %s\n", args->help);                  \
  } while (0)
 
#define pr_dbg_version(ver)                                       \
  do {                                                            \
    pr_dbg("plugin_gcc_version\n");                               \
    __pr_dbg("  basever                 = %s\n", ver->basever);   \
    __pr_dbg("  datestamp               = %s\n", ver->datestamp); \
    __pr_dbg("  devphase                = %s\n", ver->devphase);  \
    __pr_dbg("  revision                = %s\n", ver->revision);  \
    __pr_dbg("  configuration_arguments = %s\n",                  \
             ver->configuration_arguments);                       \
  } while (0)
 
int plugin_init(struct plugin_name_args *args,
                struct plugin_gcc_version *version)
{
  if (args != NULL)
    pr_dbg_args(args);
  if (version != NULL)
    pr_dbg_version(version);
  return 0;
}

2.2. ビルド方法

-Iオプションにpluginディレクトリのインクルードパスを追加する必要がある。 gcc -print-file-name=pluginを使うとGCCの版数に左右されなくて便利。

$ g++ -Wall -I`g++ -print-file-name=plugin`/include -fPIC -shared \
my_gcc_plugin.cpp -o my_gcc_plugin.so

ここでは.so拡張子にしているが、.soなしでも良いのかな?

2.3. 動作方法

-fpluginでpluginを指定。-fplugin-arg-<PLUGIN_NAME>-<KEY>=<VALUE>で pluginに渡す引数を指定。

g++ -O -fplugin=./my_gcc_plugin.so target/hello.c
plugin_init: plugin_name_args
  base_name     = my_gcc_plugin
  full_name     = ./my_gcc_plugin
  argc          = 0
  argv          = (nil)
  version       = (null)
  help          = (null)
plugin_init: plugin_gcc_version
  basever                 = 4.7
  datestamp               = 20130421
  devphase                = 
  revision                = 
  configuration_arguments = ../src/configure -v
  --with-pkgversion='Ubuntu/Linaro 4.7.3-2ubuntu1~12.04'
  --with-bugurl=file:///usr/share/doc/gcc-4.7/README.Bugs
  --enable-languages=c,c++,go,fortran,objc,obj-c++ --prefix=/usr
  --program-suffix=-4.7 --enable-shared --enable-linker-build-id
  --libexecdir=/usr/lib --without-included-gettext
  --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.7
  --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu
  --enable-libstdcxx-debug --enable-libstdcxx-time=yes
  --enable-gnu-unique-object --enable-plugin --with-system-zlib
  --enable-objc-gc --enable-multiarch --disable-werror
  --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64
  --with-tune=generic --enable-checking=release
  --build=x86_64-linux-gnu --host=x86_64-linux-gnu
  --target=x86_64-linux-gnu

2.4. plugin_is_GPL_compatible

GPL compatibleなコードであることを宣言する為の変数らしい。

4.8 Standards for Dynamic Plug-in Interfacesより抜粋。

Second, you should require plug-in developers to affirm that their
plug-ins are released under an appropriate license. This should be
enforced with a simple programmatic check. For GCC, again for example,
a plug-in must define the global symbol plugin_is_GPL_compatible, thus
asserting that the plug-in is released under a GPL-compatible license
(see Plugins in GCC Internals).

この変数を定義しないとpluginロード時にはじかれる。

cc1plus: fatal error: plugin ./my_gcc_plugin is not licensed under a
GPL-compatible license
./my_gcc_plugin: undefined symbol: plugin_is_GPL_compatible
compilation terminated.
make: *** [all] Error 1

2.5. plugin_init

pluginロード時に呼ばれる関数。ここでコールバック登録を実行することで、 GCC動作時に任意の処理を追加できる。

2.6. struct plugin_name_args

pluginの情報。引数で挙動を変えることができる。

struct plugin_name_args
     {
       char *base_name;              /* Short name of the plugin
                                        (filename without .so suffix). */
       const char *full_name;        /* Path to the plugin as
                                        specified with -fplugin=. */
       int argc;                     /* Number of arguments specified
                                        with -fplugin-arg-.... */
       struct plugin_argument *argv; /* Array of ARGC key-value
                                        pairs. */
       const char *version;          /* Version string provided by
                                        plugin. */
       const char *help;             /* Help string provided by
                                        plugin. */
     }

2.7. struct plugin_gcc_version

GCCの版数を確認できる。特定版数の場合に挙動を変える等ができそう。

struct plugin_gcc_version
     {
       const char *basever;
       const char *datestamp;
       const char *devphase;
       const char *revision;
       const char *configuration_arguments;
     };

版数確認用APIもあるようだ。

if (!plugin_default_version_check (version, &gcc_version))
         return 1;

2.8. gccではなくg++を使う理由

gccコマンドで作成した場合、GCC内部の関数がundefined symbolになってしま う場合がある。これは内部はg++で作成されている為らしい。特にこだわりが なければg++でビルドしたほうが良い。

3. コールバックを登録するplugin

コールバックを登録して、どこから呼ばれているかを表示する。

3.1. コールバックの一覧

   enum plugin_event
     {
       PLUGIN_PASS_MANAGER_SETUP,    /* To hook into pass manager.  */
       PLUGIN_FINISH_TYPE,           /* After finishing parsing a type.  */
       PLUGIN_FINISH_DECL,           /* After finishing parsing a declaration. */
       PLUGIN_FINISH_UNIT,           /* Useful for summary processing.  */
       PLUGIN_PRE_GENERICIZE,        /* Allows to see low level AST in C and C++ frontends.  */
       PLUGIN_FINISH,                /* Called before GCC exits.  */
       PLUGIN_INFO,                  /* Information about the plugin. */
       PLUGIN_GGC_START,             /* Called at start of GCC Garbage Collection. */
       PLUGIN_GGC_MARKING,           /* Extend the GGC marking. */
       PLUGIN_GGC_END,               /* Called at end of GGC. */
       PLUGIN_REGISTER_GGC_ROOTS,    /* Register an extra GGC root table. */
       PLUGIN_REGISTER_GGC_CACHES,   /* Register an extra GGC cache table. */
       PLUGIN_ATTRIBUTES,            /* Called during attribute registration */
       PLUGIN_START_UNIT,            /* Called before processing a translation unit.  */
       PLUGIN_PRAGMAS,               /* Called during pragma registration. */
       /* Called before first pass from all_passes.  */
       PLUGIN_ALL_PASSES_START,
       /* Called after last pass from all_passes.  */
       PLUGIN_ALL_PASSES_END,
       /* Called before first ipa pass.  */
       PLUGIN_ALL_IPA_PASSES_START,
       /* Called after last ipa pass.  */
       PLUGIN_ALL_IPA_PASSES_END,
       /* Allows to override pass gate decision for current_pass.  */
       PLUGIN_OVERRIDE_GATE,
       /* Called before executing a pass.  */
       PLUGIN_PASS_EXECUTION,
       /* Called before executing subpasses of a GIMPLE_PASS in
          execute_ipa_pass_list.  */
       PLUGIN_EARLY_GIMPLE_PASSES_START,
       /* Called after executing subpasses of a GIMPLE_PASS in
          execute_ipa_pass_list.  */
       PLUGIN_EARLY_GIMPLE_PASSES_END,
       /* Called when a pass is first instantiated.  */
       PLUGIN_NEW_PASS,
       PLUGIN_EVENT_FIRST_DYNAMIC    /* Dummy event used for indexing callback array.  */
     };

3.2. コードの内容

ひたすらコールバックを登録して、それぞれのコールバックでbacktraceを表 示する。ここではPLUGIN_FINISH_DECLに着目。

#include <gcc-plugin.h>
#include <stdio.h>
#include <execinfo.h>
 
int plugin_is_GPL_compatible;
 
#define __pr_dbg(...)                           \
  do {                                          \
    fprintf(stderr, __VA_ARGS__);               \
  } while (0)
 
#define pr_dbg(...)                             \
  do {                                          \
    __pr_dbg("%s: ", __FUNCTION__);             \
    __pr_dbg(__VA_ARGS__);                      \
  } while (0)
 
#define BACKTRACE_BUFFER_NR 128
static void *backtrace_buffer[BACKTRACE_BUFFER_NR];
#define pr_bt()                                                  \
  do {                                                           \
    int i, n = backtrace(backtrace_buffer, BACKTRACE_BUFFER_NR); \
    char **syms = backtrace_symbols(backtrace_buffer, n);        \
    for (i = 0; i < n; i++)                                      \
      __pr_dbg("%s\n", syms[i]);                                 \
    free(syms);                                                  \
  } while (0)
 
#define ONCE(stmt) \
  do {                                          \
    static int done = 0;                        \
    if (!done) {                                \
      done = 1;                                 \
      stmt;                                     \
    }                                           \
  } while (0)
 
#define DEFINE_CALLBACK(name) \
  static void name##_callback(void *gcc_data, void *user_data) \
  {                                                            \
    __pr_dbg("%s is called\n", __FUNCTION__);                  \
    ONCE(pr_bt());                                             \
  }
 
#define REGISTER_CALLBACK(args, user, name)                          \
  do {                                                               \
    register_callback(args->base_name, name, name##_callback, user); \
    __pr_dbg("%s is registered\n", #name);                           \
  } while (0)
 
<snip>
DEFINE_CALLBACK(PLUGIN_FINISH_DECL);
<snip>
 
int plugin_init(struct plugin_name_args *args,
                struct plugin_gcc_version *version)
{
  void *user = NULL;
<snip>
  REGISTER_CALLBACK(args, user, PLUGIN_FINISH_DECL);
<snip>
  return 0;
}

3.3. 実行結果

PLUGIN_FINISH_DECLの結果に着目。実際は何度もPLUGIN_FINISH_DECLが呼ばれ ている(stdio.h経由で多くの変数が定義される)。mangleされたシンボル名 のbacktraceが出力された。

<snip>
PLUGIN_FINISH_DECL_callback is called
./callbacks(+0xa54) [0x2b2eadbf5a54]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus(_Z28invoke_plugin_callbacks_fulliPv+0x5a) [0x7cc0aa]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus(_Z14cp_finish_declP9tree_nodeS0_bS0_i+0x8d5) [0x4f1615]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus(_Z9grokfieldPK13cp_declaratorP21cp_decl_specifier_seqP9tree_nodebS5_S5_+0x521) [0x52c801]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x55a7fd]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x5430f5]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x553200]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x5557ea]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x55aee8]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x55f17c]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x55de18]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x55e158]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x55f1f8]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x55de18]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus(_Z12c_parse_filev+0x213) [0x55f6e3]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus(_Z19c_common_parse_filev+0x65) [0x5f0ab5]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus(_Z11toplev_mainiPPc+0x9a0) [0x85e100]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed) [0x2b2eacf7376d]
/usr/lib/gcc/x86_64-linux-gnu/4.7/cc1plus() [0x4d1771]
<snip>

3.4. invoke_plugin_callbacks_full

plugins_callbacksからコールバックを取り出してgcc_dataをセットしている。

int
invoke_plugin_callbacks_full (int event, void *gcc_data)
{
  int retval = PLUGEVT_SUCCESS;
<snip>
  switch (event)
    {
<snip>
      case PLUGIN_FINISH_DECL:
<snip>
        {
          /* Iterate over every callback registered with this event and
             call it.  */
          struct callback_info *callback = plugin_callbacks[event];
 
          if (!callback)
            retval = PLUGEVT_NO_CALLBACK;
          for ( ; callback; callback = callback->next)
            (*callback->func) (gcc_data, callback->user_data);
        }
        break;
<snip>
    }
<snip>
  return retval;
}

3.5. finish_decl

tree型のdeclをvoid *gcc_dataに設定している。ようやくコールバックに渡さ れる引数の正体が判明した。

void
finish_decl (tree decl, location_t init_loc, tree init,
             tree origtype, tree asmspec_tree)
{
<snip>
        invoke_plugin_callbacks (PLUGIN_FINISH_DECL, decl);
}

4. PLUGIN_FINISH_DECLを使ったコールバック

tree declの中身を表示する。

4.1. コードの内容

DECL_NAMEで変数の名前を、TYPE_DECLで変数の型を取得する。

#include <gcc-plugin.h>
#include <tree.h>
#include <stdio.h>
#include <execinfo.h>
 
int plugin_is_GPL_compatible;
 
#define __pr_dbg(...)                           \
  do {                                          \
    fprintf(stderr, __VA_ARGS__);               \
  } while (0)
 
#define pr_dbg(...)                             \
  do {                                          \
    __pr_dbg("%s: ", __FUNCTION__);             \
    __pr_dbg(__VA_ARGS__);                      \
  } while (0)
 
static const char* get_type_string(tree decl)
{
  switch (TREE_CODE(TREE_TYPE(decl)))
    {
#define CASE(name)                              \
      case name:                                \
        {                                       \
          return #name;                         \
        }
      CASE(VOID_TYPE);
      CASE(INTEGER_TYPE);
      CASE(REAL_TYPE);
      CASE(COMPLEX_TYPE);
      CASE(VECTOR_TYPE);
      CASE(ENUMERAL_TYPE);
      CASE(BOOLEAN_TYPE);
      CASE(POINTER_TYPE);
      CASE(OFFSET_TYPE);
      CASE(REFERENCE_TYPE);
      CASE(METHOD_TYPE);
      CASE(ARRAY_TYPE);
      CASE(RECORD_TYPE);
      CASE(UNION_TYPE);
      CASE(QUAL_UNION_TYPE);
      CASE(FUNCTION_TYPE);
      CASE(LANG_TYPE);
#undef CASE
    default:
      {
        return "UNKNOWN";
      }
    }
}
 
static const char *get_name_string(tree decl)
{
  if (DECL_NAME(decl))
    return IDENTIFIER_POINTER(DECL_NAME(decl));
  return "noname";
}
 
static void PLUGIN_FINISH_DECL_callback(void *gcc_data, void *user_data)
{
  tree decl = (tree) gcc_data;
  if (decl == NULL)
    return;
  pr_dbg("%s %s\n", get_type_string(decl), get_name_string(decl));
}
 
int plugin_init(struct plugin_name_args *args,
                struct plugin_gcc_version *version)
{
  void *user = NULL;
  register_callback(args->base_name, PLUGIN_FINISH_DECL,
                    PLUGIN_FINISH_DECL_callback, user);
  return 0;
}

4.2. XXX_TYPEについて

tree.defのtree.defで定義されている。tree.hはall-tree.defをインクルード し、all-tree.defはtree.defをインクルードしている。tree.hでall-tree.def の中身をenumにしている。

#define DEFTREECODE(SYM, STRING, TYPE, NARGS)   SYM,
#define END_OF_BASE_TREE_CODES LAST_AND_UNUSED_TREE_CODE,
 
enum tree_code {
#include "all-tree.def"
MAX_TREE_CODES
};
 
#undef DEFTREECODE
#undef END_OF_BASE_TREE_CODES

tree.defでenumの値を宣言している。

DEFTREECODE (INTEGER_TYPE, "integer_type", tcc_type, 0)

4.3. 実行結果

対象のコードは以下の通り。

#include <stdio.h>
 
struct hello_struct {
  char *buffer_field;
};
 
typedef struct hello_struct hello_typedef;
 
static void echo1(struct hello_struct hello_struct)
{
  printf("%s\n", hello_struct.buffer_field);
}
 
static char echo2(struct hello_struct *hello_struct)
{
  return (char)printf("%s\n", hello_struct->buffer_field);
}
 
int main(void)
{
  static char buffer [] = "hello, world\n";
  struct hello_struct hello_struct_obj = { .buffer_field = buffer, };
  printf("%s\n", buffer);
  echo1(hello_struct_obj);
  echo2(&hello_struct_obj);
  return 0;
}

出力の抜粋は以下の通り。structのフィールド、関数内部の変数が出力される。

<snip>
PLUGIN_FINISH_DECL_callback: POINTER_TYPE buffer_field
PLUGIN_FINISH_DECL_callback: POINTER_TYPE echo1_nouse
PLUGIN_FINISH_DECL_callback: ARRAY_TYPE buffer
PLUGIN_FINISH_DECL_callback: RECORD_TYPE hello_struct_obj

5. PLUGIN_PASS_MANAGER_SETUPを使ったplugin

5.1. コードの中身

関数の戻り値の型、関数名、引数の型と名前を出力する。

#include <gcc-plugin.h>
#include <coretypes.h>
#include <diagnostic.h>
#include <gimple.h>
#include <tree.h>
#include <tree-flow.h>
#include <tree-pass.h>
#include <execinfo.h>
 
int plugin_is_GPL_compatible;
 
#define __pr_dbg(...)                           \
  do {                                          \
    fprintf(stderr, __VA_ARGS__);               \
  } while (0)
 
#define pr_dbg(...)                             \
  do {                                          \
    __pr_dbg("%s: ", __FUNCTION__);             \
    __pr_dbg(__VA_ARGS__);                      \
  } while (0)
 
static const char* get_type_string(tree decl)
{
  switch (TREE_CODE(TREE_TYPE(decl)))
    {
#define CASE(name)                              \
      case name:                                \
        {                                       \
          return #name;                         \
        }
      CASE(VOID_TYPE);
      CASE(INTEGER_TYPE);
      CASE(REAL_TYPE);
      CASE(COMPLEX_TYPE);
      CASE(VECTOR_TYPE);
      CASE(ENUMERAL_TYPE);
      CASE(BOOLEAN_TYPE);
      CASE(POINTER_TYPE);
      CASE(OFFSET_TYPE);
      CASE(REFERENCE_TYPE);
      CASE(METHOD_TYPE);
      CASE(ARRAY_TYPE);
      CASE(RECORD_TYPE);
      CASE(UNION_TYPE);
      CASE(QUAL_UNION_TYPE);
      CASE(FUNCTION_TYPE);
      CASE(LANG_TYPE);
#undef CASE
    default:
      {
        return "UNKNOWN";
      }
    }
}
 
static const char *get_name_string(tree decl)
{
  if (DECL_NAME(decl))
    return IDENTIFIER_POINTER(DECL_NAME(decl));
  return "noname";
}
 
static void pr_arg(tree decl)
{
  __pr_dbg("%s %s", get_name_string(decl), get_type_string(decl));
}
 
static void pr_curr_func(void)
{
  tree arg = DECL_ARGUMENTS(current_function_decl);
  pr_dbg("%s %s(", get_type_string(DECL_RESULT(current_function_decl)),
         get_name_string(current_function_decl));
  if (arg)
    {
      pr_arg(arg);
      for (arg = DECL_CHAIN(arg); arg; arg = DECL_CHAIN (arg)) {
        __pr_dbg(", ");
        pr_arg(arg);
      }
    }
  else
    {
      __pr_dbg("void");
    }
  __pr_dbg(")\n");
}
 
static bool PLUGIN_PASS_MANAGER_SETUP_gate(void)
{
  return true;
}
 
static unsigned PLUGIN_PASS_MANAGER_SETUP_exec(void)
{
  pr_curr_func();
  return 0;
}
 
static struct gimple_opt_pass PLUGIN_PASS_MANAGER_SETUP_pass = 
  {
    {
      .type = GIMPLE_PASS,
      .name = "PLUGIN_PASS_MANAGER_SETUP",
      .gate = PLUGIN_PASS_MANAGER_SETUP_gate,
      .execute = PLUGIN_PASS_MANAGER_SETUP_exec,
    }
  };
 
int plugin_init(struct plugin_name_args *args,
                struct plugin_gcc_version *version)
{
  struct register_pass_info pass =
    {
      .pass = &PLUGIN_PASS_MANAGER_SETUP_pass.pass,
      .reference_pass_name = "cfg",
      .ref_pass_instance_number = 1,
      .pos_op = PASS_POS_INSERT_AFTER,
    };
  register_callback(args->base_name, PLUGIN_PASS_MANAGER_SETUP,
                    NULL, &pass);
  return 0;
}

cfgの後に追加しているのは、Controll Flow Graphが作成された後だと処理の パスが明確になっているだろうと淡い期待を持っている為。見当違いかもしれ ない。

5.2. 実行結果

本体を持つ関数単位で呼ばれる。

pr_curr_func: INTEGER_TYPE main(void)
pr_curr_func: INTEGER_TYPE printf(__fmt POINTER_TYPE)
pr_curr_func: VOID_TYPE echo1(hello_struct_echo1 RECORD_TYPE)
pr_curr_func: INTEGER_TYPE echo2(hello_struct_echo2 POINTER_TYPE)

関数の本体をリスト経由でアクセスできると思うのだが、やり方がよく分から ない・・・。

5.3. reference_pass_name

PLUGIN_PASS_MANAGER_SETUPのコールバックはGCC内部でリストで管理されてお り、どのノードの後に入れるか(あるいは前に入れるか、置き換えるか)をノー ド名で指定する。
register_callbackでPLUGIN_PASS_MANAGER_SETUPのコールバックを登録すると register_passが呼ばれる。

void
register_callback (const char *plugin_name,
                   int event,
                   plugin_callback_func callback,
                   void *user_data)
{
  switch (event)
    {
      case PLUGIN_PASS_MANAGER_SETUP:
        gcc_assert (!callback);
        register_pass ((struct register_pass_info *) user_data);
        break;
<snip>
}

register_pass内で幾つかのリストを辿る。

void
register_pass (struct register_pass_info *pass_info)
{
  bool all_instances, success;
<snip>
  all_instances = pass_info->ref_pass_instance_number == 0;
  success = position_pass (pass_info, &all_lowering_passes);
  if (!success || all_instances)
    success |= position_pass (pass_info, &all_small_ipa_passes);
  if (!success || all_instances)
    success |= position_pass (pass_info, &all_regular_ipa_passes);
  if (!success || all_instances)
    success |= position_pass (pass_info, &all_lto_gen_passes);
  if (!success || all_instances)
    success |= position_pass (pass_info, &all_late_ipa_passes);
  if (!success || all_instances)
    success |= position_pass (pass_info, &all_passes);
  if (!success)
    fatal_error
      ("pass %qs not found but is referenced by new pass %qs",
       pass_info->reference_pass_name, pass_info->pass->name);

GCC内部であらかじめノードをリストに登録している箇所。

void
init_optimization_passes (void)
{
  struct opt_pass **p;
 
#define NEXT_PASS(PASS)  (p = next_pass_1 (p, &((PASS).pass)))
 
 /* All passes needed to lower the function into shape optimizers can
    operate on.  These passes are always run first on the function, but
    backend might produce already lowered functions that are not processed
    by these passes.  */
  p = &all_lowering_passes;
  NEXT_PASS (pass_warn_unused_result);
  NEXT_PASS (pass_diagnose_omp_blocks);
  NEXT_PASS (pass_diagnose_tm_blocks);
  NEXT_PASS (pass_mudflap_1);
  NEXT_PASS (pass_lower_omp);
  NEXT_PASS (pass_lower_cf);
  NEXT_PASS (pass_lower_tm);
  NEXT_PASS (pass_refactor_eh);
  NEXT_PASS (pass_lower_eh);
  NEXT_PASS (pass_build_cfg);
  NEXT_PASS (pass_warn_function_return);
  NEXT_PASS (pass_build_cgraph_edges);
  *p = NULL;
 
  /* Interprocedural optimization passes.  */
  p = &all_small_ipa_passes;
  NEXT_PASS (pass_ipa_free_lang_data);

ノード名の一覧を表示する方法があると思うのだが、よく分かっていない。

6. 感想

これ以上踏み込む為にはGCCがどのような解析木をどのように扱っているか等 を知る必要がある。特にAPIについては不明瞭な点が多い(DECL_NAMEとかは GCCのソースコードから拾ってきた)。マクロ定義が複雑でtree構造体の実態 も見えない。サンプルの収集が先決か(今更だが)。

ダウンロード
GCC plugin
今回使用したプログラムのコード。
gcc-plugin.tar.gz
GNU tar 2.8 KB