summaryrefslogtreecommitdiff
path: root/ghc/compiler/nativeGen/StixMacro.lhs
blob: b244110f0277c3593c10937bd9506b3623dce014 (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
%
% (c) The AQUA Project, Glasgow University, 1993-1995
%

\begin{code}
#include "HsVersions.h"

module StixMacro (
	genMacroCode, doHeapCheck, smStablePtrTable,

	Target, StixTree, UniqSupply, CAddrMode, CExprMacro,
	CStmtMacro
    ) where

import AbsCSyn
import PrelInfo      ( PrimOp(..)
		      IF_ATTACK_PRAGMAS(COMMA tagOf_PrimOp)
			  IF_ATTACK_PRAGMAS(COMMA pprPrimOp)
		    )
import MachDesc	    {- lots -}
import CgCompInfo   ( sTD_UF_SIZE, uF_RET, uF_SUA, uF_SUB, uF_UPDATEE )
import Stix
import UniqSupply
import Util
\end{code}

The @ARGS_CHK_A{_LOAD_NODE}@ macros check for sufficient arguments on
the A stack, and perform a tail call to @UpdatePAP@ if the arguments are
not there.  The @_LOAD_NODE@ version also loads R1 with an appropriate
closure address.

\begin{code}
mkIntCLit_0 = mkIntCLit 0 -- out here to avoid CAF (sigh)
mkIntCLit_3 = mkIntCLit 3

-- hacking with Uncle Will:
#define target_STRICT target@(Target _ _ _ _ _ _ _ _)

genMacroCode
    :: Target
    -> CStmtMacro   	    -- statement macro
    -> [CAddrMode]  	    -- args
    -> UniqSM StixTreeList

genMacroCode target_STRICT macro args
 = genmacro macro args
 where
  a2stix  = amodeToStix target
  stg_reg = stgReg target

  -- real thing: here we go -----------------------

  genmacro ARGS_CHK_A_LOAD_NODE args =
    getUniqLabelNCG					`thenUs` \ ulbl ->
    let [words, lbl] = map a2stix args
    	temp = StIndex PtrRep stgSpA words
	test = StPrim AddrGeOp [stgSuA, temp]
	cjmp = StCondJump ulbl test
	assign = StAssign PtrRep stgNode lbl
	join = StLabel ulbl
    in
	returnUs (\xs -> cjmp : assign : updatePAP : join : xs)

  genmacro ARGS_CHK_A [words] =
    getUniqLabelNCG					`thenUs` \ ulbl ->
    let temp = StIndex PtrRep stgSpA (a2stix words)
	test = StPrim AddrGeOp [stgSuA, temp]
	cjmp = StCondJump ulbl test
	join = StLabel ulbl
    in
	returnUs (\xs -> cjmp : updatePAP : join : xs)

\end{code}

Like the macros above, the @ARGS_CHK_B{_LOAD_NODE}@ macros check for
sufficient arguments on the B stack, and perform a tail call to
@UpdatePAP@ if the arguments are not there.  The @_LOAD_NODE@ version
also loads R1 with an appropriate closure address.  Note that the
directions are swapped relative to the A stack.

\begin{code}

  genmacro ARGS_CHK_B_LOAD_NODE args =
    getUniqLabelNCG					`thenUs` \ ulbl ->
    let [words, lbl] = map a2stix args
    	temp = StIndex PtrRep stgSuB (StPrim IntNegOp [words])
	test = StPrim AddrGeOp [stgSpB, temp]
	cjmp = StCondJump ulbl test
	assign = StAssign PtrRep stgNode lbl
	join = StLabel ulbl
    in
	returnUs (\xs -> cjmp : assign : updatePAP : join : xs)

  genmacro ARGS_CHK_B [words] =
    getUniqLabelNCG					`thenUs` \ ulbl ->
    let	temp = StIndex PtrRep stgSuB (StPrim IntNegOp [a2stix words])
	test = StPrim AddrGeOp [stgSpB, temp]
	cjmp = StCondJump ulbl test
	join = StLabel ulbl
    in
	returnUs (\xs -> cjmp : updatePAP : join : xs)

\end{code}

The @HEAP_CHK@ macro checks to see that there are enough words
available in the heap (before reaching @HpLim@).  When a heap check
fails, it has to call @PerformGC@ via the @PerformGC_wrapper@.  The
call wrapper saves all of our volatile registers so that we don't have to.

Since there are @HEAP_CHK@s buried at unfortunate places in the integer
primOps, this is just a wrapper.

\begin{code}

  genmacro HEAP_CHK args =
    let [liveness,words,reenter] = map a2stix args
    in
	doHeapCheck liveness words reenter
\end{code}

The @STK_CHK@ macro checks for enough space on the stack between @SpA@
and @SpB@.  A stack check can be complicated in the parallel world,
but for the sequential case, we just need to ensure that we have
enough space to continue.  Not that @_StackOverflow@ doesn't return,
so we don't have to @callWrapper@ it.

\begin{code}

  genmacro STK_CHK [liveness, aWords, bWords, spa, spb, prim, reenter] =
{- Need to check to see if we are compiling with stack checks
    getUniqLabelNCG					`thenUs` \ ulbl ->
    let words = StPrim IntNegOp
    	    [StPrim IntAddOp [a2stix aWords, a2stix bWords]]
	temp = StIndex PtrRep stgSpA words
	test = StPrim AddrGtOp [temp, stgSpB]
	cjmp = StCondJump ulbl test
	join = StLabel ulbl
    in
	returnUs (\xs -> cjmp : stackOverflow : join : xs)
-}
    returnUs id

\end{code}

@UPD_CAF@ involves changing the info pointer of the closure, adding an indirection,
and putting the new CAF on a linked list for the storage manager.

\begin{code}

  genmacro UPD_CAF args =
    let [cafptr,bhptr] = map a2stix args
    	w0 = StInd PtrRep cafptr
	w1 = StInd PtrRep (StIndex PtrRep cafptr (StInt 1))
	w2 = StInd PtrRep (StIndex PtrRep cafptr (StInt 2))
	a1 = StAssign PtrRep w0 caf_info
	a2 = StAssign PtrRep w1 smCAFlist
	a3 = StAssign PtrRep w2 bhptr
	a4 = StAssign PtrRep smCAFlist cafptr
    in
	returnUs (\xs -> a1 : a2 : a3 : a4 : xs)

\end{code}

@UPD_IND@ is complicated by the fact that we are supporting the
Appel-style garbage collector by default.  This means some extra work
if we update an old generation object.

\begin{code}

  genmacro UPD_IND args =
    getUniqLabelNCG					`thenUs` \ ulbl ->
    let [updptr, heapptr] = map a2stix args
    	test = StPrim AddrGtOp [updptr, smOldLim]
    	cjmp = StCondJump ulbl test
    	updRoots = StAssign PtrRep smOldMutables updptr
	join = StLabel ulbl
    	upd0 = StAssign PtrRep (StInd PtrRep updptr) ind_info
    	upd1 = StAssign PtrRep (StInd PtrRep
    	    	(StIndex PtrRep updptr (StInt 1))) smOldMutables
    	upd2 = StAssign PtrRep (StInd PtrRep
    	    	(StIndex PtrRep updptr (StInt 2))) heapptr
    in
    	returnUs (\xs -> cjmp : upd1 : updRoots : join : upd0 : upd2 : xs)

\end{code}

@UPD_INPLACE_NOPTRS@ is only needed for ticky-ticky profiling.

\begin{code}

  genmacro UPD_INPLACE_NOPTRS args = returnUs id

\end{code}

@UPD_INPLACE_PTRS@ is complicated by the fact that we are supporting
the Appel-style garbage collector by default.  This means some extra work
if we update an old generation object.

\begin{code}

  genmacro UPD_INPLACE_PTRS [liveness] =
    getUniqLabelNCG					`thenUs` \ ulbl ->
    let cjmp = StCondJump ulbl testOldLim
	testOldLim = StPrim AddrGtOp [stgNode, smOldLim]
	join = StLabel ulbl
	updUpd0 = StAssign PtrRep (StInd PtrRep stgNode) ind_info
    	updUpd1 = StAssign PtrRep (StInd PtrRep
	    	    (StIndex PtrRep stgNode (StInt 1))) smOldMutables
    	updUpd2 = StAssign PtrRep (StInd PtrRep
    	    	    (StIndex PtrRep stgNode (StInt 2))) hpBack2
    	hpBack2 = StIndex PtrRep stgHp (StInt (-2))
    	updOldMutables = StAssign PtrRep smOldMutables stgNode
    	updUpdReg = StAssign PtrRep stgNode hpBack2
    in
	genmacro HEAP_CHK [liveness, mkIntCLit_3, mkIntCLit_0]
							`thenUs` \ heap_chk ->
	returnUs (\xs -> (cjmp :
    	    	    	    heap_chk (updUpd0 : updUpd1 : updUpd2 :
    	    	    	    	    	updOldMutables : updUpdReg : join : xs)))

\end{code}

@UPD_BH_UPDATABLE@ is only used when running concurrent threads (in
the sequential case, the GC takes care of this).  However, we do need
to handle @UPD_BH_SINGLE_ENTRY@ in all cases.

\begin{code}

  genmacro UPD_BH_UPDATABLE args = returnUs id

  genmacro UPD_BH_SINGLE_ENTRY [arg] =
    let
    	update = StAssign PtrRep (StInd PtrRep (a2stix arg)) bh_info
    in
	returnUs (\xs -> update : xs)

\end{code}

Push a four word update frame on the stack and slide the Su[AB]
registers to the current Sp[AB] locations.

\begin{code}

  genmacro PUSH_STD_UPD_FRAME args =
    let [bhptr, aWords, bWords] = map a2stix args
    	frame n = StInd PtrRep
	    (StIndex PtrRep stgSpB (StPrim IntAddOp
    	    	[bWords, StInt (toInteger (sTD_UF_SIZE - n))]))

	a1 = StAssign PtrRep (frame uF_RET) stgRetReg
	a2 = StAssign PtrRep (frame uF_SUB) stgSuB
	a3 = StAssign PtrRep (frame uF_SUA) stgSuA
	a4 = StAssign PtrRep (frame uF_UPDATEE) bhptr

	updSuB = StAssign PtrRep
	    stgSuB (StIndex PtrRep stgSpB (StPrim IntAddOp
    	    	[bWords, StInt (toInteger sTD_UF_SIZE)]))
	updSuA = StAssign PtrRep
	    stgSuA (StIndex PtrRep stgSpA (StPrim IntNegOp [aWords]))
    in
	returnUs (\xs -> a1 : a2 : a3 : a4 : updSuB : updSuA : xs)

\end{code}

Pop a standard update frame.

\begin{code}

  genmacro POP_STD_UPD_FRAME args =
    let frame n = StInd PtrRep (StIndex PtrRep stgSpB (StInt (toInteger (-n))))

	grabRet = StAssign PtrRep stgRetReg (frame uF_RET)
	grabSuB = StAssign PtrRep stgSuB    (frame uF_SUB)
	grabSuA = StAssign PtrRep stgSuA    (frame uF_SUA)

	updSpB = StAssign PtrRep
	    stgSpB (StIndex PtrRep stgSpB (StInt (toInteger (-sTD_UF_SIZE))))
    in
	returnUs (\xs -> grabRet : grabSuB : grabSuA : updSpB : xs)

\end{code}

The @SET_ARITY@ and @CHK_ARITY@ macros are disabled for ``normal''
compilation.
\begin{code}
  genmacro SET_ARITY args = returnUs id
  genmacro CHK_ARITY args = returnUs id
\end{code}

This one only applies if we have a machine register devoted to TagReg.
\begin{code}
  genmacro SET_TAG [tag] =
    let set_tag = StAssign IntRep stgTagReg (a2stix tag)
    in
	case stg_reg TagReg of
	    Always _ -> returnUs id
	    Save   _ -> returnUs (\ xs -> set_tag : xs)
\end{code}

Do the business for a @HEAP_CHK@, having converted the args to Trees
of StixOp.

\begin{code}

doHeapCheck
    :: {- unused now: Target
    -> -}StixTree  	-- liveness
    -> StixTree  	-- words needed
    -> StixTree  	-- always reenter node? (boolean)
    -> UniqSM StixTreeList

doHeapCheck {-target:unused now-} liveness words reenter =
    getUniqLabelNCG					`thenUs` \ ulbl ->
    let newHp = StIndex PtrRep stgHp words
	assign = StAssign PtrRep stgHp newHp
	test = StPrim AddrLeOp [stgHp, stgHpLim]
	cjmp = StCondJump ulbl test
	arg = StPrim IntAddOp [StPrim IntMulOp [words, StInt 256], liveness]
	-- ToDo: Overflow?  (JSM)
	gc = StCall SLIT("PerformGC_wrapper") VoidRep [arg]
	join = StLabel ulbl
    in
	returnUs (\xs -> assign : cjmp : gc : join : xs)

\end{code}

Let's make sure that these CAFs are lifted out, shall we?

\begin{code}

-- Some common labels

bh_info, caf_info, ind_info :: StixTree

bh_info   = sStLitLbl SLIT("BH_SINGLE_info")
caf_info  = sStLitLbl SLIT("Caf_info")
ind_info  = sStLitLbl SLIT("Ind_info")

-- Some common call trees

updatePAP, stackOverflow :: StixTree

updatePAP     = StJump (sStLitLbl SLIT("UpdatePAP"))
stackOverflow = StCall SLIT("StackOverflow") VoidRep []

\end{code}

Storage manager nonsense.  Note that the indices are dependent on
the definition of the smInfo structure in SMinterface.lh

\begin{code}

#include "../../includes/platform.h"

#if alpha_TARGET_ARCH
#include "../../includes/alpha-dec-osf1.h"
#else
#if sunos4_TARGET_OS
#include "../../includes/sparc-sun-sunos4.h"
#else
#include "../../includes/sparc-sun-solaris2.h"
#endif
#endif

storageMgrInfo, smCAFlist, smOldMutables, smOldLim :: StixTree

storageMgrInfo = sStLitLbl SLIT("StorageMgrInfo")
smCAFlist  = StInd PtrRep (StIndex PtrRep storageMgrInfo (StInt SM_CAFLIST))
smOldMutables = StInd PtrRep (StIndex PtrRep storageMgrInfo (StInt SM_OLDMUTABLES))
smOldLim   = StInd PtrRep (StIndex PtrRep storageMgrInfo (StInt SM_OLDLIM))

smStablePtrTable = StInd PtrRep
    	    	    	 (StIndex PtrRep storageMgrInfo (StInt SM_STABLEPOINTERTABLE))

\end{code}