@@ -345,7 +345,19 @@ pub enum Insn {
345345 CPushAll ,
346346
347347 // C function call with N arguments (variadic)
348- CCall { opnds : Vec < Opnd > , fptr : * const u8 , out : Opnd } ,
348+ CCall {
349+ opnds : Vec < Opnd > ,
350+ fptr : * const u8 ,
351+ /// Optional PosMarker to remember the start address of the C call.
352+ /// It's embedded here to insert the PosMarker after push instructions
353+ /// that are split from this CCall on alloc_regs().
354+ start_marker : Option < PosMarkerFn > ,
355+ /// Optional PosMarker to remember the end address of the C call.
356+ /// It's embedded here to insert the PosMarker before pop instructions
357+ /// that are split from this CCall on alloc_regs().
358+ end_marker : Option < PosMarkerFn > ,
359+ out : Opnd ,
360+ } ,
349361
350362 // C function return
351363 CRet ( Opnd ) ,
@@ -1455,6 +1467,23 @@ impl Assembler
14551467 let mut asm = Assembler :: new_with_label_names ( take ( & mut self . label_names ) , live_ranges. len ( ) ) ;
14561468
14571469 while let Some ( ( index, mut insn) ) = iterator. next ( ) {
1470+ let before_ccall = match ( & insn, iterator. peek ( ) . map ( |( _, insn) | insn) ) {
1471+ ( Insn :: ParallelMov { .. } , Some ( Insn :: CCall { .. } ) ) |
1472+ ( Insn :: CCall { .. } , _) if !pool. is_empty ( ) => {
1473+ // If C_RET_REG is in use, move it to another register.
1474+ // This must happen before last-use registers are deallocated.
1475+ if let Some ( vreg_idx) = pool. vreg_for ( & C_RET_REG ) {
1476+ let new_reg = pool. alloc_reg ( vreg_idx) . unwrap ( ) ; // TODO: support spill
1477+ asm. mov ( Opnd :: Reg ( new_reg) , C_RET_OPND ) ;
1478+ pool. dealloc_reg ( & C_RET_REG ) ;
1479+ reg_mapping[ vreg_idx] = Some ( new_reg) ;
1480+ }
1481+
1482+ true
1483+ } ,
1484+ _ => false ,
1485+ } ;
1486+
14581487 // Check if this is the last instruction that uses an operand that
14591488 // spans more than one instruction. In that case, return the
14601489 // allocated register to the pool.
@@ -1477,32 +1506,20 @@ impl Assembler
14771506 }
14781507 }
14791508
1480- // If we're about to make a C call, save caller-saved registers
1481- match ( & insn, iterator. peek ( ) . map ( |( _, insn) | insn) ) {
1482- ( Insn :: ParallelMov { .. } , Some ( Insn :: CCall { .. } ) ) |
1483- ( Insn :: CCall { .. } , _) if !pool. is_empty ( ) => {
1484- // If C_RET_REG is in use, move it to another register
1485- if let Some ( vreg_idx) = pool. vreg_for ( & C_RET_REG ) {
1486- let new_reg = pool. alloc_reg ( vreg_idx) . unwrap ( ) ; // TODO: support spill
1487- asm. mov ( Opnd :: Reg ( new_reg) , C_RET_OPND ) ;
1488- pool. dealloc_reg ( & C_RET_REG ) ;
1489- reg_mapping[ vreg_idx] = Some ( new_reg) ;
1490- }
1491-
1492- // Find all live registers
1493- saved_regs = pool. live_regs ( ) ;
1509+ // Save caller-saved registers on a C call.
1510+ if before_ccall {
1511+ // Find all live registers
1512+ saved_regs = pool. live_regs ( ) ;
14941513
1495- // Save live registers
1496- for & ( reg, _) in saved_regs. iter ( ) {
1497- asm. cpush ( Opnd :: Reg ( reg) ) ;
1498- pool. dealloc_reg ( & reg) ;
1499- }
1500- // On x86_64, maintain 16-byte stack alignment
1501- if cfg ! ( target_arch = "x86_64" ) && saved_regs. len ( ) % 2 == 1 {
1502- asm. cpush ( Opnd :: Reg ( saved_regs. last ( ) . unwrap ( ) . 0 ) ) ;
1503- }
1514+ // Save live registers
1515+ for & ( reg, _) in saved_regs. iter ( ) {
1516+ asm. cpush ( Opnd :: Reg ( reg) ) ;
1517+ pool. dealloc_reg ( & reg) ;
1518+ }
1519+ // On x86_64, maintain 16-byte stack alignment
1520+ if cfg ! ( target_arch = "x86_64" ) && saved_regs. len ( ) % 2 == 1 {
1521+ asm. cpush ( Opnd :: Reg ( saved_regs. last ( ) . unwrap ( ) . 0 ) ) ;
15041522 }
1505- _ => { } ,
15061523 }
15071524
15081525 // If the output VReg of this instruction is used by another instruction,
@@ -1590,13 +1607,24 @@ impl Assembler
15901607
15911608 // Push instruction(s)
15921609 let is_ccall = matches ! ( insn, Insn :: CCall { .. } ) ;
1593- if let Insn :: ParallelMov { moves } = insn {
1594- // Now that register allocation is done, it's ready to resolve parallel moves.
1595- for ( reg, opnd) in Self :: resolve_parallel_moves ( & moves) {
1596- asm. load_into ( Opnd :: Reg ( reg) , opnd) ;
1610+ match insn {
1611+ Insn :: ParallelMov { moves } => {
1612+ // Now that register allocation is done, it's ready to resolve parallel moves.
1613+ for ( reg, opnd) in Self :: resolve_parallel_moves ( & moves) {
1614+ asm. load_into ( Opnd :: Reg ( reg) , opnd) ;
1615+ }
15971616 }
1598- } else {
1599- asm. push_insn ( insn) ;
1617+ Insn :: CCall { opnds, fptr, start_marker, end_marker, out } => {
1618+ // Split start_marker and end_marker here to avoid inserting push/pop between them.
1619+ if let Some ( start_marker) = start_marker {
1620+ asm. push_insn ( Insn :: PosMarker ( start_marker) ) ;
1621+ }
1622+ asm. push_insn ( Insn :: CCall { opnds, fptr, start_marker : None , end_marker : None , out } ) ;
1623+ if let Some ( end_marker) = end_marker {
1624+ asm. push_insn ( Insn :: PosMarker ( end_marker) ) ;
1625+ }
1626+ }
1627+ _ => asm. push_insn ( insn) ,
16001628 }
16011629
16021630 // After a C call, restore caller-saved registers
@@ -1720,38 +1748,30 @@ impl Assembler {
17201748 self . push_insn ( Insn :: Breakpoint ) ;
17211749 }
17221750
1751+ /// Call a C function without PosMarkers
17231752 pub fn ccall ( & mut self , fptr : * const u8 , opnds : Vec < Opnd > ) -> Opnd {
1724- /*
1725- // Let vm_check_canary() assert this ccall's leafness if leaf_ccall is set
1726- let canary_opnd = self.set_stack_canary(&opnds);
1727-
1728- let old_temps = self.ctx.get_reg_mapping(); // with registers
1729- // Spill stack temp registers since they are caller-saved registers.
1730- // Note that this doesn't spill stack temps that are already popped
1731- // but may still be used in the C arguments.
1732- self.spill_regs();
1733- let new_temps = self.ctx.get_reg_mapping(); // all spilled
1734-
1735- // Temporarily manipulate RegMappings so that we can use registers
1736- // to pass stack operands that are already spilled above.
1737- self.ctx.set_reg_mapping(old_temps);
1738- */
1739-
1740- // Call a C function
17411753 let out = self . new_vreg ( Opnd :: match_num_bits ( & opnds) ) ;
1742- self . push_insn ( Insn :: CCall { fptr, opnds, out } ) ;
1743-
1744- /*
1745- // Registers in old_temps may be clobbered by the above C call,
1746- // so rollback the manipulated RegMappings to a spilled version.
1747- self.ctx.set_reg_mapping(new_temps);
1748-
1749- // Clear the canary after use
1750- if let Some(canary_opnd) = canary_opnd {
1751- self.mov(canary_opnd, 0.into());
1752- }
1753- */
1754+ self . push_insn ( Insn :: CCall { fptr, opnds, start_marker : None , end_marker : None , out } ) ;
1755+ out
1756+ }
17541757
1758+ /// Call a C function with PosMarkers. This is used for recording the start and end
1759+ /// addresses of the C call and rewriting it with a different function address later.
1760+ pub fn ccall_with_pos_markers (
1761+ & mut self ,
1762+ fptr : * const u8 ,
1763+ opnds : Vec < Opnd > ,
1764+ start_marker : impl Fn ( CodePtr , & CodeBlock ) + ' static ,
1765+ end_marker : impl Fn ( CodePtr , & CodeBlock ) + ' static ,
1766+ ) -> Opnd {
1767+ let out = self . new_vreg ( Opnd :: match_num_bits ( & opnds) ) ;
1768+ self . push_insn ( Insn :: CCall {
1769+ fptr,
1770+ opnds,
1771+ start_marker : Some ( Box :: new ( start_marker) ) ,
1772+ end_marker : Some ( Box :: new ( end_marker) ) ,
1773+ out,
1774+ } ) ;
17551775 out
17561776 }
17571777
0 commit comments