#cranelift #ir #variables #builder #instructions #codegen #language

无 std alexcrichton-cranelift-frontend

Cranelift IR 构建器辅助工具

2 个不稳定版本

0.53.0 2020 年 1 月 10 日
0.52.0 2020 年 1 月 10 日

#52 in #cranelift


3 crates 中使用

Apache-2.0 WITH LLVM-exception

1.5MB
31K SLoC

此包提供了一个简单的方法来创建 Cranelift IR 函数并将其填充为从另一种语言转换的指令。它包含一个 SSA 构造模块,该模块提供了方便的方法,通过 use_vardef_var 调用来将非 SSA 变量转换为 SSA Cranelift IR 值。


lib.rs:

Cranelift IR 构建库。

提供了一种简单的方法来创建 Cranelift IR 函数,并将其填充为您用另一种语言编写的源程序对应的指令。

要开始使用,创建一个 FunctionBuilderContext 并将其作为参数传递给 FunctionBuilder

可变变量和 Cranelift IR 值

此 API 最有趣的特点是它提供了一种处理所有变量问题的唯一方式。实际上,FunctionBuilder 结构体具有一个类型 Variable,该类型应该是您源语言变量的索引。然后,通过调用 declare_vardef_varuse_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