If function f(...) calls g(...) then the first time you call f(...), g(...) would normally also get compiled. The compilation is specialized on the type of input arguments (the method is selected by multiple dispatch and code is specialized further by concrete type information).
If the concrete types of ... in the g(...) call are inferrable, then g(...) is specialized and compiled to native code immediately, and possibly even inlined. If those types can't be inferred, it will repeat this process when it is called (there is also a cache of specialized code so it only compiles once, but you need to "look up" the function in cache dynamically in that case).
In many situations the types of an entire program can be inferred and be "compiled" ahead of time, but the semantics are always that of an interpretter.
TLDR here is that Julia is better described as Just Ahead of Time (JAOT) instead of JIT. Julia isn't using a normal tracing JIT where things start running interpreted and then get replaced. When Julia first runs into a new function (or function being called with new argument types), it will statically compile (at runtime) code for that function being called with those argument types. When it does this, it will use the type information of the types the function has been called with, to do all sorts of optimization (de-virtualization, inlining, etc).
Once this method is compiled, it will be used whenever the same function is called with the same argument types.
If function f(...) calls g(...) then the first time you call f(...), g(...) would normally also get compiled. The compilation is specialized on the type of input arguments (the method is selected by multiple dispatch and code is specialized further by concrete type information).
If the concrete types of ... in the g(...) call are inferrable, then g(...) is specialized and compiled to native code immediately, and possibly even inlined. If those types can't be inferred, it will repeat this process when it is called (there is also a cache of specialized code so it only compiles once, but you need to "look up" the function in cache dynamically in that case).
In many situations the types of an entire program can be inferred and be "compiled" ahead of time, but the semantics are always that of an interpretter.