1use rustc_middle::{
4 mir::{Local, Place, ProjectionElem},
5 ty::{Ty, TyKind},
6};
7use z3::ast::{Ast, Int};
8
9use super::state::{AllocId, Allocation, Provenance, VmState, VmValue, ValueInvariants};
10
11impl<'ctx, 'tcx> VmState<'ctx, 'tcx> {
12 pub fn address_of_place(&mut self, place: &Place<'tcx>) -> Option<VmValue<'ctx, 'tcx>> {
13 self.ensure_local_allocation(place.local);
14
15 let zero = Int::from_u64(self.ctx, 0);
16
17 if place.projection.is_empty() {
18 let base_addr = self.local_address(place.local);
19 let ty = self.body.local_decls[place.local].ty;
20 let provenance = self.locals.get(&place.local)
24 .and_then(|v| v.provenance.clone())
25 .or_else(|| self.local_alloc_ids.get(&place.local).copied()
26 .map(|alloc_id| Provenance { alloc_id, offset: zero, is_field_offset: false }));
27 return Some(VmValue {
28 term: base_addr,
29 ty,
30 provenance,
31 invariants: ValueInvariants::default(),
32 });
33 }
34
35 let mut term = self.local_address(place.local);
36 let mut provenance: Option<Provenance<'ctx>> = self
37 .local_alloc_ids
38 .get(&place.local)
39 .copied()
40 .map(|alloc_id| Provenance {
41 alloc_id,
42 offset: zero.clone(),
43 is_field_offset: false,
44 });
45 let mut current_ty = self.body.local_decls[place.local].ty;
46
47 for proj in place.projection.iter() {
48 let mut handled = false;
49 if let ProjectionElem::Index(local) = proj {
50 if let Some(val) = self.locals.get(&local) {
51 if let Some(idx) = val.term.simplify().as_u64() {
52 let elem_sz = Int::from_u64(self.ctx, self.size_of_ty(current_ty));
53 let scaled = Int::mul(self.ctx, &[&Int::from_u64(self.ctx, idx), &elem_sz]);
54 term = Int::add(self.ctx, &[&term, &scaled]);
55 if let Some(ref mut prov) = provenance {
56 prov.offset = Int::add(self.ctx, &[&prov.offset, &scaled]);
57 }
58 handled = true;
59 }
60 }
61 if !handled {
62 let idx = self.fresh_int("idx");
63 let elem_size = self.size_of_ty(current_ty);
64 let elem_sz = Int::from_u64(self.ctx, elem_size);
65 let scaled = Int::mul(self.ctx, &[&idx, &elem_sz]);
66 term = Int::add(self.ctx, &[&term, &scaled]);
67 if let Some(ref mut prov) = provenance {
68 prov.offset = Int::add(self.ctx, &[&prov.offset, &scaled]);
69 }
70 }
71 continue;
72 }
73 match proj.kind() {
74 ProjectionElem::Field(field_idx, _) => {
75 let field_offset = self.field_offset_in_bytes(current_ty, field_idx.as_usize());
76 let field_off = Int::from_u64(self.ctx, field_offset);
77 term = Int::add(self.ctx, &[&term, &field_off]);
78 if let Some(ref mut prov) = provenance {
79 prov.offset = Int::add(self.ctx, &[&prov.offset, &field_off]);
80 }
81 }
82 ProjectionElem::Deref => {
83 let pointed = self.locals.get(&place.local)?;
84 term = pointed.term.clone();
85 provenance = pointed.provenance.clone();
86 if provenance.is_none()
89 && matches!(pointed.ty.kind(), TyKind::RawPtr(..))
90 {
91 if let Some(field0) = self.field_value(place.local, &[0]) {
92 provenance = field0.provenance.clone();
93 }
94 }
95 if let TyKind::Ref(_, deref_ty, _) = current_ty.kind() {
96 current_ty = *deref_ty;
97 }
98 }
99 _ => {
100 self.notes.push(format!("unsupported projection: {:?}", proj.kind()));
101 return None;
102 }
103 }
104 }
105
106 let ty = place.ty(self.body, self.tcx).ty;
107 Some(VmValue {
108 term,
109 ty,
110 provenance,
111 invariants: ValueInvariants::default(),
112 })
113 }
114
115 pub(crate) fn ensure_local_allocation(&mut self, local: Local) {
117 if self.local_alloc_ids.contains_key(&local) {
118 return;
119 }
120 let ty = self.body.local_decls[local].ty;
121 let align = self.align_of_ty(ty);
122 let base = self.local_address(local);
123 let id = AllocId(self.next_alloc_id);
124 self.next_alloc_id += 1;
125 let (size_term, element_ty, is_external) = match ty.kind() {
133 TyKind::Array(elem, const_len) => {
134 let elem_size = self.size_of_ty(*elem).max(1) as u64;
135 let const_text = format!("Ty({:?}, {:?})", self.tcx.types.usize, const_len);
141 let n_term = match const_len.try_to_target_usize(self.tcx) {
142 Some(v) => Int::from_u64(self.ctx, v),
143 None => {
144 let name = format!("const_{}", const_text.replace([':', '#', ' '], "_"));
145 Int::new_const(self.ctx, name.as_str())
146 }
147 };
148 let size = match n_term.as_u64() {
149 Some(n) => Int::from_u64(self.ctx, n.saturating_mul(elem_size)),
150 None => Int::mul(self.ctx, &[&n_term, &Int::from_u64(self.ctx, elem_size)]),
151 };
152 (size, Some(*elem), false)
153 }
154 _ => {
155 let size = self.size_of_ty(ty).max(1) as u64;
156 (Int::from_u64(self.ctx, size), Some(ty), false)
157 }
158 };
159 let alloc = Allocation {
160 base,
161 size: size_term,
162 align,
163 element_ty,
164 is_external,
165 dead: false,
166 initialized: false,
167 alive_assumed: false,
168 nul_terminated: false,
169 parent: None,
170 slice_data: None,
171 };
172 self.allocations.push(alloc);
173 self.local_alloc_ids.insert(local, id);
174 }
175
176 pub(crate) fn field_offset_in_bytes(&self, ty: Ty<'tcx>, field_idx: usize) -> u64 {
177 crate::helpers::mir_utils::field_offset_in_bytes(self.tcx, self.caller_def_id, ty, field_idx)
178 }
179
180 pub fn size_of_ty(&self, ty: Ty<'tcx>) -> u64 {
181 crate::helpers::mir_utils::layout_of_ty(self.tcx, self.caller_def_id, ty)
182 .map(|l| l.size.bytes())
183 .unwrap_or(0)
184 }
185
186 pub fn align_of_ty(&self, ty: Ty<'tcx>) -> u64 {
187 crate::helpers::mir_utils::layout_of_ty(self.tcx, self.caller_def_id, ty)
188 .map(|l| l.align.abi.bytes())
189 .unwrap_or(1)
190 }
191
192 pub fn alloc_for_local(&self, local: Local) -> Option<AllocId> {
193 self.local_alloc_ids.get(&local).copied()
194 }
195
196 pub fn allocation_size(&self, alloc_id: AllocId) -> Option<&Int<'ctx>> {
197 Some(&self.alloc(alloc_id).size)
198 }
199
200 pub fn allocation_base(&self, alloc_id: AllocId) -> Option<&Int<'ctx>> {
201 Some(&self.alloc(alloc_id).base)
202 }
203
204 pub fn pointee_elem_size(&self, ty: Ty<'tcx>) -> u64 {
207 let inner = match ty.kind() {
208 TyKind::RawPtr(inner_ty, _) | TyKind::Ref(_, inner_ty, _) => *inner_ty,
209 _ => ty,
210 };
211 match inner.kind() {
212 TyKind::Slice(elem) => self.size_of_ty(*elem),
213 _ => self.size_of_ty(inner),
214 }
215 }
216}