summaryrefslogtreecommitdiff
path: root/ghc/rts/Updates.cmm
blob: 1d2fc5fe0fb4c1a747d86d92ae0fc61f05437d41 (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
/* -----------------------------------------------------------------------------
 *
 * (c) The GHC Team, 1998-2004
 *
 * Code to perform updates.
 *
 * This file is written in a subset of C--, extended with various
 * features specific to GHC.  It is compiled by GHC directly.  For the
 * syntax of .cmm files, see the parser in ghc/compiler/cmm/CmmParse.y.
 *
 * ---------------------------------------------------------------------------*/

#include "Cmm.h"
#include "Updates.h"
#include "StgLdvProf.h"

/*
  The update frame return address must be *polymorphic*, that means
  we have to cope with both vectored and non-vectored returns.  This
  is done by putting the return vector right before the info table, and
  having a standard direct return address after the info table (pointed
  to by the return address itself, as usual).

  Each entry in the vector table points to a specialised entry code fragment
  that knows how to return after doing the update.  It would be possible to
  use a single generic piece of code that simply entered the return value
  to return, but it's quicker this way.  The direct return code of course
  just does another direct return when it's finished.
*/

/* on entry to the update code
   (1) R1 points to the closure being returned
   (2) Sp points to the update frame
*/

/* The update fragment has been tuned so as to generate good
   code with gcc, which accounts for some of the strangeness in the
   way it is written.  

   In particular, the JMP_(ret) bit is passed down and pinned on the
   end of each branch (there end up being two major branches in the
   code), since we don't mind duplicating this jump.
*/

#define UPD_FRAME_ENTRY_TEMPLATE(label,ind_info,ret)			\
        label								\
	{								\
          W_ updatee;							\
									\
          updatee = StgUpdateFrame_updatee(Sp);				\
									\
	  /* remove the update frame from the stack */			\
	  Sp = Sp + SIZEOF_StgUpdateFrame;				\
									\
	  /* ToDo: it might be a PAP, so we should check... */		\
	  TICK_UPD_CON_IN_NEW(sizeW_fromITBL(%GET_STD_INFO(updatee)));	\
									\
	  UPD_SPEC_IND(updatee, ind_info, R1, jump (ret));		\
	}

UPD_FRAME_ENTRY_TEMPLATE(stg_upd_frame_0_ret,stg_IND_0_info,%RET_VEC(Sp(0),0))
UPD_FRAME_ENTRY_TEMPLATE(stg_upd_frame_1_ret,stg_IND_1_info,%RET_VEC(Sp(0),1))
UPD_FRAME_ENTRY_TEMPLATE(stg_upd_frame_2_ret,stg_IND_2_info,%RET_VEC(Sp(0),2))
UPD_FRAME_ENTRY_TEMPLATE(stg_upd_frame_3_ret,stg_IND_3_info,%RET_VEC(Sp(0),3))
UPD_FRAME_ENTRY_TEMPLATE(stg_upd_frame_4_ret,stg_IND_4_info,%RET_VEC(Sp(0),4))
UPD_FRAME_ENTRY_TEMPLATE(stg_upd_frame_5_ret,stg_IND_5_info,%RET_VEC(Sp(0),5))
UPD_FRAME_ENTRY_TEMPLATE(stg_upd_frame_6_ret,stg_IND_6_info,%RET_VEC(Sp(0),6))
UPD_FRAME_ENTRY_TEMPLATE(stg_upd_frame_7_ret,stg_IND_7_info,%RET_VEC(Sp(0),7))

#if MAX_VECTORED_RTN > 8
#error MAX_VECTORED_RTN has changed: please modify stg_upd_frame too.
#endif

/*
  Make sure this table is big enough to handle the maximum vectored
  return size!
  */

#if defined(PROFILING)
#define UPD_FRAME_BITMAP 3
#define UPD_FRAME_WORDS  3
#else
#define UPD_FRAME_BITMAP 0
#define UPD_FRAME_WORDS  1
#endif

/* this bitmap indicates that the first word of an update frame is a
 * non-pointer - this is the update frame link.  (for profiling,
 * there's a cost-centre-stack in there too).
 */

INFO_TABLE_RET( stg_upd_frame, 
	    UPD_FRAME_WORDS, UPD_FRAME_BITMAP, UPDATE_FRAME,
	    stg_upd_frame_0_ret,
	    stg_upd_frame_1_ret,
	    stg_upd_frame_2_ret,
	    stg_upd_frame_3_ret,
	    stg_upd_frame_4_ret,
	    stg_upd_frame_5_ret,
	    stg_upd_frame_6_ret,
	    stg_upd_frame_7_ret
	    )
UPD_FRAME_ENTRY_TEMPLATE(,stg_IND_direct_info,%ENTRY_CODE(Sp(0)))


INFO_TABLE_RET( stg_marked_upd_frame, 
	    UPD_FRAME_WORDS, UPD_FRAME_BITMAP, UPDATE_FRAME,
	    stg_upd_frame_0_ret,
	    stg_upd_frame_1_ret,
	    stg_upd_frame_2_ret,
	    stg_upd_frame_3_ret,
	    stg_upd_frame_4_ret,
	    stg_upd_frame_5_ret,
	    stg_upd_frame_6_ret,
	    stg_upd_frame_7_ret
	    )
UPD_FRAME_ENTRY_TEMPLATE(,stg_IND_direct_info,%ENTRY_CODE(Sp(0)))

/*-----------------------------------------------------------------------------
  Seq frames 

  We don't have a primitive seq# operator: it is just a 'case'
  expression whose scrutinee has either a polymorphic or function type
  (constructor types can be handled by normal 'case' expressions).

  To handle a polymorphic/function typed seq, we push a SEQ frame on
  the stack.  This is a polymorphic activation record that just pops
  itself and returns (in a non-vectored way) when entered.  The
  purpose of the SEQ frame is to avoid having to make a polymorphic return
  point for each polymorphic case expression.  

  Another way of looking at it: the SEQ frame turns a vectored return
  into a direct one.
  -------------------------------------------------------------------------- */

#if MAX_VECTORED_RTN > 8
#error MAX_VECTORED_RTN has changed: please modify stg_seq_frame too.
#endif

INFO_TABLE_RET( stg_seq_frame, 0/* words */, 0/* bitmap */, RET_SMALL,
	RET_LBL(stg_seq_frame), /* 0 */
	RET_LBL(stg_seq_frame), /* 1 */
	RET_LBL(stg_seq_frame), /* 2 */
	RET_LBL(stg_seq_frame), /* 3 */
	RET_LBL(stg_seq_frame), /* 4 */
	RET_LBL(stg_seq_frame), /* 5 */
	RET_LBL(stg_seq_frame), /* 6 */
	RET_LBL(stg_seq_frame)  /* 7 */
	)
{
   Sp_adj(1);
   jump %ENTRY_CODE(Sp(0));
}