/* -----------------------------------------------------------------------------
- * $Id: StgCRun.c,v 1.8 1999/11/03 15:00:21 simonmar Exp $
+ * $Id: StgCRun.c,v 1.25 2001/08/07 20:06:41 ken Exp $
*
- * (c) The GHC Team, 1998-1999
+ * (c) The GHC Team, 1998-2000
*
- * STG-to-C glue. Some architectures have this code written in
- * straight assembler (see StgRun.S), some in C.
+ * STG-to-C glue.
+ *
+ * To run an STG function from C land, call
+ *
+ * rv = StgRun(f,BaseReg);
+ *
+ * where "f" is the STG function to call, and BaseReg is the address of the
+ * RegTable for this run (we might have separate RegTables if we're running
+ * multiple threads on an SMP machine).
+ *
+ * In the end, "f" must JMP to StgReturn (defined below),
+ * passing the return-value "rv" in R1,
+ * to return to the caller of StgRun returning "rv" in
+ * the whatever way C returns a value.
+ *
+ * NOTE: StgRun/StgReturn do *NOT* load or store Hp or any
+ * other registers (other than saving the C callee-saves
+ * registers). Instead, the called function "f" must do that
+ * in STG land.
+ *
+ * GCC will have assumed that pushing/popping of C-stack frames is
+ * going on when it generated its code, and used stack space
+ * accordingly. However, we actually {\em post-process away} all
+ * such stack-framery (see \tr{ghc/driver/ghc-asm.lprl}). Things will
+ * be OK however, if we initially make sure there are
+ * @RESERVED_C_STACK_BYTES@ on the C-stack to begin with, for local
+ * variables.
*
* -------------------------------------------------------------------------- */
+
+/*
+ * We define the following (unused) global register variables, because for
+ * some reason gcc generates sub-optimal code for StgRun() on the Alpha
+ * (unnecessarily saving extra registers on the stack) if we don't.
+ *
+ * Why do it at the top of this file, rather than near StgRun() below? Because
+ * gcc doesn't let us define global register variables after any function
+ * definition has been read. Any point after #include "Stg.h" would be too
+ * late.
+ *
+ * You can define alpha_EXTRA_CAREFUL here to save $s6, $f8 and $f9 -- registers
+ * that we don't use but which are callee-save registers. It shouldn't be
+ * necessary.
+ */
+#include "config.h"
+#ifdef alpha_TARGET_ARCH
+#undef alpha_EXTRA_CAREFUL
+register long fake_ra __asm__("$26");
+#ifdef alpha_EXTRA_CAREFUL
+register long fake_s6 __asm__("$15");
+register double fake_f8 __asm__("$f8");
+register double fake_f9 __asm__("$f9");
+#endif
+#endif
+
/* include Stg.h first because we want real machine regs in here: we
* have to get the value of R1 back from Stg land to C land intact.
*/
static jmp_buf jmp_environment;
-#if 0
+#if 1
extern StgThreadReturnCode StgRun(StgFunPtr f, StgRegTable *basereg)
{
- jmp_buf save_buf;
- /* Save jmp_environment for previous call to miniInterpret */
- memcpy((void *) jmp_environment, (void *) save_buf, sizeof(jmp_buf));
- if (setjmp(jmp_environment) == 0) {
- while ( 1 ) {
- IF_DEBUG(evaluator,
- fprintf(stderr,"Jumping to ");
- printPtr((P_)f);
- fprintf(stderr,"\n");
- );
- f = (StgFunPtr) (f)();
- }
- }
- /* Restore jmp_environment for previous call */
- memcpy((void*) save_buf, (void*) jmp_environment, sizeof(jmp_buf));
-
- return (StgThreadReturnCode)R1.i;
+ while (f) {
+ IF_DEBUG(evaluator,
+ fprintf(stderr,"Jumping to ");
+ printPtr((P_)f);
+ fprintf(stderr,"\n");
+ );
+ f = (StgFunPtr) (f)();
+ }
+ return (StgThreadReturnCode)R1.i;
}
EXTFUN(StgReturn)
{
- longjmp(jmp_environment, 1);
+ return 0;
}
#else
+#define CHECK_STACK 0
+#define STACK_DETAILS 0
+
+static int enters = 0;
+
+static void scanStackSeg ( W_* ptr, int nwords )
+{
+ W_ w;
+#if CHECK_STACK
+ int nwords0 = nwords;
+#if STACK_DETAILS
+ while (nwords > 0) {
+ w = *ptr;
+ if (IS_ARG_TAG(w)) {
+ fprintf ( stderr, "%d",w ); nwords--; ptr++;
+ while (w) { fprintf(stderr, "_"); w--; nwords--; ptr++; }
+ }
+ else {
+ fprintf(stderr, "p");
+ nwords--; ptr++;
+ }
+ }
+ if (nwords < 0) fprintf(stderr, "erk: nwords < 0\n");
+#endif
+ checkStackChunk ( ptr, ptr-nwords0 );
+#endif
+}
+
+extern StgFunPtr stg_enterStackTop;
extern StgThreadReturnCode StgRun(StgFunPtr f, StgRegTable *basereg)
{
char* nm;
- while ( f ) {
+ while (1) {
-#if 0
- //IF_DEBUG(evaluator,
- fprintf(stderr,"Jumping to ");
- nm = nameOfObjSym ( f );
- if (nm)
- fprintf(stderr, "%s (%p)", nm, f); else
- printPtr((P_)f);
- fprintf(stderr,"\n");
- // );
-if (0&& MainRegTable.rSp) {
+#if CHECK_STACK
+ {
int i;
- StgWord* p = MainRegTable.rSp;
-fprintf(stderr, "SP = %p\n", p);
- p += (8-1);
- for (i = 0; i < 8; i++, p--)
- fprintf (stderr, "-- %p: %p\n", p, *p );
-}
-#endif
+ StgTSO* tso = basereg->rCurrentTSO;
+ StgWord* sb = tso->stack + tso->stack_size;
+ StgWord* sp;
+ StgWord* su;
+ int ws;
+
+ if (f == &stg_enterStackTop) {
+ sp = tso->sp;
+ su = tso->su;
+ } else {
+ sp = basereg->rSp;
+ su = basereg->rSu;
+ }
+
+#if STACK_DETAILS
+ fprintf(stderr,
+ "== SB = %p SP = %p(%p) SU = %p SpLim = %p(%p)\n",
+ sb, sp, tso->sp, su, basereg->rSpLim, tso->stack + RESERVED_STACK_WORDS);
+#endif
+
+ if (su >= sb) goto postloop;
+ if (!sp || !su) goto postloop;
+
+ printStack ( sp, sb, su);
+ while (1) {
+ ws = su - sp;
+ switch (get_itbl((StgClosure*)su)->type) {
+ case STOP_FRAME:
+ scanStackSeg(sp,ws);
+#if STACK_DETAILS
+ fprintf(stderr, "S%d ",ws);
+ fprintf(stderr, "\n");
+#endif
+ goto postloop;
+ case UPDATE_FRAME:
+ scanStackSeg(sp,ws);
+#if STACK_DETAILS
+ fprintf(stderr,"U%d ",ws);
+#endif
+ sp = su + sizeofW(StgUpdateFrame);
+ su = ((StgUpdateFrame*)su)->link;
+ break;
+ case SEQ_FRAME:
+ scanStackSeg(sp,ws);
+#if STACK_DETAILS
+ fprintf(stderr,"Q%d ",ws);
+#endif
+ sp = su + sizeofW(StgSeqFrame);
+ su = ((StgSeqFrame*)su)->link;
+ break;
+ case CATCH_FRAME:
+ scanStackSeg(sp,ws);
+#if STACK_DETAILS
+ fprintf(stderr,"C%d ",ws);
+#endif
+ sp = su + sizeofW(StgCatchFrame);
+ su = ((StgCatchFrame*)su)->link;
+ break;
+ default:
+ fprintf(stderr, "?\nweird record on stack\n");
+ assert(0);
+ goto postloop;
+ }
+ }
+ postloop:
+ }
+#endif
+#if STACK_DETAILS
+ fprintf(stderr,"\n");
+#endif
+#if 1
+ fprintf(stderr,"-- enter %p ", f);
+ nm = nameFromOPtr ( f );
+ if (nm) fprintf(stderr, "%s", nm); else
+ printPtr((P_)f);
+ fprintf ( stderr, "\n");
+#endif
+#if STACK_DETAILS
+ fprintf(stderr,"\n");
+#endif
+ zzz:
+ if (enters % 1000 == 0) fprintf(stderr, "%d enters\n",enters);
+ enters++;
f = (StgFunPtr) (f)();
+ if (!f) break;
}
-
+ fprintf (stderr, "miniInterpreter: bye!\n\n" );
return (StgThreadReturnCode)R1.i;
}
#endif
/* -----------------------------------------------------------------------------
- sparc architecture
+ x86 architecture
+ -------------------------------------------------------------------------- */
+
+#ifdef i386_TARGET_ARCH
+
+StgThreadReturnCode
+StgRun(StgFunPtr f, StgRegTable *basereg) {
+
+ unsigned char space[ RESERVED_C_STACK_BYTES + 4*sizeof(void *) ];
+ StgThreadReturnCode r;
+
+ __asm__ volatile (
+ /*
+ * save callee-saves registers on behalf of the STG code.
+ */
+ "movl %%esp, %%eax\n\t"
+ "addl %4, %%eax\n\t"
+ "movl %%ebx,0(%%eax)\n\t"
+ "movl %%esi,4(%%eax)\n\t"
+ "movl %%edi,8(%%eax)\n\t"
+ "movl %%ebp,12(%%eax)\n\t"
+ /*
+ * Set BaseReg
+ */
+ "movl %3,%%ebx\n\t"
+ /*
+ * grab the function argument from the stack, and jump to it.
+ */
+ "movl %2,%%eax\n\t"
+ "jmp *%%eax\n\t"
+
+ ".global " STG_RETURN "\n"
+ STG_RETURN ":\n\t"
+
+ "movl %%esi, %%eax\n\t" /* Return value in R1 */
+
+ /*
+ * restore callee-saves registers. (Don't stomp on %%eax!)
+ */
+ "movl %%esp, %%edx\n\t"
+ "addl %4, %%edx\n\t"
+ "movl 0(%%edx),%%ebx\n\t" /* restore the registers saved above */
+ "movl 4(%%edx),%%esi\n\t"
+ "movl 8(%%edx),%%edi\n\t"
+ "movl 12(%%edx),%%ebp\n\t"
+
+ : "=&a" (r), "=m" (space)
+ : "m" (f), "m" (basereg), "i" (RESERVED_C_STACK_BYTES)
+ : "edx" /* stomps on %edx */
+ );
+
+ return r;
+}
+
+#endif
+
+/* -----------------------------------------------------------------------------
+ Sparc architecture
+
+ --
+ OLD COMMENT from GHC-3.02:
+
+ We want tailjumps to be calls, because `call xxx' is the only Sparc
+ branch that allows an arbitrary label as a target. (Gcc's ``goto
+ *target'' construct ends up loading the label into a register and
+ then jumping, at the cost of two extra instructions for the 32-bit
+ load.)
+
+ When entering the threaded world, we stash our return address in a
+ known location so that \tr{%i7} is available as an extra
+ callee-saves register. Of course, we have to restore this when
+ coming out of the threaded world.
+
+ I hate this god-forsaken architecture. Since the top of the
+ reserved stack space is used for globals and the bottom is reserved
+ for outgoing arguments, we have to stick our return address
+ somewhere in the middle. Currently, I'm allowing 100 extra
+ outgoing arguments beyond the first 6. --JSM
+
+ Updated info (GHC 4.06): we don't appear to use %i7 any more, so
+ I'm not sure whether we still need to save it. Incedentally, what
+ does the last paragraph above mean when it says "the top of the
+ stack is used for globals"? What globals? --SDM
+
+ Updated info (GHC 4.08.2): not saving %i7 any more (see below).
-------------------------------------------------------------------------- */
#ifdef sparc_TARGET_ARCH
StgThreadReturnCode
StgRun(StgFunPtr f, StgRegTable *basereg) {
- StgChar space[RESERVED_C_STACK_BYTES+sizeof(void *)];
+ unsigned char space[RESERVED_C_STACK_BYTES];
+#if 0
register void *i7 __asm__("%i7");
((void **)(space))[100] = i7;
+#endif
f();
- __asm__ volatile (".align 4\n"
+ __asm__ volatile (
+ ".align 4\n"
".global " STG_RETURN "\n"
- STG_RETURN ":\n"
- "\tld %1,%0" : "=r" (i7) : "m" (((void **)(space))[100]));
+ STG_RETURN ":"
+ : : : "l0","l1","l2","l3","l4","l5","l6","l7");
+ /* we tell the C compiler that l0-l7 are clobbered on return to
+ * StgReturn, otherwise it tries to use these to save eg. the
+ * address of space[100] across the call. The correct thing
+ * to do would be to save all the callee-saves regs, but we
+ * can't be bothered to do that.
+ *
+ * The code that gcc generates for this little fragment is now
+ * terrible. We could do much better by coding it directly in
+ * assembler.
+ */
+#if 0
+ /* updated 4.08.2: we don't save %i7 in the middle of the reserved
+ * space any more, since gcc tries to save its address across the
+ * call to f(), this gets clobbered in STG land and we end up
+ * dereferencing a bogus pointer in StgReturn.
+ */
+ __asm__ volatile ("ld %1,%0"
+ : "=r" (i7) : "m" (((void **)(space))[100]));
+#endif
return (StgThreadReturnCode)R1.i;
}
/* -----------------------------------------------------------------------------
alpha architecture
+
+ "The stack pointer (SP) must at all times denote an address that has octaword
+ alignment. (This restriction has the side effect that the in-memory portion
+ of the argument list, if any, will start on an octaword boundary.) Note that
+ the stack grows toward lower addresses. During a procedure invocation, SP
+ can never be set to a value that is higher than the value of SP at entry to
+ that procedure invocation.
+
+ "The contents of the stack, located above the portion of the argument list
+ (if any) that is passed in memory, belong to the calling procedure. Because
+ they are part of the calling procedure, they should not be read or written
+ by the called procedure, except as specified by indirect arguments or
+ language-controlled up-level references.
+
+ "The SP value might be used by the hardware when raising exceptions and
+ asynchronous interrupts. It must be assumed that the contents of the stack
+ below the current SP value and within the stack for the current thread are
+ continually and unpredictably modified, as specified in the _Alpha
+ Architecture Reference Manual_, and as a result of asynchronous software
+ actions."
+
+ -- Compaq Computer Corporation, Houston. Tru64 UNIX Calling Standard for
+ Alpha Systems, 5.1 edition, August 2000, section 3.2.1. http://www.
+ tru64unix.compaq.com/docs/base_doc/DOCUMENTATION/V51_PDF/ARH9MBTE.PDF
-------------------------------------------------------------------------- */
#ifdef alpha_TARGET_ARCH
StgThreadReturnCode
StgRun(StgFunPtr f, StgRegTable *basereg)
{
- __asm__ volatile ("stq $9,-8($30)\n\t"
- "stq $10,-16($30)\n\t"
- "stq $11,-24($30)\n\t"
- "stq $12,-32($30)\n\t"
- "stq $13,-40($30)\n\t"
- "stq $14,-48($30)\n\t"
- "stq $15,-56($30)\n\t"
- "stt $f2,-64($30)\n\t"
- "stt $f3,-72($30)\n\t"
- "stt $f4,-80($30)\n\t"
- "stt $f5,-88($30)\n\t"
- "stt $f6,-96($30)\n\t"
- "stt $f7,-104($30)\n\t"
- "stt $f8,-112($30)\n\t"
- "stt $f9,-120($30)\n\t"
- "lda $30,-%0($30)" : :
- "K" (RESERVED_C_STACK_BYTES+
- 8*sizeof(double)+8*sizeof(long)));
+ register long real_ra __asm__("$26"); volatile long save_ra;
+
+ register long real_s0 __asm__("$9" ); volatile long save_s0;
+ register long real_s1 __asm__("$10"); volatile long save_s1;
+ register long real_s2 __asm__("$11"); volatile long save_s2;
+ register long real_s3 __asm__("$12"); volatile long save_s3;
+ register long real_s4 __asm__("$13"); volatile long save_s4;
+ register long real_s5 __asm__("$14"); volatile long save_s5;
+#ifdef alpha_EXTRA_CAREFUL
+ register long real_s6 __asm__("$15"); volatile long save_s6;
+#endif
+
+ register double real_f2 __asm__("$f2"); volatile double save_f2;
+ register double real_f3 __asm__("$f3"); volatile double save_f3;
+ register double real_f4 __asm__("$f4"); volatile double save_f4;
+ register double real_f5 __asm__("$f5"); volatile double save_f5;
+ register double real_f6 __asm__("$f6"); volatile double save_f6;
+ register double real_f7 __asm__("$f7"); volatile double save_f7;
+#ifdef alpha_EXTRA_CAREFUL
+ register double real_f8 __asm__("$f8"); volatile double save_f8;
+ register double real_f9 __asm__("$f9"); volatile double save_f9;
+#endif
- f();
+ register StgFunPtr real_pv __asm__("$27");
- __asm__ volatile (".align 3\n"
- ".globl " STG_RETURN "\n"
- STG_RETURN ":\n\t"
- "lda $30,%0($30)\n\t"
- "ldq $9,-8($30)\n\t"
- "ldq $10,-16($30)\n\t"
- "ldq $11,-24($30)\n\t"
- "ldq $12,-32($30)\n\t"
- "ldq $13,-40($30)\n\t"
- "ldq $14,-48($30)\n\t"
- "ldq $15,-56($30)\n\t"
- "ldt $f2,-64($30)\n\t"
- "ldt $f3,-72($30)\n\t"
- "ldt $f4,-80($30)\n\t"
- "ldt $f5,-88($30)\n\t"
- "ldt $f6,-96($30)\n\t"
- "ldt $f7,-104($30)\n\t"
- "ldt $f8,-112($30)\n\t"
- "ldt $f9,-120($30)" : :
- "K" (RESERVED_C_STACK_BYTES+
- 8*sizeof(double)+8*sizeof(long)));
+ StgThreadReturnCode ret;
- return (StgThreadReturnCode)R1.i;
+ save_ra = real_ra;
+
+ save_s0 = real_s0;
+ save_s1 = real_s1;
+ save_s2 = real_s2;
+ save_s3 = real_s3;
+ save_s4 = real_s4;
+ save_s5 = real_s5;
+#ifdef alpha_EXTRA_CAREFUL
+ save_s6 = real_s6;
+#endif
+
+ save_f2 = real_f2;
+ save_f3 = real_f3;
+ save_f4 = real_f4;
+ save_f5 = real_f5;
+ save_f6 = real_f6;
+ save_f7 = real_f7;
+#ifdef alpha_EXTRA_CAREFUL
+ save_f8 = real_f8;
+ save_f9 = real_f9;
+#endif
+
+ real_pv = f;
+
+ __asm__ volatile( "lda $30,-%0($30)" "\n"
+ "\t" "jmp ($27)" "\n"
+ "\t" ".align 3" "\n"
+ ".globl " STG_RETURN "\n"
+ STG_RETURN ":" "\n"
+ "\t" "lda $30,%0($30)" "\n"
+ : : "K" (RESERVED_C_STACK_BYTES));
+
+ ret = real_s5;
+
+ real_s0 = save_s0;
+ real_s1 = save_s1;
+ real_s2 = save_s2;
+ real_s3 = save_s3;
+ real_s4 = save_s4;
+ real_s5 = save_s5;
+#ifdef alpha_EXTRA_CAREFUL
+ real_s6 = save_s6;
+#endif
+
+ real_f2 = save_f2;
+ real_f3 = save_f3;
+ real_f4 = save_f4;
+ real_f5 = save_f5;
+ real_f6 = save_f6;
+ real_f7 = save_f7;
+#ifdef alpha_EXTRA_CAREFUL
+ real_f8 = save_f8;
+ real_f9 = save_f9;
+#endif
+
+ real_ra = save_ra;
+
+ return ret;
}
#endif /* alpha_TARGET_ARCH */