Skip to content

Commit cc7dac1

Browse files
Translator: Extract frame preparation for return-call-like instructions
1 parent 89ceebf commit cc7dac1

File tree

1 file changed

+34
-32
lines changed

1 file changed

+34
-32
lines changed

Sources/WasmKit/Translator.swift

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,61 +1518,63 @@ struct InstructionTranslator<Context: TranslatorContext>: InstructionVisitor {
15181518
emit(.callIndirect(operand))
15191519
}
15201520

1521-
mutating func visitReturnCall(functionIndex: UInt32) throws {
1522-
let calleeType = try self.module.functionType(functionIndex, interner: funcTypeInterner)
1523-
try validator.validateReturnCallLike(calleeType: calleeType, callerType: type)
1524-
1525-
guard let callee = self.module.resolveCallee(functionIndex) else {
1526-
// Skip actual code emission if validation-only mode
1527-
return
1528-
}
1529-
1521+
/// Emit instructions to prepare the frame header for a return call to replace the
1522+
/// current frame header with the callee's frame header layout.
1523+
///
1524+
/// The frame header should have the callee's frame header layout and parameter
1525+
/// slots are filled with arguments on the caller's stack.
1526+
///
1527+
/// - Parameters:
1528+
/// - calleeType: The type of the callee function.
1529+
/// - stackTopHeightToCopy: The height of the stack top needed to be available at the
1530+
/// return-call-like instruction point.
1531+
private mutating func prepareFrameHeaderForReturnCall(calleeType: FunctionType, stackTopHeightToCopy: Int) throws {
15301532
let calleeFrameHeader = FrameHeaderLayout(type: calleeType)
15311533
if calleeType == self.type {
15321534
// Fast path: If the callee and the caller have the same signature, we can
15331535
// skip reconstructing the frame header and we can just copy the parameters.
1534-
try copyValuesIntoResultSlots(calleeType.parameters, frameHeader: calleeFrameHeader)
1535-
emit(.returnCall(Instruction.ReturnCallOperand(callee: callee)))
15361536
} else {
15371537
// Ensure all parameters are on stack to avoid conflicting with the next resize.
15381538
preserveOnStack(depth: calleeType.parameters.count)
15391539
// Resize the current frame header while moving stack slots after the header
15401540
// to the resized positions
15411541
let newHeaderSize = FrameHeaderLayout.size(of: calleeType)
15421542
let delta = newHeaderSize - FrameHeaderLayout.size(of: type)
1543-
let sizeToCopy = VReg(FrameHeaderLayout.numberOfSavingSlots) + valueStack.stackRegBase + VReg(valueStack.height)
1543+
let sizeToCopy = VReg(FrameHeaderLayout.numberOfSavingSlots) + valueStack.stackRegBase + VReg(stackTopHeightToCopy)
15441544
emit(.resizeFrameHeader(Instruction.ResizeFrameHeaderOperand(delta: delta, sizeToCopy: sizeToCopy)))
1545-
try copyValuesIntoResultSlots(calleeType.parameters, frameHeader: calleeFrameHeader)
1546-
emit(.returnCall(Instruction.ReturnCallOperand(callee: callee)))
15471545
}
1546+
try copyValuesIntoResultSlots(calleeType.parameters, frameHeader: calleeFrameHeader)
1547+
}
1548+
1549+
mutating func visitReturnCall(functionIndex: UInt32) throws {
1550+
let calleeType = try self.module.functionType(functionIndex, interner: funcTypeInterner)
1551+
try validator.validateReturnCallLike(calleeType: calleeType, callerType: type)
1552+
1553+
guard let callee = self.module.resolveCallee(functionIndex) else {
1554+
// Skip actual code emission if validation-only mode
1555+
return
1556+
}
1557+
try prepareFrameHeaderForReturnCall(calleeType: calleeType, stackTopHeightToCopy: valueStack.height)
1558+
emit(.returnCall(Instruction.ReturnCallOperand(callee: callee)))
15481559
try markUnreachable()
15491560
}
15501561

15511562
mutating func visitReturnCallIndirect(typeIndex: UInt32, tableIndex: UInt32) throws {
1563+
let stackTopHeightToCopy = valueStack.height
15521564
let addressType = try module.addressType(tableIndex: tableIndex)
15531565
// Preserve function index slot on stack
15541566
let address = try popOnStackOperand(addressType) // function address
1555-
let calleeType = try self.module.resolveType(typeIndex)
15561567
guard let address = address else { return }
1568+
1569+
let calleeType = try self.module.resolveType(typeIndex)
15571570
let internType = funcTypeInterner.intern(calleeType)
15581571

1559-
let calleeFrameHeader = FrameHeaderLayout(type: calleeType)
1560-
if calleeType == self.type {
1561-
// Fast path: If the callee and the caller have the same signature, we can
1562-
// skip reconstructing the frame header and we can just copy the parameters.
1563-
try copyValuesIntoResultSlots(calleeType.parameters, frameHeader: calleeFrameHeader)
1564-
} else {
1565-
// Ensure all parameters are on stack to avoid conflicting with the next resize.
1566-
preserveOnStack(depth: calleeType.parameters.count)
1567-
// Resize the current frame header while moving stack slots after the header
1568-
// to the resized positions
1569-
let newHeaderSize = FrameHeaderLayout.size(of: calleeType)
1570-
let delta = newHeaderSize - FrameHeaderLayout.size(of: type)
1571-
// +1 for address slot as it's used by return_call_indirect executed after resize_frame_header
1572-
let sizeToCopy = 1 + VReg(FrameHeaderLayout.numberOfSavingSlots) + valueStack.stackRegBase + VReg(valueStack.height)
1573-
emit(.resizeFrameHeader(Instruction.ResizeFrameHeaderOperand(delta: delta, sizeToCopy: sizeToCopy)))
1574-
try copyValuesIntoResultSlots(calleeType.parameters, frameHeader: calleeFrameHeader)
1575-
}
1572+
try prepareFrameHeaderForReturnCall(
1573+
calleeType: calleeType,
1574+
// Keep the stack space including the function index slot to be
1575+
// accessible at the `return_call_indirect` instruction point.
1576+
stackTopHeightToCopy: stackTopHeightToCopy
1577+
)
15761578

15771579
let operand = Instruction.ReturnCallIndirectOperand(
15781580
tableIndex: tableIndex,

0 commit comments

Comments
 (0)