mir/
types.rs

1use std::ffi::{CStr, c_char};
2use std::marker::PhantomData;
3use std::ptr::{NonNull, null_mut};
4use std::{fmt, ops};
5
6use paste::paste;
7use smallvec::SmallVec;
8
9use crate::{MemoryFile, MirContext, ffi};
10
11/// Type of virtual registers.
12#[derive(Clone, Copy, PartialEq, Eq)]
13#[repr(transparent)]
14pub struct Ty(pub(crate) ffi::MIR_type_t);
15
16macro_rules! impl_ty_variants {
17    ($($var:ident),* $(,)?) => {
18        impl Ty {
19            $(
20                #[doc = concat!("`MIR_T_", stringify!($var), "`")]
21                pub const $var: Self = Self(paste!(ffi::[<MIR_T_ $var>]));
22            )*
23        }
24
25        impl fmt::Debug for Ty {
26            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27                let s = match *self {
28                    $(Self::$var => concat!("Ty(MIR_T_", stringify!($var), ")"),)*
29                    Self(raw) => return f.debug_tuple("Ty").field(&raw).finish(),
30                };
31                f.write_str(s)
32            }
33        }
34    };
35}
36
37impl_ty_variants! {
38    I8, U8, I16, U16, I32, U32, I64, U64,
39    F, D, LD, P,
40    BLK, RBLK, UNDEF, BOUND,
41}
42
43#[test]
44fn ty_debug() {
45    assert_eq!(format!("{:?}", Ty::I64), "Ty(MIR_T_I64)");
46    assert_eq!(format!("{:?}", Ty(99)), "Ty(99)");
47}
48
49/// Virtual register identifier.
50#[derive(Debug, Clone, Copy)]
51#[repr(transparent)]
52pub struct Reg(pub(crate) ffi::MIR_reg_t);
53
54/// Item type.
55#[derive(Clone, Copy, PartialEq, Eq)]
56#[repr(transparent)]
57pub struct ItemType(pub(crate) ffi::MIR_item_type_t);
58
59macro_rules! impl_item_type_variants {
60    ($($var:ident),* $(,)?) => {
61        impl ItemType {
62            $(
63                #[doc = paste!(concat!("`MIR_", stringify!([<$var:lower>]), "_item"))]
64                pub const $var: Self = Self(paste!(ffi::[<MIR_ $var:lower _item>]));
65            )*
66        }
67
68        impl fmt::Debug for ItemType {
69            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70                let s = match *self {
71                    $(Self::$var => paste!(concat!("ItemType(MIR_", stringify!($var:lower), "_item)")),)*
72                    Self(raw) => return f.debug_tuple("ItemType").field(&raw).finish(),
73                };
74                f.write_str(s)
75            }
76        }
77    };
78}
79
80impl_item_type_variants! {
81    FUNC, PROTO,
82    IMPORT, EXPORT, FORWARD,
83    DATA, REF_DATA, LREF_DATA, EXPR_DATA,
84    BSS,
85}
86
87/// Reference to an item.
88#[derive(Clone, Copy)]
89#[repr(transparent)]
90pub struct ItemRef<'a>(
91    pub(crate) NonNull<ffi::MIR_item>,
92    pub(crate) PhantomData<&'a ffi::MIR_item>,
93);
94
95impl ItemRef<'_> {
96    pub(crate) unsafe fn from_raw(raw: *mut ffi::MIR_item) -> Self {
97        let raw = NonNull::new(raw).expect("item must not be null");
98        Self(raw, PhantomData)
99    }
100
101    /// Get the underlying pointer for FFI.
102    #[must_use]
103    pub fn as_raw(&self) -> *mut ffi::MIR_item {
104        self.0.as_ptr()
105    }
106
107    /// Dump the content of the context in a textual representation for human consumption.
108    #[must_use]
109    pub fn dump(&self, ctx: &MirContext) -> String {
110        MemoryFile::with(|file| unsafe { ffi::MIR_output_item(ctx.as_raw(), file, self.as_raw()) })
111            .1
112    }
113
114    /// Get the item type.
115    #[must_use]
116    pub fn type_(&self) -> ItemType {
117        unsafe { ItemType(self.0.as_ref().item_type) }
118    }
119
120    /// Get the name of the item.
121    #[must_use]
122    pub fn name(&self) -> Option<&CStr> {
123        let item = unsafe { self.0.as_ref() };
124        let data = &item.u;
125        let ptr = unsafe {
126            match item.item_type {
127                ffi::MIR_func_item => (*data.func.cast::<FuncItemData>()).0.name,
128                ffi::MIR_proto_item => (*data.func.cast::<ProtoItemData>()).0.name,
129                ffi::MIR_import_item => data.import_id,
130                ffi::MIR_export_item => data.export_id,
131                ffi::MIR_forward_item => data.forward_id,
132                ffi::MIR_data_item => (*data.data.cast::<DataItemData>()).0.name,
133                ffi::MIR_ref_data_item => (*data.ref_data.cast::<RefDataItemData>()).0.name,
134                ffi::MIR_lref_data_item => (*data.lref_data.cast::<LabelRefDataItemData>()).0.name,
135                ffi::MIR_expr_data_item => (*data.expr_data.cast::<ExprDataItemData>()).0.name,
136                ffi::MIR_bss_item => (*data.bss.cast::<BssItemData>()).0.name,
137                _ => return None,
138            }
139        };
140        if ptr.is_null() {
141            None
142        } else {
143            unsafe { Some(CStr::from_ptr(ptr)) }
144        }
145    }
146}
147
148impl<'a> From<ItemRef<'a>> for Operand<'a> {
149    fn from(item: ItemRef<'a>) -> Self {
150        Operand {
151            op: unsafe { ffi::MIR_new_ref_op(null_mut(), item.as_raw()) },
152            _marker: PhantomData,
153        }
154    }
155}
156
157impl fmt::Debug for ItemRef<'_> {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        let mut fs = f.debug_struct("ItemRef");
160        let item = unsafe { self.0.as_ref() };
161        fs.field("ptr", &std::ptr::from_ref(self))
162            .field("module", &item.module)
163            .field("addr", &item.addr)
164            .field("type", &self.type_());
165        let data = &item.u;
166        let mut cb = |data| {
167            fs.field("u", data);
168        };
169        match item.item_type {
170            ffi::MIR_func_item => cb(unsafe { &*data.func.cast::<FuncItemData>() }),
171            ffi::MIR_proto_item => cb(unsafe { &*data.func.cast::<ProtoItemData>() }),
172            ffi::MIR_import_item => cb(&DebugImportLikeItemData(
173                "ImportItemData",
174                "import_id",
175                unsafe { data.import_id },
176            )),
177            ffi::MIR_export_item => cb(&DebugImportLikeItemData(
178                "ExportItemData",
179                "export_id",
180                unsafe { data.export_id },
181            )),
182            ffi::MIR_forward_item => cb(&DebugImportLikeItemData(
183                "ForwardItemData",
184                "forward_id",
185                unsafe { data.forward_id },
186            )),
187            ffi::MIR_data_item => cb(unsafe { &*data.data.cast::<DataItemData>() }),
188            ffi::MIR_ref_data_item => cb(unsafe { &*data.ref_data.cast::<RefDataItemData>() }),
189            ffi::MIR_lref_data_item => {
190                cb(unsafe { &*data.lref_data.cast::<LabelRefDataItemData>() });
191            }
192            ffi::MIR_expr_data_item => cb(unsafe { &*data.expr_data.cast::<ExprDataItemData>() }),
193            ffi::MIR_bss_item => cb(unsafe { &*data.bss.cast::<BssItemData>() }),
194            _ => {}
195        }
196        fs.finish_non_exhaustive()
197    }
198}
199
200/// Item downcast failure.
201#[derive(Debug, Clone)]
202pub struct ItemRefDowncastError(());
203
204impl fmt::Display for ItemRefDowncastError {
205    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206        f.write_str("item type mismatch")
207    }
208}
209
210impl std::error::Error for ItemRefDowncastError {}
211
212macro_rules! def_item_ref_variant {
213    ($name:ident, $item_type:expr, $doc:literal) => {
214        /// Reference to a
215        #[doc = $doc]
216        /// item.
217        #[derive(Debug, Clone, Copy)]
218        #[repr(transparent)]
219        pub struct $name<'a>(pub(crate) ItemRef<'a>);
220
221        impl<'a> ops::Deref for $name<'a> {
222            type Target = ItemRef<'a>;
223
224            fn deref(&self) -> &Self::Target {
225                &self.0
226            }
227        }
228
229        impl<'a> From<$name<'a>> for Operand<'a> {
230            fn from(item: $name<'a>) -> Self {
231                item.0.into()
232            }
233        }
234
235        impl<'a> TryFrom<ItemRef<'a>> for $name<'a> {
236            type Error = ItemRefDowncastError;
237            fn try_from(item: ItemRef<'a>) -> Result<Self, Self::Error> {
238                if unsafe { item.0.as_ref().item_type } == $item_type {
239                    Ok(Self(item))
240                } else {
241                    Err(ItemRefDowncastError(()))
242                }
243            }
244        }
245    };
246}
247
248def_item_ref_variant!(FuncItemRef, ffi::MIR_func_item, "function");
249
250impl FuncItemRef<'_> {
251    /// # Safety
252    /// The returned reference is invalidated if any MIR functions is called.
253    #[cfg(feature = "interp")]
254    pub(crate) unsafe fn data(&self) -> &ffi::MIR_func {
255        unsafe { &*self.0.0.as_ref().u.func }
256    }
257}
258
259#[repr(transparent)]
260struct FuncItemData(ffi::MIR_func);
261
262impl fmt::Debug for FuncItemData {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        let u = &self.0;
265        f.debug_struct("FuncItemData")
266            .field("name", &unsafe { CStr::from_ptr(u.name) })
267            .field("func_item", &u.func_item)
268            .field("original_vars_num", &u.original_insns)
269            .field("nres", &u.nres)
270            .field("nargs", &u.nargs)
271            .field("res_types", &unsafe {
272                std::slice::from_raw_parts(u.res_types.cast::<Ty>(), u.nres as usize)
273            })
274            .field("varargs_p", &(u.vararg_p != 0))
275            .field("expr_p", &(u.expr_p != 0))
276            .field("jret_p", &(u.jret_p != 0))
277            .field("vars", &DebugVars(u.vars))
278            .field("global_vars", &DebugVars(u.global_vars))
279            .finish_non_exhaustive()
280    }
281}
282
283#[repr(transparent)]
284struct DebugVar(ffi::MIR_var);
285
286impl fmt::Debug for DebugVar {
287    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288        f.debug_struct("Var")
289            .field("type", &Ty(self.0.type_))
290            .field("name", &unsafe { CStr::from_ptr(self.0.name) })
291            // .field("size", &self.0.size) // Contains garbage?
292            .finish_non_exhaustive()
293    }
294}
295
296struct DebugVars(*mut ffi::VARR_MIR_var_t);
297
298impl fmt::Debug for DebugVars {
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        let vars = if self.0.is_null() {
301            &[]
302        } else {
303            unsafe {
304                let varr = &*self.0;
305                std::slice::from_raw_parts(varr.varr.cast::<DebugVar>().cast_const(), varr.els_num)
306            }
307        };
308        vars.fmt(f)
309    }
310}
311
312def_item_ref_variant!(ProtoItemRef, ffi::MIR_proto_item, "prototype");
313
314#[repr(transparent)]
315struct ProtoItemData(ffi::MIR_proto);
316
317impl fmt::Debug for ProtoItemData {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        let u = &self.0;
320        f.debug_struct("ProtoItemData")
321            .field("name", &unsafe { CStr::from_ptr(u.name) })
322            .field("nres", &u.nres)
323            .field("res_types", &unsafe {
324                std::slice::from_raw_parts(u.res_types.cast::<Ty>(), u.nres as usize)
325            })
326            .finish_non_exhaustive()
327    }
328}
329
330def_item_ref_variant!(ImportItemRef, ffi::MIR_import_item, "import");
331def_item_ref_variant!(ExportItemRef, ffi::MIR_export_item, "export");
332def_item_ref_variant!(ForwardItemRef, ffi::MIR_forward_item, "forward declaration");
333
334struct DebugImportLikeItemData(&'static str, &'static str, *const c_char);
335
336impl fmt::Debug for DebugImportLikeItemData {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        f.debug_struct(self.0)
339            .field(self.1, &unsafe { CStr::from_ptr(self.2) })
340            .finish()
341    }
342}
343
344def_item_ref_variant!(DataItemRef, ffi::MIR_data_item, "data");
345
346#[repr(transparent)]
347struct DataItemData(ffi::MIR_data);
348
349impl fmt::Debug for DataItemData {
350    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351        let u = &self.0;
352        f.debug_struct("DataItemData")
353            .field("name", &unsafe { CStr::from_ptr(u.name) })
354            .field("el_type", &Ty(u.el_type))
355            .field("nel", &u.nel)
356            .finish_non_exhaustive()
357    }
358}
359
360def_item_ref_variant!(RefDataItemRef, ffi::MIR_ref_data_item, "referential data");
361
362#[repr(transparent)]
363struct RefDataItemData(ffi::MIR_ref_data);
364
365impl fmt::Debug for RefDataItemData {
366    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367        let u = &self.0;
368        f.debug_struct("RefDataItemData")
369            .field("name", &unsafe { CStr::from_ptr(u.name) })
370            .field("ref_item", &unsafe { ItemRef::from_raw(u.ref_item) })
371            .field("disp", &u.disp)
372            .field("load_addr", &u.load_addr)
373            .finish()
374    }
375}
376
377def_item_ref_variant!(LabelRefDataItemRef, ffi::MIR_lref_data_item, "label data");
378
379#[repr(transparent)]
380struct LabelRefDataItemData(ffi::MIR_lref_data);
381
382impl fmt::Debug for LabelRefDataItemData {
383    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384        let u = &self.0;
385        f.debug_struct("LabelRefDataItemData")
386            .field("name", &unsafe { CStr::from_ptr(u.name) })
387            .field("disp", &u.disp)
388            .field("load_addr", &u.load_addr)
389            .finish()
390    }
391}
392
393def_item_ref_variant!(ExprDataItemRef, ffi::MIR_expr_data_item, "computed data");
394
395#[repr(transparent)]
396struct ExprDataItemData(ffi::MIR_expr_data);
397
398impl fmt::Debug for ExprDataItemData {
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        let u = &self.0;
401        f.debug_struct("ExprItemData")
402            .field("name", &unsafe { CStr::from_ptr(u.name) })
403            .field("expr_item", &unsafe { ItemRef::from_raw(u.expr_item) })
404            .finish_non_exhaustive()
405    }
406}
407
408def_item_ref_variant!(BssItemRef, ffi::MIR_bss_item, "writable memory segment");
409
410#[repr(transparent)]
411struct BssItemData(ffi::MIR_bss);
412
413impl fmt::Debug for BssItemData {
414    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415        let u = &self.0;
416        f.debug_struct("BssItemData")
417            .field("name", &unsafe { CStr::from_ptr(u.name) })
418            .field("len", &u.len)
419            .finish()
420    }
421}
422
423/// Operand of instructions.
424#[derive(Clone, Copy)]
425#[repr(transparent)]
426pub struct Operand<'a> {
427    pub(crate) op: ffi::MIR_op_t,
428    pub(crate) _marker: PhantomData<&'a ()>,
429}
430
431impl fmt::Debug for Operand<'_> {
432    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433        let mut s = f.debug_struct("Operand");
434        unsafe {
435            match ffi::MIR_op_mode_t::from(self.op.mode) {
436                ffi::MIR_OP_REG => s.field("reg", &self.op.u.reg),
437                ffi::MIR_OP_VAR => s.field("var", &self.op.u.var),
438                ffi::MIR_OP_INT => s.field("int", &self.op.u.i),
439                ffi::MIR_OP_UINT => s.field("uint", &self.op.u.u),
440                ffi::MIR_OP_FLOAT => s.field("float", &self.op.u.f),
441                ffi::MIR_OP_DOUBLE => s.field("double", &self.op.u.d),
442                ffi::MIR_OP_REF => s.field("ref", &ItemRef::from_raw(self.op.u.ref_)),
443                ffi::MIR_OP_MEM => s.field("mem", &self.op.u.mem),
444                ffi::MIR_OP_LABEL => s.field("label", &..),
445                // Not implemented yet.
446                // ffi::MIR_OP_STR
447                // ffi::MIR_OP_VAR_MEM
448                // ffi::MIR_OP_BOUND
449                _ => return s.finish_non_exhaustive(),
450            }
451            .finish()
452        }
453    }
454}
455
456/// Convertible to [`Operand`].
457pub trait IntoOperand<'a>: Into<Operand<'a>> {}
458
459/// Convertible to [`Operand`], but can only be used in destination place.
460pub trait IntoOutOperand<'a>: IntoOperand<'a> {}
461
462impl<'a, T> IntoOperand<'a> for T where Operand<'a>: From<T> {}
463
464impl IntoOutOperand<'_> for Reg {}
465impl From<Reg> for Operand<'_> {
466    fn from(reg: Reg) -> Self {
467        Self {
468            op: unsafe { ffi::MIR_new_reg_op(null_mut(), reg.0) },
469            _marker: PhantomData,
470        }
471    }
472}
473
474/// Label identifier.
475#[derive(Debug, Clone, Copy)]
476#[repr(transparent)]
477pub struct Label<'a>(pub(crate) ffi::MIR_label_t, pub(crate) PhantomData<&'a ()>);
478
479impl<'a> From<Label<'a>> for Operand<'a> {
480    fn from(label: Label<'a>) -> Self {
481        Self {
482            op: unsafe { ffi::MIR_new_label_op(null_mut(), label.0) },
483            _marker: PhantomData,
484        }
485    }
486}
487
488/// Memory operand.
489///
490/// It represents the data located at address `(disp + base_reg + index_reg * scale)`,
491/// where `disp` is a constant and `scale` is either 1, 2, 4 or 8.
492#[derive(Debug, Clone, Copy)]
493pub struct MemOp {
494    ty: Ty,
495    disp: i64,
496    base: Reg,
497    index: Reg,
498    scale: u8,
499}
500
501impl MemOp {
502    /// Create a new memory operand with type `ty`, based on register `base`.
503    #[must_use]
504    pub fn new_base(ty: Ty, base: Reg) -> Self {
505        Self {
506            ty,
507            disp: 0,
508            base,
509            index: Reg(0),
510            scale: 1,
511        }
512    }
513
514    /// Set the displacement and return updated operand.
515    #[must_use]
516    pub fn disp(self, disp: i64) -> Self {
517        Self { disp, ..self }
518    }
519
520    /// Set the index register and return updated operand.
521    #[must_use]
522    pub fn index(self, index: Reg) -> Self {
523        Self { index, ..self }
524    }
525
526    /// Set the scale and return updated operand.
527    ///
528    /// # Panics
529    ///
530    /// Panics if `scale` is not 1, 2, 4 or 8.
531    #[must_use]
532    pub fn scale(self, scale: u8) -> Self {
533        assert!(matches!(scale, 1 | 2 | 4 | 8), "scale must be 1, 2, 4 or 8");
534        Self { scale, ..self }
535    }
536}
537
538impl IntoOutOperand<'_> for MemOp {}
539impl From<MemOp> for Operand<'_> {
540    fn from(mem: MemOp) -> Self {
541        Self {
542            op: unsafe {
543                ffi::MIR_new_mem_op(
544                    null_mut(),
545                    mem.ty.0,
546                    mem.disp,
547                    mem.base.0,
548                    mem.index.0,
549                    mem.scale,
550                )
551            },
552            _marker: PhantomData,
553        }
554    }
555}
556
557macro_rules! impl_literal_into_op {
558    ($($ty:ident, $func:ident;)*) => {
559        $(
560            impl From<$ty> for Operand<'_> {
561                fn from(v: $ty) -> Self {
562                    Self {
563                        op: unsafe { ffi::$func(null_mut(), v) },
564                        _marker: PhantomData,
565                    }
566                }
567            }
568        )*
569    };
570}
571
572impl_literal_into_op! {
573    i64, MIR_new_int_op;
574    u64, MIR_new_uint_op;
575    f32, MIR_new_float_op;
576    f64, MIR_new_double_op;
577}
578
579macro_rules! def_simple_insn {
580    (__impl $args:tt $name:ident) => {
581        def_simple_insn!(__impl $args $name paste!(ffi::[<MIR_ $name:upper>]));
582    };
583    (__impl (dst $(, $src:ident)*) $name:ident $code:expr) => {
584        fn $name<'o>(self, dst: impl IntoOutOperand<'o>, $($src: impl IntoOperand<'o>),*) {
585            build_insn(self, $code, [dst.into(), $($src.into()),*]);
586        }
587    };
588    (__impl (_ $(, $src:ident)*) $name:ident $code:expr) => {
589        fn $name<'o>(self, $($src: impl IntoOperand<'o>),*) {
590            build_insn(self, $code, [$($src.into()),*]);
591        }
592    };
593    (
594        $args:tt;
595        $($name:ident $(($code:expr))?),*
596        $(,)?
597    ) => {
598        $(def_simple_insn!(__impl $args $name $($code)?);)*
599    };
600}
601
602macro_rules! def_jump_insn {
603    (__impl (label $(, $src:ident)*) $name:ident) => {
604        #[allow(clippy::needless_lifetimes)]
605        fn $name<'o>(self, label: Label<'o>, $($src: impl IntoOperand<'o>),*) {
606            build_insn(self, paste!(ffi::[<MIR_ $name:upper>]), [label.into(), $($src.into()),*]);
607        }
608    };
609    (
610        $args:tt;
611        $($name:ident $(($code:expr))?),*
612        $(,)?
613    ) => {
614        $(def_jump_insn!(__impl $args $name $($code)?);)*
615    };
616}
617
618macro_rules! def_call_insn {
619    ($($name:ident),* $(,)?) => {
620        $(
621            fn $name<'o>(
622                self,
623                proto: ProtoItemRef<'o>,
624                func: impl IntoOperand<'o>,
625                results: impl IntoIterator<Item = Operand<'o>>,
626                args: impl IntoIterator<Item = Operand<'o>>,
627            ) {
628                build_insn(
629                    self,
630                    paste!(ffi::[<MIR_ $name:upper>]),
631                    [proto.into(), func.into()]
632                        .into_iter()
633                        .chain(results)
634                        .chain(args),
635                );
636            }
637        )*
638    };
639}
640
641/// !! Not a public API !!
642///
643/// # Safety
644/// `get_raw_ctx` must return a valid MIR context.
645#[doc(hidden)]
646pub unsafe trait InsnBuilderBase<'func>: Sized {
647    fn get_raw_ctx(&self) -> ffi::MIR_context_t;
648    /// # Safety
649    /// `insn` must be a valid instruction that is not inserted anywhere.
650    unsafe fn insert(self, insn: ffi::MIR_insn_t);
651}
652
653impl<'func, T: InsnBuilderBase<'func>> InsnBuilder<'func> for T {}
654
655/// The instruction builder.
656///
657/// See detail instruction semantics in [MIR documentation][upstream-docs].
658///
659/// [upstream-docs]: https://github.com/vnmakarov/mir/blob/v1.0.0/MIR.md#mir-insns
660///
661/// # Example
662///
663/// ```
664/// # fn codegen(f: &mir::MirFuncBuilder<'_, '_>, [sum, a, b]: [mir::Reg; 3]) {
665/// use mir::InsnBuilder as _;
666/// // let f: &MirFuncBuilder<'_, '_>;
667/// // let a, b, c: Reg;
668/// f.ins().add(sum, a, b);
669/// f.ins().ret(sum);
670/// # }
671/// ```
672#[allow(missing_docs)]
673pub trait InsnBuilder<'func>: InsnBuilderBase<'func> {
674    // Mostly follows the order in mir.h.
675
676    // Unary ops.
677    def_simple_insn! {
678        (dst, src);
679
680        mov, fmov, dmov, // ldmov
681
682        ext8, ext16, ext32, uext8, uext16, uext32,
683
684        i2f, i2d, // i2ld
685        ui2f, ui2d, // ui2ld
686        f2i, d2i, // ld2i
687        f2d, d2f, // f2ld, d2ld, ld2f, ld2d
688
689        neg, negs, fneg, dneg, // ldneg
690
691        addr, addr8, addr16, addr32,
692    }
693
694    // Binary ops.
695    def_simple_insn! {
696        (dst, a, b);
697
698        add, adds, fadd, dadd, // ldadd
699        sub, subs, fsub, dsub, // ldsub
700        mul, muls, fmul, dmul, // ldmul
701        div, divs, udiv, udivs, fdiv, ddiv, // lddiv
702        mod_(ffi::MIR_MOD), mods, umod, umods,
703
704        and, ands, or, ors, xor, xors,
705        lsh, lshs, rsh, rshs, ursh, urshs,
706
707        eq, eqs, feq, deq, // ldeq
708        ne, nes, fne, dne, // ldne
709        lt, lts, ult, ults, flt, dlt, // ldlt
710        le, les, ule, ules, fle, dle, // ldle
711        gt, gts, ugt, ugts, fgt, dgt, // ldgt
712        ge, ges, uge, uges, fge, dge, // ldge
713
714        addo, addos, subo, subos, mulo, mulos, umulo, umulos,
715    }
716
717    // Jumps.
718    def_jump_insn!((label); jmp, bo, bno, ubo, ubno);
719    def_simple_insn!((_, label_op); jmpi);
720    def_jump_insn!((label, v); bt, bts, bf, bfs);
721    def_jump_insn! {
722        (label, a, b);
723        beq, beqs, fbeq, dbeq, // ldbeq
724        bne, bnes, fbne, dbne, // ldbne
725        blt, blts, ublt, ublts, fblt, dblt, // ldblt
726        ble, bles, uble, ubles, fble, dble, // ldble
727        bgt, bgts, ubgt, ubgts, fbgt, dbgt, // ldbgt
728        bge, bges, ubge, ubges, fbge, dbge, // ldbge
729    }
730    fn laddr<'o>(self, dst: impl IntoOutOperand<'o>, label: Label<'_>) {
731        build_insn(self, ffi::MIR_LADDR, [dst.into(), label.into()]);
732    }
733
734    // Call and return.
735    def_call_insn!(call, inline, jcall);
736    def_simple_insn!((_, v); ret, jret);
737
738    // Function frame.
739    def_simple_insn!((dst, len); alloca);
740    def_simple_insn!((_, va_list); va_start, va_end);
741    def_simple_insn!((dst, va_list, size, block_type); va_block_arg);
742    fn va_arg<'o>(self, dst: impl IntoOutOperand<'o>, va_list: impl IntoOperand<'o>, mem: MemOp) {
743        build_insn(
744            self,
745            ffi::MIR_VA_ARG,
746            [dst.into(), va_list.into(), mem.into()],
747        );
748    }
749
750    // Label.
751    /// Bind (insert) a previously unbound label to the current location.
752    fn label(self, label: Label<'_>) {
753        unsafe { self.insert(label.0) };
754    }
755    /// Create a new label on the current location (immediately insert).
756    fn new_label(self) -> Label<'func> {
757        let insn = unsafe { ffi::MIR_new_label(self.get_raw_ctx()) };
758        unsafe { self.insert(insn) };
759        Label(insn, PhantomData)
760    }
761}
762
763fn build_insn<'func, 'o>(
764    this: impl InsnBuilderBase<'func>,
765    code: mir_sys::MIR_insn_code_t,
766    ops: impl IntoIterator<Item = Operand<'o>>,
767) {
768    let ctx = this.get_raw_ctx();
769    let ops = ops.into_iter().collect::<SmallVec<[Operand; 6]>>();
770    let insn = unsafe {
771        ffi::MIR_new_insn_arr(
772            ctx,
773            code,
774            ops.len(),
775            ops.as_ptr().cast::<ffi::MIR_op_t>().cast_mut(),
776        )
777    };
778    unsafe { this.insert(insn) };
779}