1use super::*;
4use itertools::Itertools;
5
6pub(super) struct FunctionBuilder {
7 locals: Vec<mini::Type>,
8 arg_count: usize,
9 blocks: Vec<Option<mini::BasicBlock>>,
10 calling_convention: mini::CallingConvention,
11}
12
13impl FunctionBuilder {
14 pub(super) fn new<T: mini::Target>(
15 ctx: &TranslateCtx<'_, T>,
16 span: Span,
17 signature: &FunSig,
18 ) -> Result<Self> {
19 let locals = std::iter::once(&signature.output)
20 .chain(&signature.inputs)
21 .map(|ty| ctx.ty(span, ty))
22 .try_collect()?;
23 Ok(Self {
24 locals,
25 arg_count: signature.inputs.len(),
26 blocks: Vec::new(),
27 calling_convention: ctx.calling_convention(span, &signature.abi)?,
28 })
29 }
30
31 pub(super) fn return_local(&self) -> mini::LocalName {
32 mini::LocalName(name(0))
33 }
34
35 pub(super) fn argument(&self, index: usize) -> mini::LocalName {
36 assert!(index < self.arg_count);
37 mini::LocalName(name((index + 1) as u32))
38 }
39
40 pub(super) fn add_local(&mut self, ty: mini::Type) -> mini::LocalName {
41 let name = mini::LocalName(name(self.locals.len() as u32));
42 self.locals.push(ty);
43 name
44 }
45
46 pub(super) fn declare_block(&mut self) -> mini::BbName {
47 let name = mini::BbName(name(self.blocks.len() as u32));
48 self.blocks.push(None);
49 name
50 }
51
52 pub(super) fn set_block(&mut self, name: mini::BbName, block: mini::BasicBlock) {
53 assert!(
54 self.blocks[name.0.get_internal() as usize]
55 .replace(block)
56 .is_none(),
57 "MiniRust block was defined twice"
58 );
59 }
60
61 pub(super) fn finish(self) -> mini::Function {
62 let arg_retags = (0..self.arg_count)
64 .map(|index| mini::Statement::Validate {
65 place: mini::PlaceExpr::Local(self.argument(index)),
66 fn_entry: true,
67 })
68 .collect_vec();
69 let mut blocks = self
70 .blocks
71 .into_iter()
72 .map(|block| block.expect("MiniRust block was declared but never defined"))
73 .collect_vec();
74 let first_block = &mut blocks[0];
75 first_block.statements = arg_retags
76 .into_iter()
77 .chain(first_block.statements.iter())
78 .collect();
79
80 let mut function = mb::function(mb::Ret::Yes, self.arg_count, &self.locals, &blocks);
81 function.calling_convention = self.calling_convention;
82 function
83 }
84}
85
86pub(super) enum UnwindSource {
87 ExplicitPayload,
88 OpaquePayload,
89}
90
91impl<T: mini::Target> TranslateCtx<'_, T> {
92 pub(super) fn lower_intrinsic(&self, fdecl: &FunDecl) -> Result<Option<mini::Function>> {
94 let span = fdecl.item_meta.span;
95 let function = if let [PathElem::Ident(krate, _), PathElem::Ident(item, _)] =
96 fdecl.item_meta.name.as_slice_uninstantiated()
97 && krate == "intrinsics"
98 {
99 let intrinsic_op = match item.as_str() {
101 "print" => mini::IntrinsicOp::PrintStdout,
102 "eprint" => mini::IntrinsicOp::PrintStderr,
103 "exit" => mini::IntrinsicOp::Exit,
104 "allocate" => mini::IntrinsicOp::Allocate,
105 "deallocate" => mini::IntrinsicOp::Deallocate,
106 "spawn" => mini::IntrinsicOp::Spawn,
107 "join" => mini::IntrinsicOp::Join,
108 "create_lock" => mini::IntrinsicOp::Lock(mini::IntrinsicLockOp::Create),
109 "acquire" => mini::IntrinsicOp::Lock(mini::IntrinsicLockOp::Acquire),
110 "release" => mini::IntrinsicOp::Lock(mini::IntrinsicLockOp::Release),
111 "atomic_store" => mini::IntrinsicOp::AtomicStore,
112 "atomic_load" => mini::IntrinsicOp::AtomicLoad,
113 "compare_exchange" => mini::IntrinsicOp::AtomicCompareExchange,
114 "atomic_fetch_add" => mini::IntrinsicOp::AtomicFetchAndOp(mini::IntBinOp::Add),
115 "atomic_fetch_sub" => mini::IntrinsicOp::AtomicFetchAndOp(mini::IntBinOp::Sub),
116 _ => raise!(span, "unknown MiniRust test intrinsic `{item}`"),
117 };
118 self.make_intrinsic_function(span, fdecl, intrinsic_op)?
119 } else if let Some(lang_item) = &fdecl.item_meta.lang_item {
120 match lang_item {
121 LangItem::PanicFmt | LangItem::PanicImpl => {
122 self.make_unwind_function(span, fdecl, UnwindSource::OpaquePayload)?
123 }
124 _ => return Ok(None),
125 }
126 } else {
127 match &fdecl.body {
128 Body::Extern(name) => match name.as_str() {
129 "minirust_print" => {
130 self.make_intrinsic_function(span, fdecl, mini::IntrinsicOp::PrintStdout)?
131 }
132 "minirust_start_unwind" => {
133 self.make_unwind_function(span, fdecl, UnwindSource::ExplicitPayload)?
134 }
135 _ => return Ok(None),
136 },
137 Body::Intrinsic { name, .. } => match name.as_str() {
138 "abort" => {
139 self.make_intrinsic_function(span, fdecl, mini::IntrinsicOp::Abort)?
140 }
141 "assume" => {
142 self.make_intrinsic_function(span, fdecl, mini::IntrinsicOp::Assume)?
143 }
144 "raw_eq" => {
145 self.make_intrinsic_function(span, fdecl, mini::IntrinsicOp::RawEq)?
146 }
147 "catch_unwind" => self.make_catch_unwind_function(span, fdecl)?,
148 "size_of_val" => self.make_layout_of_val_function(span, fdecl, false)?,
149 "align_of_val" => self.make_layout_of_val_function(span, fdecl, true)?,
150 "cold_path" => self.make_cold_path_function(span, fdecl)?,
151 "arith_offset" => self.make_arith_offset_function(span, fdecl)?,
152 "ptr_offset_from" => self.make_ptr_offset_from_function(span, fdecl, false)?,
153 "ptr_offset_from_unsigned" => {
154 self.make_ptr_offset_from_function(span, fdecl, true)?
155 }
156 _ => return Ok(None),
157 },
158 _ => return Ok(None),
159 }
160 };
161 Ok(Some(function))
162 }
163
164 fn make_intrinsic_function(
166 &self,
167 span: Span,
168 fdecl: &FunDecl,
169 intrinsic: mini::IntrinsicOp,
170 ) -> Result<mini::Function> {
171 let signature = &fdecl.signature;
172 let mut builder = FunctionBuilder::new(self, span, signature)?;
173 let ret = builder.return_local();
174 let arguments: Vec<_> = (0..signature.inputs.len())
175 .map(|index| builder.argument(index))
176 .collect();
177 let start = builder.declare_block();
178 let return_block = builder.declare_block();
179 builder.set_block(
180 start,
181 mb::block(
182 &[],
183 mini::Terminator::Intrinsic {
184 intrinsic,
185 arguments: arguments
186 .iter()
187 .map(|argument| mb::load(mini::PlaceExpr::Local(*argument)))
188 .collect(),
189 ret: mini::PlaceExpr::Local(ret),
190 next_block: Some(return_block),
191 },
192 mini::BbKind::Regular,
193 ),
194 );
195 builder.set_block(
196 return_block,
197 mb::block(&[], mini::Terminator::Return, mini::BbKind::Regular),
198 );
199 Ok(builder.finish())
200 }
201
202 fn make_catch_unwind_function(&self, span: Span, fdecl: &FunDecl) -> Result<mini::Function> {
204 let signature = &fdecl.signature;
205 check!(
206 span,
207 signature.inputs.len() == 3
208 && matches!(signature.inputs[0].kind(), TyKind::FnPtr(_))
209 && matches!(signature.inputs[1].kind(), TyKind::RawPtr(..))
210 && matches!(signature.inputs[2].kind(), TyKind::FnPtr(_))
211 && signature.output.is_bool(),
212 "unexpected signature for `core::intrinsics::catch_unwind`"
213 );
214
215 let mut builder = FunctionBuilder::new(self, span, signature)?;
216 let ret = builder.return_local();
217 let try_fn = builder.argument(0);
218 let data = builder.argument(1);
219 let catch_fn = builder.argument(2);
220 let call_ret = builder.add_local(mini::unit_ty());
221 let payload_ty = mini::Type::Ptr(mini::PtrType::Raw {
222 meta_kind: mini::PointerMetaKind::None,
223 });
224 let payload = builder.add_local(payload_ty);
225
226 let start = builder.declare_block();
227 let returned = builder.declare_block();
228 let get_payload = builder.declare_block();
229 let call_catch = builder.declare_block();
230 let stop_unwind = builder.declare_block();
231 let caught = builder.declare_block();
232 let load = |local| mb::load(mini::PlaceExpr::Local(local));
233 builder.set_block(
234 start,
235 mb::block(
236 &[
237 mini::Statement::StorageLive(call_ret),
238 mini::Statement::StorageLive(payload),
239 ],
240 mini::Terminator::Call {
241 callee: load(try_fn),
242 calling_convention: mini::CallingConvention::Rust,
243 arguments: [mini::ArgumentExpr::ByValue(load(data))]
244 .into_iter()
245 .collect(),
246 ret: mini::PlaceExpr::Local(call_ret),
247 next_block: Some(returned),
248 unwind_block: Some(get_payload),
249 },
250 mini::BbKind::Regular,
251 ),
252 );
253 builder.set_block(
254 returned,
255 mb::block(
256 &[mb::assign(
257 mini::PlaceExpr::Local(ret),
258 mb::const_bool(false),
259 )],
260 mini::Terminator::Return,
261 mini::BbKind::Regular,
262 ),
263 );
264 builder.set_block(
265 get_payload,
266 mb::block(
267 &[],
268 mini::Terminator::Intrinsic {
269 intrinsic: mini::IntrinsicOp::GetUnwindPayload,
270 arguments: Default::default(),
271 ret: mini::PlaceExpr::Local(payload),
272 next_block: Some(call_catch),
273 },
274 mini::BbKind::Catch,
275 ),
276 );
277 builder.set_block(
278 call_catch,
279 mb::block(
280 &[],
281 mini::Terminator::Call {
282 callee: load(catch_fn),
283 calling_convention: mini::CallingConvention::Rust,
284 arguments: [
285 mini::ArgumentExpr::ByValue(load(data)),
286 mini::ArgumentExpr::ByValue(load(payload)),
287 ]
288 .into_iter()
289 .collect(),
290 ret: mini::PlaceExpr::Local(call_ret),
291 next_block: Some(stop_unwind),
292 unwind_block: None,
294 },
295 mini::BbKind::Catch,
296 ),
297 );
298 builder.set_block(
299 stop_unwind,
300 mb::block(
301 &[],
302 mini::Terminator::StopUnwind(caught),
303 mini::BbKind::Catch,
304 ),
305 );
306 builder.set_block(
307 caught,
308 mb::block(
309 &[mb::assign(
310 mini::PlaceExpr::Local(ret),
311 mb::const_bool(true),
312 )],
313 mini::Terminator::Return,
314 mini::BbKind::Regular,
315 ),
316 );
317 Ok(builder.finish())
318 }
319
320 fn make_unwind_function(
322 &self,
323 span: Span,
324 fdecl: &FunDecl,
325 source: UnwindSource,
326 ) -> Result<mini::Function> {
327 let mut builder = FunctionBuilder::new(self, span, &fdecl.signature)?;
328 let arg = builder.argument(0);
329 let start = builder.declare_block();
330 let unwind = builder.declare_block();
331 builder.set_block(
332 start,
333 mb::block(
334 &[],
335 mini::Terminator::StartUnwind {
336 unwind_payload: match source {
337 UnwindSource::ExplicitPayload => mb::load(mini::PlaceExpr::Local(arg)),
338 UnwindSource::OpaquePayload => self.opaque_panic_payload(),
339 },
340 unwind_block: unwind,
341 },
342 mini::BbKind::Regular,
343 ),
344 );
345 builder.set_block(
346 unwind,
347 mb::block(&[], mini::Terminator::ResumeUnwind, mini::BbKind::Cleanup),
348 );
349 Ok(builder.finish())
350 }
351
352 fn make_layout_of_val_function(
354 &self,
355 span: Span,
356 fdecl: &FunDecl,
357 alignment: bool,
358 ) -> Result<mini::Function> {
359 let signature = &fdecl.signature;
360 let [input] = signature.inputs.as_slice() else {
361 raise!(span, "unexpected signature for layout-of-value intrinsic")
362 };
363 let pointee = self.ty(span, input.builtin_deref(self.krate).unwrap())?;
364
365 let mut builder = FunctionBuilder::new(self, span, signature)?;
366 let ret = mini::PlaceExpr::Local(builder.return_local());
367 let arg = mini::PlaceExpr::Local(builder.argument(0));
368 let start = builder.declare_block();
369 let metadata = mb::get_metadata(mb::load(arg));
370 let value = if alignment {
371 mb::compute_align(pointee, metadata)
372 } else {
373 mb::compute_size(pointee, metadata)
374 };
375 builder.set_block(
376 start,
377 mb::block(
378 &[mb::assign(ret, value)],
379 mini::Terminator::Return,
380 mini::BbKind::Regular,
381 ),
382 );
383 Ok(builder.finish())
384 }
385
386 fn make_cold_path_function(&self, span: Span, fdecl: &FunDecl) -> Result<mini::Function> {
388 let signature = &fdecl.signature;
389 let mut builder = FunctionBuilder::new(self, span, signature)?;
390 let ret = mini::PlaceExpr::Local(builder.return_local());
391 let start = builder.declare_block();
392 builder.set_block(
393 start,
394 mb::block(
395 &[mb::assign(ret, mb::unit())],
396 mini::Terminator::Return,
397 mini::BbKind::Regular,
398 ),
399 );
400 Ok(builder.finish())
401 }
402
403 fn make_arith_offset_function(&self, span: Span, fdecl: &FunDecl) -> Result<mini::Function> {
405 let signature = &fdecl.signature;
406 let [pointer, offset] = signature.inputs.as_slice() else {
407 raise!(span, "unexpected signature for `arith_offset`");
408 };
409 let pointee = pointer
410 .builtin_deref(self.krate)
411 .ok_or("expected a pointer")
412 .context(span)?;
413 let (size, _) = self.size_and_align(span, pointee)?;
414
415 let mut builder = FunctionBuilder::new(self, span, signature)?;
416 let ret = mini::PlaceExpr::Local(builder.return_local());
417 let pointer = mb::load(mini::PlaceExpr::Local(builder.argument(0)));
418 let offset_value = mb::load(mini::PlaceExpr::Local(builder.argument(1)));
419 let size = mini::ValueExpr::Constant(
420 mini::Constant::Int(mini::Int::from(size)),
421 self.ty(span, offset)?,
422 );
423 let value = mb::ptr_offset(
424 pointer,
425 mb::mul_unchecked(offset_value, size),
426 mb::InBounds::No,
427 );
428 let start = builder.declare_block();
429 builder.set_block(
430 start,
431 mb::block(
432 &[mb::assign(ret, value)],
433 mini::Terminator::Return,
434 mini::BbKind::Regular,
435 ),
436 );
437 Ok(builder.finish())
438 }
439
440 fn make_ptr_offset_from_function(
442 &self,
443 span: Span,
444 fdecl: &FunDecl,
445 unsigned: bool,
446 ) -> Result<mini::Function> {
447 let signature = &fdecl.signature;
448 let [pointer, _base] = signature.inputs.as_slice() else {
449 raise!(span, "unexpected signature for pointer distance intrinsic");
450 };
451 let pointee = pointer
452 .builtin_deref(self.krate)
453 .ok_or("expected a pointer")
454 .context(span)?;
455 let (size, _) = self.size_and_align(span, pointee)?;
456
457 let mut builder = FunctionBuilder::new(self, span, signature)?;
458 let ret = mini::PlaceExpr::Local(builder.return_local());
459 let pointer = mb::load(mini::PlaceExpr::Local(builder.argument(0)));
460 let base = mb::load(mini::PlaceExpr::Local(builder.argument(1)));
461 let distance = if unsigned {
462 mb::ptr_offset_from_nonneg(pointer, base, mb::InBounds::No)
463 } else {
464 mb::ptr_offset_from(pointer, base, mb::InBounds::No)
465 };
466 let distance = mb::div_exact(
467 distance,
468 mb::const_int_typed::<isize>(mini::Int::from(size)),
469 );
470 let value = if unsigned {
471 mb::int_cast::<usize>(distance)
472 } else {
473 distance
474 };
475 let start = builder.declare_block();
476 builder.set_block(
477 start,
478 mb::block(
479 &[mb::assign(ret, value)],
480 mini::Terminator::Return,
481 mini::BbKind::Regular,
482 ),
483 );
484 Ok(builder.finish())
485 }
486}