Initialization of the Julia runtime
julia -e 'println("Hello World!")' の実行方法は次の通りです。
main()
実行は main() in cli/loader_exe.c で開始され、jl_load_repl() を呼び出します cli/loader_lib.c これはいくつかのライブラリを読み込み、最終的に jl_repl_entrypoint() in src/jlapi.c を呼び出します。
jl_repl_entrypoint() は libsupport_init() を呼び出して C ライブラリのロケールを設定し、「ios」ライブラリを初期化します(ios_init_stdstreams() および Legacy ios.c library を参照)。
Next jl_parse_opts() is called to process command line options. Note that jl_parse_opts() only deals with options that affect code generation or early initialization. Other options are handled later by exec_options() in base/client.jl.
jl_parse_opts() はコマンドラインオプションを global jl_options struct に保存します。
julia_init()
julia_init() in init.c は main() によって呼び出され、_julia_init() in init.c を呼び出します。
_julia_init()は再びlibsupport_init()を呼び出すことから始まります(2回目は何もしません)。
restore_signals() は、シグナルハンドラーマスクをゼロにするために呼び出されます。
jl_resolve_sysimg_location() は、ベースシステムイメージのために構成されたパスを検索します。Building the Julia system image を参照してください。
jl_gc_init() は、弱参照、保持された値、およびファイナライゼーションのためのアロケーションプールとリストを設定します。
jl_init_frontend() は、スキャナー/パーサーを含む事前コンパイルされたfemtolispイメージをロードして初期化します。
jl_init_types() は jl_datatype_t 型の説明オブジェクトを作成します。これは built-in types defined in julia.h の例です。
jl_any_type = jl_new_abstracttype(jl_symbol("Any"), core, NULL, jl_emptysvec);
jl_any_type->super = jl_any_type;
jl_type_type = jl_new_abstracttype(jl_symbol("Type"), core, jl_any_type, jl_emptysvec);
jl_int32_type = jl_new_primitivetype(jl_symbol("Int32"), core,
jl_any_type, jl_emptysvec, 32);jl_init_tasks() は jl_datatype_t* jl_task_type オブジェクトを作成し、グローバル jl_root_task 構造体を初期化し、jl_current_task をルートタスクに設定します。
jl_init_codegen() は LLVM library を初期化します。
jl_init_serializer() は、組み込みの jl_value_t 値のための 8 ビットシリアル化タグを初期化します。
!jl_options.image_file が存在しない場合、Core および Main モジュールが作成され、boot.jl が評価されます:
jl_core_module = jl_new_module(jl_symbol("Core"), NULL) は、Juliaの Core モジュールを作成します。
jl_init_intrinsic_functions() は、定数 jl_intrinsic_type シンボルを含む新しい Julia モジュール Intrinsics を作成します。これらは、各 intrinsic function に対する整数コードを定義します。 emit_intrinsic() は、コード生成中にこれらのシンボルを LLVM 命令に変換します。
jl_init_primitives() は、C関数をJulia関数シンボルにフックします。例えば、シンボル Core.:(===)() は、add_builtin_func("===", jl_f_is) を呼び出すことによってC関数ポインタ jl_f_is() にバインドされています。
jl_new_main_module() creates the global "Main" module and sets jl_current_task->current_module = jl_main_module.
注意: _julia_init() then sets jl_root_task->current_module = jl_core_module。この時点で jl_root_task は jl_current_task のエイリアスであるため、上記の jl_new_main_module() によって設定された current_module は上書きされます。
jl_load("boot.jl", sizeof("boot.jl")) は jl_parse_eval_all を繰り返し呼び出し、 jl_toplevel_eval_flex() を実行して boot.jl を実行します。 <!– TODO – evalを詳しく調べる? –>
jl_get_builtin_hooks() は、boot.jl で定義された Julia グローバルに対してグローバル C ポインタを初期化します。
jl_init_box_caches() は、1024までの値のためにグローバルなボックス化された整数値オブジェクトを事前に割り当てます。これにより、後でボックス化された整数の割り当てが高速化されます。例えば:
jl_value_t *jl_box_uint8(uint32_t x)
{
return boxed_uint8_cache[(uint8_t)x];
}_julia_init() iterates は jl_core_module->bindings.table を通じて jl_datatype_t 値を探し、型名のモジュールプレフィックスを jl_core_module に設定します。
jl_add_standard_imports(jl_main_module) does "using Base" in the "Main" module.
注意: _julia_init() は、上で jl_core_module に設定される前のように jl_root_task->current_module = jl_main_module に戻ります。
プラットフォーム固有のシグナルハンドラは、SIGSEGV(OSX、Linux)および SIGFPE(Windows)用に初期化されます。
他のシグナル(SIGINFO, SIGBUS, SIGILL, SIGTERM, SIGABRT, SIGQUIT, SIGSYS および SIGPIPE)は sigdie_handler() に接続されており、バックトレースを出力します。
jl_init_restored_module() は、各デシリアライズされたモジュールに対して __init__() 関数を実行するために jl_module_run_initializer() を呼び出します。
最終的に sigint_handler() は SIGINT に接続され、jl_throw(jl_interrupt_exception) を呼び出します。
_julia_init() は back to main() in cli/loader_exe.c を返し、main() は repl_entrypoint(argc, (char**)argv) を呼び出します。
repl_entrypoint()
repl_entrypoint() は argv[] の内容を Base.ARGS にロードします。
もしコマンドラインに.jl "プログラム"ファイルが供給された場合、exec_program()はjl_load(program,len)を呼び出し、これはjl_parse_eval_allを呼び出し、これは繰り返しjl_toplevel_eval_flex()を呼び出してプログラムを実行します。
However, in our example (julia -e 'println("Hello World!")'), jl_get_global(jl_base_module, jl_symbol("_start")) looks up Base._start and jl_apply() executes it.
Base._start
Base._start は Base.exec_options を呼び出し、さらに jl_parse_input_line("println("Hello World!")") を呼び出して式オブジェクトを作成し、Core.eval(Main, ex) を使用して、Main のモジュールコンテキストで解析された式 ex を実行します。
Core.eval
Core.eval(Main, ex) は jl_toplevel_eval_in(m, ex) を呼び出し、さらに jl_toplevel_eval_flex を呼び出します。 jl_toplevel_eval_flex は、与えられたコードスンクをコンパイルするか、インタープリタで実行するかを決定するための単純なヒューリスティックを実装しています。 println("Hello World!") が与えられた場合、通常はインタープリタでコードを実行することを決定し、その場合は jl_interpret_toplevel_thunk を呼び出し、次に eval_body を呼び出します。
スタックダンプは、インタープリターが Base.println() と Base.print() のさまざまなメソッドを通過して、最終的に write(s::IO, a::Array{T}) where T に到達する様子を示しています。これは ccall(jl_uv_write()) を実行します。
jl_uv_write() は uv_write() を呼び出して JL_STDOUT に "Hello World!" を書き込みます。 Libuv wrappers for stdio を参照してください。
Hello World!| Stack frame | Source code | Notes |
|---|---|---|
jl_uv_write() | jl_uv.c | called though ccall |
julia_write_282942 | stream.jl | function write!(s::IO, a::Array{T}) where T |
julia_print_284639 | ascii.jl | print(io::IO, s::String) = (write(io, s); nothing) |
jlcall_print_284639 | ||
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.print(Base.TTY, String) |
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.print(Base.TTY, String, Char, Char...) |
jl_apply() | julia.h | |
jl_f_apply() | builtins.c | |
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.println(Base.TTY, String, String...) |
jl_apply() | julia.h | |
jl_trampoline() | builtins.c | |
jl_apply() | julia.h | |
jl_apply_generic() | gf.c | Base.println(String,) |
jl_apply() | julia.h | |
do_call() | interpreter.c | |
eval_body() | interpreter.c | |
jl_interpret_toplevel_thunk | interpreter.c | |
jl_toplevel_eval_flex | toplevel.c | |
jl_toplevel_eval_in | toplevel.c | |
Core.eval | boot.jl |
私たちの例には関数呼び出しが1つだけあり、それが「Hello World!」を印刷する仕事を終えたので、スタックは迅速に main() に戻ります。
jl_atexit_hook()
main() は jl_atexit_hook() を呼び出します。これにより Base._atexit が呼び出され、その後 jl_gc_run_all_finalizers() が呼び出され、libuv ハンドルがクリーンアップされます。
julia_save()
最終的に、main()はjulia_save()を呼び出します。これはコマンドラインで要求されると、ランタイム状態を新しいシステムイメージに保存します。jl_compile_all()およびjl_save_system_image()を参照してください。