# Initialization of the Julia runtime

How does the Julia runtime execute `julia -e 'println("Hello World!")'` ?

## main()

Execution starts at main() in julia/ui/repl.c.

main() calls libsupport_init() to set the C library locale and to initialise the “ios” library (see ios_init_stdstreams() and Legacy ios.c library).

Next parse_opts() is called to process command line options. Note that `parse_opts()` only deals with options that affect code generation or early initialisation. Other options are handled later by process_options() in base/client.jl.

`parse_opts()` stores command line options in the global jl_options struct.

## julia_init()

julia_init() in task.c is called by main() and calls _julia_init() in init.c.

`_julia_init()` begins by calling `libsupport_init()` again (it does nothing the second time).

restore_signals() is called to zero the signal handler mask.

jl_resolve_sysimg_location() searches configured paths for the base system image. See Building the Julia system image.

jl_gc_init() sets up allocation pools and lists for: weak refs, preserved values and finalization.

jl_init_frontend() loads and initialises a pre-compiled femtolisp image containing the scanner/parser;

jl_init_types() creates `jl_datatype_t` type description objects for the built-in types defined in julia.h. e.g.

    jl_any_type = jl_new_abstracttype(jl_symbol("Any"), NULL, jl_null);
    jl_any_type->super = jl_any_type;

    jl_type_type = jl_new_abstracttype(jl_symbol("Type"), jl_any_type, jl_null);

    jl_int32_type = jl_new_bitstype(jl_symbol("Int32"),
                                    jl_any_type, jl_null, 32);

jl_init_tasks() creates the `jl_datatype_t* jl_task_type` object; initialises the global `jl_root_task` struct; and sets `jl_current_task` to the root task.

jl_init_codegen() initialises the LLVM library.

jl_init_serializer() initialises 8-bit serialisation tags for 256 frequently used `jl_value_t` values. The serialisation mechanism uses these tags as shorthand (in lieu of storing whole objects) to save storage space.

If there is no sysimg file (`!jl_options.image_file`) then then `Core` and `Main` modules are created and `boot.jl` is evaluated:

`jl_core_module = jl_new_module(jl_symbol("Core"))` creates the Julia `Core` module.

jl_init_intrinsic_functions() creates a new Julia module “Intrinsics” containing constant jl_intrinsic_type symbols. These define an integer code for each intrinsic function. emit_intrinsic() translates these symbols into LLVM instructions during code generation.

jl_init_primitives() hooks C functions up to Julia function symbols. e.g. the symbol `Base.is()` is bound to C function pointer `jl_f_is()` by calling `add_builtin_func("eval", jl_f_top_eval)`.

jl_new_main_module() creates the global “Main” module and sets `jl_current_task->current_module = jl_main_module`.

Note: _julia_init() then sets `jl_root_task->current_module = jl_core_module`. `jl_root_task` is an alias of `jl_current_task` at this point, so the current_module set by `jl_new_main_module()` above is overwritten.

jl_load(“boot.jl”, sizeof(“boot.jl”)) calls jl_parse_eval_all which repeatedly calls jl_toplevel_eval_flex() to execute boot.jl. TODO – drill down into eval?

jl_get_builtin_hooks() initialises global C pointers to Julia globals defined in `boot.jl`.

jl_init_box_caches() pre-allocates global boxed integer value objects for values up to 1024. This speeds up allocation of boxed ints later on. e.g.:

    jl_value_t *jl_box_uint8(uint32_t x)
    {
        return boxed_uint8_cache[(uint8_t)x];
    }

_julia_init() iterates over the `jl_core_module->bindings.table` looking for `jl_datatype_t` values and sets the type name’s module prefix to `jl_core_module`.

jl_add_standard_imports(jl_main_module) does “using Base” in the “Main” module.

Note: `_julia_init()` now reverts to `jl_root_task->current_module = jl_main_module` as it was before being set to `jl_core_module` above.

Platform specific signal handlers are initialised for `SIGSEGV` (OSX, Linux), and `SIGFPE` (Windows).

Other signals (`SIGINFO, SIGBUS, SIGILL, SIGTERM, SIGABRT, SIGQUIT, SIGSYS` and `SIGPIPE`) are hooked up to sigdie_handler() which prints a backtrace.

jl_init_restored_modules() calls jl_module_run_initializer() for each deserialised module to run the `__init__()` function.

Finally sigint_handler() is hooked up to `SIGINT` and calls `jl_throw(jl_interrupt_exception)`.

`_julia_init()` then returns back to main() in julia/ui/repl.c and main() calls `true_main(argc, (char**)argv)`.

## true_main()

true_main() loads the contents of `argv[]` into `Base.ARGS`.

If a .jl “program” file was supplied on the command line, then exec_program() calls jl_load(program,len) which calls jl_parse_eval_all which repeatedly calls jl_toplevel_eval_flex() to execute the program.

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 calls Base.process_options which calls jl_parse_input_line(“println(“Hello World!”)”) to create an expression object and `Base.eval()` to execute it.

## Base.eval

`Base.eval()` was mapped to jl_f_top_eval by `jl_init_primitives()`.

jl_f_top_eval() calls jl_toplevel_eval_in(jl_main_module, ex), where “ex” is the parsed expression `println("Hello World!")`.

jl_toplevel_eval_in() calls jl_toplevel_eval_flex() which calls eval() in interpreter.c.

The stack dump below shows how the interpreter works its way through various methods of `Base.println()` and `Base.print()` before arriving at write{T}(s::IO, a::Array{T}) which does `ccall(jl_uv_write())`.

jl_uv_write() calls `uv_write()` to write “Hello World!” to `JL_STDOUT`. See Libuv wrappers for stdio.:

    Hello World!

Stack frame | Source code | Notes
--- | --- | ---
jl_uv_write() | jl_uv.c | called though `Base.ccall()`
julia_write_282942 | stream.jl | function write!{T}(s::IO, a::Array{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() | interpreter.c |
jl_interpret_toplevel_expr() | interpreter.c |
jl_toplevel_eval_flex() | toplevel.c |
jl_toplevel_eval() | toplevel.c |
jl_toplevel_eval_in() | builtins.c |
jl_f_top_eval() | builtins.c |

Since our example has just one function call, which has done its job of printing “Hello World!”, the stack now rapidly unwinds back to `main()`.

## jl_atexit_hook()

`main()` calls jl_atexit_hook(). This calls _atexit for each module, then calls jl_gc_run_all_finalizers() and cleans up libuv handles.

## julia_save()

Finally, `main()` calls julia_save(), which if requested on the command line, saves the runtime state to a new system image. See jl_compile_all() and jl_save_system_image().