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}
|