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 <ezyang@mit.edu>
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
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)
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