summaryrefslogtreecommitdiff
path: root/includes/Cmm.h
diff options
context:
space:
mode:
Diffstat (limited to 'includes/Cmm.h')
-rw-r--r--includes/Cmm.h293
1 files changed, 216 insertions, 77 deletions
diff --git a/includes/Cmm.h b/includes/Cmm.h
index edcf46e7c0..afe08a26a3 100644
--- a/includes/Cmm.h
+++ b/includes/Cmm.h
@@ -9,36 +9,6 @@
*
* For the syntax of .cmm files, see the parser in ghc/compiler/cmm/CmmParse.y.
*
- * If you're used to the old HC file syntax, here's a quick cheat sheet
- * for converting HC code:
- *
- * - Remove FB_/FE_
- * - Remove all type casts
- * - Remove '&'
- * - STGFUN(foo) { ... } ==> foo { ... }
- * - FN_(foo) { ... } ==> foo { ... }
- * - JMP_(e) ==> jump e;
- * - Remove EXTFUN(foo)
- * - Sp[n] ==> Sp(n)
- * - Hp[n] ==> Hp(n)
- * - Sp += n ==> Sp_adj(n)
- * - Hp += n ==> Hp_adj(n)
- * - R1.i ==> R1 (similarly for R1.w, R1.cl etc.)
- * - You need to explicitly dereference variables; eg.
- * alloc_blocks ==> CInt[alloc_blocks]
- * - convert all word offsets into byte offsets:
- * - e ==> WDS(e)
- * - sizeofW(StgFoo) ==> SIZEOF_StgFoo
- * - ENTRY_CODE(e) ==> %ENTRY_CODE(e)
- * - get_itbl(c) ==> %GET_STD_INFO(c)
- * - Change liveness masks in STK_CHK_GEN, HP_CHK_GEN:
- * R1_PTR | R2_PTR ==> R1_PTR & R2_PTR
- * (NOTE: | becomes &)
- * - Declarations like 'StgPtr p;' become just 'W_ p;'
- * - e->payload[n] ==> PAYLOAD(e,n)
- * - Be very careful with comparisons: the infix versions (>, >=, etc.)
- * are unsigned, so use %lt(a,b) to get signed less-than for example.
- *
* Accessing fields of structures defined in the RTS header files is
* done via automatically-generated macros in DerivedConstants.h. For
* example, where previously we used
@@ -136,6 +106,8 @@
Misc useful stuff
-------------------------------------------------------------------------- */
+#define ccall foreign "C"
+
#define NULL (0::W_)
#define STRING(name,str) \
@@ -210,7 +182,7 @@
#define Sp(n) W_[Sp + WDS(n)]
#define Hp(n) W_[Hp + WDS(n)]
-#define Sp_adj(n) Sp = Sp + WDS(n)
+#define Sp_adj(n) Sp = Sp + WDS(n) /* pronounced "spadge" */
#define Hp_adj(n) Hp = Hp + WDS(n)
/* -----------------------------------------------------------------------------
@@ -278,25 +250,37 @@
#define LOAD_INFO \
info = %INFO_PTR(UNTAG(P1));
-#define UNTAG_R1 \
- P1 = UNTAG(P1);
+#define MAYBE_UNTAG(x) UNTAG(x);
#else
-#define LOAD_INFO \
- if (GETTAG(P1) != 0) { \
- jump %ENTRY_CODE(Sp(0)); \
+#define LOAD_INFO(ret,x) \
+ if (GETTAG(x) != 0) { \
+ ret(x); \
} \
- info = %INFO_PTR(P1);
+ info = %INFO_PTR(x);
-#define UNTAG_R1 /* nothing */
+#define MAYBE_UNTAG(x) (x) /* already untagged */
#endif
-#define ENTER() \
+// We need two versions of ENTER():
+// - ENTER(x) takes the closure as an argument and uses return(),
+// for use in civilized code where the stack is handled by GHC
+//
+// - ENTER_NOSTACK() where the closure is in R1, and returns are
+// explicit jumps, for use when we are doing the stack management
+// ourselves.
+
+#define ENTER(x) ENTER_(return,x)
+#define ENTER_R1() ENTER_(RET_R1,R1)
+
+#define RET_R1(x) jump %ENTRY_CODE(Sp(0)) [R1]
+
+#define ENTER_(ret,x) \
again: \
W_ info; \
- LOAD_INFO \
+ LOAD_INFO(ret,x) \
switch [INVALID_OBJECT .. N_CLOSURE_TYPES] \
(TO_W_( %INFO_TYPE(%STD_INFO(info)) )) { \
case \
@@ -304,7 +288,7 @@
IND_PERM, \
IND_STATIC: \
{ \
- P1 = StgInd_indirectee(P1); \
+ x = StgInd_indirectee(x); \
goto again; \
} \
case \
@@ -318,12 +302,12 @@
BCO, \
PAP: \
{ \
- jump %ENTRY_CODE(Sp(0)); \
+ ret(x); \
} \
default: \
{ \
- UNTAG_R1 \
- jump %ENTRY_CODE(info); \
+ x = MAYBE_UNTAG(x); \
+ jump %ENTRY_CODE(info) (x); \
} \
}
@@ -348,7 +332,6 @@
*/
#include "stg/RtsMachRegs.h"
-#include "rts/storage/Liveness.h"
#include "rts/prof/LDV.h"
#undef BLOCK_SIZE
@@ -359,6 +342,18 @@
#define MyCapability() (BaseReg - OFFSET_Capability_r)
/* -------------------------------------------------------------------------
+ Info tables
+ ------------------------------------------------------------------------- */
+
+#if defined(PROFILING)
+#define PROF_HDR_FIELDS(w_) \
+ w_ prof_hdr_1, \
+ w_ prof_hdr_2,
+#else
+#define PROF_HDR_FIELDS(w_) /* nothing */
+#endif
+
+/* -------------------------------------------------------------------------
Allocation and garbage collection
------------------------------------------------------------------------- */
@@ -371,30 +366,134 @@
* ticky-ticky. It's not clear whether eg. the size field of an array
* should be counted as "admin", or the various fields of a BCO.
*/
-#define ALLOC_PRIM(bytes,liveness,reentry) \
- HP_CHK_GEN_TICKY(bytes,liveness,reentry); \
+#define ALLOC_PRIM(bytes) \
+ HP_CHK_GEN_TICKY(bytes); \
TICK_ALLOC_PRIM(SIZEOF_StgHeader,bytes-SIZEOF_StgHeader,0); \
CCCS_ALLOC(bytes);
+#define HEAP_CHECK(bytes,failure) \
+ Hp = Hp + bytes; \
+ if (Hp > HpLim) { HpAlloc = bytes; failure; } \
+ TICK_ALLOC_HEAP_NOCTR(bytes);
+
+#define ALLOC_PRIM_WITH_CUSTOM_FAILURE(bytes,failure) \
+ HEAP_CHECK(bytes,failure) \
+ TICK_ALLOC_PRIM(SIZEOF_StgHeader,bytes-SIZEOF_StgHeader,0); \
+ CCCS_ALLOC(bytes);
+
+#define ALLOC_PRIM_P(bytes,fun,arg) \
+ ALLOC_PRIM_WITH_CUSTOM_FAILURE(bytes,GC_PRIM_P(fun,arg));
+
+#define ALLOC_PRIM_N(bytes,fun,arg) \
+ ALLOC_PRIM_WITH_CUSTOM_FAILURE(bytes,GC_PRIM_N(fun,arg));
+
/* CCS_ALLOC wants the size in words, because ccs->mem_alloc is in words */
#define CCCS_ALLOC(__alloc) CCS_ALLOC(BYTES_TO_WDS(__alloc), CCCS)
-#define HP_CHK_GEN_TICKY(alloc,liveness,reentry) \
- HP_CHK_GEN(alloc,liveness,reentry); \
+#define HP_CHK_GEN_TICKY(alloc) \
+ HP_CHK_GEN(alloc); \
TICK_ALLOC_HEAP_NOCTR(alloc);
+#define HP_CHK_P(bytes, fun, arg) \
+ HEAP_CHECK(bytes, GC_PRIM_P(fun,arg))
+
+#define ALLOC_P_TICKY(alloc, fun, arg) \
+ HP_CHK_P(alloc); \
+ TICK_ALLOC_HEAP_NOCTR(alloc);
+
+#define CHECK_GC() \
+ (bdescr_link(CurrentNursery) == NULL || \
+ generation_n_new_large_words(W_[g0]) >= TO_W_(CLong[large_alloc_lim]))
+
// allocate() allocates from the nursery, so we check to see
// whether the nursery is nearly empty in any function that uses
// allocate() - this includes many of the primops.
-#define MAYBE_GC(liveness,reentry) \
- if (bdescr_link(CurrentNursery) == NULL || \
- generation_n_new_large_words(W_[g0]) >= TO_W_(CLong[large_alloc_lim])) { \
- R9 = liveness; \
- R10 = reentry; \
- HpAlloc = 0; \
- jump stg_gc_gen_hp; \
+//
+// HACK alert: the __L__ stuff is here to coax the common-block
+// eliminator into commoning up the call stg_gc_noregs() with the same
+// code that gets generated by a STK_CHK_GEN() in the same proc. We
+// also need an if (0) { goto __L__; } so that the __L__ label isn't
+// optimised away by the control-flow optimiser prior to common-block
+// elimination (it will be optimised away later).
+//
+// This saves some code in gmp-wrappers.cmm where we have lots of
+// MAYBE_GC() in the same proc as STK_CHK_GEN().
+//
+#define MAYBE_GC(retry) \
+ if (CHECK_GC()) { \
+ HpAlloc = 0; \
+ goto __L__; \
+ __L__: \
+ call stg_gc_noregs(); \
+ goto retry; \
+ } \
+ if (0) { goto __L__; }
+
+#define GC_PRIM(fun) \
+ R9 = fun; \
+ jump stg_gc_prim();
+
+#define GC_PRIM_N(fun,arg) \
+ R9 = fun; \
+ jump stg_gc_prim_n(arg);
+
+#define GC_PRIM_P(fun,arg) \
+ R9 = fun; \
+ jump stg_gc_prim_p(arg);
+
+#define GC_PRIM_PP(fun,arg1,arg2) \
+ R9 = fun; \
+ jump stg_gc_prim_pp(arg1,arg2);
+
+#define MAYBE_GC_(fun) \
+ if (CHECK_GC()) { \
+ HpAlloc = 0; \
+ GC_PRIM(fun) \
+ }
+
+#define MAYBE_GC_N(fun,arg) \
+ if (CHECK_GC()) { \
+ HpAlloc = 0; \
+ GC_PRIM_N(fun,arg) \
+ }
+
+#define MAYBE_GC_P(fun,arg) \
+ if (CHECK_GC()) { \
+ HpAlloc = 0; \
+ GC_PRIM_P(fun,arg) \
}
+#define MAYBE_GC_PP(fun,arg1,arg2) \
+ if (CHECK_GC()) { \
+ HpAlloc = 0; \
+ GC_PRIM_PP(fun,arg1,arg2) \
+ }
+
+#define STK_CHK(n, fun) \
+ if (Sp - n < SpLim) { \
+ GC_PRIM(fun) \
+ }
+
+#define STK_CHK_P(n, fun, arg) \
+ if (Sp - n < SpLim) { \
+ GC_PRIM_P(fun,arg) \
+ }
+
+#define STK_CHK_PP(n, fun, arg1, arg2) \
+ if (Sp - n < SpLim) { \
+ GC_PRIM_PP(fun,arg1,arg2) \
+ }
+
+#define STK_CHK_ENTER(n, closure) \
+ if (Sp - n < SpLim) { \
+ jump __stg_gc_enter_1(closure); \
+ }
+
+// A funky heap check used by AutoApply.cmm
+
+#define HP_CHK_NP_ASSIGN_SP0(size,f) \
+ HEAP_CHECK(size, Sp(0) = f; jump __stg_gc_enter_1 [R1];)
+
/* -----------------------------------------------------------------------------
Closure headers
-------------------------------------------------------------------------- */
@@ -481,23 +580,6 @@
#endif
/* -----------------------------------------------------------------------------
- Voluntary Yields/Blocks
-
- We only have a generic version of this at the moment - if it turns
- out to be slowing us down we can make specialised ones.
- -------------------------------------------------------------------------- */
-
-#define YIELD(liveness,reentry) \
- R9 = liveness; \
- R10 = reentry; \
- jump stg_gen_yield;
-
-#define BLOCK(liveness,reentry) \
- R9 = liveness; \
- R10 = reentry; \
- jump stg_gen_block;
-
-/* -----------------------------------------------------------------------------
Ticky macros
-------------------------------------------------------------------------- */
@@ -585,6 +667,63 @@
TICK_BUMP_BY(ALLOC_HEAP_tot,n)
/* -----------------------------------------------------------------------------
+ Saving and restoring STG registers
+
+ STG registers must be saved around a C call, just in case the STG
+ register is mapped to a caller-saves machine register. Normally we
+ don't need to worry about this the code generator has already
+ loaded any live STG registers into variables for us, but in
+ hand-written low-level Cmm code where we don't know which registers
+ are live, we might have to save them all.
+ -------------------------------------------------------------------------- */
+
+#define SAVE_STGREGS \
+ W_ r1, r2, r3, r4, r5, r6, r7, r8; \
+ F_ f1, f2, f3, f4; \
+ D_ d1, d2; \
+ L_ l1; \
+ \
+ r1 = R1; \
+ r2 = R2; \
+ r3 = R3; \
+ r4 = R4; \
+ r5 = R5; \
+ r6 = R6; \
+ r7 = R7; \
+ r8 = R8; \
+ \
+ f1 = F1; \
+ f2 = F2; \
+ f3 = F3; \
+ f4 = F4; \
+ \
+ d1 = D1; \
+ d2 = D2; \
+ \
+ l1 = L1;
+
+
+#define RESTORE_STGREGS \
+ R1 = r1; \
+ R2 = r2; \
+ R3 = r3; \
+ R4 = r4; \
+ R5 = r5; \
+ R6 = r6; \
+ R7 = r7; \
+ R8 = r8; \
+ \
+ F1 = f1; \
+ F2 = f2; \
+ F3 = f3; \
+ F4 = f4; \
+ \
+ D1 = d1; \
+ D2 = d2; \
+ \
+ L1 = l1;
+
+/* -----------------------------------------------------------------------------
Misc junk
-------------------------------------------------------------------------- */
@@ -592,14 +731,14 @@
#define END_TSO_QUEUE stg_END_TSO_QUEUE_closure
#define END_INVARIANT_CHECK_QUEUE stg_END_INVARIANT_CHECK_QUEUE_closure
-#define recordMutableCap(p, gen, regs) \
+#define recordMutableCap(p, gen) \
W_ __bd; \
W_ mut_list; \
mut_list = Capability_mut_lists(MyCapability()) + WDS(gen); \
__bd = W_[mut_list]; \
if (bdescr_free(__bd) >= bdescr_start(__bd) + BLOCK_SIZE) { \
W_ __new_bd; \
- ("ptr" __new_bd) = foreign "C" allocBlock_lock() [regs]; \
+ ("ptr" __new_bd) = foreign "C" allocBlock_lock(); \
bdescr_link(__new_bd) = __bd; \
__bd = __new_bd; \
W_[mut_list] = __bd; \
@@ -609,13 +748,13 @@
W_[free] = p; \
bdescr_free(__bd) = free + WDS(1);
-#define recordMutable(p, regs) \
+#define recordMutable(p) \
P_ __p; \
W_ __bd; \
W_ __gen; \
__p = p; \
__bd = Bdescr(__p); \
__gen = TO_W_(bdescr_gen_no(__bd)); \
- if (__gen > 0) { recordMutableCap(__p, __gen, regs); }
+ if (__gen > 0) { recordMutableCap(__p, __gen); }
#endif /* CMM_H */