summaryrefslogtreecommitdiff
path: root/ghc/compiler/nativeGen/AsmRegAlloc.lhs
blob: 9d11e229b018dd7cdbb80989a47368ed4a311e3f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
%
% (c) The AQUA Project, Glasgow University, 1993-1995
%

\begin{code}
#include "HsVersions.h"
#include "../../includes/platform.h"
#include "../../includes/GhcConstants.h"

module AsmRegAlloc (
	FutureLive(..), RegLiveness(..), RegUsage(..), Reg(..),
	MachineRegisters(..), MachineCode(..),

	mkReg, runRegAllocate,
	extractMappedRegNos,

	-- And, for self-sufficiency
	CLabel, OrdList, PrimKind, UniqSet(..), UniqFM,
	FiniteMap, Unique
    ) where

IMPORT_Trace

import CLabelInfo	( CLabel )
import FiniteMap
import MachDesc
import Maybes		( maybeToBool, Maybe(..) )
import OrdList		-- ( mkUnitList, mkSeqList, mkParList, OrdList )
import Outputable
import Pretty
import PrimKind		( PrimKind(..) )
import UniqSet
import Unique
import Util

#if ! OMIT_NATIVE_CODEGEN

#if sparc_TARGET_ARCH
import SparcCode	-- ( SparcInstr, SparcRegs ) -- for specializing

{-# SPECIALIZE
    runRegAllocate :: SparcRegs -> [Int] -> (OrdList SparcInstr) -> [SparcInstr]
  #-}
#endif
#if alpha_TARGET_ARCH
import AlphaCode	-- ( AlphaInstr, AlphaRegs ) -- for specializing

{-# SPECIALIZE
    runRegAllocate :: AlphaRegs -> [Int] -> (OrdList AlphaInstr) -> [AlphaInstr]
  #-}
#endif

#endif

\end{code}

%************************************************************************
%*									*
\subsection[Reg]{Real registers}
%*									*
%************************************************************************

Static Registers correspond to actual machine registers.  These should
be avoided until the last possible moment.

Dynamic registers are allocated on the fly, usually to represent a single
value in the abstract assembly code (i.e. dynamic registers are usually
single assignment).  Ultimately, they are mapped to available machine
registers before spitting out the code.

\begin{code}

data Reg = FixedReg  FAST_INT		-- A pre-allocated machine register

	 | MappedReg FAST_INT		-- A dynamically allocated machine register

	 | MemoryReg Int PrimKind	-- A machine "register" actually held in a memory
					-- allocated table of registers which didn't fit
					-- in real registers.

	 | UnmappedReg Unique PrimKind	-- One of an infinite supply of registers,
					-- always mapped to one of the earlier two
					-- before we're done.
	 -- No thanks: deriving (Eq)

mkReg :: Unique -> PrimKind -> Reg
mkReg = UnmappedReg

instance Text Reg where
    showsPrec _ (FixedReg i)	= showString "%"  . shows IBOX(i)
    showsPrec _ (MappedReg i)	= showString "%"  . shows IBOX(i)
    showsPrec _ (MemoryReg i _) = showString "%M"  . shows i
    showsPrec _ (UnmappedReg i _) = showString "%U" . shows i

#ifdef DEBUG
instance Outputable Reg where
    ppr sty r = ppStr (show r)
#endif

cmpReg (FixedReg i) (FixedReg i') = cmp_ihash i i'
cmpReg (MappedReg i) (MappedReg i') = cmp_ihash i i'
cmpReg (MemoryReg i _) (MemoryReg i' _) = cmp_i i i'
cmpReg (UnmappedReg u _) (UnmappedReg u' _) = cmpUnique u u'
cmpReg r1 r2 =
    let tag1 = tagReg r1
	tag2 = tagReg r2
    in
	if tag1 _LT_ tag2 then LT_ else GT_
    where
	tagReg (FixedReg _)	 = (ILIT(1) :: FAST_INT)
	tagReg (MappedReg _)	 = ILIT(2)
	tagReg (MemoryReg _ _)	 = ILIT(3)
	tagReg (UnmappedReg _ _) = ILIT(4)

cmp_i :: Int -> Int -> TAG_
cmp_i a1 a2 = if a1 == a2 then EQ_ else if a1 < a2 then LT_ else GT_

cmp_ihash :: FAST_INT -> FAST_INT -> TAG_
cmp_ihash a1 a2 = if a1 _EQ_ a2 then EQ_ else if a1 _LT_ a2 then LT_ else GT_

instance Eq Reg where
    a == b = case cmpReg a b of { EQ_ -> True;  _ -> False }
    a /= b = case cmpReg a b of { EQ_ -> False; _ -> True  }

instance Ord Reg where
    a <= b = case cmpReg a b of { LT_ -> True;	EQ_ -> True;  GT__ -> False }
    a <	 b = case cmpReg a b of { LT_ -> True;	EQ_ -> False; GT__ -> False }
    a >= b = case cmpReg a b of { LT_ -> False; EQ_ -> True;  GT__ -> True  }
    a >	 b = case cmpReg a b of { LT_ -> False; EQ_ -> False; GT__ -> True  }
#ifdef __GLASGOW_HASKELL__
    _tagCmp a b = case cmpReg a b of { LT_ -> _LT; EQ_ -> _EQ; GT__ -> _GT }
#endif

instance NamedThing Reg where
    -- the *only* method that should be defined is "getTheUnique"!
    -- (so we can use UniqFMs/UniqSets on Regs
    getTheUnique (UnmappedReg u _) = u
    getTheUnique (FixedReg i)	   = mkPseudoUnique1 IBOX(i)
    getTheUnique (MappedReg i)	   = mkPseudoUnique2 IBOX(i)
    getTheUnique (MemoryReg i _)   = mkPseudoUnique3 i
\end{code}

This is the generic register allocator.

%************************************************************************
%*									*
\subsection[RegPlace]{Map Stix registers to {\em real} registers}
%*									*
%************************************************************************

An important point:  The @regUsage@ function for a particular assembly language
must not refer to fixed registers, such as Hp, SpA, etc.  The source and destination
lists should only refer to dynamically allocated registers or static registers
from the free list.  As far as we are concerned, the fixed registers simply don't
exist (for allocation purposes, anyway).

\begin{code}

class MachineRegisters a where
    mkMRegs	    :: [Int] -> a
    possibleMRegs   :: PrimKind -> a -> [Int]
    useMReg	    :: a -> FAST_INT -> a
    useMRegs	    :: a -> [Int] -> a
    freeMReg	    :: a -> FAST_INT -> a
    freeMRegs	    :: a -> [Int] -> a

type RegAssignment = FiniteMap Reg Reg
type RegConflicts = FiniteMap Int (UniqSet Reg)

data FutureLive
  = FL	(UniqSet Reg)
	(FiniteMap CLabel (UniqSet Reg))
fstFL (FL a b) = a

data RegHistory a
  = RH	a
	Int
	RegAssignment

data RegFuture
  = RF	(UniqSet Reg)	-- in use
	FutureLive	-- future
	RegConflicts

data RegInfo a
  = RI	(UniqSet Reg)	-- in use
	(UniqSet Reg)	-- sources
	(UniqSet Reg)	-- destinations
	[Reg]		-- last used
	RegConflicts

data RegUsage
  = RU	(UniqSet Reg)
	(UniqSet Reg)

data RegLiveness
  = RL	(UniqSet Reg)
	FutureLive

class MachineCode a where
-- OLD:
--    flatten	    :: OrdList a -> [a]
      regUsage	    :: a -> RegUsage
      regLiveness   :: a -> RegLiveness -> RegLiveness
      patchRegs	    :: a -> (Reg -> Reg) -> a
      spillReg	    :: Reg -> Reg -> OrdList a
      loadReg	    :: Reg -> Reg -> OrdList a

\end{code}

First we try something extremely simple.
If that fails, we have to do things the hard way.

\begin{code}

runRegAllocate
    :: (MachineRegisters a, MachineCode b)
    => a
    -> [Int]
    -> (OrdList b)
    -> [b]

runRegAllocate regs reserve_regs instrs =
    case simpleAlloc of 
	Just x  -> x
	Nothing -> hairyAlloc
  where
    flatInstrs	= flattenOrdList instrs
    simpleAlloc = simpleRegAlloc regs [] emptyFM flatInstrs
    hairyAlloc	= hairyRegAlloc regs reserve_regs flatInstrs

\end{code}

Here is the simple register allocator.	Just dole out registers until
we run out, or until one gets clobbered before its last use.  Don't
do anything fancy with branches.  Just pretend that you've got a block
of straight-line code and hope for the best.  Experience indicates that
this approach will suffice for about 96 percent of the code blocks that
we generate.

\begin{code}

simpleRegAlloc
    :: (MachineRegisters a, MachineCode b)
    => a		-- registers to select from
    -> [Reg]		-- live static registers
    -> RegAssignment	-- mapping of dynamics to statics
    -> [b]		-- code
    -> Maybe [b]

simpleRegAlloc _ _ _ [] = Just []
simpleRegAlloc free live env (instr:instrs) =
    if null deadSrcs && maybeToBool newAlloc && maybeToBool instrs2 then
	Just (instr3 : instrs3)
    else
	Nothing
  where
    instr3 = patchRegs instr (lookup env2)

    (srcs, dsts) = case regUsage instr of { RU s d -> (uniqSetToList s, uniqSetToList d) }

    lookup env x = case lookupFM env x of {Just y -> y; Nothing -> x}

    deadSrcs = [r | r@(UnmappedReg _ _) <- srcs, lookup env r `not_elem` live]
    newDsts  = [r | r@(UnmappedReg _ _) <- dsts, r `not_elem` keysFM env]

    newAlloc = foldr allocateNewReg (Just (free, [])) newDsts
    (free2, new) = case newAlloc of Just x -> x

    env2 = env `addListToFM` new

    live2 = map snd new ++ [x | x <- live, x `not_elem` dsts]

    instrs2 = simpleRegAlloc free2 live2 env2 instrs
    instrs3 = case instrs2 of Just x -> x

    allocateNewReg
	:: MachineRegisters a
	=> Reg
	-> Maybe (a, [(Reg, Reg)])
	-> Maybe (a, [(Reg, Reg)])

    allocateNewReg _ Nothing = Nothing

    allocateNewReg d@(UnmappedReg _ pk) (Just (free, prs)) =
	if null choices then Nothing
	else Just (free2, prs2)
      where
	choices = possibleMRegs pk free
	reg = head choices
	free2 = free `useMReg` (case reg of {IBOX(reg2) -> reg2} )
	prs2 = ((d,  MappedReg (case reg of {IBOX(reg2) -> reg2})) : prs)

\end{code}

Here is the ``clever'' bit. First go backward (i.e. left), looking for
the last use of dynamic registers. Then go forward (i.e. right), filling
registers with static placements.

\begin{code}

hairyRegAlloc
    :: (MachineRegisters a, MachineCode b)
    => a
    -> [Int]
    -> [b]
    -> [b]

hairyRegAlloc regs reserve_regs instrs =
    case mapAccumB (doRegAlloc reserve_regs)
	    (RH regs' 1 emptyFM) noFuture instrs
    of (RH _ loc' _, _, instrs') ->
	if loc' == 1 then instrs' else
	case mapAccumB do_RegAlloc_Nil
		(RH regs'' loc' emptyFM) noFuture (flattenOrdList (patchMem instrs'))
	of ((RH _ loc'' _),_,instrs'') ->
	    if loc'' == loc' then instrs'' else panic "runRegAllocate"
  where
    regs' = regs `useMRegs` reserve_regs
    regs'' = mkMRegs reserve_regs `asTypeOf` regs

do_RegAlloc_Nil = doRegAlloc [] -- out here to avoid CAF (sigh)
do_RegAlloc_Nil
    :: (MachineRegisters a, MachineCode b)
    => RegHistory a
    -> RegFuture
    -> b
    -> (RegHistory a, RegFuture, b)

noFuture :: RegFuture
noFuture = RF emptyUniqSet (FL emptyUniqSet emptyFM) emptyFM
\end{code}

Here we patch instructions that reference ``registers'' which are really in
memory somewhere (the mapping is under the control of the machine-specific
code generator).  We place the appropriate load sequences before any instructions
that use memory registers as sources, and we place the appropriate spill sequences
after any instructions that use memory registers as destinations.  The offending
instructions are rewritten with new dynamic registers, so we have to run register
allocation again after all of this is said and done.

\begin{code}

patchMem
    :: MachineCode a
    => [a]
    -> OrdList a

patchMem cs = foldr (mkSeqList . patchMem') mkEmptyList cs

patchMem'
    :: MachineCode a
    => a
    -> OrdList a

patchMem' instr =
    if null memSrcs && null memDsts then mkUnitList instr
    else mkSeqList
	    (foldr mkParList mkEmptyList loadSrcs)
	    (mkSeqList instr'
		(foldr mkParList mkEmptyList spillDsts))

    where
	(RU srcs dsts) = regUsage instr

	memToDyn (MemoryReg i pk) = UnmappedReg (mkBuiltinUnique i) pk
	memToDyn other		  = other

	memSrcs = [ r | r@(MemoryReg _ _) <- uniqSetToList srcs]
	memDsts = [ r | r@(MemoryReg _ _) <- uniqSetToList dsts]

	loadSrcs = map load memSrcs
	spillDsts = map spill memDsts

	load mem = loadReg mem (memToDyn mem)
	spill mem = spillReg (memToDyn mem) mem

	instr' = mkUnitList (patchRegs instr memToDyn)

\end{code}

\begin{code}

doRegAlloc
    :: (MachineRegisters a, MachineCode b)
    => [Int]
    -> RegHistory a
    -> RegFuture
    -> b
    -> (RegHistory a, RegFuture, b)

doRegAlloc reserved_regs free_env in_use instr = (free_env', in_use', instr')
  where
      (free_env', instr') = doRegAlloc' reserved_regs free_env info instr
      (in_use', info) = getUsage in_use instr

\end{code}

\begin{code}

getUsage
    :: MachineCode a
    => RegFuture
    -> a
    -> (RegFuture, RegInfo a)

getUsage (RF next_in_use future reg_conflicts) instr =
    (RF in_use' future' reg_conflicts',
     RI in_use' srcs dsts last_used reg_conflicts')
	 where (RU srcs dsts) = regUsage instr
	       (RL in_use future') = regLiveness instr (RL next_in_use future)
	       live_through = in_use `minusUniqSet` dsts
	       last_used = [ r | r <- uniqSetToList srcs,
			     not (r `elementOfUniqSet` (fstFL future) || r `elementOfUniqSet` in_use)]
	       in_use' = srcs `unionUniqSets` live_through
	       reg_conflicts' = case new_conflicts of
		    [] -> reg_conflicts
		    _ -> addListToFM reg_conflicts new_conflicts
	       new_conflicts = if isEmptyUniqSet live_dynamics then []
			       else [ (r, merge_conflicts r)
					| r <- extractMappedRegNos (uniqSetToList dsts) ]
	       merge_conflicts reg = case lookupFM reg_conflicts reg of
			    Nothing -> live_dynamics
			    Just conflicts -> conflicts `unionUniqSets` live_dynamics
	       live_dynamics = mkUniqSet
			    [ r | r@(UnmappedReg _ _) <- uniqSetToList live_through ]

doRegAlloc'
    :: (MachineRegisters a, MachineCode b)
    => [Int]
    -> RegHistory a
    -> RegInfo b
    -> b
    -> (RegHistory a, b)

doRegAlloc' reserved (RH frs loc env) (RI in_use srcs dsts lastu conflicts) instr =

    (RH frs'' loc' env'', patchRegs instr dynToStatic)

    where

      -- free up new registers
      free :: [Int]
      free = extractMappedRegNos (map dynToStatic lastu)

      -- (1) free registers that are used last as source operands in this instruction
      frs_not_in_use = frs `useMRegs` (extractMappedRegNos (uniqSetToList in_use))
      frs' = (frs_not_in_use `freeMRegs` free) `useMRegs` reserved

      -- (2) allocate new registers for the destination operands
      -- allocate registers for new dynamics

      new_dynamix = [ r | r@(UnmappedReg _ _) <- uniqSetToList dsts, r `not_elem` keysFM env ]

      (frs'', loc', new) = foldr allocateNewRegs (frs', loc, []) new_dynamix

      env' = addListToFM env new

      env'' = delListFromFM env' lastu

      dynToStatic :: Reg -> Reg
      dynToStatic dyn@(UnmappedReg _ _) =
	case lookupFM env' dyn of
	    Just r -> r
	    Nothing -> trace "Lost register; possibly a floating point type error in a _ccall_?" dyn
      dynToStatic other = other

      allocateNewRegs
	:: MachineRegisters a
	=> Reg -> (a, Int, [(Reg, Reg)]) -> (a, Int, [(Reg, Reg)])

      allocateNewRegs d@(UnmappedReg _ pk) (fs, mem, lst) = (fs', mem', (d, f) : lst)
	where (fs', f, mem') = case acceptable fs of
		[] -> (fs, MemoryReg mem pk, mem + 1)
		(IBOX(x2):_) -> (fs `useMReg` x2, MappedReg x2, mem)

	      acceptable regs = filter no_conflict (possibleMRegs pk regs)
	      no_conflict reg = case lookupFM conflicts reg of
		    Nothing -> True
		    Just conflicts -> not (d `elementOfUniqSet` conflicts)
\end{code}

\begin{code}
extractMappedRegNos :: [Reg] -> [Int]

extractMappedRegNos regs
  = foldr ex [] regs
  where
    ex (MappedReg i) acc = IBOX(i) : acc  -- we'll take it
    ex _	     acc = acc		  -- leave it out
\end{code}

We keep a local copy of the Prelude function \tr{notElem},
so that it can be specialised.  (Hack me gently.  [WDP 94/11])
\begin{code}
not_elem x []	    =  True
not_elem x (y:ys)   =  x /= y && not_elem x ys
\end{code}