Skip to content

Commit 5b2f2c7

Browse files
committed
feat(jit): tail-link native trace exits
1 parent 39b1848 commit 5b2f2c7

6 files changed

Lines changed: 671 additions & 19 deletions

File tree

src/vm/jit/native/lower.rs

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type TaggedConstants = (Box<[Value]>, HashMap<SsaValueId, usize>);
5757

5858
pub(crate) struct CompiledTrace {
5959
pub(crate) entry: *const u8,
60+
pub(crate) tail_entry: *const u8,
6061
pub(crate) keepalive: TraceKeepAlive,
6162
pub(crate) code: Vec<u8>,
6263
pub(crate) lowering_kind: TraceLoweringKind,
@@ -65,13 +66,15 @@ pub(crate) struct CompiledTrace {
6566
pub(crate) struct TraceKeepAlive {
6667
exec: ExecutableBuffer,
6768
_tagged_constants: Box<[Value]>,
69+
dependencies: Vec<TraceKeepAlive>,
6870
}
6971

7072
impl TraceKeepAlive {
7173
fn from_code(code: &[u8], tagged_constants: Box<[Value]>) -> VmResult<Self> {
7274
Ok(Self {
7375
exec: ExecutableBuffer::new(code)?,
7476
_tagged_constants: tagged_constants,
77+
dependencies: Vec::new(),
7578
})
7679
}
7780

@@ -94,6 +97,10 @@ impl CompiledTailFunction {
9497
pub(crate) fn code_len(&self) -> usize {
9598
self.code.len()
9699
}
100+
101+
pub(crate) fn into_parts(self) -> (*const u8, TraceKeepAlive, Vec<u8>) {
102+
(self.entry, self._keepalive, self.code)
103+
}
97104
}
98105

99106
fn tail_entry_signature(pointer_type: cranelift_codegen::ir::Type) -> Signature {
@@ -222,6 +229,89 @@ pub(crate) fn compile_system_tail_wrapper(root_entry: *const u8) -> VmResult<Com
222229
)
223230
}
224231

232+
pub(crate) fn compile_tail_trace_dispatcher(
233+
trace_entry: *const u8,
234+
trace_id: usize,
235+
links: &[(i32, usize)],
236+
) -> VmResult<CompiledTailFunction> {
237+
let trace_entry = trace_entry as usize;
238+
let links = links.to_vec();
239+
let direct_link_offset = detect_native_stack_layout()?.vm_jit_native_direct_link_count_offset;
240+
let active_trace_offset =
241+
detect_native_stack_layout()?.vm_jit_native_active_direct_trace_id_offset;
242+
compile_standalone_native_function(
243+
"pd_vm_tail_trace_dispatch",
244+
|pointer_type, _| tail_entry_signature(pointer_type),
245+
move |builder, pointer_type, default_call_conv| {
246+
let entry = builder.create_block();
247+
let return_status = builder.create_block();
248+
builder.append_block_params_for_function_params(entry);
249+
builder.append_block_param(return_status, types::I32);
250+
builder.switch_to_block(entry);
251+
let vm_ptr = builder.block_params(entry)[0];
252+
let trace_id = i64::try_from(trace_id)
253+
.map_err(|_| VmError::JitNative("native trace id exceeds i64".to_string()))?;
254+
let trace_id = builder.ins().iconst(pointer_type, trace_id);
255+
builder
256+
.ins()
257+
.store(MemFlags::new(), trace_id, vm_ptr, active_trace_offset);
258+
let trace_entry = iconst_ptr_from_addr(builder, pointer_type, trace_entry)?;
259+
let trace_signature =
260+
builder.import_signature(entry_signature(pointer_type, default_call_conv));
261+
let call = builder
262+
.ins()
263+
.call_indirect(trace_signature, trace_entry, &[vm_ptr]);
264+
let status = builder.inst_results(call)[0];
265+
266+
for (linked_status, slot_address) in links {
267+
let slot_check = builder.create_block();
268+
let next = builder.create_block();
269+
let linked = builder.create_block();
270+
builder.append_block_param(next, types::I32);
271+
let matches =
272+
builder
273+
.ins()
274+
.icmp_imm(IntCC::Equal, status, i64::from(linked_status));
275+
builder
276+
.ins()
277+
.brif(matches, slot_check, &[], next, &[status.into()]);
278+
279+
builder.switch_to_block(slot_check);
280+
let slot_address = iconst_ptr_from_addr(builder, pointer_type, slot_address)?;
281+
let target = builder
282+
.ins()
283+
.load(pointer_type, MemFlags::new(), slot_address, 0);
284+
let is_null = builder.ins().icmp_imm(IntCC::Equal, target, 0);
285+
builder
286+
.ins()
287+
.brif(is_null, return_status, &[status.into()], linked, &[]);
288+
289+
builder.switch_to_block(linked);
290+
let direct_count =
291+
builder
292+
.ins()
293+
.load(types::I64, MemFlags::new(), vm_ptr, direct_link_offset);
294+
let direct_count = builder.ins().iadd_imm(direct_count, 1);
295+
builder
296+
.ins()
297+
.store(MemFlags::new(), direct_count, vm_ptr, direct_link_offset);
298+
let tail_signature = builder.import_signature(tail_entry_signature(pointer_type));
299+
builder
300+
.ins()
301+
.return_call_indirect(tail_signature, target, &[vm_ptr]);
302+
303+
builder.switch_to_block(next);
304+
}
305+
306+
builder.ins().jump(return_status, &[status.into()]);
307+
builder.switch_to_block(return_status);
308+
let status = builder.block_params(return_status)[0];
309+
builder.ins().return_(&[status]);
310+
Ok(())
311+
},
312+
)
313+
}
314+
225315
fn tail_owned_entry_signature(pointer_type: cranelift_codegen::ir::Type) -> Signature {
226316
let mut signature = tail_entry_signature(pointer_type);
227317
signature.params.push(AbiParam::new(pointer_type));
@@ -343,7 +433,7 @@ fn try_compile_ssa_trace(
343433
ssa: &SsaTrace,
344434
internal_links: &[FusedRegionLink],
345435
interrupt_settings: Option<NativeInterruptSettings>,
346-
profile: NativeCompileProfile,
436+
_profile: NativeCompileProfile,
347437
drop_contract_events_enabled: bool,
348438
) -> VmResult<Option<CompiledTrace>> {
349439
if drop_contract_events_enabled {
@@ -355,15 +445,15 @@ fn try_compile_ssa_trace(
355445

356446
let layout = detect_native_stack_layout()?;
357447
let offsets = resolve_offsets(layout)?;
358-
let isa = native_isa(profile)?;
448+
let isa = native_tail_isa()?;
359449

360450
let jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
361451
let mut module = JITModule::new(jit_builder);
362452
let pointer_type = module.target_config().pointer_type();
363453
let call_conv = module.target_config().default_call_conv;
364454

365455
let mut ctx = module.make_context();
366-
ctx.func.signature = entry_signature(pointer_type, call_conv);
456+
ctx.func.signature = tail_entry_signature(pointer_type);
367457
let clone_value_sig = clone_value_signature(pointer_type, call_conv);
368458
let non_yielding_host_call_sig = non_yielding_host_call_signature(pointer_type, call_conv);
369459
let value_slot_sig = value_slot_signature(pointer_type, call_conv);
@@ -762,6 +852,7 @@ fn try_compile_ssa_trace(
762852

763853
Ok(Some(CompiledTrace {
764854
entry,
855+
tail_entry: entry,
765856
keepalive,
766857
code,
767858
lowering_kind: TraceLoweringKind::Ssa,
@@ -5194,7 +5285,7 @@ pub(crate) fn compile_trace(
51945285
return Err(VmError::InvalidFuelCheckInterval(0));
51955286
}
51965287

5197-
try_compile_ssa_trace(
5288+
let body = try_compile_ssa_trace(
51985289
trace,
51995290
&trace.ssa,
52005291
internal_links,
@@ -5207,6 +5298,19 @@ pub(crate) fn compile_trace(
52075298
"SSA native lowering does not support trace {} at root_ip {}",
52085299
trace.id, trace.root_ip
52095300
))
5301+
})?;
5302+
let tail_entry = body.tail_entry;
5303+
let lowering_kind = body.lowering_kind;
5304+
let mut code = body.code;
5305+
let mut wrapper = compile_system_tail_wrapper(tail_entry)?;
5306+
code.extend_from_slice(&wrapper.code);
5307+
wrapper._keepalive.dependencies.push(body.keepalive);
5308+
Ok(CompiledTrace {
5309+
entry: wrapper.entry,
5310+
tail_entry,
5311+
keepalive: wrapper._keepalive,
5312+
code,
5313+
lowering_kind,
52105314
})
52115315
}
52125316

src/vm/jit/native/mod.rs

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#![allow(dead_code)]
2-
#[cfg(not(feature = "cranelift-jit"))]
3-
use super::super::super::VmError;
4-
use super::super::super::VmResult;
2+
use super::super::super::{VmError, VmResult};
53
use super::ir::{SsaMaterialization, SsaValueRepr};
64
use crate::ValueType;
5+
use std::collections::HashMap;
6+
use std::sync::Arc;
77
use std::sync::atomic::{AtomicPtr, Ordering};
88

99
pub(crate) use crate::vm::native::{
@@ -115,6 +115,67 @@ impl Default for NativeSideLinkSlot {
115115
}
116116
}
117117

118+
pub(crate) const LINKED_CONTINUE_SLOT_ID: u32 = u32::MAX;
119+
pub(crate) const CONTINUE_SLOT_ID: u32 = u32::MAX - 1;
120+
121+
pub(crate) struct CompiledTraceDispatcher {
122+
pub(crate) entry: *const u8,
123+
pub(crate) tail_entry: *const u8,
124+
pub(crate) keepalives: Vec<TraceKeepAlive>,
125+
pub(crate) code: Vec<u8>,
126+
pub(crate) slots: HashMap<u32, Arc<NativeSideLinkSlot>>,
127+
}
128+
129+
#[cfg(feature = "cranelift-jit")]
130+
pub(crate) fn compile_native_trace_dispatcher(
131+
trace_id: usize,
132+
trace_entry: *const u8,
133+
trace: &super::JitTrace,
134+
) -> VmResult<CompiledTraceDispatcher> {
135+
let mut slots = HashMap::new();
136+
let mut descriptors = Vec::with_capacity(trace.ssa.exits.len());
137+
for exit in &trace.ssa.exits {
138+
let status =
139+
crate::vm::native::encode_jit_trace_exit_status(exit.id.raw()).ok_or_else(|| {
140+
VmError::JitNative("SSA exit id exceeds native status range".to_string())
141+
})?;
142+
let slot = Arc::new(NativeSideLinkSlot::new());
143+
descriptors.push((status, slot.address() as usize));
144+
slots.insert(exit.id.raw(), slot);
145+
}
146+
let slot = Arc::new(NativeSideLinkSlot::new());
147+
descriptors.push((STATUS_LINKED_CONTINUE, slot.address() as usize));
148+
slots.insert(LINKED_CONTINUE_SLOT_ID, slot);
149+
let slot = Arc::new(NativeSideLinkSlot::new());
150+
descriptors.push((STATUS_CONTINUE, slot.address() as usize));
151+
slots.insert(CONTINUE_SLOT_ID, slot);
152+
let dispatcher = lower::compile_tail_trace_dispatcher(trace_entry, trace_id, &descriptors)?;
153+
let tail_entry = dispatcher.entry();
154+
let wrapper = lower::compile_system_tail_wrapper(tail_entry)?;
155+
let (tail_entry, dispatcher_keepalive, dispatcher_code) = dispatcher.into_parts();
156+
let (entry, wrapper_keepalive, wrapper_code) = wrapper.into_parts();
157+
let mut code = dispatcher_code;
158+
code.extend_from_slice(&wrapper_code);
159+
Ok(CompiledTraceDispatcher {
160+
entry,
161+
tail_entry,
162+
keepalives: vec![dispatcher_keepalive, wrapper_keepalive],
163+
code,
164+
slots,
165+
})
166+
}
167+
168+
#[cfg(not(feature = "cranelift-jit"))]
169+
pub(crate) fn compile_native_trace_dispatcher(
170+
_trace_id: usize,
171+
_trace_entry: *const u8,
172+
_trace: &super::JitTrace,
173+
) -> VmResult<CompiledTraceDispatcher> {
174+
Err(VmError::JitNative(
175+
"native JIT backend is disabled (feature 'cranelift-jit' is not enabled)".to_string(),
176+
))
177+
}
178+
118179
#[cfg(feature = "cranelift-jit")]
119180
pub(crate) use lower::{CompiledTrace, TraceKeepAlive};
120181

@@ -124,6 +185,7 @@ pub(crate) struct TraceKeepAlive;
124185
#[cfg(not(feature = "cranelift-jit"))]
125186
pub(crate) struct CompiledTrace {
126187
pub entry: *const u8,
188+
pub tail_entry: *const u8,
127189
pub code: Vec<u8>,
128190
pub keepalive: TraceKeepAlive,
129191
pub lowering_kind: TraceLoweringKind,

0 commit comments

Comments
 (0)