[GHC] #13569: Optimise code-gen for field-updates of large products
GHC
ghc-devs at haskell.org
Thu Apr 13 07:58:52 UTC 2017
#13569: Optimise code-gen for field-updates of large products
-------------------------------------+-------------------------------------
Reporter: hvr | Owner: (none)
Type: task | Status: new
Priority: normal | Milestone:
Component: Compiler | Version: 8.0.1
(CodeGen) |
Keywords: | Operating System: Unknown/Multiple
Architecture: | Type of failure: Other
Unknown/Multiple |
Test Case: | Blocked By:
Blocking: | Related Tickets:
Differential Rev(s): | Wiki Page:
-------------------------------------+-------------------------------------
Here's something we talked about last year which I wanted to look into but
then got distracted (but I'm writing this one up now so it doesn't get
lost again):
Consider large field updates such as
{{{#!hs
module BigRec where
data T = C
{ fld01 :: !Int
, fld02 :: !Int
, fld03 :: Int
, fld04 :: Int
, fld05 :: Int
, fld06 :: Int
, fld07 :: Int
, fld08 :: Int
, fld09 :: Int
, fld10 :: Int
, fld11 :: Int
, fld12 :: Int
, fld13 :: Int
, fld14 :: Int
, fld15 :: Int
, fld16 :: Int
, fld17 :: Int
, fld18 :: Int
, fld19 :: Int
}
inc01 x = x { fld01 = (fld01 x) + 1 }
inc0102 x = x { fld01 = (fld01 x) + 1, fld02 = (fld02 x) + 1 }
}}}
These get translated into long chains of assignments, which in turn result
in respectively long chains of register<->memory move instructions being
generated by the NCG
{{{#!c
BigRec.inc0102_entry() // [R2]
{ [(c1em,
block_c1em_info:
const 0;
const 31;),
(c1ep,
BigRec.inc0102_info:
const 4294967301;
const 0;
const 15;)]
}
{offset
c1ep:
if ((Sp + -8) < SpLim) goto c1ez; else goto c1eA;
c1ez:
// nop
R1 = BigRec.inc0102_closure;
call (I64[BaseReg - 8])(R2, R1) args: 8, res: 0, upd: 8;
c1eA:
I64[Sp - 8] = block_c1em_info;
R1 = R2;
Sp = Sp - 8;
if (R1 & 7 != 0) goto c1em; else goto c1en;
c1en:
call (I64[R1])(R1) returns to c1em, args: 8, res: 8, upd: 8;
c1em:
Hp = Hp + 160;
if (Hp > I64[BaseReg + 856]) goto c1eD; else goto c1eC;
c1eD:
I64[BaseReg + 904] = 160;
// nop
call stg_gc_unpt_r1(R1) returns to c1em, args: 8, res: 8, upd:
8;
c1eC:
_s15F::P64 = P64[R1 + 7];
_s15G::P64 = P64[R1 + 15];
_s15H::P64 = P64[R1 + 23];
_s15I::P64 = P64[R1 + 31];
_s15J::P64 = P64[R1 + 39];
_s15K::P64 = P64[R1 + 47];
_s15L::P64 = P64[R1 + 55];
_s15M::P64 = P64[R1 + 63];
_s15N::P64 = P64[R1 + 71];
_s15O::P64 = P64[R1 + 79];
_s15P::P64 = P64[R1 + 87];
_s15Q::P64 = P64[R1 + 95];
_s15R::P64 = P64[R1 + 103];
_s15S::P64 = P64[R1 + 111];
_s15T::P64 = P64[R1 + 119];
_s15U::P64 = P64[R1 + 127];
_s15V::P64 = P64[R1 + 135];
_s15X::I64 = I64[R1 + 151] + 1;
_s15W::I64 = I64[R1 + 143] + 1;
I64[Hp - 152] = BigRec.C_con_info;
P64[Hp - 144] = _s15F::P64;
P64[Hp - 136] = _s15G::P64;
P64[Hp - 128] = _s15H::P64;
P64[Hp - 120] = _s15I::P64;
P64[Hp - 112] = _s15J::P64;
P64[Hp - 104] = _s15K::P64;
P64[Hp - 96] = _s15L::P64;
P64[Hp - 88] = _s15M::P64;
P64[Hp - 80] = _s15N::P64;
P64[Hp - 72] = _s15O::P64;
P64[Hp - 64] = _s15P::P64;
P64[Hp - 56] = _s15Q::P64;
P64[Hp - 48] = _s15R::P64;
P64[Hp - 40] = _s15S::P64;
P64[Hp - 32] = _s15T::P64;
P64[Hp - 24] = _s15U::P64;
P64[Hp - 16] = _s15V::P64;
I64[Hp - 8] = _s15W::I64;
I64[Hp] = _s15X::I64;
R1 = Hp - 151;
}}}
This happens in a few places inside GHC's source-tree (where we have
large-ish products, such as DynFlags where we update single fields), which
is where I noticed this while looking at generated ASM output.
--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13569>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler
More information about the ghc-tickets
mailing list