Commit 8a0ab97b authored by Edward Z. Yang's avatar Edward Z. Yang
Browse files

Unsafe foreign calls (fat machine instructions) do not kill all registers.

The new code generator was doing some interesting spilling across
unsafe foreign calls:

     _c1ao::I32 = Hp - 4;
     I32[Sp - 20] = _c1ao::I32;
     foreign "ccall"
       newCAF((BaseReg, PtrHint), (R1, PtrHint))[_unsafe_call_];
     _c1ao::I32 = I32[Sp - 20];

This is fairly unnecessary, and resulted from over-conservative
liveness analysis from CmmLive.  We can see that the old code
generator only saved volatile registers across unsafe foreign calls:
spilling variables was done by saveVolatileVarsAndRegs, which was
only performed for ordinary calls.

This commit removes the excess kill from the liveness analysis, as well
as the *redundant* excess kill from spilling-and-reloading, and adds a
note to CmmNode to this effect.  The only registers we need to kill
are the ones that the foreign call assigns to, just like any other
machine instruction.

Signed-off-by: Edward Z. Yang's avatarEdward Z. Yang <>
parent 1fb38442
......@@ -63,12 +63,12 @@ gen a live = foldRegsUsed extendRegSet live a
kill :: DefinerOfLocalRegs a => a -> RegSet -> RegSet
kill a live = foldRegsDefd delOneFromUniqSet live a
-- Testing!
xferLive :: BwdTransfer CmmNode CmmLive
xferLive = mkBTransfer3 fst mid lst
where fst _ f = f
mid :: CmmNode O O -> CmmLive -> CmmLive
mid n f = gen_kill n $ case n of CmmUnsafeForeignCall {} -> emptyRegSet
_ -> f
mid n f = gen_kill n f
lst :: CmmNode O C -> FactBase CmmLive -> CmmLive
lst n f = gen_kill n $ case n of CmmCall {} -> emptyRegSet
CmmForeignCall {} -> emptyRegSet
......@@ -92,6 +92,8 @@ data CmmNode e x where
A MidForeign call is used for *unsafe* foreign calls;
a LastForeign call is used for *safe* foreign calls.
Unsafe ones are easy: think of them as a "fat machine instruction".
In particular, they do *not* kill all live registers (there was a bit
of code in GHC that conservatively assumed otherwise.)
Safe ones are trickier. A safe foreign call
r = f(x)
......@@ -100,11 +100,11 @@ dualLiveTransfers entry procPoints = mkBTransfer3 first middle last
where check live id x = if id == entry then noLiveOnEntry id (in_regs live) x else x
middle :: CmmNode O O -> DualLive -> DualLive
middle m live = changeStack updSlots $ changeRegs (xferLiveMiddle m) (changeRegs regs_in live)
where xferLiveMiddle = case getBTransfer3 xferLive of (_, middle, _) -> middle
regs_in :: RegSet -> RegSet
regs_in live = case m of CmmUnsafeForeignCall {} -> emptyRegSet
_ -> live
middle m = changeStack updSlots
. changeRegs updRegs
where -- Reuse middle of liveness analysis from CmmLive
updRegs = case getBTransfer3 xferLive of (_, middle, _) -> middle m
updSlots live = foldSlotsUsed reload (foldSlotsDefd spill live m) m
spill live s@(RegSlot r, _, _) = check s $ deleteFromRegSet live r
spill live _ = live
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment