Skip to main content

llvm_ir/
instruction.rs

1//! SSA instructions: arithmetic, memory, control flow, and call instructions.
2
3use crate::context::{BlockId, TypeId, ValueRef};
4
5// ---------------------------------------------------------------------------
6// Flags
7// ---------------------------------------------------------------------------
8
9/// Flags for integer arithmetic instructions.
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub struct IntArithFlags {
12    /// No unsigned wrap.
13    pub nuw: bool,
14    /// No signed wrap.
15    pub nsw: bool,
16}
17
18/// `exact` flag for division/shift instructions.
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
20pub struct ExactFlag {
21    /// Public API for `exact`.
22    pub exact: bool,
23}
24
25/// Fast-math flags for floating-point instructions.
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
27pub struct FastMathFlags {
28    /// Public API for `nnan`.
29    pub nnan: bool,
30    /// Public API for `ninf`.
31    pub ninf: bool,
32    /// Public API for `nsz`.
33    pub nsz: bool,
34    /// Public API for `arcp`.
35    pub arcp: bool,
36    /// Public API for `contract`.
37    pub contract: bool,
38    /// Public API for `afn`.
39    pub afn: bool,
40    /// Public API for `reassoc`.
41    pub reassoc: bool,
42    /// Shorthand for all flags set.
43    pub fast: bool,
44}
45
46// ---------------------------------------------------------------------------
47// Predicates
48// ---------------------------------------------------------------------------
49
50/// Public API for `IntPredicate`.
51#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub enum IntPredicate {
53    /// `Eq` variant.
54    Eq,
55    /// `Ne` variant.
56    Ne,
57    /// `Ugt` variant.
58    Ugt,
59    /// `Uge` variant.
60    Uge,
61    /// `Ult` variant.
62    Ult,
63    /// `Ule` variant.
64    Ule,
65    /// `Sgt` variant.
66    Sgt,
67    /// `Sge` variant.
68    Sge,
69    /// `Slt` variant.
70    Slt,
71    /// `Sle` variant.
72    Sle,
73}
74
75impl IntPredicate {
76    /// Public API for `as_str`.
77    pub fn as_str(self) -> &'static str {
78        match self {
79            IntPredicate::Eq => "eq",
80            IntPredicate::Ne => "ne",
81            IntPredicate::Ugt => "ugt",
82            IntPredicate::Uge => "uge",
83            IntPredicate::Ult => "ult",
84            IntPredicate::Ule => "ule",
85            IntPredicate::Sgt => "sgt",
86            IntPredicate::Sge => "sge",
87            IntPredicate::Slt => "slt",
88            IntPredicate::Sle => "sle",
89        }
90    }
91}
92
93/// Public API for `FloatPredicate`.
94#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95pub enum FloatPredicate {
96    /// `False` variant.
97    False,
98    /// `Oeq` variant.
99    Oeq,
100    /// `Ogt` variant.
101    Ogt,
102    /// `Oge` variant.
103    Oge,
104    /// `Olt` variant.
105    Olt,
106    /// `Ole` variant.
107    Ole,
108    /// `One` variant.
109    One,
110    /// `Ord` variant.
111    Ord,
112    /// `Uno` variant.
113    Uno,
114    /// `Ueq` variant.
115    Ueq,
116    /// `Ugt` variant.
117    Ugt,
118    /// `Uge` variant.
119    Uge,
120    /// `Ult` variant.
121    Ult,
122    /// `Ule` variant.
123    Ule,
124    /// `Une` variant.
125    Une,
126    /// `True` variant.
127    True,
128}
129
130impl FloatPredicate {
131    /// Public API for `as_str`.
132    pub fn as_str(self) -> &'static str {
133        match self {
134            FloatPredicate::False => "false",
135            FloatPredicate::Oeq => "oeq",
136            FloatPredicate::Ogt => "ogt",
137            FloatPredicate::Oge => "oge",
138            FloatPredicate::Olt => "olt",
139            FloatPredicate::Ole => "ole",
140            FloatPredicate::One => "one",
141            FloatPredicate::Ord => "ord",
142            FloatPredicate::Uno => "uno",
143            FloatPredicate::Ueq => "ueq",
144            FloatPredicate::Ugt => "ugt",
145            FloatPredicate::Uge => "uge",
146            FloatPredicate::Ult => "ult",
147            FloatPredicate::Ule => "ule",
148            FloatPredicate::Une => "une",
149            FloatPredicate::True => "true",
150        }
151    }
152}
153
154/// Tail call optimization hint.
155#[derive(Clone, Copy, Debug, PartialEq, Eq)]
156pub enum TailCallKind {
157    /// `None` variant.
158    None,
159    /// `Tail` variant.
160    Tail,
161    /// `MustTail` variant.
162    MustTail,
163    /// `NoTail` variant.
164    NoTail,
165}
166
167// ---------------------------------------------------------------------------
168// Instruction kind
169// ---------------------------------------------------------------------------
170
171/// Public API for `InstrKind`.
172#[derive(Clone, Debug, PartialEq)]
173pub enum InstrKind {
174    // --- Integer arithmetic ---
175    /// `Add` variant.
176    Add {
177        flags: IntArithFlags,
178        lhs: ValueRef,
179        rhs: ValueRef,
180    },
181    /// `Sub` variant.
182    Sub {
183        flags: IntArithFlags,
184        lhs: ValueRef,
185        rhs: ValueRef,
186    },
187    /// `Mul` variant.
188    Mul {
189        flags: IntArithFlags,
190        lhs: ValueRef,
191        rhs: ValueRef,
192    },
193    /// `UDiv` variant.
194    UDiv {
195        exact: bool,
196        lhs: ValueRef,
197        rhs: ValueRef,
198    },
199    /// `SDiv` variant.
200    SDiv {
201        exact: bool,
202        lhs: ValueRef,
203        rhs: ValueRef,
204    },
205    /// `URem` variant.
206    URem {
207        lhs: ValueRef,
208        rhs: ValueRef,
209    },
210    /// `SRem` variant.
211    SRem {
212        lhs: ValueRef,
213        rhs: ValueRef,
214    },
215
216    // --- Bitwise ---
217    /// `And` variant.
218    And {
219        lhs: ValueRef,
220        rhs: ValueRef,
221    },
222    /// `Or` variant.
223    Or {
224        lhs: ValueRef,
225        rhs: ValueRef,
226    },
227    /// `Xor` variant.
228    Xor {
229        lhs: ValueRef,
230        rhs: ValueRef,
231    },
232    /// `Shl` variant.
233    Shl {
234        flags: IntArithFlags,
235        lhs: ValueRef,
236        rhs: ValueRef,
237    },
238    /// `LShr` variant.
239    LShr {
240        exact: bool,
241        lhs: ValueRef,
242        rhs: ValueRef,
243    },
244    /// `AShr` variant.
245    AShr {
246        exact: bool,
247        lhs: ValueRef,
248        rhs: ValueRef,
249    },
250
251    // --- Floating-point arithmetic ---
252    /// `FAdd` variant.
253    FAdd {
254        flags: FastMathFlags,
255        lhs: ValueRef,
256        rhs: ValueRef,
257    },
258    /// `FSub` variant.
259    FSub {
260        flags: FastMathFlags,
261        lhs: ValueRef,
262        rhs: ValueRef,
263    },
264    /// `FMul` variant.
265    FMul {
266        flags: FastMathFlags,
267        lhs: ValueRef,
268        rhs: ValueRef,
269    },
270    /// `FDiv` variant.
271    FDiv {
272        flags: FastMathFlags,
273        lhs: ValueRef,
274        rhs: ValueRef,
275    },
276    /// `FRem` variant.
277    FRem {
278        flags: FastMathFlags,
279        lhs: ValueRef,
280        rhs: ValueRef,
281    },
282    /// `FNeg` variant.
283    FNeg {
284        flags: FastMathFlags,
285        operand: ValueRef,
286    },
287
288    // --- Comparisons ---
289    /// `ICmp` variant.
290    ICmp {
291        pred: IntPredicate,
292        lhs: ValueRef,
293        rhs: ValueRef,
294    },
295    /// `FCmp` variant.
296    FCmp {
297        flags: FastMathFlags,
298        pred: FloatPredicate,
299        lhs: ValueRef,
300        rhs: ValueRef,
301    },
302
303    // --- Memory ---
304    /// `Alloca` variant.
305    Alloca {
306        alloc_ty: TypeId,
307        num_elements: Option<ValueRef>,
308        align: Option<u32>,
309    },
310    /// `Load` variant.
311    Load {
312        ty: TypeId,
313        ptr: ValueRef,
314        align: Option<u32>,
315        volatile: bool,
316    },
317    /// `Store` variant.
318    Store {
319        val: ValueRef,
320        ptr: ValueRef,
321        align: Option<u32>,
322        volatile: bool,
323    },
324    /// `GetElementPtr` variant.
325    GetElementPtr {
326        inbounds: bool,
327        base_ty: TypeId,
328        ptr: ValueRef,
329        indices: Vec<ValueRef>,
330    },
331
332    // --- Casts ---
333    /// `Trunc` variant.
334    Trunc {
335        val: ValueRef,
336        to: TypeId,
337    },
338    /// `ZExt` variant.
339    ZExt {
340        val: ValueRef,
341        to: TypeId,
342    },
343    /// `SExt` variant.
344    SExt {
345        val: ValueRef,
346        to: TypeId,
347    },
348    /// `FPTrunc` variant.
349    FPTrunc {
350        val: ValueRef,
351        to: TypeId,
352    },
353    /// `FPExt` variant.
354    FPExt {
355        val: ValueRef,
356        to: TypeId,
357    },
358    /// `FPToUI` variant.
359    FPToUI {
360        val: ValueRef,
361        to: TypeId,
362    },
363    /// `FPToSI` variant.
364    FPToSI {
365        val: ValueRef,
366        to: TypeId,
367    },
368    /// `UIToFP` variant.
369    UIToFP {
370        val: ValueRef,
371        to: TypeId,
372    },
373    /// `SIToFP` variant.
374    SIToFP {
375        val: ValueRef,
376        to: TypeId,
377    },
378    /// `PtrToInt` variant.
379    PtrToInt {
380        val: ValueRef,
381        to: TypeId,
382    },
383    /// `IntToPtr` variant.
384    IntToPtr {
385        val: ValueRef,
386        to: TypeId,
387    },
388    /// `BitCast` variant.
389    BitCast {
390        val: ValueRef,
391        to: TypeId,
392    },
393    /// `AddrSpaceCast` variant.
394    AddrSpaceCast {
395        val: ValueRef,
396        to: TypeId,
397    },
398    /// `Freeze` variant.
399    Freeze {
400        val: ValueRef,
401    },
402
403    // --- Misc ---
404    /// `Select` variant.
405    Select {
406        cond: ValueRef,
407        then_val: ValueRef,
408        else_val: ValueRef,
409    },
410    /// `Phi` variant.
411    Phi {
412        ty: TypeId,
413        incoming: Vec<(ValueRef, BlockId)>,
414    },
415    /// `ExtractValue` variant.
416    ExtractValue {
417        aggregate: ValueRef,
418        indices: Vec<u32>,
419    },
420    /// `InsertValue` variant.
421    InsertValue {
422        aggregate: ValueRef,
423        val: ValueRef,
424        indices: Vec<u32>,
425    },
426    /// `ExtractElement` variant.
427    ExtractElement {
428        vec: ValueRef,
429        idx: ValueRef,
430    },
431    /// `InsertElement` variant.
432    InsertElement {
433        vec: ValueRef,
434        val: ValueRef,
435        idx: ValueRef,
436    },
437    /// `ShuffleVector` variant.
438    ShuffleVector {
439        v1: ValueRef,
440        v2: ValueRef,
441        mask: Vec<i32>,
442    },
443
444    // --- Call ---
445    /// `Call` variant.
446    Call {
447        tail: TailCallKind,
448        callee_ty: TypeId,
449        callee: ValueRef,
450        args: Vec<ValueRef>,
451    },
452
453    // --- Terminators ---
454    /// `Ret` variant.
455    Ret {
456        val: Option<ValueRef>,
457    },
458    /// `Br` variant.
459    Br {
460        dest: BlockId,
461    },
462    /// `CondBr` variant.
463    CondBr {
464        cond: ValueRef,
465        then_dest: BlockId,
466        else_dest: BlockId,
467    },
468    /// `Switch` variant.
469    Switch {
470        val: ValueRef,
471        default: BlockId,
472        cases: Vec<(ValueRef, BlockId)>,
473    },
474    /// `Unreachable` variant.
475    Unreachable,
476}
477
478impl InstrKind {
479    /// Public API for `is_terminator`.
480    pub fn is_terminator(&self) -> bool {
481        matches!(
482            self,
483            InstrKind::Ret { .. }
484                | InstrKind::Br { .. }
485                | InstrKind::CondBr { .. }
486                | InstrKind::Switch { .. }
487                | InstrKind::Unreachable
488        )
489    }
490
491    /// Return the opcode name for printing.
492    pub fn opcode(&self) -> &'static str {
493        match self {
494            InstrKind::Add { .. } => "add",
495            InstrKind::Sub { .. } => "sub",
496            InstrKind::Mul { .. } => "mul",
497            InstrKind::UDiv { .. } => "udiv",
498            InstrKind::SDiv { .. } => "sdiv",
499            InstrKind::URem { .. } => "urem",
500            InstrKind::SRem { .. } => "srem",
501            InstrKind::And { .. } => "and",
502            InstrKind::Or { .. } => "or",
503            InstrKind::Xor { .. } => "xor",
504            InstrKind::Shl { .. } => "shl",
505            InstrKind::LShr { .. } => "lshr",
506            InstrKind::AShr { .. } => "ashr",
507            InstrKind::FAdd { .. } => "fadd",
508            InstrKind::FSub { .. } => "fsub",
509            InstrKind::FMul { .. } => "fmul",
510            InstrKind::FDiv { .. } => "fdiv",
511            InstrKind::FRem { .. } => "frem",
512            InstrKind::FNeg { .. } => "fneg",
513            InstrKind::ICmp { .. } => "icmp",
514            InstrKind::FCmp { .. } => "fcmp",
515            InstrKind::Alloca { .. } => "alloca",
516            InstrKind::Load { .. } => "load",
517            InstrKind::Store { .. } => "store",
518            InstrKind::GetElementPtr { .. } => "getelementptr",
519            InstrKind::Trunc { .. } => "trunc",
520            InstrKind::ZExt { .. } => "zext",
521            InstrKind::SExt { .. } => "sext",
522            InstrKind::FPTrunc { .. } => "fptrunc",
523            InstrKind::FPExt { .. } => "fpext",
524            InstrKind::FPToUI { .. } => "fptoui",
525            InstrKind::FPToSI { .. } => "fptosi",
526            InstrKind::UIToFP { .. } => "uitofp",
527            InstrKind::SIToFP { .. } => "sitofp",
528            InstrKind::PtrToInt { .. } => "ptrtoint",
529            InstrKind::IntToPtr { .. } => "inttoptr",
530            InstrKind::BitCast { .. } => "bitcast",
531            InstrKind::AddrSpaceCast { .. } => "addrspacecast",
532            InstrKind::Freeze { .. } => "freeze",
533            InstrKind::Select { .. } => "select",
534            InstrKind::Phi { .. } => "phi",
535            InstrKind::ExtractValue { .. } => "extractvalue",
536            InstrKind::InsertValue { .. } => "insertvalue",
537            InstrKind::ExtractElement { .. } => "extractelement",
538            InstrKind::InsertElement { .. } => "insertelement",
539            InstrKind::ShuffleVector { .. } => "shufflevector",
540            InstrKind::Call { .. } => "call",
541            InstrKind::Ret { .. } => "ret",
542            InstrKind::Br { .. } => "br",
543            InstrKind::CondBr { .. } => "br",
544            InstrKind::Switch { .. } => "switch",
545            InstrKind::Unreachable => "unreachable",
546        }
547    }
548
549    /// Collect all `ValueRef` operands (not including block successors).
550    pub fn operands(&self) -> Vec<ValueRef> {
551        match self {
552            InstrKind::Add { lhs, rhs, .. }
553            | InstrKind::Sub { lhs, rhs, .. }
554            | InstrKind::Mul { lhs, rhs, .. }
555            | InstrKind::UDiv { lhs, rhs, .. }
556            | InstrKind::SDiv { lhs, rhs, .. }
557            | InstrKind::URem { lhs, rhs }
558            | InstrKind::SRem { lhs, rhs }
559            | InstrKind::And { lhs, rhs }
560            | InstrKind::Or { lhs, rhs }
561            | InstrKind::Xor { lhs, rhs }
562            | InstrKind::Shl { lhs, rhs, .. }
563            | InstrKind::LShr { lhs, rhs, .. }
564            | InstrKind::AShr { lhs, rhs, .. }
565            | InstrKind::FAdd { lhs, rhs, .. }
566            | InstrKind::FSub { lhs, rhs, .. }
567            | InstrKind::FMul { lhs, rhs, .. }
568            | InstrKind::FDiv { lhs, rhs, .. }
569            | InstrKind::FRem { lhs, rhs, .. }
570            | InstrKind::ICmp { lhs, rhs, .. }
571            | InstrKind::FCmp { lhs, rhs, .. } => vec![*lhs, *rhs],
572
573            InstrKind::FNeg { operand, .. } => vec![*operand],
574
575            InstrKind::Alloca { num_elements, .. } => num_elements.iter().copied().collect(),
576            InstrKind::Load { ptr, .. } => vec![*ptr],
577            InstrKind::Store { val, ptr, .. } => vec![*val, *ptr],
578            InstrKind::GetElementPtr { ptr, indices, .. } => {
579                let mut v = vec![*ptr];
580                v.extend_from_slice(indices);
581                v
582            }
583
584            InstrKind::Trunc { val, .. }
585            | InstrKind::ZExt { val, .. }
586            | InstrKind::SExt { val, .. }
587            | InstrKind::FPTrunc { val, .. }
588            | InstrKind::FPExt { val, .. }
589            | InstrKind::FPToUI { val, .. }
590            | InstrKind::FPToSI { val, .. }
591            | InstrKind::UIToFP { val, .. }
592            | InstrKind::SIToFP { val, .. }
593            | InstrKind::PtrToInt { val, .. }
594            | InstrKind::IntToPtr { val, .. }
595            | InstrKind::BitCast { val, .. }
596            | InstrKind::AddrSpaceCast { val, .. }
597            | InstrKind::Freeze { val } => vec![*val],
598
599            InstrKind::Select {
600                cond,
601                then_val,
602                else_val,
603            } => {
604                vec![*cond, *then_val, *else_val]
605            }
606            InstrKind::Phi { incoming, .. } => incoming.iter().map(|(v, _)| *v).collect(),
607            InstrKind::ExtractValue { aggregate, .. } => vec![*aggregate],
608            InstrKind::InsertValue { aggregate, val, .. } => vec![*aggregate, *val],
609            InstrKind::ExtractElement { vec, idx } => vec![*vec, *idx],
610            InstrKind::InsertElement { vec, val, idx } => vec![*vec, *val, *idx],
611            InstrKind::ShuffleVector { v1, v2, .. } => vec![*v1, *v2],
612            InstrKind::Call { callee, args, .. } => {
613                let mut v = vec![*callee];
614                v.extend_from_slice(args);
615                v
616            }
617            InstrKind::Ret { val } => val.iter().copied().collect(),
618            InstrKind::Br { .. } | InstrKind::Unreachable => vec![],
619            InstrKind::CondBr { cond, .. } => vec![*cond],
620            InstrKind::Switch { val, cases, .. } => {
621                let mut v = vec![*val];
622                for (case_val, _) in cases {
623                    v.push(*case_val);
624                }
625                v
626            }
627        }
628    }
629
630    /// Return successor block ids (for terminators).
631    ///
632    /// This match is intentionally exhaustive (no wildcard) so that adding a
633    /// new `InstrKind` variant without updating `successors()` is a
634    /// **compile error**, preventing silent CFG omissions.
635    pub fn successors(&self) -> Vec<BlockId> {
636        match self {
637            InstrKind::Br { dest } => vec![*dest],
638            InstrKind::CondBr {
639                then_dest,
640                else_dest,
641                ..
642            } => {
643                vec![*then_dest, *else_dest]
644            }
645            InstrKind::Switch { default, cases, .. } => {
646                let mut v = vec![*default];
647                for (_, bb) in cases {
648                    v.push(*bb);
649                }
650                v
651            }
652            // Non-terminators and exit terminators (Ret, Unreachable) have no
653            // successors.  Listed explicitly so the compiler enforces that every
654            // future variant is consciously placed in one arm or the other.
655            InstrKind::Ret { .. }
656            | InstrKind::Unreachable
657            | InstrKind::Add { .. }
658            | InstrKind::Sub { .. }
659            | InstrKind::Mul { .. }
660            | InstrKind::UDiv { .. }
661            | InstrKind::SDiv { .. }
662            | InstrKind::URem { .. }
663            | InstrKind::SRem { .. }
664            | InstrKind::And { .. }
665            | InstrKind::Or { .. }
666            | InstrKind::Xor { .. }
667            | InstrKind::Shl { .. }
668            | InstrKind::LShr { .. }
669            | InstrKind::AShr { .. }
670            | InstrKind::FAdd { .. }
671            | InstrKind::FSub { .. }
672            | InstrKind::FMul { .. }
673            | InstrKind::FDiv { .. }
674            | InstrKind::FRem { .. }
675            | InstrKind::FNeg { .. }
676            | InstrKind::ICmp { .. }
677            | InstrKind::FCmp { .. }
678            | InstrKind::Alloca { .. }
679            | InstrKind::Load { .. }
680            | InstrKind::Store { .. }
681            | InstrKind::GetElementPtr { .. }
682            | InstrKind::Trunc { .. }
683            | InstrKind::ZExt { .. }
684            | InstrKind::SExt { .. }
685            | InstrKind::FPTrunc { .. }
686            | InstrKind::FPExt { .. }
687            | InstrKind::FPToUI { .. }
688            | InstrKind::FPToSI { .. }
689            | InstrKind::UIToFP { .. }
690            | InstrKind::SIToFP { .. }
691            | InstrKind::PtrToInt { .. }
692            | InstrKind::IntToPtr { .. }
693            | InstrKind::BitCast { .. }
694            | InstrKind::AddrSpaceCast { .. }
695            | InstrKind::Freeze { .. }
696            | InstrKind::Select { .. }
697            | InstrKind::Phi { .. }
698            | InstrKind::ExtractValue { .. }
699            | InstrKind::InsertValue { .. }
700            | InstrKind::ExtractElement { .. }
701            | InstrKind::InsertElement { .. }
702            | InstrKind::ShuffleVector { .. }
703            | InstrKind::Call { .. } => vec![],
704        }
705    }
706}
707
708/// A single SSA instruction.
709#[derive(Clone, Debug)]
710pub struct Instruction {
711    /// Optional result name (None for void or unnamed).
712    pub name: Option<String>,
713    /// Type of the result (void_ty for void instructions and terminators).
714    pub ty: TypeId,
715    /// Public API for `kind`.
716    pub kind: InstrKind,
717}
718
719impl Instruction {
720    /// Public API for `new`.
721    pub fn new(name: Option<String>, ty: TypeId, kind: InstrKind) -> Self {
722        Instruction { name, ty, kind }
723    }
724
725    /// Public API for `is_terminator`.
726    pub fn is_terminator(&self) -> bool {
727        self.kind.is_terminator()
728    }
729
730    /// Public API for `operands`.
731    pub fn operands(&self) -> Vec<ValueRef> {
732        self.kind.operands()
733    }
734
735    /// Public API for `successors`.
736    pub fn successors(&self) -> Vec<BlockId> {
737        self.kind.successors()
738    }
739}
740
741#[cfg(test)]
742mod tests {
743    use super::*;
744    use crate::context::{BlockId, ConstId, Context, InstrId};
745
746    // ── tiny helpers ────────────────────────────────────────────────────────
747    fn c0() -> ValueRef { ValueRef::Constant(ConstId(0)) }
748    fn c1() -> ValueRef { ValueRef::Constant(ConstId(1)) }
749    fn c2() -> ValueRef { ValueRef::Constant(ConstId(2)) }
750    fn v0() -> ValueRef { ValueRef::Instruction(InstrId(0)) }
751    fn b0() -> BlockId  { BlockId(0) }
752    fn b1() -> BlockId  { BlockId(1) }
753
754    // ── existing tests ───────────────────────────────────────────────────────
755
756    #[test]
757    fn terminator_check() {
758        let _ctx = Context::new();
759        let ret = InstrKind::Ret { val: None };
760        assert!(ret.is_terminator());
761        let add = InstrKind::Add {
762            flags: IntArithFlags::default(),
763            lhs: c0(),
764            rhs: c0(),
765        };
766        assert!(!add.is_terminator());
767    }
768
769    #[test]
770    fn opcode_names() {
771        assert_eq!(InstrKind::Unreachable.opcode(), "unreachable");
772        let br = InstrKind::Br { dest: b0() };
773        assert_eq!(br.opcode(), "br");
774    }
775
776    // ── operands() — integer arithmetic (7 variants) ─────────────────────────
777
778    #[test]
779    fn operands_add() {
780        let k = InstrKind::Add { flags: IntArithFlags::default(), lhs: c0(), rhs: c1() };
781        assert_eq!(k.operands(), vec![c0(), c1()]);
782    }
783
784    #[test]
785    fn operands_sub() {
786        let k = InstrKind::Sub { flags: IntArithFlags::default(), lhs: c0(), rhs: c1() };
787        assert_eq!(k.operands(), vec![c0(), c1()]);
788    }
789
790    #[test]
791    fn operands_mul() {
792        let k = InstrKind::Mul { flags: IntArithFlags::default(), lhs: c0(), rhs: c1() };
793        assert_eq!(k.operands(), vec![c0(), c1()]);
794    }
795
796    #[test]
797    fn operands_udiv() {
798        let k = InstrKind::UDiv { exact: false, lhs: c0(), rhs: c1() };
799        assert_eq!(k.operands(), vec![c0(), c1()]);
800    }
801
802    #[test]
803    fn operands_sdiv() {
804        let k = InstrKind::SDiv { exact: true, lhs: c0(), rhs: c1() };
805        assert_eq!(k.operands(), vec![c0(), c1()]);
806    }
807
808    #[test]
809    fn operands_urem() {
810        let k = InstrKind::URem { lhs: c0(), rhs: c1() };
811        assert_eq!(k.operands(), vec![c0(), c1()]);
812    }
813
814    #[test]
815    fn operands_srem() {
816        let k = InstrKind::SRem { lhs: c0(), rhs: c1() };
817        assert_eq!(k.operands(), vec![c0(), c1()]);
818    }
819
820    // ── operands() — bitwise (6 variants) ─────────────────────────────────────
821
822    #[test]
823    fn operands_and() {
824        assert_eq!(InstrKind::And { lhs: c0(), rhs: c1() }.operands(), vec![c0(), c1()]);
825    }
826
827    #[test]
828    fn operands_or() {
829        assert_eq!(InstrKind::Or { lhs: c0(), rhs: c1() }.operands(), vec![c0(), c1()]);
830    }
831
832    #[test]
833    fn operands_xor() {
834        assert_eq!(InstrKind::Xor { lhs: c0(), rhs: c1() }.operands(), vec![c0(), c1()]);
835    }
836
837    #[test]
838    fn operands_shl() {
839        let k = InstrKind::Shl { flags: IntArithFlags::default(), lhs: c0(), rhs: c1() };
840        assert_eq!(k.operands(), vec![c0(), c1()]);
841    }
842
843    #[test]
844    fn operands_lshr() {
845        assert_eq!(InstrKind::LShr { exact: false, lhs: c0(), rhs: c1() }.operands(), vec![c0(), c1()]);
846    }
847
848    #[test]
849    fn operands_ashr() {
850        assert_eq!(InstrKind::AShr { exact: false, lhs: c0(), rhs: c1() }.operands(), vec![c0(), c1()]);
851    }
852
853    // ── operands() — FP arithmetic (6 variants) ───────────────────────────────
854
855    #[test]
856    fn operands_fadd() {
857        let k = InstrKind::FAdd { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() };
858        assert_eq!(k.operands(), vec![c0(), c1()]);
859    }
860
861    #[test]
862    fn operands_fsub() {
863        let k = InstrKind::FSub { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() };
864        assert_eq!(k.operands(), vec![c0(), c1()]);
865    }
866
867    #[test]
868    fn operands_fmul() {
869        let k = InstrKind::FMul { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() };
870        assert_eq!(k.operands(), vec![c0(), c1()]);
871    }
872
873    #[test]
874    fn operands_fdiv() {
875        let k = InstrKind::FDiv { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() };
876        assert_eq!(k.operands(), vec![c0(), c1()]);
877    }
878
879    #[test]
880    fn operands_frem() {
881        let k = InstrKind::FRem { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() };
882        assert_eq!(k.operands(), vec![c0(), c1()]);
883    }
884
885    #[test]
886    fn operands_fneg() {
887        let k = InstrKind::FNeg { flags: FastMathFlags::default(), operand: c0() };
888        assert_eq!(k.operands(), vec![c0()]);
889    }
890
891    // ── operands() — comparisons (2 variants) ────────────────────────────────
892
893    #[test]
894    fn operands_icmp() {
895        let k = InstrKind::ICmp { pred: IntPredicate::Eq, lhs: c0(), rhs: c1() };
896        assert_eq!(k.operands(), vec![c0(), c1()]);
897    }
898
899    #[test]
900    fn operands_fcmp() {
901        let k = InstrKind::FCmp {
902            flags: FastMathFlags::default(),
903            pred: FloatPredicate::Oeq,
904            lhs: c0(),
905            rhs: c1(),
906        };
907        assert_eq!(k.operands(), vec![c0(), c1()]);
908    }
909
910    // ── operands() — memory (4 variants) ─────────────────────────────────────
911
912    #[test]
913    fn operands_alloca_no_count() {
914        let ctx = Context::new();
915        let k = InstrKind::Alloca { alloc_ty: ctx.i32_ty, num_elements: None, align: None };
916        assert_eq!(k.operands(), vec![]);
917    }
918
919    #[test]
920    fn operands_alloca_with_count() {
921        let ctx = Context::new();
922        let k = InstrKind::Alloca { alloc_ty: ctx.i32_ty, num_elements: Some(c0()), align: None };
923        assert_eq!(k.operands(), vec![c0()]);
924    }
925
926    #[test]
927    fn operands_load() {
928        let ctx = Context::new();
929        let k = InstrKind::Load { ty: ctx.i32_ty, ptr: c0(), align: None, volatile: false };
930        assert_eq!(k.operands(), vec![c0()]);
931    }
932
933    #[test]
934    fn operands_store() {
935        let k = InstrKind::Store { val: c0(), ptr: c1(), align: None, volatile: false };
936        assert_eq!(k.operands(), vec![c0(), c1()]);
937    }
938
939    #[test]
940    fn operands_gep_no_indices() {
941        let ctx = Context::new();
942        let k = InstrKind::GetElementPtr {
943            inbounds: false,
944            base_ty: ctx.i32_ty,
945            ptr: c0(),
946            indices: vec![],
947        };
948        assert_eq!(k.operands(), vec![c0()]);
949    }
950
951    #[test]
952    fn operands_gep_two_indices() {
953        let ctx = Context::new();
954        let k = InstrKind::GetElementPtr {
955            inbounds: true,
956            base_ty: ctx.i32_ty,
957            ptr: c0(),
958            indices: vec![c1(), v0()],
959        };
960        assert_eq!(k.operands(), vec![c0(), c1(), v0()]);
961    }
962
963    // ── operands() — casts (13 variants) ─────────────────────────────────────
964
965    #[test]
966    fn operands_trunc() {
967        let ctx = Context::new();
968        assert_eq!(InstrKind::Trunc { val: c0(), to: ctx.i8_ty }.operands(), vec![c0()]);
969    }
970
971    #[test]
972    fn operands_zext() {
973        let ctx = Context::new();
974        assert_eq!(InstrKind::ZExt { val: c0(), to: ctx.i64_ty }.operands(), vec![c0()]);
975    }
976
977    #[test]
978    fn operands_sext() {
979        let ctx = Context::new();
980        assert_eq!(InstrKind::SExt { val: c0(), to: ctx.i64_ty }.operands(), vec![c0()]);
981    }
982
983    #[test]
984    fn operands_fptrunc() {
985        let ctx = Context::new();
986        let f32_ty = ctx.f32_ty;
987        assert_eq!(InstrKind::FPTrunc { val: c0(), to: f32_ty }.operands(), vec![c0()]);
988    }
989
990    #[test]
991    fn operands_fpext() {
992        let ctx = Context::new();
993        let f64_ty = ctx.f64_ty;
994        assert_eq!(InstrKind::FPExt { val: c0(), to: f64_ty }.operands(), vec![c0()]);
995    }
996
997    #[test]
998    fn operands_fptoui() {
999        let ctx = Context::new();
1000        assert_eq!(InstrKind::FPToUI { val: c0(), to: ctx.i64_ty }.operands(), vec![c0()]);
1001    }
1002
1003    #[test]
1004    fn operands_fptosi() {
1005        let ctx = Context::new();
1006        assert_eq!(InstrKind::FPToSI { val: c0(), to: ctx.i64_ty }.operands(), vec![c0()]);
1007    }
1008
1009    #[test]
1010    fn operands_uitofp() {
1011        let ctx = Context::new();
1012        let f64_ty = ctx.f64_ty;
1013        assert_eq!(InstrKind::UIToFP { val: c0(), to: f64_ty }.operands(), vec![c0()]);
1014    }
1015
1016    #[test]
1017    fn operands_sitofp() {
1018        let ctx = Context::new();
1019        let f64_ty = ctx.f64_ty;
1020        assert_eq!(InstrKind::SIToFP { val: c0(), to: f64_ty }.operands(), vec![c0()]);
1021    }
1022
1023    #[test]
1024    fn operands_ptrtoint() {
1025        let ctx = Context::new();
1026        assert_eq!(InstrKind::PtrToInt { val: c0(), to: ctx.i64_ty }.operands(), vec![c0()]);
1027    }
1028
1029    #[test]
1030    fn operands_inttoptr() {
1031        let ctx = Context::new();
1032        let ptr_ty = ctx.ptr_ty;
1033        assert_eq!(InstrKind::IntToPtr { val: c0(), to: ptr_ty }.operands(), vec![c0()]);
1034    }
1035
1036    #[test]
1037    fn operands_bitcast() {
1038        let ctx = Context::new();
1039        let ptr_ty = ctx.ptr_ty;
1040        assert_eq!(InstrKind::BitCast { val: c0(), to: ptr_ty }.operands(), vec![c0()]);
1041    }
1042
1043    #[test]
1044    fn operands_addrspacecast() {
1045        let ctx = Context::new();
1046        let ptr_ty = ctx.ptr_ty;
1047        assert_eq!(InstrKind::AddrSpaceCast { val: c0(), to: ptr_ty }.operands(), vec![c0()]);
1048    }
1049
1050    // ── operands() — misc (7 variants) ───────────────────────────────────────
1051
1052    #[test]
1053    fn operands_select() {
1054        let k = InstrKind::Select { cond: c0(), then_val: c1(), else_val: v0() };
1055        assert_eq!(k.operands(), vec![c0(), c1(), v0()]);
1056    }
1057
1058    #[test]
1059    fn operands_phi_empty() {
1060        let ctx = Context::new();
1061        let k = InstrKind::Phi { ty: ctx.i32_ty, incoming: vec![] };
1062        assert_eq!(k.operands(), vec![]);
1063    }
1064
1065    #[test]
1066    fn operands_phi_two_incoming() {
1067        let ctx = Context::new();
1068        // Only value refs, not block ids, must appear in operands().
1069        let k = InstrKind::Phi {
1070            ty: ctx.i32_ty,
1071            incoming: vec![(c0(), b0()), (c1(), b1())],
1072        };
1073        assert_eq!(k.operands(), vec![c0(), c1()]);
1074    }
1075
1076    #[test]
1077    fn operands_extractvalue() {
1078        let k = InstrKind::ExtractValue { aggregate: c0(), indices: vec![0, 1] };
1079        assert_eq!(k.operands(), vec![c0()]);
1080    }
1081
1082    #[test]
1083    fn operands_insertvalue() {
1084        let k = InstrKind::InsertValue { aggregate: c0(), val: c1(), indices: vec![0] };
1085        assert_eq!(k.operands(), vec![c0(), c1()]);
1086    }
1087
1088    #[test]
1089    fn operands_extractelement() {
1090        let k = InstrKind::ExtractElement { vec: c0(), idx: c1() };
1091        assert_eq!(k.operands(), vec![c0(), c1()]);
1092    }
1093
1094    #[test]
1095    fn operands_insertelement() {
1096        let k = InstrKind::InsertElement { vec: c0(), val: c1(), idx: v0() };
1097        assert_eq!(k.operands(), vec![c0(), c1(), v0()]);
1098    }
1099
1100    #[test]
1101    fn operands_shufflevector() {
1102        // mask is Vec<i32> (integer literals, not ValueRefs) — v1 and v2 only.
1103        let k = InstrKind::ShuffleVector { v1: c0(), v2: c1(), mask: vec![0, 1, 0, 1] };
1104        assert_eq!(k.operands(), vec![c0(), c1()]);
1105    }
1106
1107    // ── operands() — call (1 variant, 2 cases) ────────────────────────────────
1108
1109    #[test]
1110    fn operands_call_no_args() {
1111        let mut ctx = Context::new();
1112        let callee_ty = ctx.mk_fn_type(ctx.void_ty, vec![], false);
1113        let k = InstrKind::Call {
1114            tail: TailCallKind::None,
1115            callee_ty,
1116            callee: c0(),
1117            args: vec![],
1118        };
1119        // callee is always first, then args.
1120        assert_eq!(k.operands(), vec![c0()]);
1121    }
1122
1123    #[test]
1124    fn operands_call_with_args() {
1125        let mut ctx = Context::new();
1126        let callee_ty = ctx.mk_fn_type(ctx.void_ty, vec![ctx.i32_ty, ctx.i32_ty], false);
1127        let k = InstrKind::Call {
1128            tail: TailCallKind::None,
1129            callee_ty,
1130            callee: c0(),
1131            args: vec![c1(), c2()],
1132        };
1133        assert_eq!(k.operands(), vec![c0(), c1(), c2()]);
1134    }
1135
1136    // ── operands() — terminators (5 variants) ────────────────────────────────
1137
1138    #[test]
1139    fn operands_ret_void() {
1140        assert_eq!(InstrKind::Ret { val: None }.operands(), vec![]);
1141    }
1142
1143    #[test]
1144    fn operands_ret_value() {
1145        assert_eq!(InstrKind::Ret { val: Some(c0()) }.operands(), vec![c0()]);
1146    }
1147
1148    #[test]
1149    fn operands_br() {
1150        // Unconditional branch has no value operands.
1151        assert_eq!(InstrKind::Br { dest: b0() }.operands(), vec![]);
1152    }
1153
1154    #[test]
1155    fn operands_condbr() {
1156        let k = InstrKind::CondBr { cond: c0(), then_dest: b0(), else_dest: b1() };
1157        // Only the condition is a value operand; block targets are not.
1158        assert_eq!(k.operands(), vec![c0()]);
1159    }
1160
1161    #[test]
1162    fn operands_switch_two_cases() {
1163        // val + all case values; block targets are not operands.
1164        let k = InstrKind::Switch {
1165            val: c0(),
1166            default: b0(),
1167            cases: vec![(c1(), b1()), (c2(), b0())],
1168        };
1169        assert_eq!(k.operands(), vec![c0(), c1(), c2()]);
1170    }
1171
1172    #[test]
1173    fn operands_unreachable() {
1174        assert_eq!(InstrKind::Unreachable.operands(), vec![]);
1175    }
1176
1177    // ── successors() — terminators ────────────────────────────────────────────
1178
1179    #[test]
1180    fn successors_br() {
1181        assert_eq!(InstrKind::Br { dest: b0() }.successors(), vec![b0()]);
1182    }
1183
1184    #[test]
1185    fn successors_condbr() {
1186        let k = InstrKind::CondBr { cond: c0(), then_dest: b0(), else_dest: b1() };
1187        assert_eq!(k.successors(), vec![b0(), b1()]);
1188    }
1189
1190    #[test]
1191    fn successors_switch_default_plus_cases() {
1192        let k = InstrKind::Switch {
1193            val: c0(),
1194            default: b0(),
1195            cases: vec![(c1(), b1()), (c2(), b0())],
1196        };
1197        assert_eq!(k.successors(), vec![b0(), b1(), b0()]);
1198    }
1199
1200    #[test]
1201    fn successors_switch_no_cases() {
1202        let k = InstrKind::Switch { val: c0(), default: b1(), cases: vec![] };
1203        assert_eq!(k.successors(), vec![b1()]);
1204    }
1205
1206    #[test]
1207    fn successors_ret_void() {
1208        assert_eq!(InstrKind::Ret { val: None }.successors(), vec![]);
1209    }
1210
1211    #[test]
1212    fn successors_ret_value() {
1213        assert_eq!(InstrKind::Ret { val: Some(c0()) }.successors(), vec![]);
1214    }
1215
1216    #[test]
1217    fn successors_unreachable() {
1218        assert_eq!(InstrKind::Unreachable.successors(), vec![]);
1219    }
1220
1221    // ── successors() — non-terminators all return empty ───────────────────────
1222
1223    #[test]
1224    fn successors_non_terminators_are_empty() {
1225        let mut ctx = Context::new();
1226        let callee_ty = ctx.mk_fn_type(ctx.void_ty, vec![], false);
1227        // One representative from each group of non-terminators.
1228        let cases: &[InstrKind] = &[
1229            InstrKind::Add { flags: IntArithFlags::default(), lhs: c0(), rhs: c1() },
1230            InstrKind::Sub { flags: IntArithFlags::default(), lhs: c0(), rhs: c1() },
1231            InstrKind::Mul { flags: IntArithFlags::default(), lhs: c0(), rhs: c1() },
1232            InstrKind::UDiv { exact: false, lhs: c0(), rhs: c1() },
1233            InstrKind::SDiv { exact: false, lhs: c0(), rhs: c1() },
1234            InstrKind::URem { lhs: c0(), rhs: c1() },
1235            InstrKind::SRem { lhs: c0(), rhs: c1() },
1236            InstrKind::And { lhs: c0(), rhs: c1() },
1237            InstrKind::Or  { lhs: c0(), rhs: c1() },
1238            InstrKind::Xor { lhs: c0(), rhs: c1() },
1239            InstrKind::Shl { flags: IntArithFlags::default(), lhs: c0(), rhs: c1() },
1240            InstrKind::LShr { exact: false, lhs: c0(), rhs: c1() },
1241            InstrKind::AShr { exact: false, lhs: c0(), rhs: c1() },
1242            InstrKind::FAdd { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() },
1243            InstrKind::FSub { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() },
1244            InstrKind::FMul { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() },
1245            InstrKind::FDiv { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() },
1246            InstrKind::FRem { flags: FastMathFlags::default(), lhs: c0(), rhs: c1() },
1247            InstrKind::FNeg { flags: FastMathFlags::default(), operand: c0() },
1248            InstrKind::ICmp { pred: IntPredicate::Eq, lhs: c0(), rhs: c1() },
1249            InstrKind::FCmp { flags: FastMathFlags::default(), pred: FloatPredicate::Oeq, lhs: c0(), rhs: c1() },
1250            InstrKind::Alloca { alloc_ty: ctx.i32_ty, num_elements: None, align: None },
1251            InstrKind::Load  { ty: ctx.i32_ty, ptr: c0(), align: None, volatile: false },
1252            InstrKind::Store { val: c0(), ptr: c1(), align: None, volatile: false },
1253            InstrKind::GetElementPtr { inbounds: false, base_ty: ctx.i32_ty, ptr: c0(), indices: vec![] },
1254            InstrKind::Trunc { val: c0(), to: ctx.i8_ty },
1255            InstrKind::ZExt  { val: c0(), to: ctx.i64_ty },
1256            InstrKind::SExt  { val: c0(), to: ctx.i64_ty },
1257            InstrKind::FPTrunc { val: c0(), to: ctx.f32_ty },
1258            InstrKind::FPExt   { val: c0(), to: ctx.f64_ty },
1259            InstrKind::FPToUI  { val: c0(), to: ctx.i64_ty },
1260            InstrKind::FPToSI  { val: c0(), to: ctx.i64_ty },
1261            InstrKind::UIToFP  { val: c0(), to: ctx.f64_ty },
1262            InstrKind::SIToFP  { val: c0(), to: ctx.f64_ty },
1263            InstrKind::PtrToInt { val: c0(), to: ctx.i64_ty },
1264            InstrKind::IntToPtr { val: c0(), to: ctx.ptr_ty },
1265            InstrKind::BitCast  { val: c0(), to: ctx.ptr_ty },
1266            InstrKind::AddrSpaceCast { val: c0(), to: ctx.ptr_ty },
1267            InstrKind::Select { cond: c0(), then_val: c1(), else_val: v0() },
1268            InstrKind::Phi { ty: ctx.i32_ty, incoming: vec![] },
1269            InstrKind::ExtractValue { aggregate: c0(), indices: vec![0] },
1270            InstrKind::InsertValue  { aggregate: c0(), val: c1(), indices: vec![0] },
1271            InstrKind::ExtractElement { vec: c0(), idx: c1() },
1272            InstrKind::InsertElement  { vec: c0(), val: c1(), idx: v0() },
1273            InstrKind::ShuffleVector  { v1: c0(), v2: c1(), mask: vec![0, 1] },
1274            InstrKind::Call { tail: TailCallKind::None, callee_ty, callee: c0(), args: vec![] },
1275        ];
1276        for k in cases {
1277            assert_eq!(k.successors(), vec![], "{:?} should have no successors", k);
1278        }
1279    }
1280}