cairo_lang_sierra_generator/
pre_sierra.rs

1use std::hash::Hash;
2
3use cairo_lang_debug::DebugWithDb;
4use cairo_lang_defs::diagnostic_utils::StableLocation;
5use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId;
6use cairo_lang_sierra as sierra;
7use cairo_lang_sierra::ids::ConcreteTypeId;
8use cairo_lang_sierra::program;
9use cairo_lang_utils::{LookupIntern, define_short_id, write_comma_separated};
10
11use crate::db::SierraGenGroup;
12
13/// Represents the long id of a pre-sierra label.
14/// The long id consists of the parent function and a unique identifier inside the function.
15// TODO(lior): Make sure this struct can only be constructed by expr_generator_context.
16#[derive(Clone, Debug, Eq, PartialEq, Hash)]
17pub struct LabelLongId {
18    pub parent: ConcreteFunctionWithBodyId,
19    // A unique identifier inside the function
20    pub id: usize,
21}
22define_short_id!(LabelId, LabelLongId, SierraGenGroup, lookup_intern_label_id, intern_label_id);
23
24pub struct LabelIdWithDb<'db> {
25    db: &'db dyn SierraGenGroup,
26    label_id: LabelId,
27}
28impl std::fmt::Display for LabelIdWithDb<'_> {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        let LabelLongId { parent, id } = self.label_id.lookup_intern(self.db);
31        let parent = parent.function_id(self.db.upcast()).unwrap();
32        let dbg = format!("{:?}", parent.debug(self.db));
33        write!(f, "label_{}::{}", dbg, id)
34    }
35}
36
37impl LabelId {
38    pub fn with_db<'db>(&self, db: &'db dyn SierraGenGroup) -> LabelIdWithDb<'db> {
39        LabelIdWithDb { db, label_id: *self }
40    }
41}
42
43/// Represents a compiled function before the label-resolution phase (pre-sierra).
44#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct Function {
46    /// The source function which was compiled.
47    pub id: sierra::ids::FunctionId,
48    /// The body of the function.
49    pub body: Vec<StatementWithLocation>,
50    /// A label pointing to the first instruction of the function.
51    pub entry_point: LabelId,
52    /// The parameters for the function.
53    pub parameters: Vec<program::Param>,
54    /// The return types from the function.
55    pub ret_types: Vec<sierra::ids::ConcreteTypeId>,
56}
57
58/// Represents a pre-sierra statement - a statement before label-resolution.
59#[derive(Clone, Debug, Eq, PartialEq)]
60pub enum Statement {
61    /// A compiled Sierra statement (before label resolution).
62    Sierra(program::GenStatement<LabelId>),
63    /// A label.
64    Label(Label),
65    /// An instruction to push variables onto the stack. For example, used before calling functions
66    /// and returning.
67    ///
68    /// Note that push values does not guarantee that new copies of the values will be pushed.
69    /// If a prefix of the values is already on the stack, they will not be re-pushed.
70    PushValues(Vec<PushValue>),
71}
72impl Statement {
73    pub fn into_statement_without_location(self) -> StatementWithLocation {
74        StatementWithLocation { statement: self, location: vec![] }
75    }
76    pub fn to_string(&self, db: &dyn SierraGenGroup) -> String {
77        StatementWithDb { db, statement: self.clone() }.to_string()
78    }
79}
80
81/// Represents a pre-sierra statement, with its location in the source code.
82#[derive(Clone, Debug, Eq, PartialEq)]
83pub struct StatementWithLocation {
84    pub statement: Statement,
85    pub location: Vec<StableLocation>,
86}
87
88impl StatementWithLocation {
89    pub fn set_location(&mut self, location: Vec<StableLocation>) {
90        self.location = location;
91    }
92}
93
94struct StatementWithDb<'db> {
95    db: &'db dyn SierraGenGroup,
96    statement: Statement,
97}
98impl std::fmt::Display for StatementWithDb<'_> {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        match &self.statement {
101            Statement::Sierra(value) => {
102                write!(f, "{}", value.clone().map(|label_id| label_id.with_db(self.db)))
103            }
104            Statement::Label(Label { id }) => {
105                write!(f, "{}:", id.with_db(self.db))
106            }
107            Statement::PushValues(values) => {
108                write!(f, "PushValues(")?;
109                write_comma_separated(
110                    f,
111                    values.iter().map(|PushValue { var, ty, .. }| format!("{var}: {ty}")),
112                )?;
113                write!(f, ") -> (")?;
114                write_comma_separated(
115                    f,
116                    values.iter().map(|PushValue { var_on_stack, dup, .. }| {
117                        if *dup { format!("{var_on_stack}*") } else { format!("{var_on_stack}") }
118                    }),
119                )?;
120                write!(f, ")")
121            }
122        }
123    }
124}
125
126/// Represents a single element that should be pushed onto the stack as part of
127/// [Statement::PushValues].
128#[derive(Clone, Debug, Eq, PartialEq)]
129pub struct PushValue {
130    /// The variable id to push.
131    pub var: sierra::ids::VarId,
132    /// The variable id on the stack (e.g., the result of `store_temp()`).
133    /// If `dup` is true, this variable cannot be `var`.
134    pub var_on_stack: sierra::ids::VarId,
135    /// The type of the variable.
136    pub ty: ConcreteTypeId,
137    /// Indicates whether the variable should be duplicated before it is pushed.
138    pub dup: bool,
139}
140
141/// Represents a pre-sierra label.
142#[derive(Clone, Debug, Eq, PartialEq)]
143pub struct Label {
144    pub id: LabelId,
145}