#llvm #safe-wrapper #safe #wrapper #programming-language #strongly-typed #llvm-ir

wasmer_inkwell

Inkwell 旨在帮助您通过安全地包装 llvm-sys 来创建自己的编程语言。

2 个版本

0.2.0-alpha.22021 年 5 月 28 日

FFI 中排名 177

每月下载量 29

Apache-2.0

665KB
10K SLoC

Inkwell(s)

Crates.io Build Status codecov lines of code Join the chat at https://gitter.im/inkwell-rs/Lobby Minimum rustc 1.42

It's a New Kind of Wrapper for Explosing LLVM (Safely)

Inkwell 旨在帮助您通过安全地包装 llvm-sys 来创建自己的编程语言。它提供比底层 LLVM C API 更强类型的接口,因此某些类型的错误可以在编译时而不是在 LLVM 的运行时被捕获。这意味着我们试图尽可能忠实地复制 LLVM IR 的强类型。最终目标是使 LLVM 从 Rust 端更安全,并且更容易学习和使用(通过文档)。

需求

  • Rust 1.42+
  • Rust 稳定版、Beta 版或 Nightly 版
  • LLVM 3.6, 3.7, 3.8, 3.9, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 或 11.0

用法

您需要将 Cargo.toml 指向 crates.io 上的现有预览 crate 或具有相应 LLVM 功能标志的 master 分支

[dependencies]
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm11-0"] }

支持版本

LLVM 版本 Cargo 功能标志
3.6.x llvm3-6
3.7.x llvm3-7
3.8.x llvm3-8
3.9.x llvm3-9
4.0.x llvm4-0
5.0.x llvm5-0
6.0.x llvm6-0
7.0.x llvm7-0
8.0.x llvm8-0
9.0.x llvm9-0
10.0.x llvm10-0
11.0.x llvm11-0

请注意,由于我们处于预 v1.0.0 版本,我们可能会不时在 master 上进行破坏性更改,以符合 semver。请尽可能优先选择 crates.io 发布版!

文档

文档自动基于主分支部署在此处。请访问此处。由于 rustdoc 的问题,这些文档尚未完全完成,并且只显示最新的支持版本。更多信息请参阅 #2

示例

Tari 的 Inkwell 示例,使用安全代码编写1

use inkwell::OptimizationLevel;
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::execution_engine::{ExecutionEngine, JitFunction};
use inkwell::module::Module;
use inkwell::targets::{InitializationConfig, Target};
use std::error::Error;

/// Convenience type alias for the `sum` function.
///
/// Calling this is innately `unsafe` because there's no guarantee it doesn't
/// do `unsafe` operations internally.
type SumFunc = unsafe extern "C" fn(u64, u64, u64) -> u64;

struct CodeGen<'ctx> {
    context: &'ctx Context,
    module: Module<'ctx>,
    builder: Builder<'ctx>,
    execution_engine: ExecutionEngine<'ctx>,
}

impl<'ctx> CodeGen<'ctx> {
    fn jit_compile_sum(&self) -> Option<JitFunction<SumFunc>> {
        let i64_type = self.context.i64_type();
        let fn_type = i64_type.fn_type(&[i64_type.into(), i64_type.into(), i64_type.into()], false);
        let function = self.module.add_function("sum", fn_type, None);
        let basic_block = self.context.append_basic_block(function, "entry");

        self.builder.position_at_end(basic_block);

        let x = function.get_nth_param(0)?.into_int_value();
        let y = function.get_nth_param(1)?.into_int_value();
        let z = function.get_nth_param(2)?.into_int_value();

        let sum = self.builder.build_int_add(x, y, "sum");
        let sum = self.builder.build_int_add(sum, z, "sum");

        self.builder.build_return(Some(&sum));

        unsafe { self.execution_engine.get_function("sum").ok() }
    }
}


fn main() -> Result<(), Box<dyn Error>> {
    let context = Context::create();
    let module = context.create_module("sum");
    let execution_engine = module.create_jit_execution_engine(OptimizationLevel::None)?;
    let codegen = CodeGen {
        context: &context,
        module,
        builder: context.create_builder(),
        execution_engine,
    };

    let sum = codegen.jit_compile_sum().ok_or("Unable to JIT compile `sum`")?;

    let x = 1u64;
    let y = 2u64;
    let z = 3u64;

    unsafe {
        println!("{} + {} + {} = {}", x, y, z, sum.call(x, y, z));
        assert_eq!(sum.call(x, y, z), x + y + z);
    }

    Ok(())
}

1 此示例中存在两次对 unsafe 的使用,因为实时编译和执行代码本身固有地是 unsafe。一方面,我们无法验证是否以正确的函数签名调用 get_function()。而且,调用我们获得的功能也是 unsafe,因为没有保证代码本身不会在内部执行 unsafe 操作(这也是为什么调用 C 代码时需要 unsafe 的原因)。

LLVM 的 Kaleidoscope 教程

可以在示例目录中找到。

贡献

请参阅我们的 贡献指南

依赖项

~4–5.5MB
~105K SLoC