16 个版本 (9 个重大变更)

0.10.5 2024 年 8 月 21 日
0.9.2 2024 年 5 月 9 日
0.8.0 2024 年 2 月 27 日
0.7.0 2023 年 10 月 11 日
0.1.0 2021 年 11 月 16 日

#838 in 魔法豆

Download history 1475/week @ 2024-05-01 1950/week @ 2024-05-08 1245/week @ 2024-05-15 975/week @ 2024-05-22 1515/week @ 2024-05-29 1641/week @ 2024-06-05 1832/week @ 2024-06-12 1194/week @ 2024-06-19 1613/week @ 2024-06-26 1758/week @ 2024-07-03 1272/week @ 2024-07-10 1381/week @ 2024-07-17 2236/week @ 2024-07-24 2647/week @ 2024-07-31 1954/week @ 2024-08-07 1547/week @ 2024-08-14

8,573 每月下载量
17 个crate中使用 (7 直接使用)

MIT 许可证

1MB
22K SLoC

Miden 汇编

该crate包含Miden汇编器。

汇编器的目的是将 Miden 汇编 (MASM) 源代码编译成 Miden 虚拟机程序(由 Program 结构体表示)。然后程序可以在 Miden 虚拟机 处理器 上执行。

编译 Miden 汇编

要从某些 Miden 汇编源代码编译 Miden 虚拟机程序,首先需要实例化汇编器,然后调用其提供的一个汇编方法,例如 assemble

assemble 方法接受可执行模块的源代码字符串或文件路径,将其编译为 Program,如果程序在某种方式上无效,则返回错误。返回的错误类型可以以丰富的诊断信息打印出来,类似于 Rust 编译器。

示例

use std::path::Path;
use miden_assembly::Assembler;

// Instantiate a default, empty assembler
let assembler = Assembler::default();

// Emit a program which pushes values 3 and 5 onto the stack and adds them
let program = assembler.assemble_program("begin push.3 push.5 add end").unwrap();

// Emit a program from some source code on disk (requires the `std` feature)
let program = assembler.assemble_program(&Path::new("./example.masm")).unwrap();

[!NOTE] 默认汇编器不提供内核或标准库,您必须通过 Assembler 的各种构建方法显式添加这些库,如下一节所述。

汇编器选项

如上所述,默认汇编器实例化时只包含您提供的源代码。如果您想要支持更复杂的程序,您需要将代码分解成库和模块,然后一次性将它们链接在一起。这可以通过使用Assembler结构体的一组构建方法来实现,例如with_kernel_from_modulewith_library等。

以下将详细介绍其中的一些。请参阅模块文档,了解完整的API集及其用法。

您最可能遇到的第一种用例是将一些共享代码分解成一个。库是一组属于公共命名空间并打包在一起的模块。例如,标准库就是这样的。

要从您的程序入口点调用此库中的代码,您必须使用with_librarywith_libraries方法将库添加到您将编译程序的汇编器实例中。

更精确地说,库可以是任何实现了Library特质的对象,这为它们的管理提供了一定的灵活性。上述标准库实现了此特质,因此如果我们想在自己的程序中使用Miden标准库,我们可以像这样添加它

use miden_assembly::Assembler;

let assembler = Assembler::default()
    .with_library(&miden_stdlib::StdLibrary::default())
    .unwrap();

现在生成的汇编器可以编译调用标准库过程(通过从库的命名空间导入它们)的代码,如下所示

use.std::math::u64

begin
    push.1.0
    push.2.0
    exec.u64::wrapping_add
end

为库提供了一种通用的容器格式,该格式实现了Library,并且可以用于同一命名空间内属于Miden汇编模块的任何一组,这是由MaslLibrary结构体提供的。

MaslLibrary序列化/反序列化到.masl文件格式,这是一个包含解析但未编译的Miden汇编代码(以抽象语法树的形式)的二进制格式。您可以使用MaslLibrary接口构建和加载.masl文件。

程序内核

一个程序内核定义了一组可以通过syscall指令调用的过程。Miden程序总是针对某个内核进行编译,默认情况下,这个内核是空的,因此不允许任何syscall指令。

您可以通过两种方式提供内核:一个预编译的Kernel结构体,或者通过从源代码编译内核模块,如下所示

use miden_assembly::Assembler;

let assembler = Assembler::default()
    .with_kernel_from_module("export.foo add end")
    .unwrap();

由这个汇编器编译的程序将能够通过执行syscall指令来调用foo过程,如下所示

assembler.assemble_program("
begin
    syscall.foo
end
").unwrap();

[!NOTE] 假定无修饰的syscall目标定义在内核模块中。这与需要调用者解析为本地过程、显式导入的模块中定义的过程或对应于编译过程的MAST根哈希的execcall指令不同。

这些选项也适用于syscall,但有一个前提条件,即无论使用哪种方法,它必须解析为汇编器指定的内核中的过程,否则编译将因错误而失败。

调试模式

汇编器可以在调试模式下实例化。使用此类汇编器编译的程序保留汇编指令和虚拟机操作之间的源映射。因此,当使用处理器execute_iter()函数执行此类程序时,可以将每条指令与其来源的源代码相关联。您可以按以下方式操作:

use miden_assembly::Assembler;

// Instantiate the assembler in debug mode
let assembler = Assembler::default().with_debug_mode(true);

整合所有内容

为了帮助说明如何将我们上面讨论的所有主题组合在一起,让我们来看一个最后的例子。

use miden_assembly::Assembler;
use miden_stdlib::StdLibrary;

// Source code of the kernel module
let kernel = "export.foo add end";

// Instantiate the assembler with multiple options at once
let assembler = Assembler::default()
    .with_debug_mode(true)
    .with_library(&StdLibrary::default())
    .and_then(|a| a.with_kernel_from_module(kernel))
    .unwrap();

// Assemble our program
assembler.assemble_program("
begin
    push.1.2
    syscall.foo
end
");

许可

此项目受MIT许可

依赖关系

约10–37MB
约569K SLoC