Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

HotSpot VMビルド時のADファイル処理

以前HotSpot VMのIntrinsicsを見ました。ビルド時にAD(Architecture Description)ファイルから各CPUアーキテクチャに合わせたC++のソースファイルを生成しています。

www.sakatakoichi.com

しかし、ADファイルからどのようにC++のソースを生成しているのか理解していません。

OpenJDKのビルド後buildディレクトリの中にADファイルから生成されたソースがあります(例: build/macosx-x86_64-normal-server-release/hotspot/variant-server/gensrc/adfiles)。

$ ls build/macosx-x86_64-normal-server-release/hotspot/variant-server/gensrc/adfiles
adGlobals_x86.hpp   ad_x86.hpp          ad_x86_expand.cpp   ad_x86_gen.cpp      ad_x86_peephole.cpp dfa_x86.cpp                                                                                               
ad_x86.cpp          ad_x86_clone.cpp    ad_x86_format.cpp   ad_x86_misc.cpp     ad_x86_pipeline.cpp

これらのファイルがどのように生成されるのかを調べました。

ADファイルの内容

ADファイルはsrc/hotspot/cpu以下の各CPUディレクトリの中にあります。ファイルの内容を抜粋します。

reg_def XMM31p( SOC, SOC, Op_RegF, 31, xmm31->as_VMReg()->next(15));
...
alloc_class chunk1(XMM0,  XMM0b,  XMM0c,  XMM0d,  XMM0e,  XMM0f,  XMM0g,  XMM0h,  XMM0i,  XMM0j,  XMM0k,  XMM0l,  XMM0m,  XMM0n,  XMM0o,  XMM0p,
...
                      );

関数呼び出しのようです。実は、ADファイルの中身はArchitecture Description Languageという言語で書かれています。そのシンタックスはsrc/hotspot/share/adlc/Doc/Syntax.docに記述されています。

ADファイルのパース

src/hotspot/share/adlcディレクトリにAD言語をパースするC++のソースがあります。

$ ls
Doc          adlc.hpp     adlparse.hpp archDesc.hpp arena.hpp    dict2.cpp    filebuff.cpp forms.cpp    formsopt.cpp formssel.cpp main.cpp     output_h.cpp
Test         adlparse.cpp archDesc.cpp arena.cpp    dfa.cpp      dict2.hpp    filebuff.hpp forms.hpp    formsopt.hpp formssel.hpp output_c.cpp

adlparse.hppを見ます。

  // Parse components of the register section
  void reg_def_parse(void);              // Parse register definition
  void reg_class_parse(void);            // Parse register class definition
  void reg_class_dynamic_parse(void);    // Parse dynamic register class definition
  void alloc_class_parse(void);          // Parse allocation class definition

ADファイルにあったreg_defalloc_classをパースする関数が定義されているとわかります。main.cppからADLParserが呼び出されています。

  ADL_Parse = new ADLParser(ADL_Buf, AD); // Create a parser to parse the buffer
  ADL_Parse->parse();           // Parse buffer & build description lists

これらの関数は誰が呼び出しているのか?

ここで詰まりました。ビルド時にADファイルから生成している以上、ビルド時に何かがこのadlcのソースを呼び出しているはずです。それがうまく見つかりません。

ビルド時なのでmakeのコードにあるだろうと調べました。するとmake/hotspot/gensrcディレクトリというものがありました。

$ ls
GenerateSources.gmk GensrcAdlc.gmk      GensrcDtrace.gmk    GensrcJfr.gmk       GensrcJvmti.gmk

GensrcAdlc.gmk、いかにもですね。このファイルの後ろの方にこうありました。

  # Finally copy the generated files from support/adlc into gensrc/adfiles,
  # and postprocess them by fixing dummy #line directives.

  ADLC_GENERATED_FILES := $(addprefix $(JVM_VARIANT_OUTPUTDIR)/gensrc/adfiles/, \
      ad_$(HOTSPOT_TARGET_CPU_ARCH).cpp \
      ad_$(HOTSPOT_TARGET_CPU_ARCH).hpp \
      ad_$(HOTSPOT_TARGET_CPU_ARCH)_clone.cpp \
      ad_$(HOTSPOT_TARGET_CPU_ARCH)_expand.cpp \
      ad_$(HOTSPOT_TARGET_CPU_ARCH)_format.cpp \
      ad_$(HOTSPOT_TARGET_CPU_ARCH)_gen.cpp \
      ad_$(HOTSPOT_TARGET_CPU_ARCH)_misc.cpp \
      ad_$(HOTSPOT_TARGET_CPU_ARCH)_peephole.cpp \
      ad_$(HOTSPOT_TARGET_CPU_ARCH)_pipeline.cpp \
      adGlobals_$(HOTSPOT_TARGET_CPU_ARCH).hpp \
      dfa_$(HOTSPOT_TARGET_CPU_ARCH).cpp \
  )

support/adlcディレクトリからgensrc/adfilesディレクトリにファイルをコピー、そのファイルというのはad_$(HOTSPOT_TARGET_CPU_ARCH).cppなどです。この投稿の始めに書いた、ad_x86.cppといったファイルはぴったりマッチします。ここでADファイルからソースを生成しているので間違いないでしょう。前の方に戻りながら見ます。

  ##############################################################################
  # Run the adlc tool on the single concatenated ad source file, and store the
  # output in support/adlc for further processing.
  ADLC_RUN_MARKER := $(ADLC_SUPPORT_DIR)/_adlc_run.marker

  $(ADLC_RUN_MARKER): $(BUILD_ADLC) $(SINGLE_AD_SRCFILE)
    $(call LogInfo, Generating adlc files)
    $(call MakeDir, $(@D))
    $(call ExecuteWithLog, $(ADLC_SUPPORT_DIR)/adlc_run, \
        $(FIXPATH) $(ADLC_TOOL) $(ADLCFLAGS) $(SINGLE_AD_SRCFILE) \
            -c$(ADLC_SUPPORT_DIR)/ad_$(HOTSPOT_TARGET_CPU_ARCH).cpp \
            -h$(ADLC_SUPPORT_DIR)/ad_$(HOTSPOT_TARGET_CPU_ARCH).hpp \
            -a$(ADLC_SUPPORT_DIR)/dfa_$(HOTSPOT_TARGET_CPU_ARCH).cpp \
            -v$(ADLC_SUPPORT_DIR)/adGlobals_$(HOTSPOT_TARGET_CPU_ARCH).hpp)
    $(TOUCH) $@

$(ADLC_SUPPORT_DIR)/adlc_runを実行しています。これは実際にはbuild/macosx-x86_64-normal-server-release/hotspot/variant-server/support/adlcディレクトリです。adlc_run.cmdlineというファイルがあります。

/Users/koichi.sakata/code/loom/build/macosx-x86_64-normal-server-release/hotspot/variant-server/tools/adlc/adlc -q -T -D_ALLBSD_SOURCE=1 -D_GNU_SOURCE=1 -g -DAMD64=1 -D_LP64=1 /Users/koichi.sakata/code/loom/build/macosx-x86_64-normal-server-release/hotspot/variant-server/support/adlc/all-ad-src.ad -c/Users/koichi.sakata/code/loom/build/macosx-x86_64-normal-server-release/hotspot/variant-server/support/adlc/ad_x86.cpp -h/Users/koichi.sakata/code/loom/build/macosx-x86_64-normal-server-release/hotspot/variant-server/support/adlc/ad_x86.hpp -a/Users/koichi.sakata/code/loom/build/macosx-x86_64-normal-server-release/hotspot/variant-server/support/adlc/dfa_x86.cpp -v/Users/koichi.sakata/code/loom/build/macosx-x86_64-normal-server-release/hotspot/variant-server/support/adlc/adGlobals_x86.hpp

hotspot/variant-server/tools/adlc/adlcにオプションでad_x86.cppを渡しています。ここで生成しているのでしょう。

hotspot/variant-server/tools/adlc/adlcはバイナリでした。さらに前に戻ります。

  $(eval $(call SetupNativeCompilation, BUILD_ADLC, \
      NAME := adlc, \
      TYPE := EXECUTABLE, \
      TOOLCHAIN := TOOLCHAIN_BUILD_LINK_CXX, \
      SRC := $(TOPDIR)/src/hotspot/share/adlc, \
      EXTRA_FILES := $(TOPDIR)/src/hotspot/share/opto/opcodes.cpp, \
      CFLAGS := $(ADLC_CFLAGS) $(ADLC_CFLAGS_WARNINGS), \
      LDFLAGS := $(ADLC_LDFLAGS), \
      LIBS := $(ADLC_LIBS), \
      OBJECT_DIR := $(JVM_VARIANT_OUTPUTDIR)/tools/adlc/objs, \
      OUTPUT_DIR := $(JVM_VARIANT_OUTPUTDIR)/tools/adlc, \
      DEBUG_SYMBOLS := false, \
      DISABLED_WARNINGS_clang := tautological-compare, \
      DISABLED_WARNINGS_solstudio := notemsource, \
  ))

SetupNativeCompilationを呼んで、EXECUTABLEなhotspot/variant-server/tools/adlc/adlcを生成していると読めます。SetupNativeCompilationを検索すると、make/common/NativeCompilation.gmkに定義されていました。

################################################################################
# Create the recipe needed to compile a single native source file.
#
# Parameter 1 is the name of the rule, based on the name of the library/
# program being build and the name of the source code file, e.g.
# BUILD_LIBFOO_fooMain.cpp.
#
# Remaining parameters are named arguments:
#   FILE - The full path of the source file to compiler
#   BASE - The name of the rule for the entire binary to build ($1)
#   DISABLE_THIS_FILE_DEFINE - Set to true to disable the THIS_FILE define.
#
SetupCompileNativeFile = $(NamedParamsMacroTemplate)
define SetupCompileNativeFileBody

まとめ

HotSpot VMではビルド時に使用しているCPUのADファイルをパースして、そのCPUに合わせたC++のソースファイルを生成していることがわかりました。これはmakeのコードから呼び出されています。またADファイルはいわばDSLで書かれたものと捉えることができます。

また少し、HotSpot VMの理解が深まりました。