charon_lib/ast/ullbc_ast.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
//! "Unstructured LLBC" ast (ULLBC). This is LLBC before the control-flow
//! reconstruction. In effect, this is a cleaned up version of MIR.
pub use super::ullbc_ast_utils::*;
pub use crate::ast::*;
use derive_generic_visitor::{Drive, DriveMut};
use macros::{EnumAsGetters, EnumIsA, VariantIndexArity, VariantName};
use serde::{Deserialize, Serialize};
// Block identifier. Similar to rust's `BasicBlock`.
generate_index_type!(BlockId, "Block");
// The entry block of a function is always the block with id 0
pub static START_BLOCK_ID: BlockId = BlockId::ZERO;
#[charon::rename("Blocks")]
pub type BodyContents = Vector<BlockId, BlockData>;
pub type ExprBody = GExprBody<BodyContents>;
/// A raw statement: a statement without meta data.
#[derive(
Debug, Clone, EnumIsA, EnumAsGetters, VariantName, Serialize, Deserialize, Drive, DriveMut,
)]
pub enum RawStatement {
Assign(Place, Rvalue),
/// A call. For now, we don't support dynamic calls (i.e. to a function pointer in memory).
Call(Call),
FakeRead(Place),
SetDiscriminant(Place, VariantId),
/// We translate this to [crate::llbc_ast::RawStatement::Drop] in LLBC
StorageDead(VarId),
/// We translate this to [crate::llbc_ast::RawStatement::Drop] in LLBC
Deinit(Place),
Drop(Place),
/// A built-in assert, which corresponds to runtime checks that we remove, namely: bounds
/// checks, over/underflow checks, div/rem by zero checks, pointer alignement check.
Assert(Assert),
/// Does nothing. Useful for passes.
Nop,
#[charon::opaque]
#[drive(skip)]
Error(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
pub struct Statement {
pub span: Span,
pub content: RawStatement,
/// Comments that precede this statement.
// This is filled in a late pass after all the control-flow manipulation.
#[drive(skip)]
pub comments_before: Vec<String>,
}
#[derive(
Debug,
Clone,
EnumIsA,
EnumAsGetters,
VariantName,
VariantIndexArity,
Serialize,
Deserialize,
Drive,
DriveMut,
)]
#[charon::rename("Switch")]
pub enum SwitchTargets {
/// Gives the `if` block and the `else` block
If(BlockId, BlockId),
/// Gives the integer type, a map linking values to switch branches, and the
/// otherwise block. Note that matches over enumerations are performed by
/// switching over the discriminant, which is an integer.
SwitchInt(IntegerTy, Vec<(ScalarValue, BlockId)>, BlockId),
}
/// A raw terminator: a terminator without meta data.
#[derive(Debug, Clone, EnumIsA, EnumAsGetters, Serialize, Deserialize, Drive, DriveMut)]
pub enum RawTerminator {
Goto {
target: BlockId,
},
Switch {
discr: Operand,
targets: SwitchTargets,
},
/// Handles panics and impossible cases.
Abort(AbortKind),
Return,
}
#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
pub struct Terminator {
pub span: Span,
pub content: RawTerminator,
/// Comments that precede this terminator.
// This is filled in a late pass after all the control-flow manipulation.
#[drive(skip)]
pub comments_before: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Drive, DriveMut)]
#[charon::rename("Block")]
pub struct BlockData {
pub statements: Vec<Statement>,
pub terminator: Terminator,
}