CodeReading: GCC "$cputype.cと$cputype.md"

Kazuki Ohta, 2006/02/10


コンパイラについての本を見ていた所、唐突にgccの中身を見たくなった。手元に有ったコード(version 4.0.1)を眺めていると、gcc/config以下に有るアーキテクチャ依存部分が目に止まった。ので、ここを読んでみる事にする。このディレクトリには様々なアーキテクチャに依存する部分が記述して有り、特定のCPUアーキテクチャについての知識に加えてgccの内部表現(RTL)にも精通している必要が有る。中々美味しそうな部分だ。

GCCの内部についてはGNU Compiler Collection (GCC) Internalsが参考になる。gcc/config以下を見てみると、.cや.hの他に.mdというファイルが見付かる。これはMachine Descriptionファイルと呼ばれるようだ。その辺りを考慮に入れつつ、重要そうな3つの章にざっと目を通した。


さて、とりあえず気になるのはgcc本体とアーキテクチャ依存部分(以下バックエンド)の橋渡しがどのようになっているかという点だ。それを理解していないと何も始まらない気がする。

まずは.mdファイルがどのように利用されるかを調べてみる。コンパイルされる事は無いだろうし、スクリプトみたいに処理されるのだろうと思いビルドシステム(gcc/config.gcc, gcc/configure.ac, gcc/Makefile.in)を覗いてみる。gcc/configure.acを覗いていると、こんなブロックが見付かった。
# --------------------------------------------------------
# Build, host, and target specific configuration fragments
# --------------------------------------------------------

# Collect build-machine-specific information.
. ${srcdir}/config.build

# Collect host-machine-specific information.
. ${srcdir}/config.host

target_gtfiles=

# Collect target-machine-specific information.
. ${srcdir}/config.gcc

extra_objs="${host_extra_objs} ${extra_objs}"
extra_gcc_objs="${host_extra_gcc_objs} ${extra_gcc_objs}"

# Default the target-machine variables that were not explicitly set.
if test x"$tm_file" = x
then tm_file=$cpu_type/$cpu_type.h; fi

if test x"$extra_headers" = x
then extra_headers=; fi

if test x$md_file = x
then md_file=$cpu_type/$cpu_type.md; fi

if test x$out_file = x
then out_file=$cpu_type/$cpu_type.c; fi

if test x"$tmake_file" = x
then tmake_file=$cpu_type/t-$cpu_type
fi

if test x"$dwarf2" = xyes
then tm_file="$tm_file tm-dwarf2.h"
fi

# Say what files are being used for the output code and MD file.
echo "Using \`$srcdir/config/$out_file' for machine-specific logic."
echo "Using \`$srcdir/config/$md_file' as machine description file."
$md_fileに$cpu_type/$cpu_type.mdが渡されている。$out_fileや$tmake_fileも重要な情報だな。まとめるとアーキテクチャ依存部分に必要なのは、(1) $cputype.md (2) $cputype.c (3) t-$cputypeの3つのファイルだという事か。確かにそうなってるわ。これはconfigure部分なので、次はMakefile.inを見てみる。$(md_file)をキーに探してみると、次のようなブロックが。
# The following pair of rules has this effect:
# genconfig is run only if the md has changed since genconfig was last run;
# but the file insn-config.h is touched only when its contents actually change.

# Each of the other insn-* files is handled by a similar pair of rules.

# This causes an anomaly in the results of make -n
# because insn-* is older than s-*
# and thus make -n thinks that insn-* will be updated
# and force recompilation of things that depend on it.
# We use move-if-change precisely to avoid such recompilation.
# But there is no way to teach make -n that it will be avoided.

# Each of the insn-*.[ch] rules has a semicolon at the end,
# for otherwise the system Make on SunOS 4.1 never tries
# to recompile insn-*.o.  To avoid problems and extra noise from
# versions of make which don't like empty commands (nothing after the
# trailing `;'), we call true for each.

insn-config.h: s-config ; @true
s-config : $(MD_DEPS) build/genconfig$(build_exeext)
	$(RUN_GEN) build/genconfig$(build_exeext) $(md_file) > tmp-config.h
	$(SHELL) $(srcdir)/../move-if-change tmp-config.h insn-config.h
	$(STAMP) s-config

insn-conditions.c: s-conditions ; @true
s-conditions : $(MD_DEPS) build/genconditions$(build_exeext)
	$(RUN_GEN) build/genconditions$(build_exeext) $(md_file) > tmp-conditions.c
	$(SHELL) $(srcdir)/../move-if-change tmp-conditions.c insn-conditions.c
	$(STAMP) s-conditions

build/insn-conditions.o : insn-conditions.c $(CONFIG_H) $(SYSTEM_H) \
  $(GTM_H) $(RTL_H) $(TM_P_H) $(REGS_H) function.h $(RECOG_H) real.h output.h \
  $(FLAGS_H) hard-reg-set.h $(RESOURCE_H) toplev.h reload.h gensupport.h \
  insn-constants.h coretypes.h

...以下も同じような感じの記述が続く
"genconfig"や"genconditions"といったツール郡(gcc/gen*)が$(md_file)を解析し、insn-config.hやinsn-conditions.c等のファイルを生成、最後にそれをビルドしてinsn-conditions.oを生成する。という事らしい(正確にはinsn-conditions.oについての記述は単に依存関係を記述しているだけだが)。なるほどね。実際、gcc/genconditions.cを見てみると以下の様なループを拝む事が出来る。
int
main (int argc, char **argv)
{
  rtx desc;

  ...

  if (init_md_reader_args (argc, argv) != SUCCESS_EXIT_CODE)
    return (FATAL_EXIT_CODE);

  ...

  /* Read the machine description.  */
  while (1)
    {
      desc = read_md_rtx (&pattern_lineno, &code);
      if (desc == NULL)
	break;

      ...
    }

  ...
}
全然長くないので(250行程度)全体を眺めてみると、init_md_reader_args関数やread_md_rtx関数で.mdを読み取り、それを元にcのコードを吐いているようだ。うーん、これをCでやるか。もうちょい深部まで追ってみるとgcc/read-rtl.cのread_rtx関数で読み取りが行われている事が分かった。うん、頑張ってパースしている。スクリプト言語でやりたいなぁ。

ちょっと歴史を調べてみる。gcc/read-rtl.cの冒頭には1987年のCCが。この頃にはPerlとか無かったんかな?ちょっと調べてみる。Perl Timelineによると1987年にPerl 1.000がリリースされたらしいので、read-rtl.cは丁度Perlの誕生年に書かれたソースか。スクリプト言語が生まれて来るか来ないかの時代みたいだから、まぁ使わんわな。

さて、とりあえずこれで.mdファイルの使われ方が分かった。内容はまだ全然分からんが、それは追い追い。さて、次は$cputype.cとgcc内部とのインターフェースを調べてみる。$cputype.cの方には"Target Description Macros and Functions"を記述するようだ。ドキュメントの該当部分を眺めていると14.1 The Global targetm Variableが参考になりそうだ。どうもstruct gcc_target targetmいうグローバル変数に加え、予め定められたマクロ群(ドキュメントの後半はこのマクロ群のリファレンスになっている)でマシン依存部分を抽象化しているようだ。struct gcc_targetはgcc/target.hで宣言されている。なるほどねーこいつがマシン依存を抽象化している立役者か。次はそれを初期化するTARGET_INITIALIZERマクロを見てみる。これはgcc/target-def.hで宣言されている。
/* The whole shebang.  */
#define TARGET_INITIALIZER			\
{						\
  TARGET_ASM_OUT,				\
  TARGET_SCHED,					\
  TARGET_VECTORIZE,				\
  TARGET_EH_RETURN_FILTER_MODE,			\
  TARGET_MERGE_DECL_ATTRIBUTES,			\
  TARGET_MERGE_TYPE_ATTRIBUTES,			\
  TARGET_ATTRIBUTE_TABLE,			\
  TARGET_COMP_TYPE_ATTRIBUTES,			\
  TARGET_SET_DEFAULT_TYPE_ATTRIBUTES,		\
  TARGET_INSERT_ATTRIBUTES,			\
  TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P,	\
  TARGET_MS_BITFIELD_LAYOUT_P,			\
  TARGET_ALIGN_ANON_BITFIELD,			\
  TARGET_INIT_BUILTINS,				\
  TARGET_EXPAND_BUILTIN,			\
  TARGET_FOLD_BUILTIN,				\
  TARGET_MANGLE_FUNDAMENTAL_TYPE,		\
  TARGET_INIT_LIBFUNCS,				\
  TARGET_SECTION_TYPE_FLAGS,			\
  TARGET_CANNOT_MODIFY_JUMPS_P,			\
  TARGET_BRANCH_TARGET_REGISTER_CLASS,		\
  TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED,	\
  TARGET_CANNOT_FORCE_CONST_MEM,		\
  TARGET_CANNOT_COPY_INSN_P,			\
  TARGET_DELEGITIMIZE_ADDRESS,			\
  TARGET_FUNCTION_OK_FOR_SIBCALL,		\
  TARGET_IN_SMALL_DATA_P,			\
  TARGET_BINDS_LOCAL_P,				\
  TARGET_ENCODE_SECTION_INFO,			\
  TARGET_STRIP_NAME_ENCODING,			\
  TARGET_SHIFT_TRUNCATION_MASK,			\
  TARGET_VALID_POINTER_MODE,                    \
  TARGET_SCALAR_MODE_SUPPORTED_P,		\
  TARGET_VECTOR_MODE_SUPPORTED_P,               \
  TARGET_VECTOR_OPAQUE_P,			\
  TARGET_RTX_COSTS,				\
  TARGET_ADDRESS_COST,				\
  TARGET_DWARF_REGISTER_SPAN,                   \
  TARGET_FIXED_CONDITION_CODE_REGS,		\
  TARGET_CC_MODES_COMPATIBLE,			\
  TARGET_MACHINE_DEPENDENT_REORG,		\
  TARGET_BUILD_BUILTIN_VA_LIST,			\
  TARGET_GIMPLIFY_VA_ARG_EXPR,			\
  TARGET_GET_PCH_VALIDITY,			\
  TARGET_PCH_VALID_P,				\
  TARGET_DEFAULT_SHORT_ENUMS,			\
  TARGET_BUILTIN_SETJMP_FRAME_VALUE,		\
  TARGET_MD_ASM_CLOBBERS,			\
  TARGET_DWARF_CALLING_CONVENTION,              \
  TARGET_DWARF_HANDLE_FRAME_UNSPEC,		\
  TARGET_CALLS,					\
  TARGET_CXX,					\
  TARGET_HAVE_NAMED_SECTIONS,			\
  TARGET_HAVE_CTORS_DTORS,			\
  TARGET_HAVE_TLS,				\
  TARGET_HAVE_SRODATA_SECTION,			\
  TARGET_TERMINATE_DW2_EH_FRAME_INFO,		\
  TARGET_ASM_FILE_START_APP_OFF,		\
  TARGET_ASM_FILE_START_FILE_DIRECTIVE,		\
  TARGET_HANDLE_PRAGMA_REDEFINE_EXTNAME,	\
  TARGET_HANDLE_PRAGMA_EXTERN_PREFIX,		\
  TARGET_RELAXED_ORDERING,			\
}
ここのマクロはさらに粒度の細かいマクロ展開を行っている。アーキテクチャ依存部分で宣言されていないマクロについては、対応するdefault_* というマクロが使われるようだ。まるでvirtual関数。なるほどねー。

さて、これで$cputype.cと$cputype.mdとgcc本体との結合部分が有る程度理解出来た。次からは実際に何か1つのアーキテクチャ依存部分を読んでみる事にする。

(続く)
[ return ]