1use rustc_hir::def_id::DefId;
15use rustc_middle::mir::Operand;
16use rustc_middle::ty::{Ty, TyCtxt};
17
18use super::from_raw_parts_elem_size;
19use super::{CallEffect, CallEffectSummary};
20use crate::helpers::mir_utils::{destination_stride, pointee_alignment, pointee_ty, type_layout};
21use crate::verify::api_classify;
22
23pub(crate) struct EffCtx<'a, 'tcx> {
26 pub tcx: TyCtxt<'tcx>,
27 pub caller: DefId,
28 pub func: &'a Operand<'tcx>,
29 pub dest: Option<rustc_middle::mir::Local>,
30}
31
32struct Entry {
35 matches: fn(Option<DefId>) -> bool,
36 effects: fn(&EffCtx<'_, '_>) -> Vec<CallEffect>,
37}
38
39macro_rules! ED {
41 ($m:expr, $e:ident) => {
42 Entry {
43 matches: $m,
44 effects: $e,
45 }
46 };
47}
48
49static REGISTRY: &[Entry] = &[
50 ED!(api_classify::is_layout_constant, eff_layout_const),
56 ED!(
57 api_classify::is_select_unpredictable,
58 eff_select_unpredictable
59 ),
60 ED!(api_classify::is_vec_alloc_constructor, eff_new_allocation),
67 ED!(api_classify::is_box_alloc_ctor, eff_box_alloc),
68 ED!(api_classify::is_exchange_malloc, eff_exchange_malloc),
69 ED!(api_classify::is_vec_from_box, eff_vec_from_box),
72 ED!(api_classify::is_slice_to_vec, eff_vec_from_box),
76 ED!(
77 api_classify::is_vec_with_capacity,
78 eff_new_allocation_from_cap
79 ),
80 ED!(api_classify::is_into_boxed_slice, eff_box_from_vec),
81 ED!(api_classify::is_vec_push_or_reserve, eff_write_mem),
82 ED!(api_classify::is_max, eff_return_max),
92 ED!(api_classify::is_clamp, eff_return_clamp),
93 ED!(api_classify::is_abs, eff_return_abs),
94 ED!(api_classify::is_neg, eff_return_neg),
95 ED!(api_classify::is_sat_unchecked_add, eff_return_add),
96 ED!(api_classify::is_sat_unchecked_mul, eff_return_mul),
97 ED!(api_classify::is_checked_add, eff_return_option_some_add),
98 ED!(api_classify::is_checked_mul, eff_return_option_some_mul),
99 ED!(api_classify::is_overflowing_abs_neg, eff_overflowing_nz),
100 ED!(api_classify::is_unwrap, eff_alias_arg0),
101 ED!(api_classify::is_element_ptr_add, eff_ptr_add),
104 ED!(api_classify::is_element_ptr_sub, eff_ptr_sub),
105 ED!(api_classify::is_byte_ptr_add, eff_ptr_add_byte),
106 ED!(api_classify::is_byte_ptr_sub, eff_ptr_sub_byte),
107 ED!(api_classify::is_as_ptr_valid, eff_alias_ptr),
112 ED!(api_classify::is_str_as_bytes, eff_alias_arg0),
114 ED!(api_classify::is_len, eff_len),
116 ED!(api_classify::is_capacity, eff_len),
117 ED!(api_classify::is_min_like, eff_cmp_min),
118 ED!(api_classify::is_bit_preserving_nz, eff_return_nonzero_iff),
119 ED!(
120 api_classify::is_checked_nonzero_iff,
121 eff_return_option_some_nonzero_iff
122 ),
123 ED!(
124 api_classify::is_checked_next_pow2,
125 eff_return_option_some_nonzero
126 ),
127 ED!(api_classify::is_slice_get_unchecked, eff_alias_ptr),
129 ED!(
134 api_classify::is_sliceindex_get_unchecked,
135 eff_sliceindex_get_unchecked
136 ),
137 ED!(
139 api_classify::is_ownership_reconstruction,
140 eff_ownership_recon
141 ),
142 ED!(api_classify::is_manually_drop_drop, eff_drop_memory),
144 ED!(api_classify::is_std_drop, eff_drop_memory),
146 ED!(api_classify::is_drop_in_place, eff_drop_memory),
149 ED!(api_classify::is_split_at, eff_split_at),
151 ED!(api_classify::is_from_raw_parts, eff_from_raw_parts),
152 ED!(api_classify::is_align_offset, eff_align_offset),
153 ED!(api_classify::is_cstr_from_ptr, eff_alias_arg0),
154 ED!(api_classify::is_slice_range, eff_slice_range),
155 ED!(api_classify::is_mem_replace, eff_mem_replace),
156 ED!(api_classify::is_layout_align, eff_layout_align),
158 ED!(api_classify::is_align_to_local, eff_align_to),
164 ED!(api_classify::is_iter_position, eff_option_scan_index),
165 ED!(api_classify::is_strlen, eff_scan_length),
166 ED!(api_classify::is_nonnull_checked_new, eff_none),
172 ED!(api_classify::is_nonnull_new_unchecked, eff_new_unchecked),
173 ED!(api_classify::is_maybe_uninit_uninit, eff_none),
174 ED!(api_classify::is_maybe_uninit_assume_init, eff_none),
175 ED!(api_classify::is_maybe_uninit_write, eff_write_mem),
176];
177
178pub(crate) fn is_modeled(callee: Option<DefId>) -> bool {
182 REGISTRY.iter().any(|e| (e.matches)(callee))
183}
184
185pub(crate) fn lookup_effect<'tcx>(
186 tcx: TyCtxt<'tcx>,
187 caller: DefId,
188 callee: Option<DefId>,
189 func: &Operand<'tcx>,
190 destination: rustc_middle::mir::Local,
191) -> Option<CallEffectSummary> {
192 let dest = Some(destination);
193 for e in REGISTRY {
194 if (e.matches)(callee) {
195 let ctx = EffCtx {
196 tcx,
197 caller,
198 func,
199 dest,
200 };
201 return Some(CallEffectSummary {
202 effects: (e.effects)(&ctx),
203 unsupported: false,
204 });
205 }
206 }
207 None
208}
209
210fn eff_none(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
213 Vec::new()
214}
215
216fn eff_select_unpredictable(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
217 vec![CallEffect::SelectUnpredictable]
218}
219
220fn eff_alias_ptr(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
221 let mut eff = vec![CallEffect::ReturnPointerFromArg { arg: 0 }];
222 if pointee_alignment(ctx.tcx, ctx.caller, ctx.dest).is_some() {
223 eff.push(CallEffect::ReturnAligned);
224 }
225 eff
226}
227
228fn eff_sliceindex_get_unchecked(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
233 if !dest_is_pointer(ctx.tcx, ctx.caller, ctx.dest) {
234 return Vec::new();
235 }
236 vec![CallEffect::ReturnPointerAdd {
237 base_arg: 1,
238 offset_arg: 0,
239 stride: destination_stride(ctx.tcx, ctx.caller, ctx.dest),
240 dereferenceable: true,
241 }]
242}
243
244fn eff_alias_arg0(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
245 vec![CallEffect::ReturnAliasArg { arg: 0 }]
246}
247
248fn eff_new_unchecked(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
252 vec![CallEffect::ReturnPointerFromArg { arg: 0 }]
253}
254
255fn eff_ptr_add(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
256 eff_ptr_arith(ctx, PtrDirection::Add, PtrGranularity::Element)
257}
258
259fn eff_ptr_sub(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
260 eff_ptr_arith(ctx, PtrDirection::Sub, PtrGranularity::Element)
261}
262
263fn eff_ptr_add_byte(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
264 eff_ptr_arith(ctx, PtrDirection::Add, PtrGranularity::Byte)
265}
266
267fn eff_ptr_sub_byte(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
268 eff_ptr_arith(ctx, PtrDirection::Sub, PtrGranularity::Byte)
269}
270
271enum PtrDirection {
272 Add,
273 Sub,
274}
275
276enum PtrGranularity {
277 Element,
278 Byte,
279}
280
281fn eff_ptr_arith(
288 ctx: &EffCtx<'_, '_>,
289 dir: PtrDirection,
290 granularity: PtrGranularity,
291) -> Vec<CallEffect> {
292 if !dest_is_pointer(ctx.tcx, ctx.caller, ctx.dest) {
293 return Vec::new();
294 }
295 let stride = match granularity {
296 PtrGranularity::Byte => Some(1),
297 PtrGranularity::Element => destination_stride(ctx.tcx, ctx.caller, ctx.dest),
298 };
299 let effect = match dir {
300 PtrDirection::Sub => CallEffect::ReturnPointerSub {
301 base_arg: 0,
302 offset_arg: 1,
303 stride,
304 },
305 PtrDirection::Add => CallEffect::ReturnPointerAdd {
306 base_arg: 0,
307 offset_arg: 1,
308 stride,
309 dereferenceable: false,
310 },
311 };
312 vec![effect]
313}
314
315fn eff_write_mem(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
316 vec![CallEffect::WriteMemory { pointer_arg: 0 }]
317}
318
319fn eff_len(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
320 vec![CallEffect::ReturnLengthOfArg { arg: 0 }]
321}
322
323fn eff_cmp_min(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
324 vec![CallEffect::ReturnMin {
325 lhs_arg: 0,
326 rhs_arg: 1,
327 }]
328}
329
330fn eff_return_nonzero_iff(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
331 vec![CallEffect::ReturnNonZeroIff { arg: 0 }]
332}
333
334fn eff_return_option_some_nonzero_iff(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
335 vec![CallEffect::ReturnOptionSomeNonZeroIff { arg: 0 }]
336}
337
338fn eff_return_option_some_nonzero(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
339 vec![CallEffect::ReturnOptionSomeNonZero]
340}
341
342fn eff_return_max(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
343 vec![CallEffect::ReturnMax {
344 lhs_arg: 0,
345 rhs_arg: 1,
346 }]
347}
348
349fn eff_return_clamp(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
350 vec![CallEffect::ReturnClamp {
351 value_arg: 0,
352 min_arg: 1,
353 max_arg: 2,
354 }]
355}
356
357fn eff_return_abs(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
358 vec![CallEffect::ReturnAbs { arg: 0 }]
359}
360
361fn eff_return_neg(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
362 vec![CallEffect::ReturnNeg { arg: 0 }]
363}
364
365fn eff_return_add(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
366 vec![CallEffect::ReturnAdd {
367 lhs_arg: 0,
368 rhs_arg: 1,
369 }]
370}
371
372fn eff_return_mul(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
373 vec![CallEffect::ReturnMul {
374 lhs_arg: 0,
375 rhs_arg: 1,
376 }]
377}
378
379fn eff_return_option_some_add(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
380 vec![CallEffect::ReturnOptionSomeAdd {
381 lhs_arg: 0,
382 rhs_arg: 1,
383 }]
384}
385
386fn eff_return_option_some_mul(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
387 vec![CallEffect::ReturnOptionSomeMul {
388 lhs_arg: 0,
389 rhs_arg: 1,
390 }]
391}
392
393fn eff_overflowing_nz(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
394 vec![CallEffect::ReturnTupleFieldNonZero { field: 0 }]
395}
396
397fn eff_ownership_recon(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
398 vec![
399 CallEffect::ReturnAliasArg { arg: 0 },
400 CallEffect::ReturnNonZero,
401 CallEffect::OwnsInitMemory { arg: 0 },
402 ]
403}
404
405fn eff_drop_memory(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
406 vec![CallEffect::DropMemory { pointer_arg: 0 }]
407}
408
409fn eff_align_to(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
410 vec![CallEffect::ReturnAlignTo { receiver_arg: 0 }]
411}
412
413fn eff_option_scan_index(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
414 vec![CallEffect::ReturnOptionSomeScanIndex { self_arg: 0 }]
415}
416
417fn eff_scan_length(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
418 vec![CallEffect::ReturnScanLength]
419}
420
421fn eff_align_offset(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
422 vec![CallEffect::ReturnAlignOffset {
423 ptr_arg: 0,
424 align_arg: 1,
425 }]
426}
427
428fn eff_split_at(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
429 vec![
430 CallEffect::ReturnAliasArg { arg: 0 },
431 CallEffect::ReturnTupleFieldLength {
432 field: 0,
433 from_arg: 1,
434 },
435 ]
436}
437
438fn eff_slice_range(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
439 vec![CallEffect::ReturnRange { bounds_arg: 1 }]
440}
441
442fn eff_mem_replace(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
443 vec![CallEffect::ReturnDerefArg { arg: 0 }]
444}
445
446fn eff_from_raw_parts(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
447 let elem = from_raw_parts_elem_size(ctx.tcx, ctx.caller, ctx.dest);
448 let mut eff = vec![
449 CallEffect::ReturnAliasArg { arg: 0 },
454 CallEffect::ReturnFreshAllocation {
457 pointer_arg: 0,
458 size_arg: 1,
459 elem_size: elem,
460 },
461 CallEffect::ReturnNonZero,
462 ];
463 if pointee_alignment(ctx.tcx, ctx.caller, ctx.dest).is_some() {
464 eff.push(CallEffect::ReturnAligned);
465 }
466 eff
467}
468
469fn eff_new_allocation(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
470 let elem = from_raw_parts_elem_size(ctx.tcx, ctx.caller, ctx.dest);
471 vec![CallEffect::ReturnNewAllocation {
472 size_arg: 1,
473 elem_size: elem,
474 }]
475}
476
477fn eff_box_alloc(_ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
478 vec![CallEffect::ReturnBoxAllocation]
479}
480
481fn eff_exchange_malloc(_ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
482 vec![CallEffect::ReturnExchangeMalloc { size_arg: 0 }]
483}
484
485fn eff_new_allocation_from_cap(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
486 let elem = from_raw_parts_elem_size(ctx.tcx, ctx.caller, ctx.dest);
487 vec![CallEffect::ReturnNewAllocationFromCap {
488 cap_arg: 0,
489 elem_size: elem,
490 }]
491}
492
493fn eff_vec_from_box(_ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
494 vec![CallEffect::ReturnNewAllocationFromBox]
495}
496
497fn eff_layout_align(_ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
498 vec![CallEffect::ReturnPowerOfTwo]
499}
500
501fn eff_layout_const(ctx: &EffCtx<'_, '_>) -> Vec<CallEffect> {
502 layout_constant_effect(ctx.tcx, ctx.caller, ctx.func)
503 .into_iter()
504 .collect()
505}
506
507fn dest_is_pointer(tcx: TyCtxt<'_>, caller: DefId, dest: Option<rustc_middle::mir::Local>) -> bool {
510 let Some(d) = dest else { return false };
511 pointee_ty(tcx.optimized_mir(caller).local_decls[d].ty).is_some()
512}
513
514fn layout_call_ty<'tcx>(func: &Operand<'tcx>) -> Option<Ty<'tcx>> {
515 crate::helpers::mir_utils::fn_def_first_type_arg(func)
516}
517
518fn layout_constant_effect<'tcx>(
519 tcx: TyCtxt<'tcx>,
520 caller: DefId,
521 func: &Operand<'tcx>,
522) -> Option<CallEffect> {
523 let ty = layout_call_ty(func)?;
524 let (align, size) = type_layout(tcx, caller, ty)?;
525 if align == 0 && size == 0 {
530 return None;
531 }
532 let Some(callee) = crate::helpers::mir_utils::dep_callee_def_id(func) else {
533 return None;
534 };
535 if crate::def_id::contains(
536 &[
537 crate::def_id::mem_align_of(),
538 crate::def_id::intrinsics_align_of(),
539 ],
540 callee,
541 ) {
542 Some(CallEffect::ReturnConst { value: align })
543 } else if crate::def_id::contains(
544 &[
545 crate::def_id::mem_size_of(),
546 crate::def_id::intrinsics_size_of(),
547 ],
548 callee,
549 ) {
550 Some(CallEffect::ReturnConst { value: size })
551 } else {
552 None
553 }
554}
555
556fn eff_box_from_vec(_: &EffCtx<'_, '_>) -> Vec<CallEffect> {
557 vec![CallEffect::ReturnBoxFromVec { arg: 0 }]
558}