以前HotSpot VMのIntrinsicsを見ました。ビルド時にAD(Architecture Description)ファイルから各CPUアーキテクチャに合わせたC++のソースファイルを生成しています。
しかし、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_defやalloc_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の理解が深まりました。