X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=ghc%2Fcompiler%2Fcmm%2FCmmOpt.hs;h=c454ff4c6a6c980fbf3f7db050e09b5011ef42f7;hp=95d13183c4bbdb7b486e092ea2d4f8b8d2f98864;hb=18519c934bd8cc42139df93dcbc90b656521b811;hpb=d9979e949556ee554246340aba5ac52fbd5d4092 diff --git a/ghc/compiler/cmm/CmmOpt.hs b/ghc/compiler/cmm/CmmOpt.hs index 95d1318..c454ff4 100644 --- a/ghc/compiler/cmm/CmmOpt.hs +++ b/ghc/compiler/cmm/CmmOpt.hs @@ -15,6 +15,7 @@ module CmmOpt ( #include "HsVersions.h" import Cmm +import CmmUtils ( hasNoGlobalRegs ) import CLabel ( entryLblToInfoLbl ) import MachOp import SMRep ( tablesNextToCode ) @@ -34,13 +35,50 @@ import GLAEXTS -- ----------------------------------------------------------------------------- -- The mini-inliner --- This pass inlines assignments to temporaries that are used just --- once in the very next statement only. Generalising this would be --- quite difficult (have to take into account aliasing of memory --- writes, and so on), but at the moment it catches a number of useful --- cases and lets the code generator generate much better code. - --- NB. This assumes that temporaries are single-assignment. +{- +This pass inlines assignments to temporaries that are used just +once. It works as follows: + + - count uses of each temporary + - for each temporary that occurs just once: + - attempt to push it forward to the statement that uses it + - only push forward past assignments to other temporaries + (assumes that temporaries are single-assignment) + - if we reach the statement that uses it, inline the rhs + and delete the original assignment. + +Possible generalisations: here is an example from factorial + +Fac_zdwfac_entry: + cmG: + _smi = R2; + if (_smi != 0) goto cmK; + R1 = R3; + jump I64[Sp]; + cmK: + _smn = _smi * R3; + R2 = _smi + (-1); + R3 = _smn; + jump Fac_zdwfac_info; + +We want to inline _smi and _smn. To inline _smn: + + - we must be able to push forward past assignments to global regs. + We can do this if the rhs of the assignment we are pushing + forward doesn't refer to the global reg being assigned to; easy + to test. + +To inline _smi: + + - It is a trivial replacement, reg for reg, but it occurs more than + once. + - We can inline trivial assignments even if the temporary occurs + more than once, as long as we don't eliminate the original assignment + (this doesn't help much on its own). + - We need to be able to propagate the assignment forward through jumps; + if we did this, we would find that it can be inlined safely in all + its occurrences. +-} cmmMiniInline :: [CmmBasicBlock] -> [CmmBasicBlock] cmmMiniInline blocks = map do_inline blocks @@ -85,8 +123,17 @@ lookForInline u expr (CmmNop : rest) lookForInline u expr (stmt:stmts) = case lookupUFM (getStmtUses stmt) u of - Just 1 -> Just (inlineStmt u expr stmt : stmts) + Just 1 | ok_to_inline -> Just (inlineStmt u expr stmt : stmts) _other -> Nothing + where + -- we don't inline into CmmCall if the expression refers to global + -- registers. This is a HACK to avoid global registers clashing with + -- C argument-passing registers, really the back-end ought to be able + -- to handle it properly, but currently neither PprC nor the NCG can + -- do it. See also CgForeignCall:load_args_into_temps. + ok_to_inline = case stmt of + CmmCall{} -> hasNoGlobalRegs expr + _ -> True -- ----------------------------------------------------------------------------- -- Boring Cmm traversals for collecting usage info and substitutions.