1use 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 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 traits: Default::default(),
250 vtables: Default::default(),
251 })
252 }
253}
254
255impl<T: mini::Target> TranslateCtx<'_, T> {
257 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 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 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 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 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 ullbc_ast::StatementKind::StorageLive(local)
633 | ullbc_ast::StatementKind::StorageDead(local)
634 if body.locals.is_return_or_arg(*local) => {}
635
636 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 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 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 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 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 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 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 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 UnOp::Cast(
1219 CastKind::PtrExposeProvenance(..) | CastKind::PtrWithExposedProvenance(..),
1220 ) => unreachable!(),
1221 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
1372impl<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 if *pointer_byte == 0 {
1405 relocations.push((
1406 mini_size(u64::try_from(offset).context(span)?),
1407 mini::Relocation {
1408 name,
1409 offset: mini::Size::ZERO,
1412 },
1413 ));
1414 }
1415 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 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}