#include "HsVersions.h"
import Cmm
+import CmmUtils ( hasNoGlobalRegs )
import CLabel ( entryLblToInfoLbl )
import MachOp
import SMRep ( tablesNextToCode )
-- -----------------------------------------------------------------------------
-- 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
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.