Initialization of the Julia runtime

    Execution starts at main() in cli/loader_exe.c, which calls jl_load_repl() in which loads a few libraries, eventually calling repl_entrypoint() in src/jlapi.c.

    repl_entrypoint() calls to set the C library locale and to initialize the “ios” library (see ios_init_stdstreams() and ).

    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 .

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

    julia_init())

    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).

    is called to zero the signal handler mask.

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

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

    loads and initializes a pre-compiled femtolisp image containing the scanner/parser.

    jl_init_types() creates jl_datatype_t type description objects for the . e.g.

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

    initializes the LLVM library.

    initializes 8-bit serialization tags for builtin jl_value_t values.

    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 . emit_intrinsic() translates these symbols into LLVM instructions during code generation.

    hooks C functions up to Julia function symbols. e.g. the symbol Core.:(===)() is bound to C function pointer jl_f_is() by calling add_builtin_func("===", jl_f_is).

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

    Note: _julia_init() 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 which repeatedly calls jl_toplevel_eval_flex() to execute . <!– TODO – drill down into eval? –>

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

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

    _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.

    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 initialized 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.

    calls jl_module_run_initializer() for each deserialized module to run the __init__() function.

    _julia_init() then returns and main() calls repl_entrypoint(argc, (char**)argv).

    sysimg

    If there is a sysimg file, it contains a pre-cooked image of the Core and Main modules (and whatever else is created by boot.jl). See Building the Julia system image.

    deserializes the saved sysimg into the current Julia runtime environment and initialization continues after jl_init_box_caches() below…

    Note: jl_restore_system_image() (and staticdata.c in general) uses the .

    loads the contents of argv[] into Base.ARGS.

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

    However, in our example (julia -e 'println("Hello World!")'), looks up Base._start and executes it.

    Base._start calls which calls jl_parse_input_line(“println(“Hello World!”)”) to create an expression object and to execute the parsed expression ex in the module context of Main.

    calls jl_toplevel_eval_in(m, ex), which calls . jl_toplevel_eval_flex implements a simple heuristic to decide whether to compile a given code thunk or run it by interpreter. When given println("Hello World!"), it would usually decide to run the code by interpreter, in which case it calls jl_interpret_toplevel_thunk, which then calls .

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

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

    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 . This calls Base._atexit, then calls jl_gc_run_all_finalizers() and cleans up libuv handles.