summaryrefslogtreecommitdiff
path: root/ghc/compiler/codeGen/CgTailCall.lhs
blob: 82c64a4c484c3abbbe8e3b80ce4ff1c412ba6a90 (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
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
%
% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
%
% $Id: CgTailCall.lhs,v 1.24 2000/03/23 17:45:19 simonpj Exp $
%
%********************************************************
%*							*
\section[CgTailCall]{Tail calls: converting @StgApps@}
%*							*
%********************************************************

\begin{code}
module CgTailCall (
	cgTailCall,
	performReturn, performPrimReturn,
	mkStaticAlgReturnCode, mkDynamicAlgReturnCode,
	mkUnboxedTupleReturnCode, returnUnboxedTuple,
	mkPrimReturnCode,

	tailCallFun,
	tailCallPrimOp,
	doTailCall,

	pushReturnAddress
    ) where

#include "HsVersions.h"

import CgMonad
import AbsCSyn
import PprAbsC		( pprAmode )

import AbsCUtils	( mkAbstractCs, mkAbsCStmts, getAmodeRep )
import CgBindery	( getArgAmodes, getCAddrMode, getCAddrModeAndInfo )
import CgRetConv	( dataReturnConvPrim,
			  ctrlReturnConvAlg, CtrlReturnConvention(..),
			  assignAllRegs, assignRegs
			)
import CgStackery	( mkTaggedStkAmodes, adjustStackHW )
import CgUsages		( getSpRelOffset, adjustSpAndHp )
import CgUpdate		( pushSeqFrame )
import CLabel		( mkUpdInfoLabel, mkRtsPrimOpLabel, 
			  mkBlackHoleInfoTableLabel )
import ClosureInfo	( nodeMustPointToIt,
			  getEntryConvention, EntryConvention(..),
			  LambdaFormInfo
			)
import CmdLineOpts	( opt_DoSemiTagging )
import Id		( Id, idType, idName )
import DataCon		( DataCon, dataConTyCon, dataConTag, fIRST_TAG )
import Literal		( mkMachInt )
import Maybes		( assocMaybe, maybeToBool )
import PrimRep		( PrimRep(..) )
import StgSyn		( StgArg, GenStgArg(..) )
import Type		( isUnLiftedType )
import TyCon            ( TyCon )
import PrimOp		( PrimOp )
import Util		( zipWithEqual )
import Unique		( mkPseudoUnique1 )
import Outputable
import Panic		( panic, assertPanic )
\end{code}

%************************************************************************
%*									*
\subsection[tailcall-doc]{Documentation}
%*									*
%************************************************************************

\begin{code}
cgTailCall :: Id -> [StgArg] -> Code
\end{code}

Here's the code we generate for a tail call.  (NB there may be no
arguments, in which case this boils down to just entering a variable.)

\begin{itemize}
\item	Adjust the stack ptr to \tr{tailSp + #args}.
\item	Put args in the top locations of the resulting stack.
\item	Make Node point to the function closure.
\item	Enter the function closure.
\end{itemize}

Things to be careful about:
\begin{itemize}
\item	Don't overwrite stack locations before you have finished with
	them (remember you need the function and the as-yet-unmoved
	arguments).
\item	Preferably, generate no code to replace x by x on the stack (a
	common situation in tail-recursion).
\item	Adjust the stack high water mark appropriately.
\end{itemize}

Treat unboxed locals exactly like literals (above) except use the addr
mode for the local instead of (CLit lit) in the assignment.

Case for unboxed @Ids@ first:
\begin{code}
cgTailCall fun []
  | isUnLiftedType (idType fun)
  = getCAddrMode fun		`thenFC` \ amode ->
    performPrimReturn (ppr fun) amode
\end{code}

The general case (@fun@ is boxed):
\begin{code}
cgTailCall fun args = performTailCall fun args
\end{code}

%************************************************************************
%*									*
\subsection[return-and-tail-call]{Return and tail call}
%*									*
%************************************************************************

\begin{code}
performPrimReturn :: SDoc	-- Just for debugging (sigh)
		  -> CAddrMode	-- The thing to return
		  -> Code

performPrimReturn doc amode
  = let
	kind = getAmodeRep amode
	ret_reg = WARN( case kind of { PtrRep -> True; other -> False }, text "primRet" <+> doc <+> pprAmode amode )
		  dataReturnConvPrim kind

	assign_possibly = case kind of
	  VoidRep -> AbsCNop
	  kind -> (CAssign (CReg ret_reg) amode)
    in
    performReturn assign_possibly (mkPrimReturnCode doc)

mkPrimReturnCode :: SDoc 		-- Debugging only
		 -> Sequel
		 -> Code
mkPrimReturnCode doc UpdateCode	= pprPanic "mkPrimReturnCode: Upd" doc
mkPrimReturnCode doc sequel	= sequelToAmode sequel	`thenFC` \ dest_amode ->
				  absC (CReturn dest_amode DirectReturn)
				  -- Direct, no vectoring

-- Constructor is built on the heap; Node is set.
-- All that remains is
--	(a) to set TagReg, if necessary
--	(c) to do the right sort of jump.

mkStaticAlgReturnCode :: DataCon	-- The constructor
		      -> Sequel		-- where to return to
		      -> Code

mkStaticAlgReturnCode con sequel
  =	-- Generate profiling code if necessary
    (case return_convention of
	VectoredReturn sz -> profCtrC SLIT("TICK_VEC_RETURN") [mkIntCLit sz]
	other		  -> nopC
    )					`thenC`

	-- Set tag if necessary
	-- This is done by a macro, because if we are short of registers
	-- we don't set TagReg; instead the continuation gets the tag
	-- by indexing off the info ptr
    (case return_convention of

	UnvectoredReturn no_of_constrs
	 | no_of_constrs > 1
		-> absC (CMacroStmt SET_TAG [mkIntCLit zero_indexed_tag])

	other	-> nopC
    )					`thenC`

	-- Generate the right jump or return
    (case sequel of
	UpdateCode ->	-- Ha!  We can go direct to the update code,
			-- (making sure to jump to the *correct* update
			--  code.)
    			absC (CReturn (CLbl mkUpdInfoLabel CodePtrRep)
				      return_info)

	CaseAlts _ (Just (alts, _)) ->	-- Ho! We know the constructor so
					-- we can go right to the alternative

		case assocMaybe alts tag of
		   Just (alt_absC, join_lbl) -> 
			absC (CJump (CLbl join_lbl CodePtrRep))
		   Nothing -> panic "mkStaticAlgReturnCode: default"
				-- The Nothing case should never happen; 
				-- it's the subject of a wad of special-case 
				-- code in cgReturnCon

	-- can't be a SeqFrame, because we're returning a constructor

	other ->	-- OnStack, or (CaseAlts ret_amode Nothing)
		    sequelToAmode sequel	`thenFC` \ ret_amode ->
		    absC (CReturn ret_amode return_info)
    )

  where
    tag		      = dataConTag   con
    tycon	      = dataConTyCon con
    return_convention = ctrlReturnConvAlg tycon
    zero_indexed_tag  = tag - fIRST_TAG	      -- Adjust tag to be zero-indexed
					      -- cf AbsCUtils.mkAlgAltsCSwitch

    return_info = 
       case return_convention of
		UnvectoredReturn _ -> DirectReturn
		VectoredReturn _   -> StaticVectoredReturn zero_indexed_tag

mkUnboxedTupleReturnCode :: Sequel -> Code
mkUnboxedTupleReturnCode sequel
    = case sequel of
	-- can't update with an unboxed tuple!
	UpdateCode -> panic "mkUnboxedTupleReturnCode"

	CaseAlts _ (Just ([(_,(alt_absC,join_lbl))], _)) ->
			absC (CJump (CLbl join_lbl CodePtrRep))

	-- can't be a SeqFrame

	other ->	-- OnStack, or (CaseAlts ret_amode something)
		    sequelToAmode sequel	`thenFC` \ ret_amode ->
		    absC (CReturn ret_amode DirectReturn)

-- This function is used by PrimOps that return enumerated types (i.e.
-- all the comparison operators).

mkDynamicAlgReturnCode :: TyCon -> CAddrMode -> Sequel -> Code

mkDynamicAlgReturnCode tycon dyn_tag sequel
  = case ctrlReturnConvAlg tycon of
	VectoredReturn sz ->

		profCtrC SLIT("TICK_VEC_RETURN") [mkIntCLit sz] `thenC`
		sequelToAmode sequel		`thenFC` \ ret_addr ->
		absC (CReturn ret_addr (DynamicVectoredReturn dyn_tag))

	UnvectoredReturn no_of_constrs ->

		-- Set tag if necessary
		-- This is done by a macro, because if we are short of registers
		-- we don't set TagReg; instead the continuation gets the tag
		-- by indexing off the info ptr
		(if no_of_constrs > 1 then
			absC (CMacroStmt SET_TAG [dyn_tag])
		else
			nopC
		)			`thenC`


		sequelToAmode sequel		`thenFC` \ ret_addr ->
		-- Generate the right jump or return
		absC (CReturn ret_addr DirectReturn)
\end{code}

\begin{code}
performReturn :: AbstractC	    -- Simultaneous assignments to perform
	      -> (Sequel -> Code)   -- The code to execute to actually do
				    -- the return, given an addressing mode
				    -- for the return address
	      -> Code

-- this is just a special case of doTailCall, later.
performReturn sim_assts finish_code
  = getEndOfBlockInfo	`thenFC` \ eob@(EndOfBlockInfo args_sp sequel) ->

	-- Do the simultaneous assignments,
    doSimAssts sim_assts	 	`thenC`

	-- push a return address if necessary
	-- (after the assignments above, in case we clobber a live
	--  stack location)
    pushReturnAddress eob		`thenC`

	-- Adjust Sp/Hp
    adjustSpAndHp args_sp		`thenC`

	-- Do the return
    finish_code sequel		-- "sequel" is `robust' in that it doesn't
				-- depend on stk-ptr values
\end{code}

Returning unboxed tuples.  This is mainly to support _ccall_GC_, where
we want to do things in a slightly different order to normal:

		- push return address
		- adjust stack pointer
		- r = call(args...)
		- assign regs for unboxed tuple (usually just R1 = r)
		- return to continuation

The return address (i.e. stack frame) must be on the stack before
doing the call in case the call ends up in the garbage collector.

Sadly, the information about the continuation is lost after we push it
(in order to avoid pushing it again), so we end up doing a needless
indirect jump (ToDo).

\begin{code}
returnUnboxedTuple :: [CAddrMode] -> Code -> Code
returnUnboxedTuple amodes before_jump
  = getEndOfBlockInfo	`thenFC` \ eob@(EndOfBlockInfo args_sp sequel) ->

	-- push a return address if necessary
    pushReturnAddress eob		`thenC`
    setEndOfBlockInfo (EndOfBlockInfo args_sp (OnStack args_sp)) (

	-- Adjust Sp/Hp
    adjustSpAndHp args_sp		`thenC`

    before_jump				`thenC`

    let (ret_regs, leftovers) = assignRegs [] (map getAmodeRep amodes)
    in

    profCtrC SLIT("TICK_RET_UNBOXED_TUP") [mkIntCLit (length amodes)] `thenC`

    doTailCall amodes ret_regs
		mkUnboxedTupleReturnCode
		(length leftovers)  {- fast args arity -}
		AbsCNop {-no pending assigments-}
		Nothing {-not a let-no-escape-}
		False   {-node doesn't point-}
     )
\end{code}

\begin{code}
performTailCall :: Id		-- Function
		-> [StgArg]	-- Args
		-> Code

performTailCall fun args
  =	-- Get all the info we have about the function and args and go on to
	-- the business end
    getCAddrModeAndInfo fun	`thenFC` \ (fun_amode, lf_info) ->
    getArgAmodes args		`thenFC` \ arg_amodes ->

    tailCallFun
		fun fun_amode lf_info arg_amodes
		AbsCNop {- No pending assignments -}


-- generating code for a tail call to a function (or closure)

tailCallFun :: Id -> CAddrMode	-- Function and its amode
		 -> LambdaFormInfo	-- Info about the function
		 -> [CAddrMode]		-- Arguments

		 -> AbstractC		-- Pending simultaneous assignments
					-- *** GUARANTEED to contain only stack 
					-- assignments.

					-- In ptic, we don't need to look in 
					-- here to discover all live regs

		 -> Code

tailCallFun fun fun_amode lf_info arg_amodes pending_assts
  = nodeMustPointToIt lf_info			`thenFC` \ node_points ->
    getEntryConvention (idName fun) lf_info
	(map getAmodeRep arg_amodes)		`thenFC` \ entry_conv ->
    let
	node_asst
	  = if node_points then
		CAssign (CReg node) fun_amode
	    else
		AbsCNop

	(arg_regs, finish_code, arity)
	  = case entry_conv of
	      ViaNode ->
		([],
		     profCtrC SLIT("TICK_ENT_VIA_NODE") [] `thenC`
		     absC (CJump (CMacroExpr CodePtrRep ENTRY_CODE 
			        [CVal (nodeRel 0) DataPtrRep]))
		     , 0)
	      StdEntry lbl -> ([], absC (CJump (CLbl lbl CodePtrRep)), 0)
	      DirectEntry lbl arity regs  ->
		(regs,	 absC (CJump (CLbl lbl CodePtrRep)), 
	         arity - length regs)

	-- set up for a let-no-escape if necessary
	join_sp = case fun_amode of
			CJoinPoint sp -> Just sp
			other         -> Nothing
    in
    doTailCall arg_amodes arg_regs (const finish_code) arity
		(mkAbstractCs [node_asst,pending_assts]) join_sp node_points


-- this generic tail call code is used for both function calls and returns.

doTailCall 
	:: [CAddrMode] 			-- args to pass to function
	-> [MagicId]			-- registers to use
	-> (Sequel->Code)		-- code to perform jump
	-> Int				-- number of "fast" stack arguments
	-> AbstractC			-- pending assignments
	-> Maybe VirtualSpOffset	-- sp offset to trim stack to: 
					-- USED iff destination is a let-no-escape
	-> Bool				-- node points to the closure to enter
	-> Code

doTailCall arg_amodes arg_regs finish_code arity pending_assts
		maybe_join_sp node_points
  = getEndOfBlockInfo	`thenFC` \ eob@(EndOfBlockInfo args_sp sequel) ->

    let
	no_of_args = length arg_amodes

	(reg_arg_amodes, stk_arg_amodes) = splitAt (length arg_regs) arg_amodes
	    -- We get some stk_arg_amodes if (a) no regs, or 
	    --				     (b) args beyond arity

	reg_arg_assts
	  = mkAbstractCs (zipWithEqual "assign_to_reg2" 
				assign_to_reg arg_regs reg_arg_amodes)

	assign_to_reg reg_id amode = CAssign (CReg reg_id) amode

	join_sp = case maybe_join_sp of
			Just sp -> ASSERT(not (args_sp > sp)) sp
	      -- If ASSERTion fails: Oops: the join point has *lower*
	      -- stack ptrs than the continuation Note that we take
	      -- the Sp point without the return address here.	 The
	      -- return address is put on by the let-no-escapey thing
	      -- when it finishes.
			Nothing -> args_sp

	(fast_stk_amodes, tagged_stk_amodes) = 
		splitAt arity stk_arg_amodes

	-- eager blackholing, at the end of the basic block.
	node_save = CTemp (mkPseudoUnique1 2) DataPtrRep
	(r1_tmp_asst, bh_asst)
	 = case sequel of
#if 0
	-- no: UpdateCode doesn't tell us that we're in a thunk's entry code.
	-- we might be in a case continuation later down the line.  Also,
	-- we might have pushed a return address on the stack, if we're in
	-- a case scrut, and still be in the thunk's entry code.
		UpdateCode -> 
		   (CAssign node_save nodeReg,
		    CAssign (CVal (CIndex node_save (mkIntCLit 0) PtrRep) 
				  PtrRep)
			    (CLbl mkBlackHoleInfoTableLabel DataPtrRep))
#endif
		_ -> (AbsCNop, AbsCNop)
    in
	-- We can omit tags on the arguments passed to the fast entry point, 
	-- but we have to be careful to fill in the tags on any *extra*
	-- arguments we're about to push on the stack.

	mkTaggedStkAmodes join_sp tagged_stk_amodes `thenFC`
			    \ (fast_sp, tagged_arg_assts, tag_assts) ->

	mkTaggedStkAmodes fast_sp fast_stk_amodes `thenFC`
			    \ (final_sp, fast_arg_assts, _) ->

	-- adjust the high-water mark if necessary
	adjustStackHW final_sp	`thenC`

		-- The stack space for the pushed return addess, 
		-- with any args pushed on top, is recorded in final_sp.
	
			-- Do the simultaneous assignments,
	doSimAssts (mkAbstractCs [r1_tmp_asst,
				  pending_assts,
			          reg_arg_assts, 
				  fast_arg_assts, 
				  tagged_arg_assts,
			          tag_assts])	`thenC`
	absC bh_asst `thenC`
	
		-- push a return address if necessary
		-- (after the assignments above, in case we clobber a live
		--  stack location)

		-- DONT push the return address when we're about
		-- to jump to a let-no-escape: the final tail call
		-- in the let-no-escape will do this.
	(if (maybeToBool maybe_join_sp)
		then nopC
		else pushReturnAddress eob)		`thenC`

		-- Final adjustment of Sp/Hp
	adjustSpAndHp final_sp		`thenC`
	
		-- Now decide about semi-tagging
	let
		semi_tagging_on = opt_DoSemiTagging
	in
	case (semi_tagging_on, arg_amodes, node_points, sequel) of

	--
	-- *************** The semi-tagging case ***************
	--
	{- XXX leave this out for now.
	      (	  True,		   [],		True,	     CaseAlts _ (Just (st_alts, maybe_deflt_join_details))) ->

		-- Whoppee!  Semi-tagging rules OK!
		-- (a) semi-tagging is switched on
		-- (b) there are no arguments,
		-- (c) Node points to the closure
		-- (d) we have a case-alternative sequel with
		--	some visible alternatives

		-- Why is test (c) necessary?
		-- Usually Node will point to it at this point, because we're
		-- scrutinsing something which is either a thunk or a
		-- constructor.
		-- But not always!  The example I came across is when we have
		-- a top-level Double:
		--	lit.3 = D# 3.000
		--	... (case lit.3 of ...) ...
		-- Here, lit.3 is built as a re-entrant thing, which you must enter.
		-- (OK, the simplifier should have eliminated this, but it's
		--  easy to deal with the case anyway.)
		let
		    join_details_to_code (load_regs_and_profiling_code, join_lbl)
			= load_regs_and_profiling_code		`mkAbsCStmts`
			  CJump (CLbl join_lbl CodePtrRep)

		    semi_tagged_alts = [ (mkMachInt (fromInt (tag - fIRST_TAG)),
					  join_details_to_code join_details)
				       | (tag, join_details) <- st_alts
				       ]

		    enter_jump
		      -- Enter Node (we know infoptr will have the info ptr in it)!
		      = mkAbstractCs [
			CCallProfCtrMacro SLIT("RET_SEMI_FAILED")
					[CMacroExpr IntRep INFO_TAG [CReg infoptr]],
			CJump (CMacroExpr CodePtrRep ENTRY_CODE [CReg infoptr]) ]
		in
			-- Final switch
		absC (mkAbstractCs [
			    CAssign (CReg infoptr)
				    (CVal (NodeRel zeroOff) DataPtrRep),

			    case maybe_deflt_join_details of
				Nothing ->
				    CSwitch (CMacroExpr IntRep INFO_TAG [CReg infoptr])
					(semi_tagged_alts)
					(enter_jump)
				Just (_, details) ->
				    CSwitch (CMacroExpr IntRep EVAL_TAG [CReg infoptr])
				     [(mkMachInt 0, enter_jump)]
				     (CSwitch
					 (CMacroExpr IntRep INFO_TAG [CReg infoptr])
					 (semi_tagged_alts)
					 (join_details_to_code details))
		])
		-}

	--
	-- *************** The non-semi-tagging case ***************
	--
	      other -> finish_code sequel
\end{code}

%************************************************************************
%*									*
\subsection[tailCallPrimOp]{@tailCallPrimOp@}
%*									*
%************************************************************************

\begin{code}
tailCallPrimOp :: PrimOp -> [StgArg] -> Code
tailCallPrimOp op args =
    -- we're going to perform a normal-looking tail call, 
    -- except that *all* the arguments will be in registers.
    getArgAmodes args		`thenFC` \ arg_amodes ->
    let (arg_regs, leftovers) = assignAllRegs [] (map getAmodeRep arg_amodes)
    in
    ASSERT(null leftovers) -- no stack-resident args
    doTailCall arg_amodes arg_regs 
	(const (absC (CJump (CLbl (mkRtsPrimOpLabel op) CodePtrRep))))
	0       {- arity shouldn't matter, all args in regs -}
	AbsCNop {- no pending assignments -}
	Nothing {- not a let-no-escape -}
	False   {- node doesn't point -}
\end{code}

%************************************************************************
%*									*
\subsection[doSimAssts]{@doSimAssts@}
%*									*
%************************************************************************

@doSimAssts@ happens at the end of every block of code.
They are separate because we sometimes do some jiggery-pokery in between.

\begin{code}
doSimAssts :: AbstractC -> Code

doSimAssts sim_assts
  = absC (CSimultaneous sim_assts)
\end{code}

%************************************************************************
%*									*
\subsection[retAddr]{@Return Addresses@}
%*									*
%************************************************************************

We always push the return address just before performing a tail call
or return.  The reason we leave it until then is because the stack
slot that the return address is to go into might contain something
useful.

If the end of block info is CaseAlts, then we're in the scrutinee of a
case expression and the return address is still to be pushed.

There are cases where it doesn't look necessary to push the return
address: for example, just before doing a return to a known
continuation.  However, the continuation will expect to find the
return address on the stack in case it needs to do a heap check.

\begin{code}
pushReturnAddress :: EndOfBlockInfo -> Code
pushReturnAddress (EndOfBlockInfo args_sp sequel@(CaseAlts amode _)) =
    getSpRelOffset args_sp			 `thenFC` \ sp_rel ->
    absC (CAssign (CVal sp_rel RetRep) amode)
pushReturnAddress (EndOfBlockInfo args_sp sequel@(SeqFrame amode _)) =
    pushSeqFrame args_sp			 `thenFC` \ ret_sp ->
    getSpRelOffset ret_sp			 `thenFC` \ sp_rel ->
    absC (CAssign (CVal sp_rel RetRep) amode)
pushReturnAddress _ = nopC
\end{code}