cairo_lang_sierra_to_casm/
relocations.rs1use cairo_lang_casm::instructions::{
2 AssertEqInstruction, CallInstruction, Instruction, InstructionBody, JnzInstruction,
3 JumpInstruction,
4};
5use cairo_lang_casm::operand::{BinOpOperand, DerefOrImmediate, ResOperand};
6use cairo_lang_sierra::ids::ConcreteTypeId;
7use cairo_lang_sierra::program::StatementIdx;
8use cairo_lang_sierra_gas::objects::ConstCost;
9
10use crate::compiler::ConstsInfo;
11
12pub type CodeOffset = usize;
13
14#[derive(Debug, Eq, PartialEq)]
15pub enum Relocation {
16 RelativeStatementId(StatementIdx),
19 SegmentStart(u32),
21 ConstStart(u32, ConcreteTypeId),
23 CircuitStart(ConcreteTypeId),
25 EndOfProgram,
28}
29
30impl Relocation {
31 pub fn apply(
32 &self,
33 instruction_offset: CodeOffset,
34 statement_offsets: &[CodeOffset],
35 consts_info: &ConstsInfo,
36 instruction: &mut Instruction,
37 ) {
38 let target_pc = match self {
39 Relocation::RelativeStatementId(statement_idx) => statement_offsets[statement_idx.0],
40 Relocation::SegmentStart(segment_index) => {
41 let segment = consts_info.segments.get(segment_index).expect("Segment not found.");
42 *statement_offsets.last().unwrap() + segment.segment_offset
43 }
44 Relocation::ConstStart(segment_index, ty) => {
45 let segment = consts_info.segments.get(segment_index).expect("Segment not found.");
46 *statement_offsets.last().unwrap()
47 + segment.segment_offset
48 + segment.const_offset.get(ty).expect("Const type not found in const segments.")
49 }
50 Relocation::EndOfProgram => {
51 *statement_offsets.last().unwrap() + consts_info.total_segments_size
52 }
53 Relocation::CircuitStart(circ_ty) => {
54 let segment_index =
55 consts_info.circuit_segments.get(circ_ty).expect("Circuit not found");
56
57 let segment = consts_info.segments.get(segment_index).expect("Segment not found.");
58
59 *statement_offsets.last().unwrap()
60 + segment.segment_offset
61 + segment
62 .const_offset
63 .get(circ_ty)
64 .expect("Const type not found in const segments.")
65 }
66 };
67 match instruction {
68 Instruction {
69 body:
70 InstructionBody::Call(CallInstruction {
71 target: DerefOrImmediate::Immediate(value),
72 relative: true,
73 }),
74 inc_ap: false,
75 ..
76 }
77 | Instruction {
78 body:
79 InstructionBody::Jnz(JnzInstruction {
80 jump_offset: DerefOrImmediate::Immediate(value),
81 condition: _,
82 }),
83 ..
84 }
85 | Instruction {
86 body:
87 InstructionBody::Jump(JumpInstruction {
88 target: DerefOrImmediate::Immediate(value),
89 relative: true,
90 }),
91 ..
92 }
93 | Instruction {
94 body:
95 InstructionBody::AssertEq(AssertEqInstruction {
96 b:
97 ResOperand::BinOp(BinOpOperand {
98 b: DerefOrImmediate::Immediate(value),
99 ..
100 }),
101 ..
102 }),
103 ..
104 } => {
105 value.value += target_pc as i128 - instruction_offset as i128;
106 }
107 _ => panic!("Bad relocation."),
108 }
109 }
110}
111
112#[derive(Debug, Eq, PartialEq)]
113pub struct RelocationEntry {
114 pub instruction_idx: CodeOffset,
116 pub relocation: Relocation,
118}
119
120pub fn relocate_instructions(
126 relocations: &[RelocationEntry],
127 statement_offsets: &[usize],
128 consts_info: &ConstsInfo,
129 instructions: &mut [Instruction],
130) {
131 let mut program_offset = 0;
132 let mut relocations_iter = relocations.iter();
133 let mut relocation_entry = relocations_iter.next();
134 for (instruction_idx, instruction) in instructions.iter_mut().enumerate() {
135 if let Some(RelocationEntry { instruction_idx: relocation_idx, relocation }) =
136 relocation_entry
137 {
138 if *relocation_idx == instruction_idx {
139 relocation.apply(program_offset, statement_offsets, consts_info, instruction);
140 relocation_entry = relocations_iter.next();
141 } else {
142 assert!(
143 *relocation_idx > instruction_idx,
144 "Relocation for already handled instruction #{relocation_idx} - currently \
145 handling #{instruction_idx}."
146 );
147 }
148 }
149
150 program_offset += instruction.body.op_size();
151 }
152 assert!(
153 relocation_entry.is_none(),
154 "No relocations should be left when done with all instructions."
155 );
156}
157
158#[derive(Default)]
160pub struct InstructionsWithRelocations {
161 pub instructions: Vec<Instruction>,
162 pub relocations: Vec<RelocationEntry>,
163 pub cost: ConstCost,
165}