zksync_vm2/instruction_handlers/
ret.rs1use primitive_types::U256;
2use zksync_vm2_interface::{
3 opcodes::{self, Normal, Panic, Revert, TypeLevelReturnType},
4 ReturnType, Tracer,
5};
6
7use super::{
8 common::full_boilerplate,
9 far_call::get_calldata,
10 monomorphization::{match_boolean, monomorphize, parameterize},
11};
12use crate::{
13 addressing_modes::{Arguments, Immediate1, Register1, Source, INVALID_INSTRUCTION_COST},
14 callframe::FrameRemnant,
15 instruction::{ExecutionEnd, ExecutionStatus},
16 mode_requirements::ModeRequirements,
17 page_ids::base_page_from_heap,
18 predication::Flags,
19 tracing::VmAndWorld,
20 Instruction, Predicate, VirtualMachine, World,
21};
22
23fn naked_ret<T: Tracer, W: World<T>, RT: TypeLevelReturnType, const TO_LABEL: bool>(
24 vm: &mut VirtualMachine<T, W>,
25 args: &Arguments,
26) -> ExecutionStatus {
27 let mut return_type = RT::VALUE;
28 let near_call_leftover_gas = vm.state.current_frame.gas;
29
30 let (snapshot, leftover_gas) = if let Some(FrameRemnant {
31 exception_handler,
32 snapshot,
33 }) = vm.state.current_frame.pop_near_call()
34 {
35 if TO_LABEL {
36 let pc = Immediate1::get_u16(args);
37 vm.state.current_frame.set_pc_from_u16(pc);
38 } else if return_type.is_failure() {
39 vm.state.current_frame.set_pc_from_u16(exception_handler);
40 }
41
42 (snapshot, near_call_leftover_gas)
43 } else {
44 let (raw_abi, is_pointer) = Register1::get_with_pointer_flag(args, &mut vm.state);
45 let return_value_or_panic = if return_type == ReturnType::Panic {
46 get_calldata(raw_abi, is_pointer, vm, true);
52 None
53 } else {
54 let result = get_calldata(raw_abi, is_pointer, vm, false).filter(|pointer| {
55 if vm.state.current_frame.is_kernel {
56 true
57 } else {
58 pointer.memory_page.as_u32() >= base_page_from_heap(vm.state.current_frame.heap)
62 && pointer.memory_page != vm.state.current_frame.calldata_heap
63 }
64 });
65
66 if result.is_none() {
67 return_type = ReturnType::Panic;
68 }
69 result
70 };
71
72 let leftover_gas = vm.state.current_frame.gas;
73
74 let Some(FrameRemnant {
75 exception_handler,
76 snapshot,
77 }) = vm.pop_frame(
78 return_value_or_panic
79 .as_ref()
80 .map(|pointer| pointer.memory_page),
81 return_value_or_panic
82 .as_ref()
83 .map(|pointer| (pointer.start, pointer.length)),
84 )
85 else {
86 vm.state.current_frame.pc = invalid_instruction();
94
95 return if let Some(return_value) = return_value_or_panic {
96 let output = vm.state.heaps[return_value.memory_page]
97 .read_range_big_endian(
98 return_value.start..return_value.start + return_value.length,
99 )
100 .clone();
101 if return_type == ReturnType::Revert {
102 ExecutionStatus::Stopped(ExecutionEnd::Reverted(output))
103 } else {
104 ExecutionStatus::Stopped(ExecutionEnd::ProgramFinished(output))
105 }
106 } else {
107 ExecutionStatus::Stopped(ExecutionEnd::Panicked)
108 };
109 };
110
111 vm.state.set_context_u128(0);
112 vm.state.registers = [U256::zero(); 16];
113
114 if let Some(return_value) = return_value_or_panic {
115 vm.state.registers[1] = return_value.into_u256();
116 }
117 vm.state.register_pointer_flags = 2;
118
119 if return_type.is_failure() {
120 vm.state.current_frame.set_pc_from_u16(exception_handler);
121 }
122
123 (snapshot, leftover_gas)
124 };
125
126 if return_type.is_failure() {
127 vm.world_diff.append_rollback_logs(&snapshot);
128 vm.world_diff.rollback(snapshot);
129 }
130
131 vm.state.flags = Flags::new(return_type == ReturnType::Panic, false, false);
132 vm.state.current_frame.gas += leftover_gas;
133
134 ExecutionStatus::Running
135}
136
137fn ret<T: Tracer, W: World<T>, RT: TypeLevelReturnType, const TO_LABEL: bool>(
138 vm: &mut VirtualMachine<T, W>,
139 world: &mut W,
140 tracer: &mut T,
141) -> ExecutionStatus {
142 full_boilerplate::<opcodes::Ret<RT>, _, _>(vm, world, tracer, |vm, args, _, _| {
143 naked_ret::<T, W, RT, TO_LABEL>(vm, args)
144 })
145}
146
147pub(crate) fn free_panic<T: Tracer, W: World<T>>(
157 vm: &mut VirtualMachine<T, W>,
158 world: &mut W,
159 tracer: &mut T,
160) -> ExecutionStatus {
161 tracer.before_instruction::<opcodes::Ret<Panic>, _>(&mut VmAndWorld { vm, world });
162 naked_ret::<T, W, Panic, false>(
166 vm,
167 &Arguments::new(Predicate::Always, 0, ModeRequirements::none()),
168 )
169 .merge_tracer(tracer.after_instruction::<opcodes::Ret<Panic>, _>(&mut VmAndWorld { vm, world }))
170}
171
172fn invalid<T: Tracer, W: World<T>>(
173 vm: &mut VirtualMachine<T, W>,
174 world: &mut W,
175 tracer: &mut T,
176) -> ExecutionStatus {
177 vm.state.current_frame.gas = 0;
178 free_panic(vm, world, tracer)
179}
180
181trait GenericStatics<T, W> {
182 const PANIC: Instruction<T, W>;
183 const INVALID: Instruction<T, W>;
184}
185
186impl<T: Tracer, W: World<T>> GenericStatics<T, W> for () {
187 const PANIC: Instruction<T, W> = Instruction::from_spontaneous_panic();
188 const INVALID: Instruction<T, W> = Instruction::from_invalid();
189}
190
191pub(crate) fn spontaneous_panic<'a, T: Tracer, W: World<T>>() -> &'a Instruction<T, W> {
196 &<()>::PANIC
197}
198
199pub(crate) fn invalid_instruction<'a, T: Tracer, W: World<T>>() -> &'a Instruction<T, W> {
201 &<()>::INVALID
202}
203
204pub(crate) const RETURN_COST: u32 = 5;
205
206impl<T: Tracer, W: World<T>> Instruction<T, W> {
208 pub fn from_ret(src1: Register1, label: Option<Immediate1>, arguments: Arguments) -> Self {
210 let to_label = label.is_some();
211 Self {
212 handler: monomorphize!(ret [T W Normal] match_boolean to_label),
213 arguments: arguments.write_source(&src1).write_source(&label),
214 }
215 }
216
217 pub fn from_revert(src1: Register1, label: Option<Immediate1>, arguments: Arguments) -> Self {
219 let to_label = label.is_some();
220 Self {
221 handler: monomorphize!(ret [T W Revert] match_boolean to_label),
222 arguments: arguments.write_source(&src1).write_source(&label),
223 }
224 }
225
226 pub fn from_panic(src1: Register1, label: Option<Immediate1>, arguments: Arguments) -> Self {
231 let to_label = label.is_some();
232 Self {
233 handler: monomorphize!(ret [T W Panic] match_boolean to_label),
234 arguments: arguments.write_source(&src1).write_source(&label),
235 }
236 }
237
238 pub(crate) const fn from_spontaneous_panic() -> Self {
241 Self {
242 handler: ret::<T, W, Panic, false>,
243 arguments: Arguments::new(Predicate::Always, RETURN_COST, ModeRequirements::none()),
244 }
245 }
246
247 pub const fn from_invalid() -> Self {
249 Self {
250 handler: invalid,
251 arguments: Arguments::new(
252 Predicate::Always,
253 INVALID_INSTRUCTION_COST,
254 ModeRequirements::none(),
255 ),
256 }
257 }
258}