1use itertools::Itertools;
14
15use crate::transform::TransformCtx;
16use crate::transform::ctx::{BodyTransformCtx, UllbcPass, UllbcStatementTransformCtx};
17use crate::ullbc_ast::*;
18
19fn new_promoted_global(
22 ctx: &mut UllbcStatementTransformCtx<'_>,
23 value: ConstantExpr,
24 ptr_metadata: ConstantExpr,
25) -> GlobalDeclRef {
26 let krate = &mut ctx.ctx.translated;
27 let id = ItemId::Fun(*ctx.def_id);
28 let mut name = krate.item_names.get(&id).unwrap().clone();
29 let promoted_count = krate
30 .item_names
31 .values()
32 .filter_map(|n| n.name.split_last())
33 .filter(|(last, prefix)| {
34 matches!(last, PathElem::Builtin(BuiltinPathElem::PromotedConst, _))
35 && *prefix == name.name.as_slice()
36 })
37 .count();
38 let disambiguator = Disambiguator::new(promoted_count);
39 name.name.push(PathElem::Builtin(
40 BuiltinPathElem::PromotedConst,
41 disambiguator,
42 ));
43 let ty = value.ty().clone();
44 let id = krate.global_decls.push_with(|def_id| GlobalDecl {
45 def_id,
46 item_meta: ItemMeta::dummy_public(ctx.span, name.clone(), true, ItemOpacity::Transparent),
47 generics: GenericParams::empty(),
48 ty: ty.clone(),
49 size: Size::from_expr(SizeExpr::size_of(&ty)),
50 align: Size::from_expr(SizeExpr::align_of(&ty)),
51 ptr_metadata,
52 src: GlobalSource::Normal,
53 global_kind: GlobalKind::AnonConst,
54 value,
55 });
56 krate.item_names.insert(id.into(), name);
57 GlobalDeclRef {
58 id,
59 generics: Box::new(GenericArgs::empty()),
60 }
61}
62
63fn transform_constant_expr(
70 ctx: &mut UllbcStatementTransformCtx<'_>,
71 mut val: ConstantExpr,
72) -> Operand {
73 let rval = match val.kind() {
74 ConstantExprKind::Global(global_ref) => {
76 return Operand::Copy(Place::new_global(global_ref.clone(), val.ty().clone()));
77 }
78 ConstantExprKind::PtrNoProvenance(ptr) => {
79 let usize_ty = Ty::mk_usize();
80 let ptr_usize = ConstantExprKind::Integer(IntegerValue::Unsigned(UIntTy::Usize, *ptr));
81 let cast = UnOp::Cast(CastKind::RawPtr(val.ty().clone()));
82 Rvalue::UnaryOp(cast, Operand::Const(ConstantExpr::new(ptr_usize, usize_ty)))
83 }
84 cexpr @ (ConstantExprKind::Ref(gplace, metadata)
85 | ConstantExprKind::Ptr(_, gplace, metadata)) => {
86 let rk = cexpr.as_ptr().map(|(rk, _, _)| *rk);
87 let bval_is_sized = gplace.ty.get_ptr_metadata(ctx.get_crate()).is_none();
88
89 let mut projs = vec![];
91 let mut base = gplace;
92 while let GlobalPlaceKind::Projection(sub, proj) = &base.kind {
93 projs.push((proj, &base.ty));
94 base = sub;
95 }
96 let mut place = match &base.kind {
97 GlobalPlaceKind::Global(GlobalProvenance::Global(global_ref))
99 if let Some(decl) = ctx.ctx.translated.global_decls.get(global_ref.id)
100 && matches!(decl.src, GlobalSource::VTableInstance { .. }) =>
101 {
102 let decl_ty = decl.ty.clone().substitute(&global_ref.generics);
103 let vtable = Place::new_global(global_ref.clone(), decl_ty);
104 let vtable_ptr = ctx.raw_borrow_to_new_var(vtable, RefKind::Shared, None);
105 let ptr_ty = TyKind::RawPtr(base.ty.clone(), RefKind::Shared).into_ty();
106 let cast = UnOp::Cast(CastKind::RawPtr(ptr_ty.clone()));
107 let rval = Rvalue::UnaryOp(cast, Operand::Move(vtable_ptr));
108 ctx.rval_to_place(rval, ptr_ty).deref()
109 }
110 GlobalPlaceKind::Global(GlobalProvenance::Global(global_ref)) => {
111 Place::new_global(global_ref.clone(), base.ty.clone())
112 }
113 GlobalPlaceKind::Global(GlobalProvenance::Function(fn_ptr, fn_ptr_ty)) => {
114 let fn_ptr = ConstantExpr::new(
115 ConstantExprKind::FnPtr(fn_ptr.clone()),
116 fn_ptr_ty.clone(),
117 );
118 let fn_ptr = transform_constant_expr(ctx, fn_ptr);
119 let ptr_ty = TyKind::RawPtr(base.ty.clone(), RefKind::Shared).into_ty();
120 let cast = UnOp::Cast(CastKind::RawPtr(ptr_ty.clone()));
121 ctx.rval_to_place(Rvalue::UnaryOp(cast, fn_ptr), ptr_ty)
122 .deref()
123 }
124 GlobalPlaceKind::Global(GlobalProvenance::TypeId(_)) => {
125 let kind = ConstantExprKind::Opaque("pointer into a `TypeId`".into());
126 return Operand::Const(ConstantExpr::new(kind, val.ty().clone()));
127 }
128 GlobalPlaceKind::Projection(..) => unreachable!(),
129 GlobalPlaceKind::Value(bval) if !bval_is_sized => {
131 let meta = match metadata.as_ref().unwrap() {
132 UnsizingMetadata::Length(meta) | UnsizingMetadata::VTable(_, meta) => meta,
133 UnsizingMetadata::VTableUpcast(..) | UnsizingMetadata::Unknown => {
134 unreachable!("unexpected const metadata")
135 }
136 };
137 let global_ref = new_promoted_global(ctx, bval.clone(), meta.clone());
138 Place::new_global(global_ref, bval.ty().clone())
139 }
140 GlobalPlaceKind::Value(bval) => {
141 let bval = transform_constant_expr(ctx, bval.clone());
143
144 let bval_ty = bval.ty().clone();
146 ctx.rval_to_place(Rvalue::Use(bval, WithRetag::No), bval_ty)
147 }
148 };
149 for (proj, proj_ty) in projs.into_iter().rev() {
150 let op = |n: IntegerValue| Box::new(Operand::Const(n.to_constant()));
151 let proj = match *proj {
152 ConstProjectionElem::Field(variant_id, field_id) => {
153 ProjectionElem::Field(variant_id, field_id)
154 }
155 ConstProjectionElem::Index(i) => ProjectionElem::Index {
156 offset: op(i),
157 from_end: false,
158 },
159 ConstProjectionElem::Subslice { from, to } => ProjectionElem::Subslice {
160 from: op(from),
161 to: op(to),
162 from_end: false,
163 },
164 ConstProjectionElem::Offset {
165 count,
166 ref ty,
167 in_bounds,
168 ref pointee_is_sized,
169 } => ctx.offset_projection(
170 *op(count),
171 ty.as_ref(),
172 in_bounds,
173 proj_ty.clone(),
174 pointee_is_sized.clone(),
175 ),
176 };
177 place = place.project(proj, proj_ty.clone());
178 }
179 let mut rval = match (rk, metadata.clone().filter(|_| bval_is_sized)) {
181 (None, None) => ctx.borrow(place, BorrowKind::Shared),
183 (Some(rk), None) => ctx.raw_borrow(place, rk),
184 (None, Some(metadata)) => {
186 let sized_ref = ctx.borrow_to_new_var(place, BorrowKind::Shared, None);
187 Rvalue::UnaryOp(
188 UnOp::Cast(CastKind::Unsize(val.ty().clone(), metadata, vec![])),
189 Operand::Move(sized_ref),
190 )
191 }
192 (Some(rk), Some(metadata)) => {
193 let sized_raw_ref = ctx.raw_borrow_to_new_var(place, rk, None);
194 Rvalue::UnaryOp(
195 UnOp::Cast(CastKind::Unsize(val.ty().clone(), metadata, vec![])),
196 Operand::Move(sized_raw_ref),
197 )
198 }
199 };
200 if let Rvalue::Ref { ptr_metadata, .. } | Rvalue::RawPtr { ptr_metadata, .. } =
202 &mut rval
203 {
204 transform_operand(ctx, ptr_metadata);
205 }
206 rval
207 }
208 ConstantExprKind::Adt(..) if val.ty().is_unit() => {
209 return Operand::Const(val);
211 }
212 ConstantExprKind::Adt(variant, fields) => {
213 let fields = fields
214 .iter()
215 .cloned()
216 .map(|x| transform_constant_expr(ctx, x))
217 .collect();
218
219 let tref = val.ty().kind().as_adt().unwrap();
221 let aggregate_kind = AggregateKind::Adt(tref.clone(), *variant, None);
222 Rvalue::Aggregate(aggregate_kind, fields)
223 }
224 ConstantExprKind::Array(fields)
225 if let TyKind::Array(ty, _, ty_is_sized) = val.ty().kind() =>
226 {
227 let fields = fields
228 .iter()
229 .cloned()
230 .map(|x| transform_constant_expr(ctx, x))
231 .collect_vec();
232 let len = ConstantExpr::mk_usize(fields.len() as u128);
233 Rvalue::Aggregate(
234 AggregateKind::Array(ty.clone(), len, ty_is_sized.clone()),
235 fields,
236 )
237 }
238 ConstantExprKind::Cast(value, kind) => {
239 let value = transform_constant_expr(ctx, value.clone());
240 if let TyKind::Ref(_, pointee, rk) = val.ty().kind() {
241 let raw_ty = TyKind::RawPtr(pointee.clone(), *rk).into_ty();
243 let mut kind = kind.clone();
244 match &mut kind {
245 CastKind::RawPtr(tgt) | CastKind::Unsize(tgt, ..) => *tgt = raw_ty.clone(),
246 _ => unreachable!("unexpected cast to a reference: {kind:?}"),
247 }
248 let raw_ptr = ctx.rval_to_place(Rvalue::UnaryOp(UnOp::Cast(kind), value), raw_ty);
249 ctx.borrow(raw_ptr.deref(), (*rk).into())
250 } else {
251 Rvalue::UnaryOp(UnOp::Cast(kind.clone()), value)
252 }
253 }
254 ConstantExprKind::VTableRef(tref)
255 if let Some(vtable_ref) = tref.vtable_ref(&ctx.ctx.translated)
256 && let TyKind::Ref(_, vtable_ty, _) = val.ty().kind() =>
257 {
258 let inner = GlobalPlace::new_global(vtable_ref.clone(), vtable_ty.clone());
259 val.with_contents_mut(|kind, _| *kind = ConstantExprKind::Ref(inner, None));
260 return transform_constant_expr(ctx, val);
262 }
263 _ => return Operand::Const(val),
264 };
265 Operand::Move(ctx.rval_to_place(rval, val.ty().clone()))
266}
267
268fn transform_operand(ctx: &mut UllbcStatementTransformCtx<'_>, op: &mut Operand) {
269 take_mut::take(op, |op| {
271 if let Operand::Const(val) = op {
272 transform_constant_expr(ctx, val)
273 } else {
274 op
275 }
276 })
277}
278
279pub struct Transform;
280impl UllbcPass for Transform {
281 fn should_run(&self, options: &crate::options::TranslateOptions) -> bool {
282 !options.raw_consts
283 }
284
285 fn transform_function(&self, ctx: &mut TransformCtx, fun_decl: &mut FunDecl) {
286 fun_decl.transform_ullbc_statements(ctx, |ctx, st| {
287 let assigns_const = matches!(
288 st.kind,
289 StatementKind::Assign(_, Rvalue::Use(Operand::Const(_), _))
290 );
291 st.kind
292 .dyn_visit_in_body_mut(|op: &mut Operand| transform_operand(ctx, op));
293 if assigns_const
295 && let StatementKind::Assign(_, rval) = &mut st.kind
296 && let Rvalue::Use(Operand::Move(tmp), _) = rval
297 && let Some(last) = ctx.statements.last()
298 && let StatementKind::Assign(last_dest, last_rval) = &last.kind
299 && last_dest == tmp
300 {
301 *rval = last_rval.clone();
302 ctx.statements.pop();
303 }
304 });
305 fun_decl.transform_ullbc_terminators(ctx, |ctx, term| {
306 term.kind
307 .dyn_visit_in_body_mut(|op: &mut Operand| transform_operand(ctx, op));
308 });
309 if let Some(body) = fun_decl.body.as_unstructured_mut() {
310 for block in body.body.iter_mut() {
311 block.dyn_visit_in_body_mut(|rvalue: &mut Rvalue| {
313 take_mut::take(rvalue, |rvalue| match rvalue {
314 Rvalue::Use(Operand::Const(e), _)
315 if e.kind().is_adt() && e.ty().is_unit() =>
316 {
317 Rvalue::unit_value()
318 }
319 _ => rvalue,
320 });
321 });
322 }
323 }
324 }
325}