Skip to main content

cubecl_core/post_processing/
constant_prop.rs

1use alloc::{vec, vec::Vec};
2use cubecl_ir::{
3    Arithmetic, Bitwise, Comparison, ConstantValue, GlobalState, Instruction, Operation, Operator,
4    Type, Value,
5};
6
7use crate::post_processing::{
8    analysis_helper::GlobalAnalyses, util::AtomicCounter, visitor::InstructionVisitor,
9};
10
11/// Simplifies certain expressions where one operand is constant.
12/// For example: `out = x * 1` to `out = x`
13#[derive(Debug)]
14pub struct ConstOperandSimplify;
15
16impl InstructionVisitor for ConstOperandSimplify {
17    fn visit_instruction(
18        &mut self,
19        mut inst: Instruction,
20        _state: &GlobalState,
21        _analyses: &GlobalAnalyses,
22        changes: &AtomicCounter,
23    ) -> Vec<Instruction> {
24        match &mut inst.operation {
25            Operation::Arithmetic(operator) => match operator {
26                // 0 * x == 0
27                Arithmetic::Mul(bin_op) if bin_op.lhs.is_constant(0) => {
28                    inst.operation = Operation::Copy(bin_op.lhs);
29                    changes.inc();
30                }
31                // x * 0 == 0
32                Arithmetic::Mul(bin_op) if bin_op.rhs.is_constant(0) => {
33                    inst.operation = Operation::Copy(bin_op.rhs);
34                    changes.inc();
35                }
36                // 1 * x == x
37                Arithmetic::Mul(bin_op) if bin_op.lhs.is_constant(1) => {
38                    inst.operation = Operation::Copy(bin_op.rhs);
39                    changes.inc();
40                }
41                // x * 1 == x
42                Arithmetic::Mul(bin_op) if bin_op.rhs.is_constant(1) => {
43                    inst.operation = Operation::Copy(bin_op.lhs);
44                    changes.inc();
45                }
46                // 0 + x = x
47                Arithmetic::Add(bin_op) if bin_op.lhs.is_constant(0) => {
48                    inst.operation = Operation::Copy(bin_op.rhs);
49                    changes.inc();
50                }
51                // x + 0 = x
52                Arithmetic::Add(bin_op) if bin_op.rhs.is_constant(0) => {
53                    inst.operation = Operation::Copy(bin_op.lhs);
54                    changes.inc();
55                }
56                // x - 0 == x
57                Arithmetic::Sub(bin_op) if bin_op.rhs.is_constant(0) => {
58                    inst.operation = Operation::Copy(bin_op.lhs);
59                    changes.inc();
60                }
61                // x / 1 == x, 0 / x == 0
62                Arithmetic::Div(bin_op)
63                    if bin_op.lhs.is_constant(0) || bin_op.rhs.is_constant(1) =>
64                {
65                    inst.operation = Operation::Copy(bin_op.lhs);
66                    changes.inc();
67                }
68                // x % 1 == 0, 0 % x == 0
69                Arithmetic::ModFloor(bin_op) | Arithmetic::Rem(bin_op)
70                    if bin_op.rhs.is_constant(1) || bin_op.lhs.is_constant(0) =>
71                {
72                    inst.operation = Operation::Copy(inst.ty().constant(ConstantValue::Int(0)));
73                    changes.inc();
74                }
75                _ => {}
76            },
77
78            Operation::Operator(operator) => match operator {
79                // true || x == true, x || true == true
80                Operator::Or(bin_op) if bin_op.lhs.is_true() || bin_op.rhs.is_true() => {
81                    inst.operation = Operation::Copy(true.into());
82                    changes.inc();
83                }
84                // false || x == x, x || false == x
85                Operator::Or(bin_op) if bin_op.lhs.is_false() => {
86                    inst.operation = Operation::Copy(bin_op.rhs);
87                    changes.inc();
88                }
89                // x || false == x
90                Operator::Or(bin_op) if bin_op.rhs.is_false() => {
91                    inst.operation = Operation::Copy(bin_op.lhs);
92                    changes.inc();
93                }
94                // false && x == false, x && false == false
95                Operator::And(bin_op) if bin_op.lhs.is_false() || bin_op.rhs.is_false() => {
96                    inst.operation = Operation::Copy(false.into());
97                    changes.inc();
98                }
99                // true && x == x
100                Operator::And(bin_op) if bin_op.lhs.is_true() => {
101                    inst.operation = Operation::Copy(bin_op.rhs);
102                    changes.inc();
103                }
104                // x && true == x
105                Operator::And(bin_op) if bin_op.rhs.is_true() => {
106                    inst.operation = Operation::Copy(bin_op.lhs);
107                    changes.inc();
108                }
109                // select(true, a, b) == a
110                Operator::Select(select) if select.cond.is_true() => {
111                    inst.operation = Operation::Copy(select.then);
112                    changes.inc();
113                }
114                // select(false, a, b) == b
115                Operator::Select(select) if select.cond.is_false() => {
116                    inst.operation = Operation::Copy(select.or_else);
117                    changes.inc();
118                }
119                _ => {}
120            },
121            _ => {}
122        };
123        vec![inst]
124    }
125}
126
127/// Evaluates expressions where both operands are constant and replaces them with simple constant
128/// assignments. This can often be applied as a result of assignment merging or constant operand
129/// simplification.
130#[derive(Debug)]
131pub struct ConstEval;
132
133impl InstructionVisitor for ConstEval {
134    fn visit_instruction(
135        &mut self,
136        mut inst: Instruction,
137        _state: &GlobalState,
138        _analyses: &GlobalAnalyses,
139        changes: &AtomicCounter,
140    ) -> Vec<Instruction> {
141        if let Some(const_eval) = try_const_eval(&mut inst) {
142            let input = Value::constant(const_eval, inst.out().ty);
143            inst.operation = Operation::Copy(input);
144            changes.inc();
145        }
146        vec![inst]
147    }
148}
149
150macro_rules! const_eval {
151    ($op:tt $lhs:expr, $rhs:expr) => {{
152        use ConstantValue::*;
153
154        let ty = $lhs.ty;
155        let lhs = $lhs.as_const();
156        let rhs = $rhs.as_const();
157        if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
158            let rhs = rhs.cast_to(ty);
159            Some(match (lhs, rhs) {
160                (Int(lhs), Int(rhs)) => ConstantValue::Int(lhs $op rhs),
161                (Float(lhs), Float(rhs)) => ConstantValue::Float(lhs $op rhs),
162                (UInt(lhs), UInt(rhs)) => ConstantValue::UInt(lhs $op rhs),
163                _ => unreachable!(),
164            })
165        } else {
166            None
167        }
168    }};
169}
170
171macro_rules! const_eval_int {
172    ($op:tt $lhs:expr, $rhs:expr) => {{
173        use ConstantValue::*;
174
175        let ty = $lhs.ty;
176        let lhs = $lhs.as_const();
177        let rhs = $rhs.as_const();
178        if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
179            let rhs = rhs.cast_to(ty);
180            Some(match (lhs, rhs) {
181                (Int(lhs), Int(rhs)) => Int(lhs $op rhs),
182                (UInt(lhs), UInt(rhs)) => UInt(lhs $op rhs),
183                _ => unreachable!(),
184            })
185        } else {
186            None
187        }
188    }};
189    ($lhs:expr, $rhs:expr; $op:path) => {{
190        use ConstantValue::*;
191
192        let ty = $lhs.ty;
193        let lhs = $lhs.as_const();
194        let rhs = $rhs.as_const();
195        if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
196            let rhs = rhs.cast_to(ty);
197            Some(match (lhs, rhs) {
198                (Int(lhs), Int(rhs)) => Int($op(lhs, rhs)),
199                (UInt(lhs), UInt(rhs)) => UInt($op(lhs, rhs)),
200                _ => unreachable!(),
201            })
202        } else {
203            None
204        }
205    }};
206}
207
208macro_rules! const_eval_float {
209    ($lhs:expr, $rhs:expr; $fn:path) => {{
210        use ConstantValue::*;
211
212        let ty = $lhs.ty;
213        let lhs = $lhs.as_const();
214        let rhs = $rhs.as_const();
215        if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
216            let rhs = rhs.cast_to(ty);
217            Some(match (lhs, rhs) {
218                (Float(lhs), Float(rhs)) => ConstantValue::Float($fn(lhs, rhs)),
219                _ => unreachable!(),
220            })
221        } else {
222            None
223        }
224    }};
225    ($input:expr; $fn:path) => {{
226        use ConstantValue::*;
227
228        if let Some(input) = $input.as_const() {
229            Some(match input {
230                Float(input) => ConstantValue::Float($fn(input)),
231                _ => unreachable!(),
232            })
233        } else {
234            None
235        }
236    }};
237}
238
239macro_rules! const_eval_cmp {
240    ($op:tt $lhs:expr, $rhs:expr) => {{
241        use ConstantValue::*;
242
243        let lhs = $lhs.as_const();
244        let rhs = $rhs.as_const();
245        if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
246            Some(match (lhs, rhs) {
247                (Int(lhs), Int(rhs)) => ConstantValue::Bool(lhs $op rhs),
248                (Float(lhs), Float(rhs)) => ConstantValue::Bool(lhs $op rhs),
249                (UInt(lhs), UInt(rhs)) => ConstantValue::Bool(lhs $op rhs),
250                _ => unreachable!(),
251            })
252        } else {
253            None
254        }
255    }};
256}
257
258macro_rules! const_eval_bool {
259    ($op:tt $lhs:expr, $rhs:expr) => {{
260        use ConstantValue::*;
261
262        let lhs = $lhs.as_const();
263        let rhs = $rhs.as_const();
264        if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
265            Some(match (lhs, rhs) {
266                (Bool(lhs), Bool(rhs)) => ConstantValue::Bool(lhs $op rhs),
267                _ => unreachable!(),
268            })
269        } else {
270            None
271        }
272    }};
273}
274
275fn try_const_eval(inst: &mut Instruction) -> Option<ConstantValue> {
276    match &mut inst.operation {
277        Operation::Arithmetic(op) => try_const_eval_arithmetic(op),
278        Operation::Comparison(op) => try_const_eval_cmp(op),
279        Operation::Bitwise(op) => try_const_eval_bitwise(op),
280        Operation::Operator(op) => try_const_eval_operator(op, inst.out.map(|it| it.ty)),
281        _ => None,
282    }
283}
284
285fn try_const_eval_arithmetic(op: &mut Arithmetic) -> Option<ConstantValue> {
286    match op {
287        Arithmetic::Add(op) => const_eval!(+ op.lhs, op.rhs),
288        Arithmetic::Sub(op) => const_eval!(-op.lhs, op.rhs),
289        Arithmetic::Mul(op) => const_eval!(*op.lhs, op.rhs),
290        Arithmetic::Div(op) => const_eval!(/ op.lhs, op.rhs),
291        Arithmetic::SaturatingAdd(op) => {
292            use ConstantValue::*;
293
294            let ty = op.lhs.ty;
295            let lhs = op.lhs.as_const();
296            let rhs = op.rhs.as_const();
297            if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
298                let rhs = rhs.cast_to(ty);
299                let width = ty.size();
300                Some(match (lhs, rhs, width) {
301                    (Int(lhs), Int(rhs), 4) => Int((lhs as i32).saturating_add(rhs as i32) as i64),
302                    (Int(lhs), Int(rhs), 8) => Int(lhs.saturating_add(rhs)),
303                    (UInt(lhs), UInt(rhs), 4) => {
304                        UInt((lhs as u32).saturating_add(rhs as u32) as u64)
305                    }
306                    (UInt(lhs), UInt(rhs), 8) => UInt(lhs.saturating_add(rhs)),
307                    _ => unreachable!(),
308                })
309            } else {
310                None
311            }
312        }
313        Arithmetic::SaturatingSub(op) => {
314            use ConstantValue::*;
315
316            let ty = op.lhs.ty;
317            let lhs = op.lhs.as_const();
318            let rhs = op.rhs.as_const();
319            if let (Some(lhs), Some(rhs)) = (lhs, rhs) {
320                let rhs = rhs.cast_to(ty);
321                let width = ty.size();
322                Some(match (lhs, rhs, width) {
323                    (Int(lhs), Int(rhs), 4) => Int((lhs as i32).saturating_sub(rhs as i32) as i64),
324                    (Int(lhs), Int(rhs), 8) => Int(lhs.saturating_sub(rhs)),
325                    (UInt(lhs), UInt(rhs), 4) => {
326                        UInt((lhs as u32).saturating_sub(rhs as u32) as u64)
327                    }
328                    (UInt(lhs), UInt(rhs), 8) => UInt(lhs.saturating_sub(rhs)),
329                    _ => unreachable!(),
330                })
331            } else {
332                None
333            }
334        }
335        Arithmetic::Powf(op) => const_eval_float!(op.lhs, op.rhs; num_traits::Float::powf),
336        // powf is fast enough for const eval
337        Arithmetic::Powi(op) => {
338            const_eval_float!(op.lhs, op.rhs; num_traits::Float::powf)
339        }
340        Arithmetic::ModFloor(op) => const_eval_int!(op.lhs, op.rhs; num_integer::mod_floor),
341        Arithmetic::Rem(op) => const_eval!(% op.lhs, op.rhs),
342        Arithmetic::MulHi(op) => {
343            use ConstantValue::*;
344            let ty = op.lhs.ty;
345            if let (Some(lhs), Some(rhs)) = (op.lhs.as_const(), op.rhs.as_const()) {
346                let rhs = rhs.cast_to(ty);
347                Some(match (lhs, rhs) {
348                    (Int(lhs), Int(rhs)) => {
349                        let mul = (lhs * rhs) >> 32;
350                        ConstantValue::Int(mul as i32 as i64)
351                    }
352                    (UInt(lhs), UInt(rhs)) => {
353                        let mul = (lhs * rhs) >> 32;
354                        ConstantValue::UInt(mul as u32 as u64)
355                    }
356                    _ => unreachable!(),
357                })
358            } else {
359                None
360            }
361        }
362        Arithmetic::Max(op) => {
363            use ConstantValue::*;
364            let ty = op.lhs.ty;
365            if let (Some(lhs), Some(rhs)) = (op.lhs.as_const(), op.rhs.as_const()) {
366                let rhs = rhs.cast_to(ty);
367                Some(match (lhs, rhs) {
368                    (Int(lhs), Int(rhs)) => ConstantValue::Int(lhs.max(rhs)),
369                    (Float(lhs), Float(rhs)) => ConstantValue::Float(lhs.max(rhs)),
370                    (UInt(lhs), UInt(rhs)) => ConstantValue::UInt(lhs.max(rhs)),
371                    _ => unreachable!(),
372                })
373            } else {
374                None
375            }
376        }
377        Arithmetic::Min(op) => {
378            use ConstantValue::*;
379            let ty = op.lhs.ty;
380            if let (Some(lhs), Some(rhs)) = (op.lhs.as_const(), op.rhs.as_const()) {
381                let rhs = rhs.cast_to(ty);
382                Some(match (lhs, rhs) {
383                    (Int(lhs), Int(rhs)) => ConstantValue::Int(lhs.min(rhs)),
384                    (Float(lhs), Float(rhs)) => ConstantValue::Float(lhs.min(rhs)),
385                    (UInt(lhs), UInt(rhs)) => ConstantValue::UInt(lhs.min(rhs)),
386                    _ => unreachable!(),
387                })
388            } else {
389                None
390            }
391        }
392        Arithmetic::Dot(op) => const_eval!(*op.lhs, op.rhs),
393
394        Arithmetic::Abs(op) => {
395            use ConstantValue::*;
396            op.input.as_const().map(|input| match input {
397                Int(input) => ConstantValue::Int(input.abs()),
398                Float(input) => ConstantValue::Float(input.abs()),
399                _ => unreachable!(),
400            })
401        }
402        Arithmetic::Exp(op) => const_eval_float!(op.input; num_traits::Float::exp),
403        Arithmetic::Log(op) => const_eval_float!(op.input; num_traits::Float::ln),
404        Arithmetic::Log1p(op) => const_eval_float!(op.input; num_traits::Float::ln_1p),
405        Arithmetic::Expm1(op) => const_eval_float!(op.input; num_traits::Float::exp_m1),
406        Arithmetic::Cos(op) => const_eval_float!(op.input; num_traits::Float::cos),
407        Arithmetic::Sin(op) => const_eval_float!(op.input; num_traits::Float::sin),
408        Arithmetic::Tan(op) => const_eval_float!(op.input; num_traits::Float::tan),
409        Arithmetic::Tanh(op) => const_eval_float!(op.input; num_traits::Float::tanh),
410        Arithmetic::Sinh(op) => const_eval_float!(op.input; num_traits::Float::sinh),
411        Arithmetic::Cosh(op) => const_eval_float!(op.input; num_traits::Float::cosh),
412        Arithmetic::ArcCos(op) => const_eval_float!(op.input; num_traits::Float::acos),
413        Arithmetic::ArcSin(op) => const_eval_float!(op.input; num_traits::Float::asin),
414        Arithmetic::ArcTan(op) => const_eval_float!(op.input; num_traits::Float::atan),
415        Arithmetic::ArcSinh(op) => const_eval_float!(op.input; num_traits::Float::asinh),
416        Arithmetic::ArcCosh(op) => const_eval_float!(op.input; num_traits::Float::acosh),
417        Arithmetic::ArcTanh(op) => const_eval_float!(op.input; num_traits::Float::atanh),
418        Arithmetic::Degrees(op) => const_eval_float!(op.input; num_traits::Float::to_degrees),
419        Arithmetic::Radians(op) => const_eval_float!(op.input; num_traits::Float::to_radians),
420        Arithmetic::ArcTan2(op) => const_eval_float!(op.lhs, op.rhs; num_traits::Float::atan2),
421        Arithmetic::Sqrt(op) => const_eval_float!(op.input; num_traits::Float::sqrt),
422        Arithmetic::Hypot(op) => const_eval_float!(op.lhs, op.rhs; num_traits::Float::hypot),
423        Arithmetic::Rhypot(op) => {
424            let hypot = const_eval_float!(op.lhs, op.rhs; num_traits::Float::hypot)?;
425            let ConstantValue::Float(val) = hypot else {
426                unreachable!()
427            };
428            Some(ConstantValue::Float(1.0 / val))
429        }
430        Arithmetic::InverseSqrt(op) => {
431            let sqrt = const_eval_float!(op.input; num_traits::Float::sqrt)?;
432            let ConstantValue::Float(val) = sqrt else {
433                unreachable!()
434            };
435            Some(ConstantValue::Float(1.0 / val))
436        }
437        Arithmetic::Round(op) => const_eval_float!(op.input; num_traits::Float::round),
438        Arithmetic::Floor(op) => const_eval_float!(op.input; num_traits::Float::floor),
439        Arithmetic::Ceil(op) => const_eval_float!(op.input; num_traits::Float::ceil),
440        Arithmetic::Trunc(op) => const_eval_float!(op.input; num_traits::Float::trunc),
441        Arithmetic::Recip(op) => const_eval_float!(op.input; num_traits::Float::recip),
442        Arithmetic::Neg(op) => {
443            use ConstantValue::*;
444            op.input.as_const().map(|input| match input {
445                Int(input) => ConstantValue::Int(-input),
446                Float(input) => ConstantValue::Float(-input),
447                _ => unreachable!(),
448            })
449        }
450
451        Arithmetic::Fma(op) => {
452            use ConstantValue::*;
453            let ty = op.a.ty;
454            let a = op.a.as_const();
455            let b = op.b.as_const();
456            let c = op.c.as_const();
457
458            a.zip(b).zip(c).map(|((a, b), c)| {
459                let b = b.cast_to(ty);
460                let c = c.cast_to(ty);
461                match (a, b, c) {
462                    (Float(a), Float(b), Float(c)) => ConstantValue::Float(a * b + c),
463                    _ => unreachable!(),
464                }
465            })
466        }
467        Arithmetic::Clamp(op) => {
468            use ConstantValue::*;
469            let ty = op.input.ty;
470            let a = op.input.as_const();
471            let b = op.min_value.as_const();
472            let c = op.max_value.as_const();
473
474            a.zip(b).zip(c).map(|((a, b), c)| {
475                let b = b.cast_to(ty);
476                let c = c.cast_to(ty);
477                match (a, b, c) {
478                    (Int(a), Int(b), Int(c)) => ConstantValue::Int(a.clamp(b, c)),
479                    (Float(a), Float(b), Float(c)) => ConstantValue::Float(a.clamp(b, c)),
480                    (UInt(a), UInt(b), UInt(c)) => ConstantValue::UInt(a.clamp(b, c)),
481                    _ => unreachable!(),
482                }
483            })
484        }
485        Arithmetic::Erf(_)
486        | Arithmetic::Magnitude(_)
487        | Arithmetic::Normalize(_)
488        | Arithmetic::VectorSum(_) => None,
489    }
490}
491
492fn try_const_eval_cmp(op: &mut Comparison) -> Option<ConstantValue> {
493    match op {
494        Comparison::Equal(op) => const_eval_cmp!(== op.lhs, op.rhs),
495        Comparison::NotEqual(op) => const_eval_cmp!(!= op.lhs, op.rhs),
496        Comparison::Lower(op) => const_eval_cmp!(< op.lhs, op.rhs),
497        Comparison::Greater(op) => const_eval_cmp!(> op.lhs, op.rhs),
498        Comparison::LowerEqual(op) => const_eval_cmp!(<= op.lhs, op.rhs),
499        Comparison::GreaterEqual(op) => const_eval_cmp!(>= op.lhs, op.rhs),
500        Comparison::IsNan(op) => {
501            use ConstantValue::*;
502            op.input.as_const().map(|input| match input {
503                Float(val) => Bool(val.is_nan()),
504                // Integers, bools, uints can't be NaN, so always false
505                Int(_) | UInt(_) | Bool(_) => Bool(false),
506            })
507        }
508        Comparison::IsInf(op) => {
509            use ConstantValue::*;
510            op.input.as_const().map(|input| match input {
511                Float(val) => Bool(val.is_infinite()),
512                // Integers, bools, uints can't be infinite, so always false
513                Int(_) | UInt(_) | Bool(_) => Bool(false),
514            })
515        }
516    }
517}
518
519fn try_const_eval_bitwise(op: &mut Bitwise) -> Option<ConstantValue> {
520    match op {
521        Bitwise::BitwiseAnd(op) => const_eval_int!(&op.lhs, op.rhs),
522        Bitwise::BitwiseOr(op) => const_eval_int!(| op.lhs, op.rhs),
523        Bitwise::BitwiseXor(op) => const_eval_int!(^ op.lhs, op.rhs),
524        Bitwise::ShiftLeft(op) => const_eval_int!(<< op.lhs, op.rhs),
525        Bitwise::ShiftRight(op) => const_eval_int!(>> op.lhs, op.rhs),
526        Bitwise::BitwiseNot(op) => {
527            use ConstantValue::*;
528            op.input.as_const().map(|input| match input {
529                Int(input) => ConstantValue::Int(!input),
530                UInt(input) => ConstantValue::UInt(!input),
531                _ => unreachable!(),
532            })
533        }
534        Bitwise::CountOnes(op) => {
535            use ConstantValue::*;
536            op.input.as_const().map(|input| match input {
537                Int(input) => ConstantValue::Int(input.count_ones() as i64),
538                UInt(input) => ConstantValue::UInt(input.count_ones() as u64),
539                _ => unreachable!(),
540            })
541        }
542        Bitwise::ReverseBits(op) => {
543            use ConstantValue::*;
544            op.input.as_const().map(|input| match input {
545                Int(input) => ConstantValue::Int(input.reverse_bits()),
546                UInt(input) => ConstantValue::UInt(input.reverse_bits()),
547                _ => unreachable!(),
548            })
549        }
550        Bitwise::LeadingZeros(_) | Bitwise::TrailingZeros(_) | Bitwise::FindFirstSet(_) => {
551            // Depends too much on type width and Rust semantics, leave this one out of const eval
552            None
553        }
554    }
555}
556
557fn try_const_eval_operator(op: &mut Operator, out_ty: Option<Type>) -> Option<ConstantValue> {
558    match op {
559        Operator::And(op) => const_eval_bool!(&&op.lhs, op.rhs),
560        Operator::Or(op) => const_eval_bool!(|| op.lhs, op.rhs),
561        Operator::Not(op) => {
562            use ConstantValue::*;
563            op.input.as_const().map(|input| match input {
564                Bool(input) => ConstantValue::Bool(!input),
565                _ => unreachable!(),
566            })
567        }
568        Operator::Cast(op) => op.input.as_const().map(|val| val.cast_to(out_ty.unwrap())),
569        Operator::InitVector(_)
570        | Operator::InsertComponent(_)
571        | Operator::ExtractComponent(_)
572        | Operator::Reinterpret(_)
573        | Operator::Select(_)
574        | Operator::ReadBuiltin(_)
575        | Operator::ReadScalar(_) => None,
576    }
577}