Skip to content

Commit b826fc9

Browse files
authored
Inline {enter,exit}_sync_call trampolines into sync-to-sync fused adapters (#13695)
* Add tests for guest-to-guest sync-call context preservation * Add `FuncKey::FactInlineIntrinsic` for inline FACT adapter intrinsics * Add `VMLazyThread`/`VMDeferredThread` to `VMStoreContext` * Route host current-thread reads through `StoreOpaque::current_thread` * Promote deferred guest threads on demand in `current_thread` * Inline `{enter,exit}_sync_call` trampoline calls in sync adapters This commit inlines fast paths for the `{enter,exit}_sync_call` component trampolines in sync-to-sync fused adapters. For cross-component calls of trivial functions, after inlining, we should avoid all calls in the fused adapter (other than the `trap` libcall when `may_leave` is set). The inlining happens during translation, rather than via the regular inliner, since the lazy thread is stack-allocated within the fused adapter; there would otherwise be no easy way to reference it. Fixes #12311 * cargo fmt * first set of review feedback * Move the reset-current-thread-on-trap into the array-to-wasm trampoline * fix compilation after rebase * fix alias regions after rebase, use existing `vm_store_ctx` * fix more disas tests after rebase * fix ci * cargo fmt
1 parent fabee3a commit b826fc9

77 files changed

Lines changed: 3090 additions & 706 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/cranelift/src/alias_region.rs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ enum VmType {
2121
VMMemoryDefinition,
2222
VMTableDefinition,
2323
VMComponentContext,
24+
VMDeferredThread,
2425
}
2526

2627
/// A key that uniquely identifies an alias region across an entire compilation.
@@ -111,6 +112,7 @@ impl AliasRegionKey {
111112
const VM_MEMORY_DEFINITION_KIND: u32 = Self::new_kind(0b1001);
112113
const VM_TABLE_DEFINITION_KIND: u32 = Self::new_kind(0b1010);
113114
const VM_COMPONENT_CONTEXT_KIND: u32 = Self::new_kind(0b1011);
115+
const VM_DEFERRED_THREAD_KIND: u32 = Self::new_kind(0b1100);
114116

115117
/// Encode this key into a raw `u32` suitable for use as an
116118
/// `AliasRegionData::user_id`.
@@ -124,6 +126,7 @@ impl AliasRegionKey {
124126
VmType::VMMemoryDefinition => Self::VM_MEMORY_DEFINITION_KIND,
125127
VmType::VMTableDefinition => Self::VM_TABLE_DEFINITION_KIND,
126128
VmType::VMComponentContext => Self::VM_COMPONENT_CONTEXT_KIND,
129+
VmType::VMDeferredThread => Self::VM_DEFERRED_THREAD_KIND,
127130
};
128131
kind | (offset & Self::OFFSET_MASK)
129132
}
@@ -1099,6 +1102,44 @@ where
10991102
)
11001103
}
11011104

1105+
/// Load the `VMStoreContext::current_thread` field (the JIT-visible
1106+
/// deferred-thread pointer; see `VMLazyThread`).
1107+
pub fn vmstore_context_current_thread(
1108+
&mut self,
1109+
cursor: &mut FuncCursor<'_>,
1110+
vmstore_ctx: ir::Value,
1111+
) -> ir::Value {
1112+
self.vmstore_context_load(
1113+
cursor,
1114+
self.pointer_type,
1115+
ir::MemFlagsData::trusted(),
1116+
vmstore_ctx,
1117+
self.offsets
1118+
.get_ptr_size()
1119+
.vmstore_context_current_thread()
1120+
.into(),
1121+
)
1122+
}
1123+
1124+
/// Store the `VMStoreContext::current_thread` field.
1125+
pub fn store_vmstore_context_current_thread(
1126+
&mut self,
1127+
cursor: &mut FuncCursor<'_>,
1128+
vmstore_ctx: ir::Value,
1129+
new_thread: ir::Value,
1130+
) {
1131+
self.vmstore_context_store(
1132+
cursor,
1133+
ir::MemFlagsData::trusted(),
1134+
vmstore_ctx,
1135+
self.offsets
1136+
.get_ptr_size()
1137+
.vmstore_context_current_thread()
1138+
.into(),
1139+
new_thread,
1140+
)
1141+
}
1142+
11021143
/// Get a `Load` of the GC heap base pointer (`VMStoreContext::gc_heap.base`).
11031144
///
11041145
/// The caller supplies the base flags because whether the base pointer is
@@ -1333,6 +1374,194 @@ where
13331374
}
13341375
}
13351376

1377+
/// `VMDeferredThread`-related methods.
1378+
impl<Offsets> AliasRegions<Offsets>
1379+
where
1380+
Offsets: GetPtrSize,
1381+
{
1382+
fn vmdeferred_thread_region(
1383+
&mut self,
1384+
func: &mut ir::Function,
1385+
offset: u32,
1386+
) -> ir::AliasRegion {
1387+
self.region(
1388+
func,
1389+
AliasRegionKey::Vm {
1390+
ty: VmType::VMDeferredThread,
1391+
offset,
1392+
},
1393+
)
1394+
}
1395+
1396+
fn vmdeferred_thread_load(
1397+
&mut self,
1398+
cursor: &mut FuncCursor<'_>,
1399+
ty: ir::Type,
1400+
base_flags: ir::MemFlagsData,
1401+
vmdeferred_thread_ptr: ir::Value,
1402+
offset: u32,
1403+
) -> ir::Value {
1404+
let region = self.vmdeferred_thread_region(cursor.func, offset);
1405+
cursor.ins().load(
1406+
ty,
1407+
base_flags.with_alias_region(Some(region)),
1408+
vmdeferred_thread_ptr,
1409+
i32::try_from(offset).unwrap(),
1410+
)
1411+
}
1412+
1413+
fn vmdeferred_thread_store(
1414+
&mut self,
1415+
cursor: &mut FuncCursor<'_>,
1416+
base_flags: ir::MemFlagsData,
1417+
vmdeferred_thread_ptr: ir::Value,
1418+
offset: u32,
1419+
val: ir::Value,
1420+
) {
1421+
let region = self.vmdeferred_thread_region(cursor.func, offset);
1422+
cursor.ins().store(
1423+
base_flags.with_alias_region(Some(region)),
1424+
val,
1425+
vmdeferred_thread_ptr,
1426+
i32::try_from(offset).unwrap(),
1427+
);
1428+
}
1429+
1430+
/// Load `VMDeferredThread::parent` (the current thread this frame replaced).
1431+
pub fn vmdeferred_thread_parent(
1432+
&mut self,
1433+
cursor: &mut FuncCursor<'_>,
1434+
vmdeferred_thread_ptr: ir::Value,
1435+
) -> ir::Value {
1436+
self.vmdeferred_thread_load(
1437+
cursor,
1438+
self.pointer_type,
1439+
ir::MemFlagsData::trusted(),
1440+
vmdeferred_thread_ptr,
1441+
self.offsets
1442+
.get_ptr_size()
1443+
.vmdeferred_thread_parent()
1444+
.into(),
1445+
)
1446+
}
1447+
1448+
/// Store `VMDeferredThread::parent`.
1449+
pub fn store_vmdeferred_thread_parent(
1450+
&mut self,
1451+
cursor: &mut FuncCursor<'_>,
1452+
vmdeferred_thread_ptr: ir::Value,
1453+
parent: ir::Value,
1454+
) {
1455+
self.vmdeferred_thread_store(
1456+
cursor,
1457+
ir::MemFlagsData::trusted(),
1458+
vmdeferred_thread_ptr,
1459+
self.offsets
1460+
.get_ptr_size()
1461+
.vmdeferred_thread_parent()
1462+
.into(),
1463+
parent,
1464+
)
1465+
}
1466+
1467+
/// Store `VMDeferredThread::caller_instance`.
1468+
pub fn store_vmdeferred_thread_caller_instance(
1469+
&mut self,
1470+
cursor: &mut FuncCursor<'_>,
1471+
vmdeferred_thread_ptr: ir::Value,
1472+
caller_instance: ir::Value,
1473+
) {
1474+
self.vmdeferred_thread_store(
1475+
cursor,
1476+
ir::MemFlagsData::trusted(),
1477+
vmdeferred_thread_ptr,
1478+
self.offsets
1479+
.get_ptr_size()
1480+
.vmdeferred_thread_caller_instance()
1481+
.into(),
1482+
caller_instance,
1483+
)
1484+
}
1485+
1486+
/// Store `VMDeferredThread::callee_async`.
1487+
pub fn store_vmdeferred_thread_callee_async(
1488+
&mut self,
1489+
cursor: &mut FuncCursor<'_>,
1490+
vmdeferred_thread_ptr: ir::Value,
1491+
callee_async: ir::Value,
1492+
) {
1493+
self.vmdeferred_thread_store(
1494+
cursor,
1495+
ir::MemFlagsData::trusted(),
1496+
vmdeferred_thread_ptr,
1497+
self.offsets
1498+
.get_ptr_size()
1499+
.vmdeferred_thread_callee_async()
1500+
.into(),
1501+
callee_async,
1502+
)
1503+
}
1504+
1505+
/// Store `VMDeferredThread::callee_instance`.
1506+
pub fn store_vmdeferred_thread_callee_instance(
1507+
&mut self,
1508+
cursor: &mut FuncCursor<'_>,
1509+
vmdeferred_thread_ptr: ir::Value,
1510+
callee_instance: ir::Value,
1511+
) {
1512+
self.vmdeferred_thread_store(
1513+
cursor,
1514+
ir::MemFlagsData::trusted(),
1515+
vmdeferred_thread_ptr,
1516+
self.offsets
1517+
.get_ptr_size()
1518+
.vmdeferred_thread_callee_instance()
1519+
.into(),
1520+
callee_instance,
1521+
)
1522+
}
1523+
1524+
/// Load `VMDeferredThread::saved_context[i]` (a saved `context.{get,set}`
1525+
/// slot).
1526+
pub fn vmdeferred_thread_saved_context(
1527+
&mut self,
1528+
cursor: &mut FuncCursor<'_>,
1529+
vmdeferred_thread_ptr: ir::Value,
1530+
i: u8,
1531+
) -> ir::Value {
1532+
self.vmdeferred_thread_load(
1533+
cursor,
1534+
ir::types::I32,
1535+
ir::MemFlagsData::trusted(),
1536+
vmdeferred_thread_ptr,
1537+
self.offsets
1538+
.get_ptr_size()
1539+
.vmdeferred_thread_saved_context(i)
1540+
.into(),
1541+
)
1542+
}
1543+
1544+
/// Store `VMDeferredThread::saved_context[i]`.
1545+
pub fn store_vmdeferred_thread_saved_context(
1546+
&mut self,
1547+
cursor: &mut FuncCursor<'_>,
1548+
vmdeferred_thread_ptr: ir::Value,
1549+
i: u8,
1550+
val: ir::Value,
1551+
) {
1552+
self.vmdeferred_thread_store(
1553+
cursor,
1554+
ir::MemFlagsData::trusted(),
1555+
vmdeferred_thread_ptr,
1556+
self.offsets
1557+
.get_ptr_size()
1558+
.vmdeferred_thread_saved_context(i)
1559+
.into(),
1560+
val,
1561+
)
1562+
}
1563+
}
1564+
13361565
/// `VMMemoryDefinition`-related methods.
13371566
///
13381567
/// The `base` and `current_length` fields are reached either directly through

crates/cranelift/src/compiler.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,20 @@ impl Compiler {
15231523
// succeed". Note that register restoration is part of the `try_call`
15241524
// and handler implementation.
15251525
builder.switch_to_block(exceptional_return);
1526+
if cfg!(feature = "component-model") && self.tunables().concurrency_support {
1527+
// NB: A trap unwinds over any fused adapter frames without running
1528+
// their `exit-sync-call`s. That can leave
1529+
// `VMStoreContext::current_thread` pointing at an old, invalid
1530+
// `VMDeferredThread` inside an old, since-unwound stack
1531+
// frame. Therefore, we must reset `current_thread` to avoid
1532+
// potential use-after-free bugs.
1533+
let forced = builder.ins().iconst(pointer_type, 1);
1534+
alias_regions.store_vmstore_context_current_thread(
1535+
&mut builder.cursor(),
1536+
vm_store_ctx,
1537+
forced,
1538+
);
1539+
}
15261540
let false_return = builder.ins().iconst(ir::types::I8, 0);
15271541
builder.ins().return_(&[false_return]);
15281542

0 commit comments

Comments
 (0)