#instructions #dispatch #closures #function #interpreter #proc-macro

无 std union-fn-macro

创建高效 "内联闭包" 的过程宏实现。

2 个不稳定版本

0.2.0 2023年1月16日
0.1.0 2023年1月11日

#46 in #dispatch


union-fn 中使用

MIT 许可证

70KB
968

[2]: https://github.com/Robbepop/union-fn/actions/workflows/rust.yml [4]: https://docs.rs/union-fn [6]: https://crates.io/crates/union-fn

持续集成 文档 Crates.io
[ci][2] [docs][4] [crates][6]

#[union_fn]:高效解释器的数据结构

此软件包提供了一个过程宏 #[union_fn],可以应用于 Rust trait 定义。

#[union_fn] 可以被视为一组多态、参数化的函数,这些函数针对数据局部性和多态调用进行了优化。

动机与想法

解释器通常使用基于 switch-loop 的指令调度,其中简单的 enum 代表所有不同类型的指令,如 AddBranch。指令调度发生在指令执行之间,当使用这种形式的调度通过分支表时,通常会存在大量的开销,而这种分支表通常不会被理想地优化。

#[union_fn] 宏通过将函数指针直接嵌入到其函数参数旁边的类型中,将调度成本降低到最小。这样,就不需要分支表,调用调度等于间接函数调用。

由于Rust enum的区分符对齐,enum表示形式浪费了很多空间,而这些空间被优化表示正确利用,通过存储函数指针而不是enum的区分符。因此,这两种类型通常具有相同的size_of。指向的函数知道如何解码通过union编码的函数参数,且没有额外开销。

代码生成

#[union_fn]宏主要生成两种不同的类型

  • 使用特质的标识符引用的所有特质的枚举表示。
    • 用于检查、调试和创建不同的调用。
    • 通过特质的标识符访问,例如Foo
    • 每个方法生成一个具有相同名称和参数的构造函数。
  • 一种优化数据局部性和多态调用的类型。
    • 主要用于计算阶段的实际调用。
    • 通过<Foo as union_fn::IntoOpt>::Opt>访问,其中Foo是特质的标识符。
    • 每个方法生成一个具有相同名称和参数的构造函数,或者可以通过union_fn::IntoOpt特质从enum表示形式转换。

示例

解释器

一个完整的计算器示例,它可以作为解释器的灵感来源,可以在这里找到。

代码生成

给定以下Rust代码

use union_fn::union_fn;

#[union_fn]
trait Counter {
    type Context = i64;

    /// Bumps the value `by` the amount.
    fn bump_by(value: &mut Self::Context, by: i64) {
        *value += by;
    }

    /// Selects the values in `choices` depending on `value`.
    fn select(value: &mut Self::Context, choices: [i64; 4]) {
        *value = choices.get(*value as usize).copied().unwrap_or(0)
    }

    /// Resets the `value` to zero.
    fn reset(value: &mut Self::Context) {
        *value = 0;
    }
}

fn main() {
    let mut value = 0;

    Counter::bump_by(1).call(&mut value);
    assert_eq!(value, 1);

    Counter::bump_by(41).call(&mut value);
    assert_eq!(value, 42);

    Counter::reset().call(&mut value);
    assert_eq!(value, 0);

    let choices = [11, 22, 33, 44];
    let opt = Counter::select(choices).into_opt();
    for i in 0..5 {
        let mut value = i;
        opt.call(&mut value);
        assert_eq!(value, choices.get(i as usize).copied().unwrap_or(0));
    }
}

此过程宏将扩展为以下代码(注意,为了演示目的,空格和derive宏扩展已更改。)

#[derive(::core::marker::Copy, ::core::clone::Clone)]
pub enum Counter {
    /// Bumps the value `by` the amount.
    BumpBy { by: i64 },
    /// Selects the values in `choices` depending on `value`.
    Select { choices: [i64; 4] },
    /// Resets the `value` to zero.
    Reset {},
}

impl Counter {
    /// Bumps the value `by` the amount.
    pub fn bump_by(by: i64) -> Self {
        Self::BumpBy { by }
    }

    /// Selects the values in `choices` depending on `value`.
    pub fn select(choices: [i64; 4]) -> Self {
        Self::Select { choices }
    }

    /// Resets the `value` to zero.
    pub fn reset() -> Self {
        Self::Reset {}
    }
}

impl ::union_fn::CallWithContext for Counter {
    type Context = i64;

    fn call(
        self,
        ctx: &mut Self::Context,
    ) -> <Counter as ::union_fn::UnionFn>::Output {
        match self {
            Self::BumpBy { by } => {
                <Self as ::union_fn::IntoOpt>::Impls::bump_by(ctx, by)
            }
            Self::Select { choices } => {
                <Self as ::union_fn::IntoOpt>::Impls::select(ctx, choices)
            }
            Self::Reset { } => {
                <Self as ::union_fn::IntoOpt>::Impls::reset(ctx,)
            }
        }
    }
}

const _: () = {
    ///Call optimized structure of the [`Counter`] type.
    #[derive(::core::marker::Copy, ::core::clone::Clone)]
    pub struct CounterOpt {
        handler: fn(
            ctx: &mut <Counter as ::union_fn::CallWithContext>::Context,
            &<Counter as ::union_fn::UnionFn>::Args,
        ) -> <Counter as ::union_fn::UnionFn>::Output,
        args: <Counter as ::union_fn::UnionFn>::Args,
    }

    impl ::union_fn::IntoOpt for Counter {
        type Opt = CounterOpt;
        type Delegator = CounterDelegate;
        type Impls = CounterImpls;

        fn into_opt(self) -> Self::Opt {
            match self {
                Self::BumpBy { by } => <Self as ::union_fn::IntoOpt>::Opt::bump_by(by),
                Self::Select { choices } => {
                    <Self as ::union_fn::IntoOpt>::Opt::select(choices)
                }
                Self::Reset {} => <Self as ::union_fn::IntoOpt>::Opt::reset(),
            }
        }
    }

    impl ::union_fn::CallWithContext for CounterOpt {
        type Context = i64;

        fn call(
            self,
            ctx: &mut Self::Context,
        ) -> <Counter as ::union_fn::UnionFn>::Output {
            (self.handler)(ctx, &self.args)
        }
    }

    impl CounterOpt {
        /// Bumps the value `by` the amount.
        pub fn bump_by(by: i64) -> Self {
            Self {
                handler: <Counter as ::union_fn::IntoOpt>::Delegator::bump_by,
                args: <Counter as ::union_fn::UnionFn>::Args::bump_by(by),
            }
        }

        /// Selects the values in `choices` depending on `value`.
        pub fn select(choices: [i64; 4]) -> Self {
            Self {
                handler: <Counter as ::union_fn::IntoOpt>::Delegator::select,
                args: <Counter as ::union_fn::UnionFn>::Args::select(choices),
            }
        }

        /// Resets the `value` to zero.
        pub fn reset() -> Self {
            Self {
                handler: <Counter as ::union_fn::IntoOpt>::Delegator::reset,
                args: <Counter as ::union_fn::UnionFn>::Args::reset(),
            }
        }
    }

    ///Efficiently packed method arguments for the [`Counter`] type.
    #[derive(::core::marker::Copy, ::core::clone::Clone)]
    pub union CounterArgs {
        /// Bumps the value `by` the amount.
        bump_by: i64,
        /// Selects the values in `choices` depending on `value`.
        select: [i64; 4],
        /// Resets the `value` to zero.
        reset: (),
    }

    impl CounterArgs {
        /// Bumps the value `by` the amount.
        pub fn bump_by(by: i64) -> Self {
            Self { bump_by: by }
        }

        /// Selects the values in `choices` depending on `value`.
        pub fn select(choices: [i64; 4]) -> Self {
            Self { select: choices }
        }

        /// Resets the `value` to zero.
        pub fn reset() -> Self {
            Self { reset: () }
        }
    }

    impl ::union_fn::UnionFn for CounterOpt {
        type Output = ();
        type Args = CounterArgs;
    }

    impl ::union_fn::UnionFn for Counter {
        type Output = ();
        type Args = CounterArgs;
    }

    ///Decodes and delegates packed arguments to the implementation of [`Counter`] methods.
    pub enum CounterDelegate {}

    impl CounterDelegate {
        /// Bumps the value `by` the amount.
        fn bump_by(
            value: &mut <Counter as ::union_fn::CallWithContext>::Context,
            args: &<Counter as ::union_fn::UnionFn>::Args,
        ) -> <Counter as ::union_fn::UnionFn>::Output {
            let by = unsafe { args.bump_by };
            <Counter as ::union_fn::IntoOpt>::Impls::bump_by(value, by)
        }

        /// Selects the values in `choices` depending on `value`.
        fn select(
            value: &mut <Counter as ::union_fn::CallWithContext>::Context,
            args: &<Counter as ::union_fn::UnionFn>::Args,
        ) -> <Counter as ::union_fn::UnionFn>::Output {
            let choices = unsafe { args.select };
            <Counter as ::union_fn::IntoOpt>::Impls::select(value, choices)
        }

        /// Resets the `value` to zero.
        fn reset(
            value: &mut <Counter as ::union_fn::CallWithContext>::Context,
            args: &<Counter as ::union_fn::UnionFn>::Args,
        ) -> <Counter as ::union_fn::UnionFn>::Output {
            let () = unsafe { args.reset };
            <Counter as ::union_fn::IntoOpt>::Impls::reset(value)
        }
    }

    ///Implements all methods of the [`Counter`] type.
    pub enum CounterImpls {}

    impl CounterImpls {
        /// Bumps the value `by` the amount.
        fn bump_by(
            value: &mut <Counter as ::union_fn::CallWithContext>::Context,
            by: i64,
        ) -> <Counter as ::union_fn::UnionFn>::Output {
            *value += by;
        }

        /// Selects the values in `choices` depending on `value`.
        fn select(
            value: &mut <Counter as ::union_fn::CallWithContext>::Context,
            choices: [i64; 4],
        ) -> <Counter as ::union_fn::UnionFn>::Output {
            *value = choices.get(*value as usize).copied().unwrap_or(0);
        }

        /// Resets the `value` to zero.
        fn reset(
            value: &mut <Counter as ::union_fn::CallWithContext>::Context,
        ) -> <Counter as ::union_fn::UnionFn>::Output {
            *value = 0;
        }
    }
};

生成的汇编代码

以下是针对上述Counter示例中生成的用户界面enum和调用优化的opt类型,根据编译器探索器生成的汇编代码

调用[WithContext] for <enum>

<example::Counter as example::union_fn::CallWithContext>::call:
        sub     rsp, 40
        mov     rax, qword ptr [rdi]
        test    rax, rax
        je      .LBB3_8
        cmp     eax, 1
        jne     .LBB3_6
        movups  xmm0, xmmword ptr [rdi + 8]
        movups  xmm1, xmmword ptr [rdi + 24]
        movaps  xmmword ptr [rsp + 16], xmm1
        movaps  xmmword ptr [rsp], xmm0
        mov     rax, qword ptr [rsi]
        cmp     rax, 3
        ja      .LBB3_3
        mov     rax, qword ptr [rsp + 8*rax]
        mov     qword ptr [rsi], rax
        add     rsp, 40
        ret
.LBB3_8:
        mov     rax, qword ptr [rdi + 8]
        add     qword ptr [rsi], rax
        add     rsp, 40
        ret
.LBB3_6:
        mov     qword ptr [rsi], 0
        add     rsp, 40
        ret
.LBB3_3:
        xor     eax, eax
        mov     qword ptr [rsi], rax
        add     rsp, 40
        ret

调用[WithContext] for <opt>

<example::_::CounterOpt as example::union_fn::CallWithContext>::call:
        mov     rax, rsi
        mov     rcx, qword ptr [rdi]
        lea     rsi, [rdi + 8]
        mov     rdi, rax
        jmp     rcx

依赖关系

~1.5MB
~36K SLoC