@@ -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