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