On 25/02/2010 02:47, David Terei wrote:
Simon Marlow wrote:
Here's a patch review.
Oh there is one other issue I keep forgetting about. LLVM requires that
all basic blocks end in a control flow statement. Cmm basic blocks have
this property for everything except the hand written cmm.
e.g rts/Apply.cmm has the code:
INFO_TABLE(stg_PAP,/*special layout*/0,0,PAP,"PAP","PAP")
{ foreign "C" barf("PAP object entered!") never returns; }
This get passed to back-end as on x86-64:
stg_PAP_entry()
{ has static closure: False update_frame: <none>
type: 0
desc: 0
tag: 26
ptrs: 0
nptrs: 0
srt: _no_srt_
}
cp: I64[BaseReg + 16] = R3;
I64[BaseReg + 24] = R4;
I64[BaseReg + 32] = R5;
I64[BaseReg + 40] = R6;
F32[BaseReg + 80] = F1;
F32[BaseReg + 84] = F2;
F32[BaseReg + 88] = F3;
F32[BaseReg + 92] = F4;
F64[BaseReg + 96] = D1;
F64[BaseReg + 104] = D2;
foreign "ccall"
barf((cn_str, PtrHint))[_unsafe_call_]
never returns;
R3 = I64[BaseReg + 16];
R4 = I64[BaseReg + 24];
R5 = I64[BaseReg + 32];
R6 = I64[BaseReg + 40];
F1 = F32[BaseReg + 80];
F2 = F32[BaseReg + 84];
F3 = F32[BaseReg + 88];
F4 = F32[BaseReg + 92];
D1 = F64[BaseReg + 96];
D2 = F64[BaseReg + 104];
}
This case is the only one that occurs where a Cmm block doesn't end with
a control flow statement and its only really since it does a 'never
returns' call before hand with exits the function for good.
I believe the new C-- is going to "fix" this, because it has a similar
constraint to LLVM. You can see the new form of C-- in
compiler/cmm/ZipCfgCmmRep.hs.
The plan is to switch over to this new form for the input to the NCG,
and presumably the LLVM backend at some point.
This can be handled in two ways:
1) Have a pass in the LLVM back-end which checks each basic block and
adds an assumed 'return void' at the end if it doesn't end with a
control flow statement.
2) Modify 'compiler/codeGen/CgForeignCall.hs', changing the function
emitForeignCall' as so:
-- alternative entry point, used by CmmParse
emitForeignCall'
:: Safety
-> HintedCmmFormals -- where to put the results
-> CmmCallTarget -- the op
-> [CmmHinted CmmExpr] -- arguments
-> Maybe [GlobalReg] -- live vars, in case we save them
-> C_SRT -- the SRT of the calls continuation
-> CmmReturnInfo
-> Code
emitForeignCall' safety results target args vols _srt ret
| not (playSafe safety) = do
temp_args <- load_args_into_temps args
let (caller_save, caller_load) = callerSaveVolatileRegs vols
+ let caller_load' = if ret == CmmNeverReturns then [] else caller_load
stmtsC caller_save
stmtC (CmmCall target results temp_args CmmUnsafe ret)
- stmtsC caller_load
+ stmtsC caller_load'
This stops caller save registers being restored if the call is never
meant to return, which should be fine since the code is dead code anyway.
For the moment I've handled it in the first way as I felt my patch had a
better chance of being merged if it changed existing code in GHC as
little as possible. I prefer the second way though as adding in 'return
void' to cmm basic blocks feels like a hack and the second approach give
Cmm the property that all blocks end in a control flow statement which
seems pretty useful to me.
So should I change to the second approach, keep the first or start all
over again?
Either approach is fine for now, I wouldn't worry about changing it, I
think the new code generator handles this slightly differently, by
chopping off the code after the call later. It appears to be done by
CmmCvt.toZGraph.
Cheers,
Simon
_______________________________________________
Cvs-ghc mailing list
Cvs-ghc@haskell.org
http://www.haskell.org/mailman/listinfo/cvs-ghc