summaryrefslogtreecommitdiff
path: root/ghc/compiler/codeGen/CgStackery.lhs
blob: 12b96a8cd1a564ed8004e60ddf2026db69257fae (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
%
% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
%
% $Id: CgStackery.lhs,v 1.23 2002/12/11 15:36:27 simonmar Exp $
%
\section[CgStackery]{Stack management functions}

Stack-twiddling operations, which are pretty low-down and grimy.
(This is the module that knows all about stack layouts, etc.)

\begin{code}
module CgStackery (
	allocStack, allocPrimStack, allocStackTop, deAllocStackTop,
	adjustStackHW, getFinalStackHW, 
	setStackFrame, getStackFrame,
	mkVirtStkOffsets, mkStkAmodes,
	freeStackSlots, dataStackSlots, addFreeSlots,
	updateFrameSize,
	constructSlowCall, slowArgs,
    ) where

#include "HsVersions.h"

import CgMonad
import AbsCSyn
import CLabel		( mkRtsApplyInfoLabel, mkRtsApplyEntryLabel )

import CgUsages		( getRealSp )
import AbsCUtils	( mkAbstractCs, getAmodeRep )
import PrimRep
import CmdLineOpts	( opt_SccProfilingOn, opt_GranMacros )
import Constants
import Util		( sortLt )
import FastString	( LitString )
import Panic
	
import TRACE		( trace )
\end{code}

%************************************************************************
%*									*
\subsection[CgStackery-layout]{Laying out a stack frame}
%*									*
%************************************************************************

'mkVirtStkOffsets' is given a list of arguments.  The first argument
gets the /largest/ virtual stack offset (remember, virtual offsets
increase towards the top of stack).

\begin{code}
mkVirtStkOffsets
	  :: VirtualSpOffset 	-- Offset of the last allocated thing
	  -> (a -> PrimRep)	-- to be able to grab kinds
	  -> [a]			-- things to make offsets for
	  -> (VirtualSpOffset,		-- OUTPUTS: Topmost allocated word
	      [(a, VirtualSpOffset)])	-- things with offsets

mkVirtStkOffsets init_Sp_offset kind_fun things
    = loop init_Sp_offset [] (reverse things)
  where
    loop offset offs [] = (offset,offs)
    loop offset offs (t:things) =
	     let
		 size = getPrimRepSize (kind_fun t)
		 thing_slot = offset + size
	     in
	     loop thing_slot ((t,thing_slot):offs) things
    -- offset of thing is offset+size, because we're growing the stack
    -- *downwards* as the offsets increase.


-- | 'mkStkAmodes' is a higher-level version of
-- 'mkVirtStkOffsets'.  It starts from the tail-call locations.
-- It returns a single list of addressing modes for the stack
-- locations, and therefore is in the monad.  It /doesn't/ adjust the
-- high water mark.

mkStkAmodes 
	:: VirtualSpOffset	    -- Tail call positions
	-> [CAddrMode]		    -- things to make offsets for
	-> FCode (VirtualSpOffset,  -- OUTPUTS: Topmost allocated word
	          AbstractC)	    -- Assignments to appropriate stk slots

mkStkAmodes tail_Sp things
  = getRealSp `thenFC` \ realSp ->
    let
      (last_Sp_offset, offsets) = mkVirtStkOffsets tail_Sp getAmodeRep things

      abs_cs =
	  [ CAssign (CVal (spRel realSp offset) (getAmodeRep thing)) thing
	  | (thing, offset) <- offsets
	  ]
    in
    returnFC (last_Sp_offset, mkAbstractCs abs_cs)
\end{code}

%************************************************************************
%*									*
\subsection{Pushing the arguments for a slow call}
%*									*
%************************************************************************

For a slow call, we must take a bunch of arguments and intersperse
some stg_ap_<pattern>_ret_info return addresses.

\begin{code}
constructSlowCall :: [CAddrMode] -> (CAddrMode, [CAddrMode])
   -- don't forget the zero case
constructSlowCall [] = (CLbl stg_ap_0 CodePtrRep , []) 
constructSlowCall amodes = 
  -- traceSlowCall amodes $ 	
  (CLbl lbl CodePtrRep, these ++ slowArgs rest)
  where (tag, these, rest) = matchSlowPattern amodes
	lbl = mkRtsApplyEntryLabel tag

stg_ap_0 = mkRtsApplyEntryLabel SLIT("0")

-- | 'slowArgs' takes a list of function arguments and prepares them for
-- pushing on the stack for "extra" arguments to a function which requires
-- fewer arguments than we currently have.
slowArgs :: [CAddrMode] -> [CAddrMode]
slowArgs [] = []
slowArgs amodes = CLbl lbl RetRep : args ++ slowArgs rest
  where	(tag, args, rest) = matchSlowPattern amodes
	lbl = mkRtsApplyInfoLabel tag
  
matchSlowPattern :: [CAddrMode] -> (LitString, [CAddrMode], [CAddrMode])
matchSlowPattern amodes = (tag, these, rest)
  where reps = map getAmodeRep amodes
        (tag, n) = findMatch (map primRepToArgRep reps)
	(these, rest) = splitAt n amodes

-- These cases were found to cover about 99% of all slow calls:
findMatch (RepP: RepP: RepP: RepP: RepP: RepP: RepP: _) = (SLIT("ppppppp"), 7)
findMatch (RepP: RepP: RepP: RepP: RepP: RepP: _)       = (SLIT("pppppp"), 6)
findMatch (RepP: RepP: RepP: RepP: RepP: _) 	   	= (SLIT("ppppp"), 5)
findMatch (RepP: RepP: RepP: RepP: _) 		     	= (SLIT("pppp"), 4)
findMatch (RepP: RepP: RepP: _)       		     	= (SLIT("ppp"), 3)
findMatch (RepP: RepP: RepV: _)       		     	= (SLIT("ppv"), 3)
findMatch (RepP: RepP: _)				= (SLIT("pp"), 2)
findMatch (RepP: RepV: _)				= (SLIT("pv"), 2)
findMatch (RepP: _)					= (SLIT("p"), 1)
findMatch (RepV: _)					= (SLIT("v"), 1)
findMatch (RepN: _)					= (SLIT("n"), 1)
findMatch (RepF: _)					= (SLIT("f"), 1)
findMatch (RepD: _)					= (SLIT("d"), 1)
findMatch (RepL: _)					= (SLIT("l"), 1)
findMatch _  = panic "CgStackery.findMatch"

#ifdef DEBUG
primRepChar p | isFollowableRep p     = 'p'
primRepChar VoidRep                   = 'v'
primRepChar FloatRep                  = 'f'
primRepChar DoubleRep                 = 'd'
primRepChar p | getPrimRepSize p == 1 = 'n'
primRepChar p | is64BitRep p          = 'l'

traceSlowCall amodes and_then 
 = trace ("call: " ++ map primRepChar (map getAmodeRep amodes)) and_then
#endif
\end{code}

%************************************************************************
%*									*
\subsection[CgStackery-monadery]{Inside-monad functions for stack manipulation}
%*									*
%************************************************************************

Allocate a virtual offset for something.

\begin{code}
allocStack :: FCode VirtualSpOffset
allocStack = allocPrimStack 1

allocPrimStack :: Int -> FCode VirtualSpOffset
allocPrimStack size = do
	((virt_sp, frame, free_stk, real_sp, hw_sp),h_usage) <- getUsage
	let push_virt_sp = virt_sp + size
	let (chosen_slot, new_stk_usage) = 
		case find_block free_stk of
		   Nothing -> (push_virt_sp, 
				 (push_virt_sp, frame, free_stk, real_sp,
				  hw_sp `max` push_virt_sp))
						-- Adjust high water mark
		   Just slot -> (slot, 
				  (virt_sp, frame, 
				   delete_block free_stk slot, 
				   real_sp, hw_sp))
	setUsage (new_stk_usage, h_usage)
	return chosen_slot
	
	where
		-- find_block looks for a contiguous chunk of free slots
		find_block :: [(VirtualSpOffset,Slot)] -> Maybe VirtualSpOffset
		find_block [] = Nothing
		find_block ((off,free):slots)
			| take size ((off,free):slots) == 
			  zip [off..top_slot] (repeat Free) = Just top_slot
			| otherwise				   = find_block slots
				-- The stack grows downwards, with increasing virtual offsets.
				-- Therefore, the address of a multi-word object is the *highest*
				-- virtual offset it occupies (top_slot below).
			where top_slot = off+size-1

		delete_block free_stk slot = [ (s,f) | (s,f) <- free_stk, 
				           (s<=slot-size) || (s>slot) ]
			      -- Retain slots which are not in the range
			      -- slot-size+1..slot
\end{code}

Allocate a chunk ON TOP OF the stack.  

ToDo: should really register this memory as NonPointer stuff in the
free list.

\begin{code}
allocStackTop :: Int -> FCode VirtualSpOffset
allocStackTop size = do
	((virt_sp, frame, free_stk, real_sp, hw_sp), h_usage) <- getUsage
	let push_virt_sp = virt_sp + size
	let new_stk_usage = (push_virt_sp, frame, free_stk, real_sp, 
				hw_sp `max` push_virt_sp)
	setUsage (new_stk_usage, h_usage)
	return push_virt_sp
\end{code}

Pop some words from the current top of stack.  This is used for
de-allocating the return address in a case alternative.

\begin{code}
deAllocStackTop :: Int -> FCode VirtualSpOffset
deAllocStackTop size = do
	((virt_sp, frame, free_stk, real_sp, hw_sp), h_usage) <- getUsage
	let pop_virt_sp = virt_sp - size
	let new_stk_usage = (pop_virt_sp, frame, free_stk, real_sp, hw_sp)
	setUsage (new_stk_usage, h_usage)
	return pop_virt_sp
\end{code}

\begin{code}
adjustStackHW :: VirtualSpOffset -> Code
adjustStackHW offset = do
	((vSp,fTop,fSp,realSp,hwSp), h_usage) <- getUsage
	setUsage ((vSp, fTop, fSp, realSp, max offset hwSp), h_usage)
\end{code}

A knot-tying beast.

\begin{code}
getFinalStackHW :: (VirtualSpOffset -> Code) -> Code
getFinalStackHW fcode = do
	fixC (\hwSp -> do
		fcode hwSp
		((_,_,_,_, hwSp),_) <- getUsage
		return hwSp)
	return ()
\end{code}

\begin{code}
setStackFrame :: VirtualSpOffset -> Code
setStackFrame offset = do
	((vSp,_,fSp,realSp,hwSp), h_usage) <- getUsage
	setUsage ((vSp, offset, fSp, realSp, hwSp), h_usage)

getStackFrame :: FCode VirtualSpOffset
getStackFrame = do
	((vSp,frame,fSp,realSp,hwSp), h_usage) <- getUsage
	return frame
\end{code}

\begin{code}
updateFrameSize | opt_SccProfilingOn = pROF_UF_SIZE
		| opt_GranMacros     = trace ("updateFrameSize = " ++ (show gRAN_UF_SIZE))gRAN_UF_SIZE
		| otherwise          = uF_SIZE
\end{code}			

%************************************************************************
%*									*
\subsection[CgStackery-free]{Free stack slots}
%*									*
%************************************************************************

Explicitly free some stack space.

\begin{code}
addFreeStackSlots :: [VirtualSpOffset] -> Slot -> Code
addFreeStackSlots extra_free slot = do
	((vsp, frame,free, real, hw),heap_usage) <- getUsage
	let all_free = addFreeSlots free (zip (sortLt (<) extra_free) (repeat slot))
	let (new_vsp, new_free) = trim vsp all_free
	let new_usage = ((new_vsp, frame, new_free, real, hw), heap_usage)
	setUsage new_usage

freeStackSlots :: [VirtualSpOffset] -> Code
freeStackSlots slots = addFreeStackSlots slots Free

dataStackSlots :: [VirtualSpOffset] -> Code
dataStackSlots slots = addFreeStackSlots slots NonPointer

addFreeSlots :: [(Int,Slot)] -> [(Int,Slot)] -> [(Int,Slot)]
addFreeSlots cs [] = cs
addFreeSlots [] ns = ns
addFreeSlots ((c,s):cs) ((n,s'):ns)
 = if c < n then
	(c,s) : addFreeSlots cs ((n,s'):ns)
   else if c > n then
	(n,s') : addFreeSlots ((c,s):cs) ns
   else if s /= s' then -- c == n
	(c,s') : addFreeSlots cs ns
   else
	panic ("addFreeSlots: equal slots: " ++ show (c:map fst cs)
					     ++ show (n:map fst ns))

trim :: Int{-offset-} -> [(Int,Slot)] -> (Int{-offset-}, [(Int,Slot)])
trim current_sp free_slots
  = try current_sp free_slots
  where
	try csp [] = (csp,[])

	try csp (slot@(off,state):slots) = 
		if state == Free && null slots' then
		    if csp' < off then 
			(csp', [])
		    else if csp' == off then
			(csp'-1, [])
		    else 
			(csp',[slot])
		else
		    (csp', slot:slots')
		where
		    (csp',slots') = try csp slots
\end{code}