1use crate::ast::*;
2use crate::errors::{ErrorCtx, Level};
3use crate::formatter::{FmtCtx, IntoFormatter};
4use crate::llbc_ast;
5use crate::options::TranslateOptions;
6use crate::pretty::FmtWithCtx;
7use crate::ullbc_ast;
8use std::cell::RefCell;
9use std::{fmt, mem};
10
11pub struct TransformCtx {
14 pub options: TranslateOptions,
16 pub translated: TranslatedCrate,
18 pub errors: RefCell<ErrorCtx>,
20}
21
22pub trait UllbcPass: Sync {
24 fn should_run(&self, _options: &TranslateOptions) -> bool {
26 true
27 }
28
29 fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut ullbc_ast::ExprBody) {}
31
32 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
34 if let Some(body) = decl.body.as_unstructured_mut() {
35 self.transform_body(ctx, body)
36 }
37 }
38
39 fn transform_ctx(&self, ctx: &mut TransformCtx) {
41 ctx.for_each_fun_decl(|ctx, decl| {
42 self.log_before_body(ctx, &decl.item_meta.name, &decl.body);
43 self.transform_function(ctx, decl);
44 });
45 }
46
47 fn name(&self) -> &str {
50 std::any::type_name::<Self>()
51 }
52
53 fn log_before_body(&self, ctx: &TransformCtx, name: &Name, body: &Body) {
55 let fmt_ctx = &ctx.into_fmt();
56 let body_str = body.to_string_with_ctx(fmt_ctx);
57 trace!(
58 "# About to run pass [{}] on `{}`:\n{}",
59 self.name(),
60 name.with_ctx(fmt_ctx),
61 body_str,
62 );
63 }
64}
65
66pub trait LlbcPass: Sync {
68 fn should_run(&self, _options: &TranslateOptions) -> bool {
70 true
71 }
72
73 fn transform_body(&self, _ctx: &mut TransformCtx, _body: &mut llbc_ast::ExprBody) {}
75
76 fn transform_function(&self, ctx: &mut TransformCtx, decl: &mut FunDecl) {
78 if let Some(body) = decl.body.as_structured_mut() {
79 self.transform_body(ctx, body)
80 }
81 }
82
83 fn transform_ctx(&self, ctx: &mut TransformCtx) {
85 ctx.for_each_fun_decl(|ctx, decl| {
86 self.log_before_body(ctx, &decl.item_meta.name, &decl.body);
87 self.transform_function(ctx, decl);
88 });
89 }
90
91 fn name(&self) -> &str {
94 std::any::type_name::<Self>()
95 }
96
97 fn log_before_body(&self, ctx: &TransformCtx, name: &Name, body: &Body) {
99 let fmt_ctx = &ctx.into_fmt();
100 let body_str = body.to_string_with_ctx(fmt_ctx);
101 trace!(
102 "# About to run pass [{}] on `{}`:\n{}",
103 self.name(),
104 name.with_ctx(fmt_ctx),
105 body_str,
106 );
107 }
108}
109
110pub trait TransformPass: Sync {
112 fn should_run(&self, _options: &TranslateOptions) -> bool {
114 true
115 }
116
117 fn transform_ctx(&self, ctx: &mut TransformCtx);
118
119 fn name(&self) -> &str {
122 std::any::type_name::<Self>()
123 }
124}
125
126impl<'ctx> TransformCtx {
127 pub(crate) fn has_errors(&self) -> bool {
128 self.errors.borrow().has_errors()
129 }
130
131 pub(crate) fn span_err(&self, span: Span, msg: &str, level: Level) -> Error {
133 self.errors
134 .borrow_mut()
135 .span_err(&self.translated, span, msg, level)
136 }
137
138 pub(crate) fn opacity_for_name(&self, name: &Name) -> ItemOpacity {
139 self.options.opacity_for_name(&self.translated, name)
140 }
141
142 pub(crate) fn with_def_id<F, T>(
143 &mut self,
144 def_id: impl Into<ItemId>,
145 def_id_is_local: bool,
146 f: F,
147 ) -> T
148 where
149 F: FnOnce(&mut Self) -> T,
150 {
151 let mut errors = self.errors.borrow_mut();
152 let current_def_id = mem::replace(&mut errors.def_id, Some(def_id.into()));
153 let current_def_id_is_local = mem::replace(&mut errors.def_id_is_local, def_id_is_local);
154 drop(errors); let ret = f(self);
156 let mut errors = self.errors.borrow_mut();
157 errors.def_id = current_def_id;
158 errors.def_id_is_local = current_def_id_is_local;
159 ret
160 }
161
162 pub(crate) fn for_each_body(&mut self, mut f: impl FnMut(&mut Self, &mut Body)) {
166 let fn_ids = self.translated.fun_decls.all_indices();
167 for id in fn_ids {
168 if let Some(decl) = self.translated.fun_decls.get_mut(id) {
169 if decl.body.has_contents() {
170 let mut body = mem::replace(&mut decl.body, Body::Opaque);
171 let fun_decl_id = decl.def_id;
172 let is_local = decl.item_meta.is_local;
173 self.with_def_id(fun_decl_id, is_local, |ctx| f(ctx, &mut body));
174 self.translated.fun_decls[id].body = body;
175 }
176 }
177 }
178 }
179
180 pub(crate) fn for_each_fun_decl(&mut self, mut f: impl FnMut(&mut Self, &mut FunDecl)) {
183 let fn_ids = self.translated.fun_decls.all_indices();
184 for id in fn_ids {
185 if let Some(mut decl) = self.translated.fun_decls.remove(id) {
186 let fun_decl_id = decl.def_id;
187 let is_local = decl.item_meta.is_local;
188 self.with_def_id(fun_decl_id, is_local, |ctx| f(ctx, &mut decl));
189 self.translated.fun_decls.set_slot(id, decl);
190 }
191 }
192 }
193
194 pub fn for_each_item_mut(&mut self, mut f: impl for<'a> FnMut(&'a mut Self, ItemRefMut<'a>)) {
197 for id in self.translated.all_ids() {
198 if let Some(mut decl) = self.translated.remove_item(id) {
199 f(self, decl.as_mut());
200 self.translated.set_item_slot(id, decl);
201 }
202 }
203 }
204}
205
206impl<'a> IntoFormatter for &'a TransformCtx {
207 type C = FmtCtx<'a>;
208
209 fn into_fmt(self) -> Self::C {
210 self.translated.into_fmt()
211 }
212}
213
214impl fmt::Display for TransformCtx {
215 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216 self.translated.fmt(f)
217 }
218}
219
220pub trait BodyTransformCtx: Sized {
222 fn get_crate(&self) -> &TranslatedCrate;
223 fn get_options(&self) -> &TranslateOptions;
224 fn get_params(&self) -> &GenericParams;
225 fn get_locals_mut(&mut self) -> &mut Locals;
226
227 fn insert_storage_live_stmt(&mut self, local: LocalId);
228 fn insert_storage_dead_stmt(&mut self, local: LocalId);
229 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue);
230
231 fn into_fmt(&self) -> FmtCtx<'_> {
232 self.get_crate().into_fmt()
233 }
234
235 fn fresh_var(&mut self, name: Option<String>, ty: Ty) -> Place {
237 let var = self.get_locals_mut().new_var(name, ty);
238 self.insert_storage_live_stmt(var.local_id().unwrap());
239 var
240 }
241
242 fn rval_to_place(&mut self, rvalue: Rvalue, ty: Ty) -> Place {
245 if let Rvalue::Use(Operand::Move(place)) = rvalue {
246 place
247 } else {
248 let var = self.fresh_var(None, ty);
249 self.insert_assn_stmt(var.clone(), rvalue);
250 var
251 }
252 }
253
254 fn compute_subslice_end_idx(
260 &mut self,
261 len_place: &Place,
262 last_arg: Operand,
263 from_end: bool,
264 ) -> Operand {
265 if from_end {
266 let len_var = self.fresh_var(None, Ty::mk_usize());
269 let len = match len_place.ty().kind() {
270 TyKind::Array(_, len) => Some(len.clone()),
271 TyKind::Slice(_) => None,
272 _ => panic!(
273 "called `compute_subslice_end_idx` on something that isn't an array or slice: {:?}",
274 len_place.ty()
275 ),
276 };
277 self.insert_assn_stmt(
278 len_var.clone(),
279 Rvalue::Len(len_place.clone(), len_place.ty().clone(), len),
280 );
281
282 let index_var = self.fresh_var(None, Ty::mk_usize());
286 self.insert_assn_stmt(
287 index_var.clone(),
288 Rvalue::BinaryOp(
289 BinOp::Sub(OverflowMode::UB),
290 Operand::Copy(len_var.clone()),
291 last_arg,
292 ),
293 );
294 self.insert_storage_dead_stmt(len_var.local_id().unwrap());
295 Operand::Copy(index_var)
296 } else {
297 last_arg
298 }
299 }
300
301 fn is_sized_type_var(&mut self, ty: &Ty) -> bool {
302 match ty.kind() {
303 TyKind::TypeVar(..) => {
304 if self.get_options().hide_marker_traits {
305 return true;
307 }
308 let params = self.get_params();
309 for clause in ¶ms.trait_clauses {
310 let tref = clause.trait_.clone().erase();
311 if tref.generics.types[0] == *ty
313 && self
314 .get_crate()
315 .trait_decls
316 .get(tref.id)
317 .and_then(|decl| decl.item_meta.lang_item.clone())
318 == Some("sized".into())
319 {
320 return true;
321 }
322 }
323 false
324 }
325 _ => false,
326 }
327 }
328
329 fn compute_place_metadata(&mut self, place: &Place) -> Operand {
344 fn no_metadata<T: BodyTransformCtx>(ctx: &T) -> Operand {
347 let unit_meta = ctx.get_crate().unit_metadata.clone().unwrap();
348 Operand::Copy(Place::new_global(unit_meta, Ty::mk_unit()))
349 }
350
351 fn compute_place_metadata_inner<T: BodyTransformCtx>(
353 ctx: &mut T,
354 place: &Place,
355 metadata_ty: &Ty,
356 ) -> Option<Operand> {
357 let (subplace, proj) = place.as_projection()?;
358 match proj {
359 ProjectionElem::Deref => {
361 let metadata_place = subplace
362 .clone()
363 .project(ProjectionElem::PtrMetadata, metadata_ty.clone());
364 Some(Operand::Copy(metadata_place))
365 }
366 ProjectionElem::Field { .. } => {
367 compute_place_metadata_inner(ctx, subplace, metadata_ty)
368 }
369 ProjectionElem::Index { .. } => None,
371 ProjectionElem::PtrMetadata { .. } => None,
373 ProjectionElem::Subslice { from, to, from_end } => {
375 let to_idx = ctx.compute_subslice_end_idx(subplace, *to.clone(), *from_end);
376 let diff_place = ctx.fresh_var(None, Ty::mk_usize());
377 ctx.insert_assn_stmt(
378 diff_place.clone(),
379 Rvalue::BinaryOp(BinOp::Sub(OverflowMode::UB), to_idx, *from.clone()),
381 );
382 Some(Operand::Copy(diff_place))
383 }
384 }
385 }
386 trace!(
387 "getting ptr metadata for place: {}",
388 place.with_ctx(&self.into_fmt())
389 );
390 let metadata_ty = place.ty().get_ptr_metadata(&self.get_crate()).into_type();
391 if metadata_ty.is_unit()
392 || matches!(metadata_ty.kind(), TyKind::PtrMetadata(ty) if self.is_sized_type_var(ty))
393 {
394 return no_metadata(self);
396 }
397 trace!(
398 "computed metadata type: {}",
399 metadata_ty.with_ctx(&self.into_fmt())
400 );
401 compute_place_metadata_inner(self, place, &metadata_ty).unwrap_or_else(|| no_metadata(self))
402 }
403
404 fn borrow(&mut self, place: Place, kind: BorrowKind) -> Rvalue {
406 let ptr_metadata = self.compute_place_metadata(&place);
407 Rvalue::Ref {
408 place,
409 kind,
410 ptr_metadata,
411 }
412 }
413 fn raw_borrow(&mut self, place: Place, kind: RefKind) -> Rvalue {
415 let ptr_metadata = self.compute_place_metadata(&place);
416 Rvalue::RawPtr {
417 place,
418 kind,
419 ptr_metadata,
420 }
421 }
422
423 fn borrow_to_new_var(&mut self, place: Place, kind: BorrowKind, name: Option<String>) -> Place {
425 let ref_ty = TyKind::Ref(Region::Erased, place.ty().clone(), kind.into()).into_ty();
426 let target_place = self.fresh_var(name, ref_ty);
427 let rvalue = self.borrow(place, kind);
428 self.insert_assn_stmt(target_place.clone(), rvalue);
429 target_place
430 }
431 fn raw_borrow_to_new_var(
433 &mut self,
434 place: Place,
435 kind: RefKind,
436 name: Option<String>,
437 ) -> Place {
438 let ref_ty = TyKind::RawPtr(place.ty().clone(), kind).into_ty();
439 let target_place = self.fresh_var(name, ref_ty);
440 let rvalue = self.raw_borrow(place, kind);
441 self.insert_assn_stmt(target_place.clone(), rvalue);
442 target_place
443 }
444}
445
446pub struct UllbcStatementTransformCtx<'a> {
447 pub ctx: &'a mut TransformCtx,
448 pub params: &'a GenericParams,
449 pub locals: &'a mut Locals,
450 pub span: Span,
452 pub statements: Vec<ullbc_ast::Statement>,
454}
455
456impl BodyTransformCtx for UllbcStatementTransformCtx<'_> {
457 fn get_crate(&self) -> &TranslatedCrate {
458 &self.ctx.translated
459 }
460 fn get_options(&self) -> &TranslateOptions {
461 &self.ctx.options
462 }
463 fn get_params(&self) -> &GenericParams {
464 self.params
465 }
466 fn get_locals_mut(&mut self) -> &mut Locals {
467 self.locals
468 }
469
470 fn insert_storage_live_stmt(&mut self, local: LocalId) {
471 self.statements.push(ullbc_ast::Statement::new(
472 self.span,
473 ullbc_ast::StatementKind::StorageLive(local),
474 ));
475 }
476
477 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
478 self.statements.push(ullbc_ast::Statement::new(
479 self.span,
480 ullbc_ast::StatementKind::Assign(place, rvalue),
481 ));
482 }
483
484 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
485 self.statements.push(ullbc_ast::Statement::new(
486 self.span,
487 ullbc_ast::StatementKind::StorageDead(local),
488 ));
489 }
490}
491
492pub struct LlbcStatementTransformCtx<'a> {
493 pub ctx: &'a mut TransformCtx,
494 pub params: &'a GenericParams,
495 pub locals: &'a mut Locals,
496 pub span: Span,
498 pub statements: Vec<llbc_ast::Statement>,
500}
501
502impl BodyTransformCtx for LlbcStatementTransformCtx<'_> {
503 fn get_crate(&self) -> &TranslatedCrate {
504 &self.ctx.translated
505 }
506 fn get_options(&self) -> &TranslateOptions {
507 &self.ctx.options
508 }
509 fn get_params(&self) -> &GenericParams {
510 self.params
511 }
512 fn get_locals_mut(&mut self) -> &mut Locals {
513 self.locals
514 }
515
516 fn insert_storage_live_stmt(&mut self, local: LocalId) {
517 self.statements.push(llbc_ast::Statement::new(
518 self.span,
519 llbc_ast::StatementKind::StorageLive(local),
520 ));
521 }
522
523 fn insert_assn_stmt(&mut self, place: Place, rvalue: Rvalue) {
524 self.statements.push(llbc_ast::Statement::new(
525 self.span,
526 llbc_ast::StatementKind::Assign(place, rvalue),
527 ));
528 }
529
530 fn insert_storage_dead_stmt(&mut self, local: LocalId) {
531 self.statements.push(llbc_ast::Statement::new(
532 self.span,
533 llbc_ast::StatementKind::StorageDead(local),
534 ));
535 }
536}
537
538impl FunDecl {
539 pub fn transform_ullbc_statements(
540 &mut self,
541 ctx: &mut TransformCtx,
542 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Statement),
543 ) {
544 if let Some(body) = self.body.as_unstructured_mut() {
545 let mut ctx = UllbcStatementTransformCtx {
546 ctx,
547 params: &self.generics,
548 locals: &mut body.locals,
549 span: self.item_meta.span,
550 statements: Vec::new(),
551 };
552 body.body.iter_mut().for_each(|block| {
553 ctx.statements = Vec::with_capacity(block.statements.len());
554 for mut st in mem::take(&mut block.statements) {
555 ctx.span = st.span;
556 f(&mut ctx, &mut st);
557 ctx.statements.push(st);
558 }
559 block.statements = mem::take(&mut ctx.statements);
560 });
561 }
562 }
563
564 pub fn transform_ullbc_terminators(
565 &mut self,
566 ctx: &mut TransformCtx,
567 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut ullbc_ast::Terminator),
568 ) {
569 if let Some(body) = self.body.as_unstructured_mut() {
570 let mut ctx = UllbcStatementTransformCtx {
571 ctx,
572 params: &self.generics,
573 locals: &mut body.locals,
574 span: self.item_meta.span,
575 statements: Vec::new(),
576 };
577 body.body.iter_mut().for_each(|block| {
578 ctx.span = block.terminator.span;
579 ctx.statements = mem::take(&mut block.statements);
580 f(&mut ctx, &mut block.terminator);
581 block.statements = mem::take(&mut ctx.statements);
582 });
583 }
584 }
585
586 pub fn transform_ullbc_operands(
587 &mut self,
588 ctx: &mut TransformCtx,
589 mut f: impl FnMut(&mut UllbcStatementTransformCtx, &mut Operand),
590 ) {
591 self.transform_ullbc_statements(ctx, |ctx, st| {
592 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
593 });
594 self.transform_ullbc_terminators(ctx, |ctx, st| {
595 st.kind.dyn_visit_in_body_mut(|op: &mut Operand| f(ctx, op));
596 });
597 }
598
599 pub fn transform_llbc_statements(
600 &mut self,
601 ctx: &mut TransformCtx,
602 mut f: impl FnMut(&mut LlbcStatementTransformCtx, &mut llbc_ast::Statement),
603 ) {
604 if let Some(body) = self.body.as_structured_mut() {
605 let mut ctx = LlbcStatementTransformCtx {
606 ctx,
607 locals: &mut body.locals,
608 statements: Vec::new(),
609 span: self.item_meta.span,
610 params: &self.generics,
611 };
612 body.body.visit_blocks_bwd(|block: &mut llbc_ast::Block| {
613 ctx.statements = Vec::with_capacity(block.statements.len());
614 for mut st in mem::take(&mut block.statements) {
615 ctx.span = st.span;
616 f(&mut ctx, &mut st);
617 ctx.statements.push(st);
618 }
619 block.statements = mem::take(&mut ctx.statements)
620 })
621 }
622 }
623}