2 个不稳定版本
0.53.0 | 2020 年 1 月 10 日 |
---|---|
0.52.0 | 2020 年 1 月 10 日 |
#52 in #cranelift
在 3 crates 中使用
1.5MB
31K SLoC
此包提供了一个简单的方法来创建 Cranelift IR 函数并将其填充为从另一种语言转换的指令。它包含一个 SSA 构造模块,该模块提供了方便的方法,通过 use_var
和 def_var
调用来将非 SSA 变量转换为 SSA Cranelift IR 值。
lib.rs
:
Cranelift IR 构建库。
提供了一种简单的方法来创建 Cranelift IR 函数,并将其填充为您用另一种语言编写的源程序对应的指令。
要开始使用,创建一个 FunctionBuilderContext
并将其作为参数传递给 FunctionBuilder
。
可变变量和 Cranelift IR 值
此 API 最有趣的特点是它提供了一种处理所有变量问题的唯一方式。实际上,FunctionBuilder
结构体具有一个类型 Variable
,该类型应该是您源语言变量的索引。然后,通过调用 declare_var
、def_var
和 use_var
函数,FunctionBuilder
将为您创建所有对应于您的变量的 Cranelift IR 值。
此API旨在帮助您将可变变量转换为SSA
形式。 use_var
将返回对应于程序中特定点的可变变量的Cranelift IR值。然而,如果您事先知道其中一个变量只定义了一次,例如它是表达式语言中中间表达式的结果,那么您可以直接通过指令构建器返回的Cranelift IR值来转换它。对于这种不可变变量使用use_var
API也是可以的,但会有轻微的性能开销(SSA算法事先不知道变量是否不可变)。
教训是,您应该使用这三个函数来处理所有可变变量,即使它们不在源代码中,但也是翻译的产物。您需要自己保持语言中的可变变量与Cranelift使用的Variable
索引之间的映射。注意:因为Variable
被Cranelift用来索引包含您的可变变量信息的数组,当您使用[Variable::new(var_index)
]创建一个新的Variable
时,您应该确保var_index
是由每次遇到新的可变变量时递增1的计数器提供的。
示例
以下是我们想要转换成Cranelift IR的伪程序
function(x) {
x, y, z : i32
block0:
y = 2;
z = x + y;
jump block1
block1:
z = z + y;
brnz y, block3;
jump block2
block2:
z = z - x;
return y
block3:
y = y - x
jump block1
}
以下是使用FunctionBuilderContext
构建相应的Cranelift IR函数的方法
extern crate cranelift_codegen;
extern crate cranelift_frontend;
use cranelift_codegen::entity::EntityRef;
use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{AbiParam, ExternalName, Function, InstBuilder, Signature};
use cranelift_codegen::isa::CallConv;
use cranelift_codegen::settings;
use cranelift_codegen::verifier::verify_function;
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
fn main() {
let mut sig = Signature::new(CallConv::SystemV);
sig.returns.push(AbiParam::new(I32));
sig.params.push(AbiParam::new(I32));
let mut fn_builder_ctx = FunctionBuilderContext::new();
let mut func = Function::with_name_signature(ExternalName::user(0, 0), sig);
{
let mut builder = FunctionBuilder::new(&mut func, &mut fn_builder_ctx);
let block0 = builder.create_ebb();
let block1 = builder.create_ebb();
let block2 = builder.create_ebb();
let block3 = builder.create_ebb();
let x = Variable::new(0);
let y = Variable::new(1);
let z = Variable::new(2);
builder.declare_var(x, I32);
builder.declare_var(y, I32);
builder.declare_var(z, I32);
builder.append_ebb_params_for_function_params(block0);
builder.switch_to_block(block0);
builder.seal_block(block0);
{
let tmp = builder.ebb_params(block0)[0]; // the first function parameter
builder.def_var(x, tmp);
}
{
let tmp = builder.ins().iconst(I32, 2);
builder.def_var(y, tmp);
}
{
let arg1 = builder.use_var(x);
let arg2 = builder.use_var(y);
let tmp = builder.ins().iadd(arg1, arg2);
builder.def_var(z, tmp);
}
builder.ins().jump(block1, &[]);
builder.switch_to_block(block1);
{
let arg1 = builder.use_var(y);
let arg2 = builder.use_var(z);
let tmp = builder.ins().iadd(arg1, arg2);
builder.def_var(z, tmp);
}
{
let arg = builder.use_var(y);
builder.ins().brnz(arg, block3, &[]);
}
builder.ins().jump(block2, &[]);
builder.switch_to_block(block2);
builder.seal_block(block2);
{
let arg1 = builder.use_var(z);
let arg2 = builder.use_var(x);
let tmp = builder.ins().isub(arg1, arg2);
builder.def_var(z, tmp);
}
{
let arg = builder.use_var(y);
builder.ins().return_(&[arg]);
}
builder.switch_to_block(block3);
builder.seal_block(block3);
{
let arg1 = builder.use_var(y);
let arg2 = builder.use_var(x);
let tmp = builder.ins().isub(arg1, arg2);
builder.def_var(y, tmp);
}
builder.ins().jump(block1, &[]);
builder.seal_block(block1);
builder.finalize();
}
let flags = settings::Flags::new(settings::builder());
let res = verify_function(&func, &flags);
println!("{}", func.display(None));
if let Err(errors) = res {
panic!("{}", errors);
}
}
依赖项
~0.8–1.7MB
~37K SLoC