@@ -37,9 +37,10 @@ use crate::vm::native::{
3737use cranelift_codegen:: ir:: condcodes:: { FloatCC , IntCC } ;
3838use cranelift_codegen:: ir:: immediates:: Ieee64 ;
3939use cranelift_codegen:: ir:: {
40- Block , BlockArg , InstBuilder , MemFlags , StackSlot , StackSlotData , StackSlotKind , types,
40+ AbiParam , Block , BlockArg , InstBuilder , MemFlags , Signature , StackSlot , StackSlotData ,
41+ StackSlotKind , types,
4142} ;
42- use cranelift_codegen:: isa:: OwnedTargetIsa ;
43+ use cranelift_codegen:: isa:: { CallConv , OwnedTargetIsa } ;
4344use cranelift_codegen:: settings:: { self , Configurable } ;
4445use cranelift_frontend:: { FunctionBuilder , FunctionBuilderContext } ;
4546use cranelift_jit:: { JITBuilder , JITModule } ;
@@ -50,6 +51,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
5051
5152static CRANELIFT_TRACE_ID : AtomicU64 = AtomicU64 :: new ( 1 ) ;
5253static CRANELIFT_JIT_ISA : OnceLock < Result < OwnedTargetIsa , String > > = OnceLock :: new ( ) ;
54+ static CRANELIFT_TAIL_ISA : OnceLock < Result < OwnedTargetIsa , String > > = OnceLock :: new ( ) ;
5355
5456type TaggedConstants = ( Box < [ Value ] > , HashMap < SsaValueId , usize > ) ;
5557
@@ -78,6 +80,148 @@ impl TraceKeepAlive {
7880 }
7981}
8082
83+ pub ( crate ) struct CompiledTailFunction {
84+ entry : * const u8 ,
85+ _keepalive : TraceKeepAlive ,
86+ code : Vec < u8 > ,
87+ }
88+
89+ impl CompiledTailFunction {
90+ pub ( crate ) fn entry ( & self ) -> * const u8 {
91+ self . entry
92+ }
93+
94+ pub ( crate ) fn code_len ( & self ) -> usize {
95+ self . code . len ( )
96+ }
97+ }
98+
99+ fn tail_entry_signature ( pointer_type : cranelift_codegen:: ir:: Type ) -> Signature {
100+ let mut signature = Signature :: new ( CallConv :: Tail ) ;
101+ signature. params . push ( AbiParam :: new ( pointer_type) ) ;
102+ signature. returns . push ( AbiParam :: new ( types:: I32 ) ) ;
103+ signature
104+ }
105+
106+ fn compile_standalone_native_function (
107+ prefix : & str ,
108+ signature : impl FnOnce ( cranelift_codegen:: ir:: Type , CallConv ) -> Signature ,
109+ lower : impl FnOnce ( & mut FunctionBuilder < ' _ > , cranelift_codegen:: ir:: Type , CallConv ) -> VmResult < ( ) > ,
110+ ) -> VmResult < CompiledTailFunction > {
111+ let isa = native_tail_isa ( ) ?;
112+ let jit_builder = JITBuilder :: with_isa ( isa, cranelift_module:: default_libcall_names ( ) ) ;
113+ let mut module = JITModule :: new ( jit_builder) ;
114+ let pointer_type = module. target_config ( ) . pointer_type ( ) ;
115+ let default_call_conv = module. target_config ( ) . default_call_conv ;
116+ let mut ctx = module. make_context ( ) ;
117+ ctx. func . signature = signature ( pointer_type, default_call_conv) ;
118+ let function_id = CRANELIFT_TRACE_ID . fetch_add ( 1 , Ordering :: Relaxed ) ;
119+ let function_name = format ! ( "{prefix}_{function_id}" ) ;
120+ let func_id = module
121+ . declare_function ( & function_name, Linkage :: Local , & ctx. func . signature )
122+ . map_err ( |err| VmError :: JitNative ( format ! ( "declare {prefix} failed: {err}" ) ) ) ?;
123+ {
124+ let mut fb_ctx = FunctionBuilderContext :: new ( ) ;
125+ let mut builder = FunctionBuilder :: new ( & mut ctx. func , & mut fb_ctx) ;
126+ lower ( & mut builder, pointer_type, default_call_conv) ?;
127+ builder. seal_all_blocks ( ) ;
128+ builder. finalize ( ) ;
129+ }
130+ module
131+ . define_function ( func_id, & mut ctx)
132+ . map_err ( |err| VmError :: JitNative ( format ! ( "define {prefix} failed: {err}" ) ) ) ?;
133+ let code_len = ctx
134+ . compiled_code ( )
135+ . ok_or_else ( || VmError :: JitNative ( format ! ( "{prefix} produced no machine code" ) ) ) ?
136+ . code_buffer ( )
137+ . len ( ) ;
138+ module. clear_context ( & mut ctx) ;
139+ module
140+ . finalize_definitions ( )
141+ . map_err ( |err| VmError :: JitNative ( format ! ( "finalize {prefix} failed: {err}" ) ) ) ?;
142+ let module_entry = module. get_finalized_function ( func_id) ;
143+ let code = unsafe { std:: slice:: from_raw_parts ( module_entry, code_len) . to_vec ( ) } ;
144+ let keepalive = TraceKeepAlive :: from_code ( & code, Box :: new ( [ ] ) ) ?;
145+ let entry = keepalive. entry ( ) ;
146+ Ok ( CompiledTailFunction {
147+ entry,
148+ _keepalive : keepalive,
149+ code,
150+ } )
151+ }
152+
153+ pub ( crate ) fn compile_tail_status_body ( status : i32 ) -> VmResult < CompiledTailFunction > {
154+ compile_standalone_native_function (
155+ "pd_vm_tail_status" ,
156+ |pointer_type, _| tail_entry_signature ( pointer_type) ,
157+ move |builder, _, _| {
158+ let entry = builder. create_block ( ) ;
159+ builder. append_block_params_for_function_params ( entry) ;
160+ builder. switch_to_block ( entry) ;
161+ let status = builder. ins ( ) . iconst ( types:: I32 , i64:: from ( status) ) ;
162+ builder. ins ( ) . return_ ( & [ status] ) ;
163+ Ok ( ( ) )
164+ } ,
165+ )
166+ }
167+
168+ pub ( crate ) fn compile_tail_side_link_body (
169+ slot_address : usize ,
170+ deopt_status : i32 ,
171+ ) -> VmResult < CompiledTailFunction > {
172+ compile_standalone_native_function (
173+ "pd_vm_tail_side_link" ,
174+ |pointer_type, _| tail_entry_signature ( pointer_type) ,
175+ move |builder, pointer_type, _| {
176+ let entry = builder. create_block ( ) ;
177+ let deopt = builder. create_block ( ) ;
178+ let linked = builder. create_block ( ) ;
179+ builder. append_block_params_for_function_params ( entry) ;
180+ builder. switch_to_block ( entry) ;
181+ let vm_ptr = builder. block_params ( entry) [ 0 ] ;
182+ let slot_address = iconst_ptr_from_addr ( builder, pointer_type, slot_address) ?;
183+ let target = builder
184+ . ins ( )
185+ . load ( pointer_type, MemFlags :: new ( ) , slot_address, 0 ) ;
186+ let is_null = builder. ins ( ) . icmp_imm ( IntCC :: Equal , target, 0 ) ;
187+ builder. ins ( ) . brif ( is_null, deopt, & [ ] , linked, & [ ] ) ;
188+
189+ builder. switch_to_block ( deopt) ;
190+ let status = builder. ins ( ) . iconst ( types:: I32 , i64:: from ( deopt_status) ) ;
191+ builder. ins ( ) . return_ ( & [ status] ) ;
192+
193+ builder. switch_to_block ( linked) ;
194+ let signature = builder. import_signature ( tail_entry_signature ( pointer_type) ) ;
195+ builder
196+ . ins ( )
197+ . return_call_indirect ( signature, target, & [ vm_ptr] ) ;
198+ Ok ( ( ) )
199+ } ,
200+ )
201+ }
202+
203+ pub ( crate ) fn compile_system_tail_wrapper ( root_entry : * const u8 ) -> VmResult < CompiledTailFunction > {
204+ let root_entry = root_entry as usize ;
205+ compile_standalone_native_function (
206+ "pd_vm_tail_wrapper" ,
207+ entry_signature,
208+ move |builder, pointer_type, _| {
209+ let entry = builder. create_block ( ) ;
210+ builder. append_block_params_for_function_params ( entry) ;
211+ builder. switch_to_block ( entry) ;
212+ let vm_ptr = builder. block_params ( entry) [ 0 ] ;
213+ let root_entry = iconst_ptr_from_addr ( builder, pointer_type, root_entry) ?;
214+ let signature = builder. import_signature ( tail_entry_signature ( pointer_type) ) ;
215+ let call = builder
216+ . ins ( )
217+ . call_indirect ( signature, root_entry, & [ vm_ptr] ) ;
218+ let status = builder. inst_results ( call) [ 0 ] ;
219+ builder. ins ( ) . return_ ( & [ status] ) ;
220+ Ok ( ( ) )
221+ } ,
222+ )
223+ }
224+
81225fn try_compile_ssa_trace (
82226 trace : & JitTrace ,
83227 ssa : & SsaTrace ,
@@ -4950,6 +5094,24 @@ pub(crate) fn compile_trace(
49505094 } )
49515095}
49525096
5097+ fn native_tail_isa ( ) -> VmResult < OwnedTargetIsa > {
5098+ let cached = CRANELIFT_TAIL_ISA . get_or_init ( || {
5099+ let mut flag_builder = settings:: builder ( ) ;
5100+ flag_builder
5101+ . set ( "opt_level" , "speed" )
5102+ . map_err ( |err| format ! ( "failed to set cranelift opt_level: {err}" ) ) ?;
5103+ flag_builder
5104+ . set ( "preserve_frame_pointers" , "true" )
5105+ . map_err ( |err| format ! ( "failed to preserve tail-call frame pointers: {err}" ) ) ?;
5106+ let isa_builder = cranelift_native:: builder ( )
5107+ . map_err ( |err| format ! ( "failed to build native tail ISA: {err}" ) ) ?;
5108+ isa_builder
5109+ . finish ( settings:: Flags :: new ( flag_builder) )
5110+ . map_err ( |err| format ! ( "failed to finalize cranelift tail ISA: {err}" ) )
5111+ } ) ;
5112+ cached. clone ( ) . map_err ( VmError :: JitNative )
5113+ }
5114+
49535115fn native_isa ( profile : NativeCompileProfile ) -> VmResult < OwnedTargetIsa > {
49545116 let cached = match profile {
49555117 NativeCompileProfile :: Jit => & CRANELIFT_JIT_ISA ,
0 commit comments