AnyMath 文档

Julia代码的Eval

学习Julia语言如何运行代码最难的部分之一是学习所有部分如何协同工作以执行代码块。

每个代码块通常会通过许多可能不熟悉的名称的步骤进行旅行,例如(没有特定的顺序):flisp,AST,C++,LLVM, 埃瓦尔, n.打字,打字, 宏扩展,sysimg(或系统映像),引导,编译,解析,执行,JIT,解释,框,拆箱,内在函数和原始函数,然后变成所需的结果(希望)。

定义-REPL

REPL代表Read-Eval-Print循环。 这就是我们所说的命令行环境的简称。
-AST
  抽象语法树AST是代码结构的数字表示。 在这种形式下,代码已经被标记为意义,以便更适合操作和执行。
compiler diagram

朱莉娅执行

整个过程的10,000英尺视图如下:

  1. 用户启动 朱莉娅.

  2. C函数 主要()cli/loader_exe。c 被叫来。 此函数处理命令行参数,填写 jl_选项 结构和设置变量 阿格斯. 然后它初始化Julia(通过调用https://github.com/JuliaLang/julia/blob/master/src/init.c[脧锚脧赂`朱莉亚*尼特` 在 初始化。c],这可能会加载一个以前编译 sysimg)。 最后,它通过调用将控制权传递给Julia 脧锚脧赂`基地。_启动()`.

  3. 何时 _启动() 接管控制权,随后的命令序列取决于给出的命令行参数。 例如,如果提供了文件名,它将继续执行该文件。 否则,它将启动交互式REPL。

  4. 跳过REPL如何与用户交互的细节,让我们说程序最终得到一个它想要运行的代码块。

  5. 如果要运行的代码块在文件中,https://github.com/JuliaLang/julia/blob/master/src/toplevel.c[脧锚脧赂`jl_load(char*文件名)]被调用来加载文件和 解析它。 然后将每个代码片段传递给 `埃瓦尔 来执行。

  6. 每个代码片段(或AST)都被传递给 埃瓦尔()变成结果。

  7. 埃瓦尔()获取每个代码片段并尝试运行它https://github.com/JuliaLang/julia/blob/master/src/toplevel.c[脧锚脧赂`jl_toplevel_eval_flex()`].

  8. jl_toplevel_eval_flex() 决定代码是否为"顶层"操作(例如 使用模块),这在函数内部是无效的。 如果是这样,它将代码传递给顶级解释器。

  9. jl_toplevel_eval_flex() 然后 展开代码以消除任何宏并"降低"AST以使其更易于执行。

  10. jl_toplevel_eval_flex() 然后使用一些简单的启发式来决定是JIT编译AST还是直接解释它。

  11. 解释代码的大部分工作由https://github.com/JuliaLang/julia/blob/master/src/interpreter.c[脧锚脧赂`埃瓦尔` 在 翻译。c].

  12. 如果相反,代码是编译的,那么大部分工作由 科德根。cpp. 每当第一次使用给定的参数类型调用Julia函数时, 类型推断将在该函数上运行。 此信息由 codegen步骤生成更快的代码。

  13. 最终,用户退出REPL,或者到达程序结束,并且 _启动() 方法返回。

  14. 就在退出之前, 主要() 电话https://github.com/JuliaLang/julia/blob/master/src/init.c[脧锚脧赂`jl_atexit_hook(exit_code)]. 这就要求 `基地。_atexit() (它调用注册到的任何函数 atexit()里面朱莉娅)。 然后它调用https://github.com/JuliaLang/julia/blob/master/src/gc.c[脧锚脧赂`jl_gc_run_all_finalizers()]. 最后,它优雅地清理了所有 `利布夫 处理并等待它们冲洗和关闭。

解析;解析

默认情况下,Julia使用https://github.com/JuliaLang/JuliaSyntax.jl[JuliaSyntax.jl]来产生AST。 从历史上看,它使用了一个用femtolisp编写的小型lisp程序,其源代码分布在Julia in src/flisp.如果 JULIA_USE_FLISP_PARSER 环境变量设置为 1,将使用旧的解析器代替。

宏观扩张

何时 埃瓦尔()遇到宏,它会在尝试计算表达式之前展开该AST节点。 宏观扩展涉及从 埃瓦尔()(在Julia中),到解析器函数 jl_macroexpand() (写在 flisp)到Julia宏本身(写在-还有什么-Julia)通过 fl_invoke_julia_macro(),然后回来。

通常,宏扩展作为调用的第一步被调用。 元。下()/jl_expand(),虽然它也可以通过调用直接调用 宏扩展()/jl_macroexpand().

类型推断

类型推断在Julia中通过以下方式实现https://github.com/JuliaLang/julia/blob/master/base/compiler/typeinfer.jl[脧锚脧赂`打字()` 在 编译器/typeinfer。jl]. 类型推断是检查Julia函数并确定其每个变量类型的边界以及函数返回值类型的边界的过程。 这可以实现许多未来的优化,例如已知不可变值的拆箱,以及各种运行时操作的编译时提升,例如计算字段偏移量和函数指针。 类型推断还可以包括诸如恒定传播和内联的其他步骤。

更多定义-JIT

实时编译在需要时将本机机器代码生成到内存中的过程。
-LLVM
  低级虚拟机(编译器)Julia JIT编译器是一个名为libLLVM的程序/库。 Julia中的Codegen指的是获取Julia AST并将其转换为LLVM指令的过程,以及LLVM优化并将其转换为本机汇编指令的过程。

-C++
  LLVM实现的编程语言,这意味着codegen也是用这种语言实现的。 Julia库的其余部分是用C实现的,部分原因是其较小的功能集使其更可用作跨语言接口层。

-盒子
  此术语用于描述在垃圾收集器(gc)跟踪并用对象类型标记的数据周围获取值和分配包装器的过程。

-拆箱
  装箱值的反面。 当数据的类型在编译时完全已知时(通过类型推断),此操作可以更有效地操作数据。

-泛型功能
  由多个"方法"组成的Julia函数,这些方法根据参数类型签名被选择用于动态调度

-匿名函数或"方法"
  没有名称和类型分派功能的Julia函数

-原始函数
  在C中实现的函数,但在Julia中作为命名函数"方法"公开(尽管没有通用函数调度功能,类似于匿名函数)

-内在功能
  作为Julia中的函数公开的低级操作。 这些伪函数实现了对原始位的操作,例如add和sign extend,这些操作不能以任何其他方式直接表示。 由于它们直接对位进行操作,因此必须将它们编译为函数,并通过调用 `核心。内在。箱(T,。..)` 以将类型信息重新分配给值。

JIT代码生成

Codegen是将Julia AST转换为本机机器代码的过程。

JIT环境是通过早期调用来初始化的。https://github.com/JuliaLang/julia/blob/master/src/codegen.cpp[脧锚脧赂`jl_init_codegen` 在 科德根。cpp].

根据需要,Julia方法由函数转换为本机函数 emit_function(jl_method_instance_t*). (注意,当使用MCJIT(在LLVM v3.4+中)时,每个函数都必须JIT到一个新模块中。)此函数递归调用 emit_expr() 直到整个功能已经被发射。

此文件的大部分剩余部分用于特定代码模式的各种手动优化。 例如, emit_known_call() 知道如何内联许多原始函数(在https://github.com/JuliaLang/julia/blob/master/src/builtins.c[脧锚脧赂`建造。c`])用于参数类型的各种组合。

Codegen的其他部分由各种帮助程序文件处理:

*脧锚脧赂`debuginfo。cpp` 处理JIT函数的回溯 *脧锚脧赂`ccall。cpp` 处理ccall和llvmcallffi,以及各种 abi_*。cpp 档案 *脧锚脧赂`内在。cpp` 处理各种低级本征函数的发射

引导创建新系统映像的过程称为"引导"。

这个词的词源来自"通过自举拉起自己"这个短语,指的是从一组非常有限的可用功能和定义开始,并以创建一个全功能环境结束的想法。

系统图像

系统映像是一组Julia文件的预编译存档。 该 系统。纪 与Julia一起分发的文件就是这样一个系统映像,通过执行文件生成https://github.com/JuliaLang/julia/blob/master/base/sysimg.jl[脧锚脧赂`sysimg的。jl`],并将生成的环境(包括类型,函数,模块和所有其他定义的值)序列化到一个文件中。 因此,它包含一个冻结版本的 主要, 核心,而 基地 模块(以及引导结束时环境中的其他任何内容)。 这个串行器/解串器是由实现https://github.com/JuliaLang/julia/blob/master/src/staticdata.c[脧锚脧赂`jl_save_system_图像`/jl_restore_system_图像静态数据。c].

如果没有sysimg文件(jl_options。image_file==NULL),这也暗示着 --建造 是在命令行上给出的,所以最终的结果应该是一个新的sysimg文件。 在Julia初始化期间,minimal 核心主要 模块被创建。 然后一个名为 靴子。jl 从当前目录进行评估。 然后Julia计算任何作为命令行参数给出的文件,直到它到达结尾。 最后,它将生成的环境保存到"sysimg"文件中,以用作未来Julia运行的起点。