Skip to main content

charon_lib/
minirust.rs

1//! Translate a monomorphized ULLBC crate into MiniRust program.
2//!
3//! Unsupported features (we translate this incorrectly):
4//! - Pointers to statics that aren't at offset 0;
5//!
6//! Unsupported features (will raise an error):
7//! - `dyn Trait`;
8//! - Unsized ADTs;
9//! - Packed and overaligned layouts;
10//! - ub_checks/contract_checks/overflow_checks booleans values;
11//! - Unions, because of precise union padding;
12use itertools::Itertools;
13use minirust_rs::{
14    lang::Machine, libspecr::DynWrite, mem::TreeBorrowsMemory, prelude::TerminationInfo,
15};
16use smallvec::{SmallVec, smallvec};
17use std::{io::Write, marker::PhantomData};
18
19use crate::{
20    ast::{from_rustc::LangItem, *},
21    errors::{Error, ErrorContext},
22    formatter::{FmtCtx, IntoFormatter},
23    ids::Generator,
24    pretty::FmtWithCtx,
25    ullbc_ast::{BlockId, START_BLOCK_ID},
26};
27
28mod mini {
29    pub use minirust_rs::lang::*;
30    pub use minirust_rs::libspecr::hidden::GcCow;
31    pub use minirust_rs::libspecr::prelude::*;
32    pub use minirust_rs::libspecr::{Align, Int, Map, Name, Size};
33    pub use minirust_rs::mem::*;
34    pub use minirust_rs::prelude::{Target, x86_64};
35}
36
37type Result<T> = std::result::Result<T, Error>;
38
39macro_rules! raise {
40    ($span:expr, $($fmt:tt)*) => {
41        return Err(Error::new($span, format!($($fmt)*)))
42    };
43}
44
45macro_rules! check {
46    ($span:expr, $condition:expr, $($fmt:tt)*) => {
47        if !$condition {
48            raise!($span, $($fmt)*);
49        }
50    };
51}
52
53mod types;
54
55fn name(index: u32) -> mini::Name {
56    mini::Name::from_internal(index)
57}
58
59fn mini_size(bytes: u64) -> mini::Size {
60    mini::Size::from_bytes_const(bytes)
61}
62
63fn mini_align(span: Span, bytes: u64) -> Result<mini::Align> {
64    mini::Align::from_bytes(bytes)
65        .ok_or("invalid MiniRust alignment")
66        .context(span)
67}
68
69fn mini_int(value: IntegerValue) -> mini::Int {
70    match value {
71        IntegerValue::Unsigned(_, value) => mini::Int::from(value),
72        IntegerValue::Signed(_, value) => mini::Int::from(value),
73    }
74}
75
76fn size_bytes(span: Span, size: mini::Size) -> Result<u64> {
77    let bytes = size
78        .bytes()
79        .try_to_usize()
80        .ok_or("MiniRust size does not fit usize")
81        .context(span)?;
82    u64::try_from(bytes)
83        .map_err(|_| "MiniRust size does not fit u64")
84        .context(span)
85}
86
87fn align_bytes(span: Span, align: mini::Align) -> Result<u64> {
88    let bytes = align
89        .bytes()
90        .try_to_usize()
91        .ok_or("MiniRust alignment does not fit usize")
92        .context(span)?;
93    u64::try_from(bytes)
94        .map_err(|_| "MiniRust alignment does not fit u64")
95        .context(span)
96}
97
98pub fn serialize(krate: &TranslatedCrate, writer: impl Write) -> Result<()> {
99    let program = TranslateCtx::<mini::x86_64>::new(krate)?.translate()?;
100    serde_json::to_writer_pretty(writer, &program)
101        .map_err(|error| format!("serializing MiniRust program: {error}"))
102        .context(Span::dummy())
103}
104
105struct TranslateCtx<'a, T: mini::Target> {
106    krate: &'a TranslatedCrate,
107    fmt: FmtCtx<'a>,
108    target_name: &'a TargetTriple,
109    target: &'a TargetInfo,
110    mini_target: PhantomData<T>,
111}
112
113impl<'a, T: mini::Target> TranslateCtx<'a, T> {
114    fn new(krate: &'a TranslatedCrate) -> Result<Self> {
115        let (target_name, target) = krate
116            .target_information
117            .iter()
118            .exactly_one()
119            .map_err(|_| "MiniRust requires exactly one compilation target")
120            .context(Span::dummy())?;
121        check!(
122            Span::dummy(),
123            target.target_pointer_size == size_bytes(Span::dummy(), T::PTR_SIZE)?
124                && target.is_little_endian == (T::ENDIANNESS == mini::LittleEndian),
125            "the Charon and MiniRust targets do not match"
126        );
127        Ok(Self {
128            krate,
129            fmt: krate.into_fmt(),
130            target_name,
131            target,
132            mini_target: PhantomData,
133        })
134    }
135
136    fn fn_name(&self, id: FunDeclId) -> mini::FnName {
137        mini::FnName(name(id.index() as u32))
138    }
139
140    fn global_name(&self, id: GlobalDeclId) -> mini::GlobalName {
141        mini::GlobalName(name(id.index() as u32))
142    }
143
144    fn local_name(&self, local: LocalId) -> mini::LocalName {
145        mini::LocalName(name(local.index() as u32))
146    }
147
148    fn field_name(&self, field: FieldId) -> mini::Int {
149        mini::Int::from(field.index() as u128)
150    }
151
152    fn block_name(&self, id: BlockId) -> mini::BbName {
153        mini::BbName(name(id.index() as u32))
154    }
155
156    fn translate(&self) -> Result<mini::Program> {
157        let mut functions: mini::Map<mini::FnName, mini::Function> = Default::default();
158        for (id, fdecl) in self.krate.fun_decls.iter_enumerated() {
159            let span = fdecl.item_meta.span;
160            let path = fdecl.item_meta.name.as_slice_uninstantiated();
161            // Recognize the intrinsics used by MiniRust's `minimize` test suite.
162            let intrinsic = match path {
163                [PathElem::Ident(krate, _), PathElem::Ident(item, _)] if krate == "intrinsics" => {
164                    Some(match item.as_str() {
165                        "print" => mini::IntrinsicOp::PrintStdout,
166                        "eprint" => mini::IntrinsicOp::PrintStderr,
167                        "exit" => mini::IntrinsicOp::Exit,
168                        "allocate" => mini::IntrinsicOp::Allocate,
169                        "deallocate" => mini::IntrinsicOp::Deallocate,
170                        "spawn" => mini::IntrinsicOp::Spawn,
171                        "join" => mini::IntrinsicOp::Join,
172                        "create_lock" => mini::IntrinsicOp::Lock(mini::IntrinsicLockOp::Create),
173                        "acquire" => mini::IntrinsicOp::Lock(mini::IntrinsicLockOp::Acquire),
174                        "release" => mini::IntrinsicOp::Lock(mini::IntrinsicLockOp::Release),
175                        "atomic_store" => mini::IntrinsicOp::AtomicStore,
176                        "atomic_load" => mini::IntrinsicOp::AtomicLoad,
177                        "compare_exchange" => mini::IntrinsicOp::AtomicCompareExchange,
178                        "atomic_fetch_add" => {
179                            mini::IntrinsicOp::AtomicFetchAndOp(mini::IntBinOp::Add)
180                        }
181                        "atomic_fetch_sub" => {
182                            mini::IntrinsicOp::AtomicFetchAndOp(mini::IntBinOp::Sub)
183                        }
184                        _ => raise!(span, "unknown MiniRust test intrinsic `{item}`"),
185                    })
186                }
187                _ => None,
188            };
189            let mini_function = if let Some(intrinsic) = intrinsic {
190                self.make_intrinsic_function(span, fdecl, intrinsic)
191            } else {
192                match &fdecl.body {
193                    Body::Unstructured(body) => self.function(span, fdecl, body),
194                    Body::Extern(name) if name == "minirust_print" => {
195                        self.make_intrinsic_function(span, fdecl, mini::IntrinsicOp::PrintStdout)
196                    }
197                    Body::Extern(name) if name == "minirust_start_unwind" => {
198                        self.make_start_unwind_function(span, fdecl)
199                    }
200                    Body::Intrinsic { name, .. } if name == "catch_unwind" => {
201                        self.make_catch_unwind_function(span, fdecl)
202                    }
203                    _ => raise!(
204                        span,
205                        "unable to translate {} to MiniRust",
206                        fdecl.def_id.with_ctx(&self.fmt)
207                    ),
208                }
209            }?;
210            functions.insert(self.fn_name(id), mini_function);
211        }
212
213        let mut globals: mini::Map<mini::GlobalName, mini::Global> = Default::default();
214        for (id, gdecl) in self.krate.global_decls.iter_enumerated() {
215            if matches!(
216                gdecl.global_kind,
217                GlobalKind::Static {
218                    is_thread_local: false,
219                    ..
220                } | GlobalKind::AnonConst
221            ) {
222                let mini_global = self.global(gdecl.item_meta.span, gdecl)?;
223                globals.insert(self.global_name(id), mini_global);
224            }
225        }
226
227        let main = self
228            .krate
229            .fun_decls
230            .iter_enumerated()
231            .find(|(_, fdecl)| {
232                fdecl
233                    .item_meta
234                    .name
235                    .equals_ref_name(&[&self.krate.crate_name, "main"])
236            })
237            .map(|(id, _)| id)
238            .ok_or("MiniRust needs a `main()` function")
239            .context(Span::dummy())?;
240        let main_span = self.krate.fun_decls[main].item_meta.span;
241        let start = self.fn_name(self.krate.fun_decls.next_id());
242        functions.insert(start, self.make_start_function(main_span, main)?);
243
244        Ok(mini::Program {
245            functions,
246            start,
247            globals,
248            // FIXME(minirust): translate vtables
249            traits: Default::default(),
250            vtables: Default::default(),
251        })
252    }
253}
254
255/// Function bodies.
256impl<T: mini::Target> TranslateCtx<'_, T> {
257    /// Make a start function with the right calling convention that just calls into `main()`.
258    fn make_start_function(&self, span: Span, main: FunDeclId) -> Result<mini::Function> {
259        let ret = self.local_name(LocalId::ZERO);
260        let mut block_ids = Generator::new();
261        let start = self.block_name(block_ids.fresh_id());
262        let exit = self.block_name(block_ids.fresh_id());
263        let abort = self.block_name(block_ids.fresh_id());
264        let mut blocks = mini::Map::new();
265        let signature = &self.krate.fun_decls[main].signature;
266        check!(
267            span,
268            signature.inputs.is_empty() && signature.output.is_unit(),
269            "MiniRust output only supports an entry point with signature `fn main()`"
270        );
271        blocks.insert(
272            start,
273            mini::BasicBlock {
274                statements: Default::default(),
275                terminator: mini::Terminator::Call {
276                    callee: self.fn_pointer(main),
277                    calling_convention: mini::CallingConvention::Rust,
278                    arguments: Default::default(),
279                    ret: mini::PlaceExpr::Local(ret),
280                    next_block: Some(exit),
281                    unwind_block: Some(abort),
282                },
283                kind: mini::BbKind::Regular,
284            },
285        );
286        blocks.insert(
287            exit,
288            mini::BasicBlock {
289                statements: Default::default(),
290                terminator: mini::Terminator::Intrinsic {
291                    intrinsic: mini::IntrinsicOp::Exit,
292                    arguments: Default::default(),
293                    ret: mini::PlaceExpr::Local(ret),
294                    next_block: None,
295                },
296                kind: mini::BbKind::Regular,
297            },
298        );
299        blocks.insert(
300            abort,
301            mini::BasicBlock {
302                statements: Default::default(),
303                terminator: mini::Terminator::Intrinsic {
304                    intrinsic: mini::IntrinsicOp::Abort,
305                    arguments: Default::default(),
306                    ret: mini::PlaceExpr::Local(ret),
307                    next_block: None,
308                },
309                kind: mini::BbKind::Catch,
310            },
311        );
312
313        Ok(mini::Function {
314            locals: [(ret, mini::unit_ty())].into_iter().collect(),
315            args: Default::default(),
316            ret,
317            calling_convention: mini::CallingConvention::C,
318            blocks,
319            start,
320            implicit_writes: true,
321        })
322    }
323
324    /// Make a function that invokes the matching MiniRust intrinsic.
325    fn make_intrinsic_function(
326        &self,
327        span: Span,
328        fdecl: &FunDecl,
329        intrinsic: mini::IntrinsicOp,
330    ) -> Result<mini::Function> {
331        let signature = &fdecl.signature;
332
333        let mut local_ids = Generator::new();
334        let ret = self.local_name(local_ids.fresh_id());
335        let arguments: Vec<_> = signature
336            .inputs
337            .iter()
338            .map(|_| self.local_name(local_ids.fresh_id()))
339            .collect();
340        let mut block_ids = Generator::new();
341        let start = self.block_name(block_ids.fresh_id());
342        let return_block = self.block_name(block_ids.fresh_id());
343        let mut blocks = mini::Map::new();
344        blocks.insert(
345            start,
346            mini::BasicBlock {
347                statements: Default::default(),
348                terminator: mini::Terminator::Intrinsic {
349                    intrinsic,
350                    arguments: arguments
351                        .iter()
352                        .map(|argument| mini::ValueExpr::Load {
353                            source: mini::GcCow::new(mini::PlaceExpr::Local(*argument)),
354                        })
355                        .collect(),
356                    ret: mini::PlaceExpr::Local(ret),
357                    next_block: Some(return_block),
358                },
359                kind: mini::BbKind::Regular,
360            },
361        );
362        blocks.insert(
363            return_block,
364            mini::BasicBlock {
365                statements: Default::default(),
366                terminator: mini::Terminator::Return,
367                kind: mini::BbKind::Regular,
368            },
369        );
370
371        Ok(mini::Function {
372            locals: std::iter::once((ret, self.ty(span, &signature.output)?))
373                .chain(
374                    arguments
375                        .iter()
376                        .zip(&signature.inputs)
377                        .map(|(local, ty)| Ok((*local, self.ty(span, ty)?)))
378                        .collect::<Result<Vec<_>>>()?,
379                )
380                .collect(),
381            args: arguments.into_iter().collect(),
382            ret,
383            calling_convention: self.calling_convention(span, &signature.abi)?,
384            blocks,
385            start,
386            implicit_writes: true,
387        })
388    }
389
390    /// Implement `core::intrinsics::catch_unwind` in MiniRust.
391    fn make_catch_unwind_function(&self, span: Span, fdecl: &FunDecl) -> Result<mini::Function> {
392        let signature = &fdecl.signature;
393        check!(
394            span,
395            signature.inputs.len() == 3
396                && matches!(signature.inputs[0].kind(), TyKind::FnPtr(_))
397                && matches!(signature.inputs[1].kind(), TyKind::RawPtr(..))
398                && matches!(signature.inputs[2].kind(), TyKind::FnPtr(_))
399                && signature.output.is_bool(),
400            "unexpected signature for `core::intrinsics::catch_unwind`"
401        );
402
403        let mut local_ids = Generator::new();
404        let ret = self.local_name(local_ids.fresh_id());
405        let try_fn = self.local_name(local_ids.fresh_id());
406        let data = self.local_name(local_ids.fresh_id());
407        let catch_fn = self.local_name(local_ids.fresh_id());
408        let call_ret = self.local_name(local_ids.fresh_id());
409        let payload = self.local_name(local_ids.fresh_id());
410
411        let mut block_ids = Generator::new();
412        let start = self.block_name(block_ids.fresh_id());
413        let returned = self.block_name(block_ids.fresh_id());
414        let get_payload = self.block_name(block_ids.fresh_id());
415        let call_catch = self.block_name(block_ids.fresh_id());
416        let stop_unwind = self.block_name(block_ids.fresh_id());
417        let caught = self.block_name(block_ids.fresh_id());
418        let mut blocks = mini::Map::new();
419
420        let load = |local| mini::ValueExpr::Load {
421            source: mini::GcCow::new(mini::PlaceExpr::Local(local)),
422        };
423        blocks.insert(
424            start,
425            mini::BasicBlock {
426                statements: [
427                    mini::Statement::StorageLive(call_ret),
428                    mini::Statement::StorageLive(payload),
429                ]
430                .into_iter()
431                .collect(),
432                terminator: mini::Terminator::Call {
433                    callee: load(try_fn),
434                    calling_convention: mini::CallingConvention::Rust,
435                    arguments: [mini::ArgumentExpr::ByValue(load(data))]
436                        .into_iter()
437                        .collect(),
438                    ret: mini::PlaceExpr::Local(call_ret),
439                    next_block: Some(returned),
440                    unwind_block: Some(get_payload),
441                },
442                kind: mini::BbKind::Regular,
443            },
444        );
445        blocks.insert(
446            returned,
447            mini::BasicBlock {
448                statements: [mini::Statement::Assign {
449                    destination: mini::PlaceExpr::Local(ret),
450                    source: mini::ValueExpr::Constant(
451                        mini::Constant::Bool(false),
452                        mini::Type::Bool,
453                    ),
454                }]
455                .into_iter()
456                .collect(),
457                terminator: mini::Terminator::Return,
458                kind: mini::BbKind::Regular,
459            },
460        );
461        blocks.insert(
462            get_payload,
463            mini::BasicBlock {
464                statements: Default::default(),
465                terminator: mini::Terminator::Intrinsic {
466                    intrinsic: mini::IntrinsicOp::GetUnwindPayload,
467                    arguments: Default::default(),
468                    ret: mini::PlaceExpr::Local(payload),
469                    next_block: Some(call_catch),
470                },
471                kind: mini::BbKind::Catch,
472            },
473        );
474        blocks.insert(
475            call_catch,
476            mini::BasicBlock {
477                statements: Default::default(),
478                terminator: mini::Terminator::Call {
479                    callee: load(catch_fn),
480                    calling_convention: mini::CallingConvention::Rust,
481                    arguments: [
482                        mini::ArgumentExpr::ByValue(load(data)),
483                        mini::ArgumentExpr::ByValue(load(payload)),
484                    ]
485                    .into_iter()
486                    .collect(),
487                    ret: mini::PlaceExpr::Local(call_ret),
488                    next_block: Some(stop_unwind),
489                    // The intrinsic contract requires this function not to unwind.
490                    unwind_block: None,
491                },
492                kind: mini::BbKind::Catch,
493            },
494        );
495        blocks.insert(
496            stop_unwind,
497            mini::BasicBlock {
498                statements: Default::default(),
499                terminator: mini::Terminator::StopUnwind(caught),
500                kind: mini::BbKind::Catch,
501            },
502        );
503        blocks.insert(
504            caught,
505            mini::BasicBlock {
506                statements: [mini::Statement::Assign {
507                    destination: mini::PlaceExpr::Local(ret),
508                    source: mini::ValueExpr::Constant(mini::Constant::Bool(true), mini::Type::Bool),
509                }]
510                .into_iter()
511                .collect(),
512                terminator: mini::Terminator::Return,
513                kind: mini::BbKind::Regular,
514            },
515        );
516
517        let payload_ty = mini::Type::Ptr(mini::PtrType::Raw {
518            meta_kind: mini::PointerMetaKind::None,
519        });
520        Ok(mini::Function {
521            locals: [
522                (ret, mini::Type::Bool),
523                (try_fn, self.ty(span, &signature.inputs[0])?),
524                (data, self.ty(span, &signature.inputs[1])?),
525                (catch_fn, self.ty(span, &signature.inputs[2])?),
526                (call_ret, mini::unit_ty()),
527                (payload, payload_ty),
528            ]
529            .into_iter()
530            .collect(),
531            args: [try_fn, data, catch_fn].into_iter().collect(),
532            ret,
533            calling_convention: self.calling_convention(span, &signature.abi)?,
534            blocks,
535            start,
536            implicit_writes: true,
537        })
538    }
539
540    /// Give the `minirust_start_unwind` foreign symbol a body that starts unwinding.
541    fn make_start_unwind_function(&self, span: Span, fdecl: &FunDecl) -> Result<mini::Function> {
542        let signature = &fdecl.signature;
543        check!(
544            span,
545            signature.inputs.len() == 1 && matches!(signature.inputs[0].kind(), TyKind::RawPtr(..)),
546            "`minirust_start_unwind` must take a thin raw-pointer payload"
547        );
548
549        let mut local_ids = Generator::new();
550        let ret = self.local_name(local_ids.fresh_id());
551        let payload = self.local_name(local_ids.fresh_id());
552        let mut block_ids = Generator::new();
553        let start = self.block_name(block_ids.fresh_id());
554        let unwind = self.block_name(block_ids.fresh_id());
555        let blocks = [
556            (
557                start,
558                mini::BasicBlock {
559                    statements: Default::default(),
560                    terminator: mini::Terminator::StartUnwind {
561                        unwind_payload: mini::ValueExpr::Load {
562                            source: mini::GcCow::new(mini::PlaceExpr::Local(payload)),
563                        },
564                        unwind_block: unwind,
565                    },
566                    kind: mini::BbKind::Regular,
567                },
568            ),
569            (
570                unwind,
571                mini::BasicBlock {
572                    statements: Default::default(),
573                    terminator: mini::Terminator::ResumeUnwind,
574                    kind: mini::BbKind::Cleanup,
575                },
576            ),
577        ]
578        .into_iter()
579        .collect();
580
581        Ok(mini::Function {
582            locals: [
583                (ret, self.ty(span, &signature.output)?),
584                (payload, self.ty(span, &signature.inputs[0])?),
585            ]
586            .into_iter()
587            .collect(),
588            args: [payload].into_iter().collect(),
589            ret,
590            calling_convention: self.calling_convention(span, &signature.abi)?,
591            blocks,
592            start,
593            implicit_writes: true,
594        })
595    }
596
597    fn function(
598        &self,
599        span: Span,
600        fdecl: &FunDecl,
601        body: &ullbc_ast::ExprBody,
602    ) -> Result<mini::Function> {
603        let locals: mini::Map<mini::LocalName, mini::Type> = body
604            .locals
605            .iter()
606            .map(|local| {
607                Ok((
608                    self.local_name(local.index),
609                    self.ty(local.span, &local.ty)?,
610                ))
611            })
612            .collect::<Result<_>>()?;
613        let args: mini::List<mini::LocalName> = body
614            .locals
615            .arguments()
616            .map(|local| self.local_name(local.index))
617            .collect();
618
619        let mut blocks = mini::Map::new();
620        let mut block_id_gen = Generator::new_with_init_value(body.body.next_idx());
621        for (id, block) in body.body.iter_enumerated() {
622            let kind = if block.is_cleanup {
623                mini::BbKind::Cleanup
624            } else {
625                mini::BbKind::Regular
626            };
627            let mut current_block = self.block_name(id);
628            let mut statements = Vec::new();
629            for statement in &block.statements {
630                match &statement.kind {
631                    // MiniRust keeps the return place and arguments live for the entire call.
632                    ullbc_ast::StatementKind::StorageLive(local)
633                    | ullbc_ast::StatementKind::StorageDead(local)
634                        if body.locals.is_return_or_arg(*local) => {}
635
636                    // These casts are intrinsics for MiniRust.
637                    ullbc_ast::StatementKind::Assign(
638                        destination,
639                        Rvalue::UnaryOp(
640                            UnOp::Cast(
641                                cast @ (CastKind::PtrExposeProvenance(..)
642                                | CastKind::PtrWithExposedProvenance(..)),
643                            ),
644                            operand,
645                        ),
646                    ) => {
647                        let intrinsic = match cast {
648                            CastKind::PtrExposeProvenance(_, target) => {
649                                check!(
650                                    statement.span,
651                                    target.is_usize(),
652                                    "MiniRust only supports exposing pointer provenance to `usize`"
653                                );
654                                mini::IntrinsicOp::PointerExposeProvenance
655                            }
656                            CastKind::PtrWithExposedProvenance(source, _) => {
657                                check!(
658                                    statement.span,
659                                    source.is_usize(),
660                                    "MiniRust only supports creating a pointer from `usize`"
661                                );
662                                mini::IntrinsicOp::PointerWithExposedProvenance
663                            }
664                            _ => unreachable!(),
665                        };
666                        let next_block = self.block_name(block_id_gen.fresh_id());
667                        blocks.insert(
668                            current_block,
669                            mini::BasicBlock {
670                                statements: std::mem::take(&mut statements).into_iter().collect(),
671                                terminator: mini::Terminator::Intrinsic {
672                                    intrinsic,
673                                    arguments: [self.operand(statement.span, operand)?]
674                                        .into_iter()
675                                        .collect(),
676                                    ret: self.place(statement.span, destination)?,
677                                    next_block: Some(next_block),
678                                },
679                                kind,
680                            },
681                        );
682                        current_block = next_block;
683                    }
684
685                    _ => statements.extend(self.statement(statement)?),
686                }
687            }
688            let terminator = self.terminator(
689                &block.terminator,
690                block.is_cleanup,
691                &mut blocks,
692                &mut block_id_gen,
693            )?;
694            blocks.insert(
695                current_block,
696                mini::BasicBlock {
697                    statements: statements.into_iter().collect(),
698                    terminator,
699                    kind,
700                },
701            );
702        }
703
704        Ok(mini::Function {
705            locals,
706            args,
707            ret: self.local_name(body.locals.return_local().index),
708            calling_convention: self.calling_convention(span, &fdecl.signature.abi)?,
709            blocks,
710            start: self.block_name(START_BLOCK_ID),
711            implicit_writes: true,
712        })
713    }
714
715    fn statement(
716        &self,
717        statement: &ullbc_ast::Statement,
718    ) -> Result<SmallVec<[mini::Statement; 1]>> {
719        use ullbc_ast::StatementKind as S;
720        let span = statement.span;
721        Ok(match &statement.kind {
722            S::Assign(place, value) => {
723                let destination = self.place(span, place)?;
724                let mut statements = smallvec![mini::Statement::Assign {
725                    destination,
726                    source: self.rvalue(span, value)?,
727                }];
728                if let Rvalue::Use(_, WithRetag::Yes) = value {
729                    statements.push(mini::Statement::Validate {
730                        place: destination,
731                        fn_entry: false,
732                    });
733                }
734                statements
735            }
736            S::SetDiscriminant(place, variant) => smallvec![mini::Statement::SetDiscriminant {
737                destination: self.place(span, place)?,
738                value: self.variant_discriminant(span, place.ty(), *variant)?,
739            }],
740            S::StorageLive(local) => {
741                smallvec![mini::Statement::StorageLive(self.local_name(*local))]
742            }
743            S::StorageDead(local) => {
744                smallvec![mini::Statement::StorageDead(self.local_name(*local))]
745            }
746            S::PlaceMention(place) => {
747                smallvec![mini::Statement::PlaceMention(self.place(span, place)?)]
748            }
749            S::Borrowck(_) | S::Nop => SmallVec::new(),
750            S::Assert { .. } => {
751                raise!(
752                    span,
753                    "MiniRust is incompatible with `--reconstruct-asserts`"
754                )
755            }
756        })
757    }
758
759    fn terminator(
760        &self,
761        terminator: &ullbc_ast::Terminator,
762        is_cleanup: bool,
763        extra_blocks: &mut mini::Map<mini::BbName, mini::BasicBlock>,
764        block_id_gen: &mut Generator<BlockId>,
765    ) -> Result<mini::Terminator> {
766        use ullbc_ast::TerminatorKind as T;
767        let span = terminator.span;
768        Ok(match &terminator.kind {
769            T::Goto { target } => mini::Terminator::Goto(self.block_name(*target)),
770            T::Switch { data, branches } => {
771                let value = self.switch_value(span, &data.scrutinee)?;
772                let cases = data
773                    .branches
774                    .iter()
775                    .map(|(value, branch)| -> Result<_> {
776                        Ok((
777                            self.constant_int(span, value)?,
778                            self.block_name(branches[*branch]),
779                        ))
780                    })
781                    .try_collect()?;
782                let fallback = if let Some(branch) = data.fallback {
783                    self.block_name(branches[branch])
784                } else {
785                    let name = self.block_name(block_id_gen.fresh_id());
786                    extra_blocks.insert(
787                        name,
788                        mini::BasicBlock {
789                            statements: Default::default(),
790                            terminator: mini::Terminator::Unreachable,
791                            kind: mini::BbKind::Regular,
792                        },
793                    );
794                    name
795                };
796                mini::Terminator::Switch {
797                    value,
798                    cases,
799                    fallback,
800                }
801            }
802            T::Call {
803                call,
804                target,
805                on_unwind,
806            } => mini::Terminator::Call {
807                callee: match &call.func {
808                    FnOperand::Regular(fn_ptr) => match *fn_ptr.kind {
809                        FnPtrKind::Fun(id) => self.fn_pointer(id),
810                        FnPtrKind::Trait(..) => {
811                            raise!(span, "trait-method call remained after monomorphization")
812                        }
813                    },
814                    FnOperand::Dynamic(operand) => self.operand(span, operand)?,
815                },
816                calling_convention: self.callee_convention(span, &call.func)?,
817                arguments: call
818                    .args
819                    .iter()
820                    .map(|arg| {
821                        Ok(match arg {
822                            Operand::Move(place) => {
823                                mini::ArgumentExpr::InPlace(self.place(span, place)?)
824                            }
825                            _ => mini::ArgumentExpr::ByValue(self.operand(span, arg)?),
826                        })
827                    })
828                    .collect::<Result<_>>()?,
829                ret: self.place(span, &call.dest)?,
830                next_block: Some(self.block_name(*target)),
831                unwind_block: Some(self.block_name(*on_unwind)),
832            },
833            T::Assert {
834                assert,
835                target,
836                on_unwind,
837            } => {
838                let condition = self.bool_to_int(self.operand(span, &assert.cond)?);
839                let success = self.block_name(*target);
840                let failure = if is_cleanup {
841                    // A second panic while unwinding follows the pre-existing terminate path.
842                    self.block_name(*on_unwind)
843                } else {
844                    let name = self.block_name(block_id_gen.fresh_id());
845                    extra_blocks.insert(
846                        name,
847                        mini::BasicBlock {
848                            statements: Default::default(),
849                            terminator: self.start_unwind(*on_unwind),
850                            kind: mini::BbKind::Regular,
851                        },
852                    );
853                    name
854                };
855                let (cases, fallback) = if assert.expected {
856                    ([(mini::Int::from(1), success)], failure)
857                } else {
858                    ([(mini::Int::from(0), success)], failure)
859                };
860                mini::Terminator::Switch {
861                    value: condition,
862                    cases: cases.into_iter().collect(),
863                    fallback,
864                }
865            }
866            T::Panic { on_unwind, .. } if is_cleanup => {
867                mini::Terminator::Goto(self.block_name(*on_unwind))
868            }
869            T::Panic { on_unwind, .. } => self.start_unwind(*on_unwind),
870            T::UndefinedBehavior => mini::Terminator::Unreachable,
871            T::UnwindTerminate => mini::Terminator::Intrinsic {
872                intrinsic: mini::IntrinsicOp::Abort,
873                arguments: Default::default(),
874                ret: mini::PlaceExpr::Local(self.local_name(LocalId::ZERO)),
875                next_block: None,
876            },
877            T::Return => mini::Terminator::Return,
878            T::UnwindResume => mini::Terminator::ResumeUnwind,
879            T::Drop { .. } => raise!(span, "MiniRust requires drops to be desugared"),
880            T::InlineAsm { .. } => raise!(span, "MiniRust does not support inline assembly"),
881        })
882    }
883
884    fn start_unwind(&self, on_unwind: BlockId) -> mini::Terminator {
885        mini::Terminator::StartUnwind {
886            // FIXME(minirust): preserve Rust's actual panic payload. For now we use a dangling
887            // pointer.
888            unwind_payload: mini::ValueExpr::Constant(
889                mini::Constant::PointerWithoutProvenance(mini::Int::from(1)),
890                mini::Type::Ptr(mini::PtrType::Raw {
891                    meta_kind: mini::PointerMetaKind::None,
892                }),
893            ),
894            unwind_block: self.block_name(on_unwind),
895        }
896    }
897
898    fn place(&self, span: Span, place: &Place) -> Result<mini::PlaceExpr> {
899        Ok(match &place.kind {
900            PlaceKind::Local(local) => mini::PlaceExpr::Local(self.local_name(*local)),
901            PlaceKind::Global(gref) => mini::PlaceExpr::Deref {
902                operand: mini::GcCow::new(mini::ValueExpr::Constant(
903                    mini::Constant::GlobalPointer(mini::Relocation {
904                        name: self.global_name(gref.id),
905                        offset: mini_size(0),
906                    }),
907                    mini::Type::Ptr(mini::PtrType::Raw {
908                        meta_kind: mini::PointerMetaKind::None,
909                    }),
910                )),
911                ty: self.ty(span, &place.ty)?,
912            },
913            PlaceKind::Projection(subplace, projection) => {
914                let subplace_expr = self.place(span, subplace)?;
915                match projection {
916                    ProjectionElem::Deref => mini::PlaceExpr::Deref {
917                        operand: mini::GcCow::new(mini::ValueExpr::Load {
918                            source: mini::GcCow::new(subplace_expr),
919                        }),
920                        ty: self.ty(span, &place.ty)?,
921                    },
922                    ProjectionElem::Field(variant, field) => {
923                        let subplace_expr = if let Some(variant) = variant {
924                            mini::PlaceExpr::Downcast {
925                                root: mini::GcCow::new(subplace_expr),
926                                discriminant: self.variant_discriminant(
927                                    span,
928                                    subplace.ty(),
929                                    *variant,
930                                )?,
931                            }
932                        } else {
933                            subplace_expr
934                        };
935                        mini::PlaceExpr::Field {
936                            root: mini::GcCow::new(subplace_expr),
937                            field: self.field_name(*field),
938                        }
939                    }
940                    ProjectionElem::Index {
941                        offset,
942                        from_end: false,
943                    } => mini::PlaceExpr::Index {
944                        root: mini::GcCow::new(subplace_expr),
945                        index: mini::GcCow::new(self.operand(span, offset)?),
946                    },
947                    ProjectionElem::Index { from_end: true, .. }
948                    | ProjectionElem::Subslice { .. }
949                    | ProjectionElem::PtrMetadata => {
950                        raise!(
951                            span,
952                            "Failed to translate place to MiniRust: {}",
953                            place.with_ctx(&self.fmt)
954                        )
955                    }
956                }
957            }
958        })
959    }
960
961    fn operand(&self, span: Span, operand: &Operand) -> Result<mini::ValueExpr> {
962        Ok(match operand {
963            Operand::Copy(place) | Operand::Move(place) => mini::ValueExpr::Load {
964                source: mini::GcCow::new(self.place(span, place)?),
965            },
966            Operand::Const(value) => self.constant(span, value)?,
967        })
968    }
969
970    fn switch_value(&self, span: Span, scrutinee: &SwitchScrutinee) -> Result<mini::ValueExpr> {
971        let (value, ty) = match scrutinee {
972            SwitchScrutinee::Value(operand) => (self.operand(span, operand)?, operand.ty()),
973            SwitchScrutinee::Discriminant(place) => (
974                mini::ValueExpr::GetDiscriminant {
975                    place: mini::GcCow::new(self.place(span, place)?),
976                },
977                place.ty(),
978            ),
979        };
980        if matches!(ty.kind(), TyKind::Scalar(ScalarTy::Bool)) {
981            Ok(self.bool_to_int(value))
982        } else {
983            Ok(value)
984        }
985    }
986
987    fn rvalue(&self, span: Span, value: &Rvalue) -> Result<mini::ValueExpr> {
988        Ok(match value {
989            // Retagging is handled at the statement level.
990            Rvalue::Use(operand, _) => self.operand(span, operand)?,
991            Rvalue::Ref { place, kind, .. } => mini::ValueExpr::AddrOf {
992                target: mini::GcCow::new(self.place(span, place)?),
993                ptr_ty: mini::PtrType::Ref {
994                    mutbl: if kind.is_mut() {
995                        mini::Mutability::Mutable
996                    } else {
997                        mini::Mutability::Immutable
998                    },
999                    pointee: self.pointee_info(span, place.ty())?,
1000                },
1001            },
1002            Rvalue::RawPtr { place, .. } => mini::ValueExpr::AddrOf {
1003                target: mini::GcCow::new(self.place(span, place)?),
1004                ptr_ty: mini::PtrType::Raw {
1005                    meta_kind: self.metadata_kind(span, place.ty())?,
1006                },
1007            },
1008            Rvalue::BinaryOp(op, left, right) => self.binop(span, *op, left, right)?,
1009            Rvalue::UnaryOp(op, operand) => self.unop(span, op, operand)?,
1010            Rvalue::NullaryOp(_) => {
1011                // FIXME(minirust): Charon does not translate the session values.
1012                raise!(span, "can't determine which runtime checks are available")
1013            }
1014            Rvalue::Discriminant(place) => mini::ValueExpr::GetDiscriminant {
1015                place: mini::GcCow::new(self.place(span, place)?),
1016            },
1017            Rvalue::Aggregate(kind, operands) => self.aggregate(span, kind, operands)?,
1018            Rvalue::Len(place, _, known_len) => {
1019                if let Some(len) = known_len {
1020                    self.constant(span, len)?
1021                } else {
1022                    let ptr = mini::ValueExpr::AddrOf {
1023                        target: mini::GcCow::new(self.place(span, place)?),
1024                        ptr_ty: mini::PtrType::Raw {
1025                            meta_kind: mini::PointerMetaKind::ElementCount,
1026                        },
1027                    };
1028                    mini::ValueExpr::UnOp {
1029                        operator: mini::UnOp::GetMetadata,
1030                        operand: mini::GcCow::new(ptr),
1031                    }
1032                }
1033            }
1034            Rvalue::Repeat(operand, ty, count, _) => {
1035                let count = count
1036                    .as_usize_literal()
1037                    .ok_or("non-concrete array length")
1038                    .context(span)?;
1039                let value = self.operand(span, operand)?;
1040                mini::ValueExpr::Tuple((0..count).map(|_| value).collect(), self.ty(span, ty)?)
1041            }
1042        })
1043    }
1044
1045    fn binop(
1046        &self,
1047        span: Span,
1048        op: BinOp,
1049        left: &Operand,
1050        right: &Operand,
1051    ) -> Result<mini::ValueExpr> {
1052        let left_value = self.operand(span, left)?;
1053        let mut right_value = self.operand(span, right)?;
1054
1055        let overflowing_op = |mode, regular, unchecked| {
1056            Ok(match mode {
1057                OverflowMode::Wrap => regular,
1058                OverflowMode::UB => unchecked,
1059                OverflowMode::Panic => raise!(
1060                    span,
1061                    "MiniRust translation is incompatible with `--reconstruct-fallible-operations`"
1062                ),
1063            })
1064        };
1065        let operator = match op {
1066            BinOp::BitXor => mini::BinOp::Int(mini::IntBinOp::BitXor),
1067            BinOp::BitAnd => mini::BinOp::Int(mini::IntBinOp::BitAnd),
1068            BinOp::BitOr => mini::BinOp::Int(mini::IntBinOp::BitOr),
1069            BinOp::Eq => mini::BinOp::Rel(mini::RelOp::Eq),
1070            BinOp::Lt => mini::BinOp::Rel(mini::RelOp::Lt),
1071            BinOp::Le => mini::BinOp::Rel(mini::RelOp::Le),
1072            BinOp::Ne => mini::BinOp::Rel(mini::RelOp::Ne),
1073            BinOp::Ge => mini::BinOp::Rel(mini::RelOp::Ge),
1074            BinOp::Gt => mini::BinOp::Rel(mini::RelOp::Gt),
1075            BinOp::AddChecked => mini::BinOp::IntWithOverflow(mini::IntBinOpWithOverflow::Add),
1076            BinOp::SubChecked => mini::BinOp::IntWithOverflow(mini::IntBinOpWithOverflow::Sub),
1077            BinOp::MulChecked => mini::BinOp::IntWithOverflow(mini::IntBinOpWithOverflow::Mul),
1078            BinOp::Add(mode) => mini::BinOp::Int(overflowing_op(
1079                mode,
1080                mini::IntBinOp::Add,
1081                mini::IntBinOp::AddUnchecked,
1082            )?),
1083            BinOp::Sub(mode) => mini::BinOp::Int(overflowing_op(
1084                mode,
1085                mini::IntBinOp::Sub,
1086                mini::IntBinOp::SubUnchecked,
1087            )?),
1088            BinOp::Mul(mode) => mini::BinOp::Int(overflowing_op(
1089                mode,
1090                mini::IntBinOp::Mul,
1091                mini::IntBinOp::MulUnchecked,
1092            )?),
1093            BinOp::Div(_) => mini::BinOp::Int(mini::IntBinOp::Div),
1094            BinOp::Rem(_) => mini::BinOp::Int(mini::IntBinOp::Rem),
1095            BinOp::Shl(mode) => mini::BinOp::Int(overflowing_op(
1096                mode,
1097                mini::IntBinOp::Shl,
1098                mini::IntBinOp::ShlUnchecked,
1099            )?),
1100            BinOp::Shr(mode) => mini::BinOp::Int(overflowing_op(
1101                mode,
1102                mini::IntBinOp::Shr,
1103                mini::IntBinOp::ShrUnchecked,
1104            )?),
1105            BinOp::Cmp => mini::BinOp::Rel(mini::RelOp::Cmp),
1106            BinOp::Offset => {
1107                let pointee = left
1108                    .ty()
1109                    .builtin_deref()
1110                    .ok_or("pointer offset on a non-pointer")
1111                    .context(span)?;
1112                let (size, _) = self.size_and_align(span, pointee)?;
1113                let size = mini::ValueExpr::Constant(
1114                    mini::Constant::Int(mini::Int::from(size)),
1115                    self.ty(span, right.ty())?,
1116                );
1117                right_value = mini::ValueExpr::BinOp {
1118                    operator: mini::BinOp::Int(mini::IntBinOp::MulUnchecked),
1119                    left: mini::GcCow::new(right_value),
1120                    right: mini::GcCow::new(size),
1121                };
1122                mini::BinOp::PtrOffset { inbounds: true }
1123            }
1124        };
1125
1126        if left.ty().is_bool() && matches!(operator, mini::BinOp::Int(_)) {
1127            let value = mini::ValueExpr::BinOp {
1128                operator,
1129                left: mini::GcCow::new(self.bool_to_int(left_value)),
1130                right: mini::GcCow::new(self.bool_to_int(right_value)),
1131            };
1132            Ok(self.int_to_bool(value))
1133        } else {
1134            Ok(mini::ValueExpr::BinOp {
1135                operator,
1136                left: mini::GcCow::new(left_value),
1137                right: mini::GcCow::new(right_value),
1138            })
1139        }
1140    }
1141
1142    fn unop(&self, span: Span, op: &UnOp, operand: &Operand) -> Result<mini::ValueExpr> {
1143        let mut operand_value = self.operand(span, operand)?;
1144        let operator = match op {
1145            UnOp::Not if operand.ty().is_bool() => {
1146                let one = mini::ValueExpr::Constant(
1147                    mini::Constant::Int(mini::Int::from(1)),
1148                    self.ty(span, &Ty::mk_u8())?,
1149                );
1150                let value = mini::ValueExpr::BinOp {
1151                    operator: mini::BinOp::Int(mini::IntBinOp::Sub),
1152                    left: mini::GcCow::new(one),
1153                    right: mini::GcCow::new(self.bool_to_int(operand_value)),
1154                };
1155                return Ok(self.int_to_bool(value));
1156            }
1157            UnOp::Not => mini::UnOp::Int(mini::IntUnOp::BitNot),
1158            UnOp::Neg(_) => mini::UnOp::Int(mini::IntUnOp::Neg),
1159            UnOp::Cast(CastKind::Scalar(source_ty, target_ty)) => {
1160                if matches!(source_ty, ScalarTy::Bool) {
1161                    operand_value = self.bool_to_int(operand_value);
1162                }
1163                mini::UnOp::Cast(mini::CastOp::IntToInt(match *target_ty {
1164                    ScalarTy::Integer(ty) => self.int_type(ty),
1165                    ScalarTy::Char => self.int_type(IntegerTy::Unsigned(UIntTy::U32)),
1166                    ScalarTy::Bool => self.int_type(IntegerTy::Unsigned(UIntTy::U8)),
1167                    ScalarTy::Float(_) => raise!(span, "float unexpected in a scalar cast"),
1168                }))
1169            }
1170            UnOp::Cast(CastKind::Transmute(_, target_ty)) => {
1171                mini::UnOp::Cast(mini::CastOp::Transmute(self.ty(span, target_ty)?))
1172            }
1173            UnOp::Cast(CastKind::RawPtr(source_ty, target_ty)) => {
1174                // MiniRust does not track the type of pointers, so the only effect of this cast is
1175                // on metadata.
1176                let old = self.metadata_kind(
1177                    span,
1178                    source_ty
1179                        .builtin_deref()
1180                        .ok_or("raw-pointer cast from a non-pointer")
1181                        .context(span)?,
1182                )?;
1183                let new = self.metadata_kind(
1184                    span,
1185                    target_ty
1186                        .builtin_deref()
1187                        .ok_or("raw-pointer cast to a non-pointer")
1188                        .context(span)?,
1189                )?;
1190                if old == new {
1191                    return Ok(operand_value);
1192                }
1193                if matches!(new, mini::PointerMetaKind::None) {
1194                    mini::UnOp::GetThinPointer
1195                } else {
1196                    raise!(span, "raw-pointer cast adds metadata")
1197                }
1198            }
1199            UnOp::Cast(CastKind::FnPtr(source_ty, _)) => {
1200                match source_ty.kind() {
1201                    // Turn the function-item ZST into a function pointer.
1202                    TyKind::FnDef(fn_ptr) => {
1203                        let FnPtrKind::Fun(id) = *fn_ptr.skip_binder.kind else {
1204                            raise!(span, "method reference remained after monomorphization")
1205                        };
1206                        return Ok(self.fn_pointer(id));
1207                    }
1208                    // MiniRust function pointer values are untyped.
1209                    TyKind::FnPtr(..) => return Ok(operand_value),
1210                    _ => raise!(
1211                        span,
1212                        "unexpected type for function pointer cast: {}",
1213                        source_ty.with_ctx(&self.fmt)
1214                    ),
1215                }
1216            }
1217            // Handled at the statement level.
1218            UnOp::Cast(
1219                CastKind::PtrExposeProvenance(..) | CastKind::PtrWithExposedProvenance(..),
1220            ) => unreachable!(),
1221            // FIXME(minirust): add vtable support
1222            UnOp::Cast(CastKind::Unsize(..) | CastKind::Concretize(..)) => {
1223                raise!(span, "MiniRust output does not support `dyn Trait`")
1224            }
1225        };
1226        Ok(mini::ValueExpr::UnOp {
1227            operator,
1228            operand: mini::GcCow::new(operand_value),
1229        })
1230    }
1231
1232    fn aggregate(
1233        &self,
1234        span: Span,
1235        kind: &AggregateKind,
1236        operands: &[Operand],
1237    ) -> Result<mini::ValueExpr> {
1238        let values: Vec<_> = operands
1239            .iter()
1240            .map(|operand| self.operand(span, operand))
1241            .try_collect()?;
1242        Ok(match kind {
1243            AggregateKind::Adt(tref, variant, union_field) => {
1244                let ty = self.ty(span, &TyKind::Adt(tref.clone()).into_ty())?;
1245                if let Some(field) = union_field {
1246                    mini::ValueExpr::Union {
1247                        field: self.field_name(*field),
1248                        expr: mini::GcCow::new(values.into_iter().next().unwrap()),
1249                        union_ty: ty,
1250                    }
1251                } else if let Some(variant) = variant {
1252                    let discriminant = self.variant_discriminant_for_tref(span, tref, *variant)?;
1253                    let variant_ty = match &ty {
1254                        mini::Type::Enum { variants, .. } => variants
1255                            .iter()
1256                            .find(|(candidate, _)| *candidate == discriminant)
1257                            .map(|(_, variant)| variant.ty)
1258                            .ok_or("missing MiniRust enum variant")
1259                            .context(span)?,
1260                        _ => raise!(span, "enum aggregate has non-enum type"),
1261                    };
1262                    mini::ValueExpr::Variant {
1263                        discriminant,
1264                        data: mini::GcCow::new(mini::ValueExpr::Tuple(
1265                            values.into_iter().collect(),
1266                            variant_ty,
1267                        )),
1268                        enum_ty: ty,
1269                    }
1270                } else {
1271                    mini::ValueExpr::Tuple(values.into_iter().collect(), ty)
1272                }
1273            }
1274            AggregateKind::Array(element, count, _) => {
1275                let count = count
1276                    .as_usize_literal()
1277                    .ok_or("non-concrete array length")
1278                    .context(span)?;
1279                mini::ValueExpr::Tuple(
1280                    values.into_iter().collect(),
1281                    mini::Type::Array {
1282                        elem: mini::GcCow::new(self.ty(span, element)?),
1283                        count: mini::Int::from(count),
1284                    },
1285                )
1286            }
1287            AggregateKind::RawPtr(pointee, _) => {
1288                check!(
1289                    span,
1290                    values.len() == 2,
1291                    "raw pointer aggregate does not have two fields"
1292                );
1293                let ptr_ty = mini::PtrType::Raw {
1294                    meta_kind: self.metadata_kind(span, pointee)?,
1295                };
1296                mini::ValueExpr::BinOp {
1297                    operator: mini::BinOp::ConstructWidePointer(ptr_ty),
1298                    left: mini::GcCow::new(values[0]),
1299                    right: mini::GcCow::new(values[1]),
1300                }
1301            }
1302        })
1303    }
1304
1305    fn transmute(&self, value: mini::ValueExpr, ty: mini::Type) -> mini::ValueExpr {
1306        mini::ValueExpr::UnOp {
1307            operator: mini::UnOp::Cast(mini::CastOp::Transmute(ty)),
1308            operand: mini::GcCow::new(value),
1309        }
1310    }
1311
1312    fn bool_to_int(&self, value: mini::ValueExpr) -> mini::ValueExpr {
1313        self.transmute(
1314            value,
1315            mini::Type::Int(self.int_type(IntegerTy::Unsigned(UIntTy::U8))),
1316        )
1317    }
1318
1319    fn int_to_bool(&self, value: mini::ValueExpr) -> mini::ValueExpr {
1320        self.transmute(value, mini::Type::Bool)
1321    }
1322
1323    fn fn_pointer(&self, id: FunDeclId) -> mini::ValueExpr {
1324        mini::ValueExpr::Constant(
1325            mini::Constant::FnPointer(self.fn_name(id)),
1326            mini::Type::Ptr(mini::PtrType::FnPtr),
1327        )
1328    }
1329
1330    fn calling_convention(&self, span: Span, abi: &Abi) -> Result<mini::CallingConvention> {
1331        Ok(match abi {
1332            Abi::Rust => mini::CallingConvention::Rust,
1333            Abi::C => mini::CallingConvention::C,
1334            Abi::Other(name) => {
1335                raise!(
1336                    span,
1337                    "calling convention `{name}` is not supported by MiniRust"
1338                )
1339            }
1340        })
1341    }
1342
1343    fn callee_convention(
1344        &self,
1345        span: Span,
1346        operand: &FnOperand,
1347    ) -> Result<mini::CallingConvention> {
1348        let signature = match operand {
1349            FnOperand::Regular(fn_ptr) => match *fn_ptr.kind {
1350                FnPtrKind::Fun(id) => {
1351                    &self
1352                        .krate
1353                        .fun_decls
1354                        .get(id)
1355                        .ok_or_else(|| format!("missing function {}", id.with_ctx(&self.fmt)))
1356                        .context(span)?
1357                        .signature
1358                }
1359                FnPtrKind::Trait(..) => {
1360                    raise!(span, "trait-method call remained after monomorphization")
1361                }
1362            },
1363            FnOperand::Dynamic(operand) => match operand.ty().kind() {
1364                TyKind::FnPtr(signature) => &signature.skip_binder,
1365                _ => raise!(span, "dynamic callee is not a function pointer"),
1366            },
1367        };
1368        self.calling_convention(span, &signature.abi)
1369    }
1370}
1371
1372/// Constants
1373impl<T: mini::Target> TranslateCtx<'_, T> {
1374    pub(super) fn global(&self, span: Span, gdecl: &GlobalDecl) -> Result<mini::Global> {
1375        let ConstantExprKind::RawMemory(memory) = gdecl.value.kind() else {
1376            raise!(
1377                span,
1378                "MiniRust output needs all globals to be evaluated to raw bytes: {}",
1379                gdecl.value.with_ctx(&self.fmt)
1380            )
1381        };
1382        check!(
1383            span,
1384            u64::try_from(memory.len()).context(span)? == self.size(span, &gdecl.size)?,
1385            "global value and layout have different sizes"
1386        );
1387        let mut bytes = mini::List::new();
1388        let mut relocations = mini::List::new();
1389        for (offset, byte) in memory.iter().enumerate() {
1390            match byte {
1391                Byte::Uninit => bytes.push(None),
1392                Byte::Value(value) => bytes.push(Some(*value)),
1393                Byte::Provenance(provenance, pointer_byte) => {
1394                    let name = match provenance {
1395                        Provenance::Global(gref) => self.global_name(gref.id),
1396                        Provenance::Function(_) => {
1397                            raise!(span, "MiniRust globals cannot contain function pointers")
1398                        }
1399                        Provenance::Unknown => {
1400                            raise!(span, "global contains a pointer with unknown provenance")
1401                        }
1402                    };
1403                    // FIXME(minirust): MiniRust does not seem to support pointer fragments
1404                    if *pointer_byte == 0 {
1405                        relocations.push((
1406                            mini_size(u64::try_from(offset).context(span)?),
1407                            mini::Relocation {
1408                                name,
1409                                // FIXME(minirust): Charon's raw bytes do not record the offset
1410                                // within the target allocation.
1411                                offset: mini::Size::ZERO,
1412                            },
1413                        ));
1414                    }
1415                    // MiniRust overwrites these bytes when applying the relocation.
1416                    bytes.push(None);
1417                }
1418            }
1419        }
1420        Ok(mini::Global {
1421            bytes,
1422            relocations,
1423            align: mini_align(span, self.size(span, &gdecl.align)?)?,
1424        })
1425    }
1426
1427    pub(super) fn constant(&self, span: Span, constant: &ConstantExpr) -> Result<mini::ValueExpr> {
1428        let ty = self.ty(span, constant.ty())?;
1429        Ok(match constant.kind() {
1430            ConstantExprKind::Bool(value) => {
1431                mini::ValueExpr::Constant(mini::Constant::Bool(*value), ty)
1432            }
1433            ConstantExprKind::Integer(value) => {
1434                mini::ValueExpr::Constant(mini::Constant::Int(mini_int(*value)), ty)
1435            }
1436            // FIXME(minirust): MiniRust does not check the validity of chars.
1437            ConstantExprKind::Char(_) => raise!(span, "MiniRust does not support the `char` type"),
1438            ConstantExprKind::Adt(variant, fields) => {
1439                let values = fields
1440                    .iter()
1441                    .map(|field| self.constant(span, field))
1442                    .try_collect()?;
1443                if let Some(variant) = variant {
1444                    let tref = constant
1445                        .ty()
1446                        .as_adt()
1447                        .ok_or("enum constant without ADT type")
1448                        .context(span)?;
1449                    let discriminant = self.variant_discriminant_for_tref(span, tref, *variant)?;
1450                    let variant_ty = match &ty {
1451                        mini::Type::Enum { variants, .. } => variants
1452                            .iter()
1453                            .find(|(candidate, _)| *candidate == discriminant)
1454                            .map(|(_, variant)| variant.ty)
1455                            .ok_or("missing enum variant")
1456                            .context(span)?,
1457                        _ => raise!(span, "enum constant translated to non-enum type"),
1458                    };
1459                    mini::ValueExpr::Variant {
1460                        discriminant,
1461                        data: mini::GcCow::new(mini::ValueExpr::Tuple(values, variant_ty)),
1462                        enum_ty: ty,
1463                    }
1464                } else {
1465                    mini::ValueExpr::Tuple(values, ty)
1466                }
1467            }
1468            ConstantExprKind::Array(values) => mini::ValueExpr::Tuple(
1469                values
1470                    .iter()
1471                    .map(|value| self.constant(span, value))
1472                    .try_collect()?,
1473                ty,
1474            ),
1475            ConstantExprKind::FnDef(_) => mini::ValueExpr::Tuple(Default::default(), ty),
1476            ConstantExprKind::FnPtr(fn_ptr) => match *fn_ptr.kind {
1477                FnPtrKind::Fun(id) => self.fn_pointer(id),
1478                FnPtrKind::Trait(..) => {
1479                    raise!(
1480                        span,
1481                        "trait function pointer remained after monomorphization"
1482                    )
1483                }
1484            },
1485            ConstantExprKind::PtrNoProvenance(value) => mini::ValueExpr::Constant(
1486                mini::Constant::PointerWithoutProvenance(mini::Int::from(*value)),
1487                ty,
1488            ),
1489            ConstantExprKind::Discriminant(tref, variant) => mini::ValueExpr::Constant(
1490                mini::Constant::Int(self.variant_discriminant_for_tref(span, tref, *variant)?),
1491                ty,
1492            ),
1493            ConstantExprKind::SizeOf(value) => {
1494                let (size, _) = self.size_and_align(span, value)?;
1495                mini::ValueExpr::Constant(mini::Constant::Int(mini::Int::from(size)), ty)
1496            }
1497            ConstantExprKind::AlignOf(value) => {
1498                let (_, align) = self.size_and_align(span, value)?;
1499                mini::ValueExpr::Constant(mini::Constant::Int(mini::Int::from(align)), ty)
1500            }
1501            _ => raise!(
1502                span,
1503                "unsupported MiniRust constant: {}",
1504                constant.with_ctx(&self.fmt)
1505            ),
1506        })
1507    }
1508
1509    pub(super) fn constant_int(&self, span: Span, constant: &ConstantExpr) -> Result<mini::Int> {
1510        Ok(match self.constant(span, constant)? {
1511            mini::ValueExpr::Constant(c, _) => match c {
1512                mini::Constant::Int(int) => int,
1513                mini::Constant::Bool(b) => mini::Int::from(u128::from(b)),
1514                _ => raise!(
1515                    span,
1516                    "switch case is not an integer constant: {}",
1517                    constant.with_ctx(&self.fmt)
1518                ),
1519            },
1520            _ => raise!(
1521                span,
1522                "switch case is not an integer constant: {}",
1523                constant.with_ctx(&self.fmt)
1524            ),
1525        })
1526    }
1527}
1528
1529pub enum RunError {
1530    Translation(Error),
1531    Panic,
1532    Ub(String),
1533    Other(TerminationInfo),
1534}
1535
1536pub fn run<T>(krate: &TranslatedCrate) -> std::result::Result<(), RunError>
1537where
1538    T: mini::Target + serde::Serialize + serde::de::DeserializeOwned,
1539{
1540    let translator = TranslateCtx::<T>::new(krate).map_err(RunError::Translation)?;
1541    let program = translator.translate().map_err(RunError::Translation)?;
1542    let mut machine = Machine::<TreeBorrowsMemory<T>>::new(
1543        program,
1544        mini::TreeBorrowsParams::default(),
1545        DynWrite::new(std::io::stdout()),
1546        DynWrite::new(std::io::stderr()),
1547    )
1548    .get_internal()
1549    .map_err(|error| {
1550        RunError::Translation(Error::new(
1551            Span::dummy(),
1552            format!("MiniRust rejected the program: {error:?}"),
1553        ))
1554    })?;
1555
1556    loop {
1557        match machine.step().get_internal() {
1558            Ok(()) => {}
1559            Err(TerminationInfo::MachineStop) => return Ok(()),
1560            Err(TerminationInfo::IllFormed(message)) => {
1561                return Err(RunError::Translation(Error::new(
1562                    Span::dummy(),
1563                    format!("MiniRust rejected the program: {message}"),
1564                )));
1565            }
1566            Err(TerminationInfo::Abort) => return Err(RunError::Panic),
1567            Err(TerminationInfo::Ub(message)) => return Err(RunError::Ub(message.to_string())),
1568            Err(error) => return Err(RunError::Other(error)),
1569        }
1570    }
1571}