Skip to main content

cubecl_core/frontend/operation/
branch.rs

1use cubecl_macros::intrinsic;
2
3use crate as cubecl;
4use crate::prelude::{CubePrimitive, Vector};
5use crate::{ir::Scope, prelude::*};
6
7/// Executes both branches, *then* selects a value based on the condition. This *should* be
8/// branchless, but might depend on the compiler.
9///
10/// # Safety
11///
12/// Since both branches are *evaluated* regardless of the condition, both branches must be *valid*
13/// regardless of the condition. Illegal memory accesses should not be done in either branch.
14pub fn select<C: CubePrimitive>(condition: bool, then: C, or_else: C) -> C {
15    if condition { then } else { or_else }
16}
17
18/// Same as [`select()`] but with vectors instead.
19#[cube]
20pub fn select_many<C: Scalar, N: Size>(
21    condition: Vector<bool, N>,
22    then: Vector<C, N>,
23    or_else: Vector<C, N>,
24) -> Vector<C, N> {
25    intrinsic!(|scope| select::expand(scope, condition.expand.into(), then, or_else))
26}
27
28pub mod select {
29    use cubecl_ir::{ExpandValue, dialect::general::SelectOp};
30
31    use super::*;
32
33    pub fn expand<C: CubePrimitive>(
34        scope: &Scope,
35        condition: NativeExpand<bool>,
36        then: NativeExpand<C>,
37        or_else: NativeExpand<C>,
38    ) -> NativeExpand<C> {
39        if let ExpandValue::Constant { value, .. } = condition.expand {
40            if value.as_bool() {
41                return then;
42            } else {
43                return or_else;
44            }
45        }
46
47        let condition = condition.read_value(scope);
48        let then = then.read_value(scope);
49        let or_else = or_else.read_value(scope);
50
51        let [condition, then, or_else] =
52            normalize_same_vectorization(scope, [condition, then, or_else]);
53        let select = SelectOp::new(scope.ctx_mut(), condition, then, or_else);
54        scope.register_with_result(&select).into()
55    }
56}