charon_lib/ast/ullbc_ast_utils.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
//! Implementations for [crate::ullbc_ast]
use crate::ids::Vector;
use crate::meta::Span;
use crate::ullbc_ast::*;
use std::mem;
use take_mut::take;
impl SwitchTargets {
pub fn get_targets(&self) -> Vec<BlockId> {
match self {
SwitchTargets::If(then_tgt, else_tgt) => {
vec![*then_tgt, *else_tgt]
}
SwitchTargets::SwitchInt(_, targets, otherwise) => {
let mut all_targets = vec![];
for (_, target) in targets {
all_targets.push(*target);
}
all_targets.push(*otherwise);
all_targets
}
}
}
}
impl Statement {
pub fn new(span: Span, content: RawStatement) -> Self {
Statement {
span,
content,
comments_before: vec![],
}
}
}
impl Terminator {
pub fn new(span: Span, content: RawTerminator) -> Self {
Terminator {
span,
content,
comments_before: vec![],
}
}
}
impl BlockData {
pub fn targets(&self) -> Vec<BlockId> {
match &self.terminator.content {
RawTerminator::Goto { target } => {
vec![*target]
}
RawTerminator::Switch { targets, .. } => targets.get_targets(),
RawTerminator::Abort(..) | RawTerminator::Return => {
vec![]
}
}
}
/// See [body_transform_operands]
pub fn transform_operands<F: FnMut(&Span, &mut Vec<Statement>, &mut Operand)>(
mut self,
mut f: F,
) -> Self {
// The new vector of statements
let mut nst = vec![];
// Explore the operands in the statements
for mut st in self.statements {
st.content
.dyn_visit_in_body_mut(|op: &mut Operand| f(&st.span, &mut nst, op));
// Add the statement to the vector of statements
nst.push(st)
}
// Explore the terminator
self.terminator
.content
.dyn_visit_in_body_mut(|op: &mut Operand| f(&self.terminator.span, &mut nst, op));
// Update the vector of statements
self.statements = nst;
// Return
self
}
/// Apply a transformer to all the statements.
///
/// The transformer should:
/// - mutate the current statement in place
/// - return the sequence of statements to introduce before the current statement
pub fn transform<F: FnMut(&mut Statement) -> Vec<Statement>>(&mut self, mut f: F) {
self.transform_sequences(|slice| {
let new_statements = f(&mut slice[0]);
if new_statements.is_empty() {
vec![]
} else {
vec![(0, new_statements)]
}
});
}
/// Apply a transformer to all the statements.
///
/// The transformer should:
/// - mutate the current statements in place
/// - return a list of `(i, statements)` where `statements` will be inserted before index `i`.
pub fn transform_sequences<F>(&mut self, mut f: F)
where
F: FnMut(&mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
{
let mut to_insert = vec![];
let mut final_len = self.statements.len();
for i in (0..self.statements.len()).rev() {
let new_to_insert = f(&mut self.statements[i..]);
to_insert.extend(new_to_insert.into_iter().map(|(j, stmts)| {
final_len += stmts.len();
(i + j, stmts)
}));
}
if !to_insert.is_empty() {
to_insert.sort_by_key(|(i, _)| *i);
// Make it so the first element is always at the end so we can pop it.
to_insert.reverse();
// Construct the merged list of statements.
let old_statements = mem::replace(&mut self.statements, Vec::with_capacity(final_len));
for (i, stmt) in old_statements.into_iter().enumerate() {
while let Some((j, _)) = to_insert.last()
&& *j == i
{
let (_, mut stmts) = to_insert.pop().unwrap();
self.statements.append(&mut stmts);
}
self.statements.push(stmt);
}
}
}
}
impl ExprBody {
pub fn transform_sequences<F>(&mut self, mut f: F)
where
F: FnMut(&mut Locals, &mut [Statement]) -> Vec<(usize, Vec<Statement>)>,
{
for block in &mut self.body {
block.transform_sequences(|seq| f(&mut self.locals, seq));
}
}
/// Apply a function to all the statements, in a bottom-up manner.
pub fn visit_statements<F: FnMut(&mut Statement)>(&mut self, mut f: F) {
for block in self.body.iter_mut().rev() {
for st in block.statements.iter_mut().rev() {
f(st);
}
}
}
}
/// Transform a body by applying a function to its operands, and
/// inserting the statements generated by the operands at the end of the
/// block.
/// Useful to implement a pass on operands (see e.g., [crate::extract_global_assignments]).
///
/// The span argument given to `f` is the span argument of the [Terminator]
/// containing the operand. `f` should explore the operand it receives, and
/// push statements to the vector it receives as input.
pub fn body_transform_operands<F: FnMut(&Span, &mut Vec<Statement>, &mut Operand)>(
blocks: &mut Vector<BlockId, BlockData>,
mut f: F,
) {
for block in blocks.iter_mut() {
take(block, |b| b.transform_operands(&mut f));
}
}