@@ -9,6 +9,8 @@ use syn::{
9
9
GenericParam , Ident , Meta , Token ,
10
10
} ;
11
11
12
+ mod tco;
13
+
12
14
#[ proc_macro_derive( PreflightExecutor ) ]
13
15
pub fn preflight_executor_derive ( input : TokenStream ) -> TokenStream {
14
16
let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
@@ -172,6 +174,18 @@ pub fn executor_derive(input: TokenStream) -> TokenStream {
172
174
Ctx : :: openvm_circuit:: arch:: execution_mode:: ExecutionCtxTrait , {
173
175
self . 0 . pre_compute( pc, inst, data)
174
176
}
177
+
178
+ #[ cfg( feature = "tco" ) ]
179
+ fn handler<Ctx >(
180
+ & self ,
181
+ pc: u32 ,
182
+ inst: & :: openvm_circuit:: arch:: instructions:: instruction:: Instruction <F >,
183
+ data: & mut [ u8 ] ,
184
+ ) -> Result <:: openvm_circuit:: arch:: Handler <F , Ctx >, :: openvm_circuit:: arch:: StaticProgramError >
185
+ where
186
+ Ctx : :: openvm_circuit:: arch:: execution_mode:: ExecutionCtxTrait , {
187
+ self . 0 . handler( pc, inst, data)
188
+ }
175
189
}
176
190
}
177
191
. into ( )
@@ -205,18 +219,21 @@ pub fn executor_derive(input: TokenStream) -> TokenStream {
205
219
} ) ;
206
220
// Use full path ::openvm_circuit... so it can be used either within or outside the vm
207
221
// crate. Assume F is already generic of the field.
208
- let ( pre_compute_size_arms, pre_compute_arms, where_predicates) : ( Vec < _ > , Vec < _ > , Vec < _ > ) = multiunzip ( variants. iter ( ) . map ( |( variant_name, field) | {
222
+ let ( pre_compute_size_arms, pre_compute_arms, handler_arms , where_predicates) : ( Vec < _ > , Vec < _ > , Vec < _ > , Vec < _ > ) = multiunzip ( variants. iter ( ) . map ( |( variant_name, field) | {
209
223
let field_ty = & field. ty ;
210
224
let pre_compute_size_arm = quote ! {
211
225
#name:: #variant_name( x) => <#field_ty as :: openvm_circuit:: arch:: Executor <#first_ty_generic>>:: pre_compute_size( x)
212
226
} ;
213
227
let pre_compute_arm = quote ! {
214
228
#name:: #variant_name( x) => <#field_ty as :: openvm_circuit:: arch:: Executor <#first_ty_generic>>:: pre_compute( x, pc, instruction, data)
215
229
} ;
230
+ let handler_arm = quote ! {
231
+ #name:: #variant_name( x) => <#field_ty as :: openvm_circuit:: arch:: Executor <#first_ty_generic>>:: handler( x, pc, instruction, data)
232
+ } ;
216
233
let where_predicate = syn:: parse_quote! {
217
234
#field_ty: :: openvm_circuit:: arch:: Executor <#first_ty_generic>
218
235
} ;
219
- ( pre_compute_size_arm, pre_compute_arm, where_predicate)
236
+ ( pre_compute_size_arm, pre_compute_arm, handler_arm , where_predicate)
220
237
} ) ) ;
221
238
let where_clause = new_generics. make_where_clause ( ) ;
222
239
for predicate in where_predicates {
@@ -247,6 +264,20 @@ pub fn executor_derive(input: TokenStream) -> TokenStream {
247
264
#( #pre_compute_arms, ) *
248
265
}
249
266
}
267
+
268
+ #[ cfg( feature = "tco" ) ]
269
+ fn handler<Ctx >(
270
+ & self ,
271
+ pc: u32 ,
272
+ instruction: & :: openvm_circuit:: arch:: instructions:: instruction:: Instruction <F >,
273
+ data: & mut [ u8 ] ,
274
+ ) -> Result <:: openvm_circuit:: arch:: Handler <F , Ctx >, :: openvm_circuit:: arch:: StaticProgramError >
275
+ where
276
+ Ctx : :: openvm_circuit:: arch:: execution_mode:: ExecutionCtxTrait , {
277
+ match self {
278
+ #( #handler_arms, ) *
279
+ }
280
+ }
250
281
}
251
282
}
252
283
. into ( )
@@ -501,7 +532,7 @@ fn generate_config_traits_impl(name: &Ident, inner: &DataStruct) -> syn::Result<
501
532
. iter ( )
502
533
. filter ( |f| f. attrs . iter ( ) . any ( |attr| attr. path ( ) . is_ident ( "config" ) ) )
503
534
. exactly_one ( )
504
- . clone ( )
535
+ . ok ( )
505
536
. expect ( "Exactly one field must have the #[config] attribute" ) ;
506
537
let ( source_name, source_name_upper) =
507
538
gen_name_with_uppercase_idents ( source_field. ident . as_ref ( ) . unwrap ( ) ) ;
@@ -700,3 +731,30 @@ fn parse_executor_type(
700
731
} )
701
732
}
702
733
}
734
+
735
+ /// An attribute procedural macro for creating TCO (Tail Call Optimization) handlers.
736
+ ///
737
+ /// This macro generates a handler function that wraps an execute implementation
738
+ /// with tail call optimization using the `become` keyword. It extracts the generics
739
+ /// and where clauses from the original function.
740
+ ///
741
+ /// # Usage
742
+ ///
743
+ /// Place this attribute above a function definition:
744
+ /// ```
745
+ /// #[create_tco_handler = "handler_name"]
746
+ /// unsafe fn execute_e1_impl<F: PrimeField32, CTX, const B_IS_IMM: bool>(
747
+ /// pre_compute: &[u8],
748
+ /// state: &mut VmExecState<F, GuestMemory, CTX>,
749
+ /// ) where
750
+ /// CTX: ExecutionCtxTrait,
751
+ /// {
752
+ /// // function body
753
+ /// }
754
+ /// ```
755
+ ///
756
+ /// This will generate a TCO handler function with the same generics and where clauses.
757
+ #[ proc_macro_attribute]
758
+ pub fn create_tco_handler ( _attr : TokenStream , item : TokenStream ) -> TokenStream {
759
+ tco:: tco_impl ( item)
760
+ }
0 commit comments