Skip to main content

ruda_kernel/dsl/frontend/operation/
branch.rs

1use ruda_kernel_macros::intrinsic;
2
3use crate::dsl::prelude::{RudaPrimitive, Vector};
4use crate::dsl::{
5    ir::{Operator, Scope, Select},
6    prelude::*,
7};
8
9/// Executes both branches, *then* selects a value based on the condition. This *should* be
10/// branchless, but might depend on the compiler.
11///
12/// # Safety
13///
14/// Since both branches are *evaluated* regardless of the condition, both branches must be *valid*
15/// regardless of the condition. Illegal memory accesses should not be done in either branch.
16pub fn select<C: RudaPrimitive>(condition: bool, then: C, or_else: C) -> C {
17    if condition { then } else { or_else }
18}
19
20/// Same as [`select()`] but with vectors instead.
21#[ruda]
22#[allow(unused_variables)]
23pub fn select_many<C: Scalar, N: Size>(
24    condition: Vector<bool, N>,
25    then: Vector<C, N>,
26    or_else: Vector<C, N>,
27) -> Vector<C, N> {
28    intrinsic!(|scope| select::expand(scope, condition.expand.into(), then, or_else))
29}
30
31pub mod select {
32    use ruda_core::ir::VariableKind;
33
34    use crate::dsl::ir::Instruction;
35
36    use super::*;
37
38    pub fn expand<C: RudaPrimitive>(
39        scope: &mut Scope,
40        condition: NativeExpand<bool>,
41        then: NativeExpand<C>,
42        or_else: NativeExpand<C>,
43    ) -> NativeExpand<C> {
44        let cond = condition.expand.consume();
45
46        if let VariableKind::Constant(value) = cond.kind {
47            if value.as_bool() {
48                return then;
49            } else {
50                return or_else;
51            }
52        }
53
54        let then = then.expand.consume();
55        let or_else = or_else.expand.consume();
56
57        let vf = cond.vector_size();
58        let vf = Ord::max(vf, then.vector_size());
59        let vf = Ord::max(vf, or_else.vector_size());
60
61        let output = scope.create_local(then.ty.with_vector_size(vf));
62        let out = *output;
63
64        let select = Operator::Select(Select {
65            cond,
66            then,
67            or_else,
68        });
69        scope.register(Instruction::new(select, out));
70
71        output.into()
72    }
73}