2 /* --------------------------------------------------------------------------
5 * The Hugs 98 system is Copyright (c) Mark P Jones, Alastair Reid, the
6 * Yale Haskell Group, and the Oregon Graduate Institute of Science and
7 * Technology, 1994-1999, All rights reserved. It is distributed as
8 * free software under the license in the file "License", which is
9 * included in the distribution.
11 * $RCSfile: codegen.c,v $
13 * $Date: 2000/04/27 16:35:29 $
14 * ------------------------------------------------------------------------*/
16 #include "hugsbasictypes.h"
21 #include "Rts.h" /* to make StgPtr visible in Assembler.h */
22 #include "Assembler.h"
25 /*#define DEBUG_CODEGEN*/
27 /* (JRS, 27 Apr 2000):
29 A total rewrite of the BCO assembler/linker, and rationalisation of
30 the code management and code generation phases of Hugs.
32 Problems with the old linker:
34 * Didn't have a clean way to insert a pointer to GHC code into a BCO.
35 This meant CAF GC didn't work properly in combined mode.
37 * Leaked memory. Each BCO, caf and constructor generated by Hugs had
38 a corresponding malloc'd record used in its construction. These
39 records existed forever. Pointers from the Hugs symbol tables into
40 the runtime heap always went via these intermediates, for no apparent
43 * A global variable holding a list of top-level stg trees was used
44 during code generation. It was hard to associate trees in this
45 list with entries in the name/tycon tables. Just too many
48 The New World Order is as follows:
50 * The global code list (stgGlobals) is gone.
52 * Each name in the name table has a .closure field. This points
53 to the top-level code for that name. Before bytecode generation
54 this points to a STG tree. During bytecode generation but before
55 bytecode linking it is a MPtr pointing to a malloc'd intermediate
56 structure (an AsmObject). After linking, it is a real live pointer
57 into the execution heap (CPtr) which is treated as a root during GC.
59 Because tuples do not have name table entries, tycons which are
60 tuples also have a .closure field, which is treated identically
61 to those of name table entries.
63 * Each module has a code list -- a list of names and tuples. If you
64 are a name or tuple and you have something (code, CAF or Con) which
65 needs to wind up in the execution heap, you MUST be on your module's
66 code list. Otherwise you won't get code generated.
68 * Lambda lifting generates new name table entries, which of course
69 also wind up on the code list.
71 * The initial phase of code generation for a module m traverses m's
72 code list. The stg trees referenced in the .closure fields are
73 code generated, creating AsmObject (AsmBCO, AsmCAF, AsmCon) in
74 mallocville. The .closure fields then point to these AsmObjects.
75 Since AsmObjects can be mutually recursive, they can contain
77 * Other AsmObjects Asm_RefObject
78 * Existing closures Asm_RefNoOp
79 * name/tycon table entries Asm_RefHugs
80 AsmObjects can also contain BCO insns and non-ptr words.
82 * A second copy-and-link phase copies the AsmObjects into the
83 execution heap, resolves the Asm_Ref* items, and frees up
84 the malloc'd entities.
86 * Minor cleanups in compile-time storage. There are now 3 kinds of
87 address-y things available:
88 CPtr/mkCPtr/cptrOf -- ptrs to Closures, probably in exec heap
89 ie anything which the exec GC knows about
90 MPtr/mkMPtr/mptrOf -- ptrs to mallocville, which the exec GC
92 Addr/mkAddr/addrOf -- literal addresses (like literal ints)
94 * Many hacky cases removed from codegen.c. Referencing code or
95 data during code generation is a lot simpler, since an entity
97 a CPtr, in which case use it as is
98 a MPtr -- stuff it into the AsmObject and the linker will fix it
102 * I've checked, using Purify that, at least in standalone mode,
103 no longer leaks mallocd memory. Prior to this it would leak at
104 the rate of about 300k per Prelude.
108 * Reinstate peephole optimisation for BCOs.
110 * Nuke magic number headers in AsmObjects, used for debugging.
112 * Profile and accelerate. Code generation is slower because linking
113 is slower. Evaluation GC is slower because markHugsObjects has
116 * Make setCurrentModule ignore name table entries created by the
119 * Zap various #if 0 in codegen.c/Assembler.c.
121 * Zap CRUDE_PROFILING.
125 /* --------------------------------------------------------------------------
126 * Local function prototypes:
127 * ------------------------------------------------------------------------*/
129 #define getPos(v) intOf(stgVarInfo(v))
130 #define setPos(v,sp) stgVarInfo(v) = mkInt(sp)
131 #define getObj(v) mptrOf(stgVarInfo(v))
132 #define setObj(v,obj) stgVarInfo(v) = mkMPtr(obj)
134 #define repOf(x) charOf(stgVarRep(x))
136 static void cgBind ( AsmBCO bco, StgVar v );
137 static Void pushAtom ( AsmBCO bco, StgAtom atom );
138 static Void alloc ( AsmBCO bco, StgRhs rhs );
139 static Void build ( AsmBCO bco, StgRhs rhs );
140 static Void cgExpr ( AsmBCO bco, AsmSp root, StgExpr e );
142 static AsmBCO cgAlts ( AsmSp root, AsmSp sp, List alts );
143 static void testPrimPats ( AsmBCO bco, AsmSp root, List pats, StgExpr e );
144 static AsmBCO cgLambda ( StgExpr e );
145 static AsmBCO cgRhs ( StgRhs rhs );
146 static void beginTop ( StgVar v );
147 static AsmObject endTop ( StgVar v );
149 static StgVar currentTop;
151 /* --------------------------------------------------------------------------
153 * ------------------------------------------------------------------------*/
155 static void* /* StgClosure*/ cptrFromName ( Name n )
159 Module m = name(n).mod;
160 Text mt = module(m).text;
161 sprintf(buf, MAYBE_LEADING_UNDERSCORE_STR("%s_%s_closure"),
163 textToStr( enZcodeThenFindText (
164 textToStr (name(n).text) ) ) );
165 p = lookupOTabName ( m, buf );
167 ERRMSG(0) "Can't find object symbol %s", buf
173 char* lookupHugsName( void* closure )
177 for( nm = NAME_BASE_ADDR;
178 nm < NAME_BASE_ADDR+tabNameSz; ++nm )
179 if (tabName[nm-NAME_BASE_ADDR].inUse) {
180 Cell cl = name(nm).closure;
181 if (isCPtr(cl) && cptrOf(cl) == closure)
182 return textToStr(name(nm).text);
187 static void cgBindRep( AsmBCO bco, StgVar v, AsmRep rep )
189 setPos(v,asmBind(bco,rep));
192 static void cgBind( AsmBCO bco, StgVar v )
194 cgBindRep(bco,v,repOf(v));
197 static void cgAddPtrToObject ( AsmObject obj, Cell ptrish )
199 switch (whatIs(ptrish)) {
201 asmAddRefNoOp ( obj, (StgPtr)cptrOf(ptrish) ); break;
203 asmAddRefObject ( obj, mptrOf(ptrish) ); break;
205 internal("cgAddPtrToObject");
210 static void cgPushRef ( AsmBCO bco, Cell c )
214 asmPushRefNoOp(bco,(StgPtr)cptrOf(c)); break;
216 asmPushRefObject(bco,ptrOf(c)); break;
219 asmPushRefHugs(bco,c); break;
221 internal("cgPushRef");
226 /* Get a pointer to atom e onto the stack. */
227 static Void pushAtom ( AsmBCO bco, StgAtom e )
232 printf ( "pushAtom: %d ", e ); fflush(stdout);
233 print(e,10);printf("\n");
237 info = stgVarInfo(e);
239 asmVar(bco,intOf(info),repOf(e));
243 asmPushRefNoOp(bco,cptrOf(info));
247 asmPushRefObject(bco,mptrOf(info));
250 internal("pushAtom: STGVAR");
255 cl = getNameOrTupleClosure(e);
257 /* a stg tree which hasn't yet been translated */
258 asmPushRefHugs(bco,e);
262 /* a pointer to something in the heap */
263 asmPushRefNoOp(bco,(StgPtr)cptrOf(cl));
267 /* a pointer to an AsmBCO/AsmCAF/AsmCon object */
268 asmPushRefObject(bco,mptrOf(cl));
273 addr = cptrFromName(e);
275 fprintf ( stderr, "nativeAtom: name %s\n",
276 nameFromOPtr(addr) );
278 asmPushRefNoOp(bco,(StgPtr)addr);
282 asmConstChar(bco,charOf(e));
285 asmConstInt(bco,intOf(e));
288 asmConstAddr(bco,addrOf(e));
291 asmConstInteger(bco,bignumToString(e));
294 asmConstDouble(bco,floatOf(e));
297 # if USE_ADDR_FOR_STRINGS
298 asmConstAddr(bco,textToStr(textOf(e)));
300 asmClosure(bco,asmStringObj(textToStr(textOf(e))));
304 asmPushRefNoOp(bco,cptrOf(e));
307 asmPushRefObject(bco,mptrOf(e));
310 fprintf(stderr,"\nYoiks1: "); printExp(stderr,e);
311 internal("pushAtom");
315 static AsmBCO cgAlts( AsmSp root, AsmSp sp, List alts )
317 #ifdef CRUDE_PROFILING
318 AsmBCO bco = asmBeginContinuation(sp, currentTop + 1000000000);
320 AsmBCO bco = asmBeginContinuation(sp, alts);
323 = length(alts) == 2 &&
324 isDefaultAlt(hd(tl(alts))) &&
325 !isDefaultAlt(hd(alts));
327 /* refine the condition */
331 con = stgCaseAltCon(hd(alts));
333 /* special case: dictionary constructors */
334 if (isName(con) && strncmp(":D",textToStr(name(con).text),2)==0) {
338 /* special case: Tuples */
339 if (isTuple(con) || (isName(con) && con==nameUnit)) {
344 t = name(con).parent;
345 if (tycon(t).what == DATATYPE) {
346 if (length(tycon(t).defn) == 1) omit_test = TRUE;
352 for(; nonNull(alts); alts=tl(alts)) {
353 StgCaseAlt alt = hd(alts);
354 if (isDefaultAlt(alt)) {
355 cgBind(bco,stgDefaultVar(alt));
356 cgExpr(bco,root,stgDefaultBody(alt));
357 asmEndContinuation(bco);
358 return bco; /* ignore any further alternatives */
360 StgDiscr con = stgCaseAltCon(alt);
361 List vs = stgCaseAltVars(alt);
362 AsmSp begin = asmBeginAlt(bco);
364 if (omit_test) fix=-1; else fix = asmTest(bco,stgDiscrTag(con));
366 asmBind(bco,PTR_REP); /* Adjust simulated sp to acct for return value */
367 if (isBoxingCon(con)) {
368 setPos(hd(vs),asmUnbox(bco,boxingConRep(con)));
371 map1Proc(cgBind,bco,reverse(vs));
374 cgExpr(bco,root,stgCaseAltBody(alt));
375 asmEndAlt(bco,begin);
376 if (fix != -1) asmFixBranch(bco,fix);
379 /* if we got this far and didn't match, panic! */
381 asmEndContinuation(bco);
385 static void testPrimPats( AsmBCO bco, AsmSp root, List pats, StgExpr e )
390 StgVar pat = hd(pats);
391 if (isInt(stgVarBody(pat))) {
392 /* asmTestInt leaves stack unchanged - so no need to adjust it */
393 AsmPc tst = asmTestInt(bco,getPos(pat),intOf(stgVarBody(pat)));
394 assert(repOf(pat) == INT_REP);
395 testPrimPats(bco,root,tl(pats),e);
396 asmFixBranch(bco,tst);
398 testPrimPats(bco,root,tl(pats),e);
404 static AsmBCO cgLambda( StgExpr e )
406 AsmBCO bco = asmBeginBCO(e);
408 AsmSp root = asmBeginArgCheck(bco);
409 map1Proc(cgBind,bco,reverse(stgLambdaArgs(e)));
410 asmEndArgCheck(bco,root);
413 cgExpr(bco,root,stgLambdaBody(e));
419 static AsmBCO cgRhs( StgRhs rhs )
421 AsmBCO bco = asmBeginBCO(rhs );
423 AsmSp root = asmBeginArgCheck(bco);
424 asmEndArgCheck(bco,root);
426 /* ppStgExpr(rhs); */
427 cgExpr(bco,root,rhs);
434 static Void cgExpr( AsmBCO bco, AsmSp root, StgExpr e )
437 printf("cgExpr:");ppStgExpr(e);printf("\n");
442 List binds = stgLetBinds(e);
443 map1Proc(alloc,bco,binds);
444 map1Proc(build,bco,binds);
445 cgExpr(bco,root,stgLetBody(e));
450 AsmSp begin = asmBeginEnter(bco);
451 asmPushRefObject(bco,cgLambda(e));
452 asmEndEnter(bco,begin,root);
457 List alts = stgCaseAlts(e);
458 AsmSp sp = asmBeginCase(bco);
459 AsmSp caseroot = asmContinuation(bco,cgAlts(root,sp,alts));
460 cgExpr(bco,caseroot,stgCaseScrut(e));
466 StgExpr scrut = stgPrimCaseScrut(e);
467 List alts = stgPrimCaseAlts(e);
468 if (whatIs(scrut) == STGPRIM) { /* this is an optimisation */
470 /* No need to use return address or to Slide */
471 AsmSp beginPrim = asmBeginPrim(bco);
472 map1Proc(pushAtom,bco,reverse(stgPrimArgs(scrut)));
473 asmEndPrim(bco,(AsmPrim*)name(stgPrimOp(scrut)).primop,beginPrim);
475 for(; nonNull(alts); alts=tl(alts)) {
476 StgPrimAlt alt = hd(alts);
477 List pats = stgPrimAltVars(alt);
478 StgExpr body = stgPrimAltBody(alt);
479 AsmSp altBegin = asmBeginAlt(bco);
480 map1Proc(cgBind,bco,reverse(pats));
481 testPrimPats(bco,root,pats,body);
482 asmEndAlt(bco,altBegin);
484 /* if we got this far and didn't match, panic! */
487 } else if (whatIs(scrut) == STGVAR) { /* another optimisation */
489 /* No need to use return address or to Slide */
491 /* only part different from primop code... todo */
492 AsmSp beginCase = asmBeginCase(bco);
493 pushAtom /*pushVar*/ (bco,scrut);
494 asmEndAlt(bco,beginCase); /* hack, hack - */
496 for(; nonNull(alts); alts=tl(alts)) {
497 StgPrimAlt alt = hd(alts);
498 List pats = stgPrimAltVars(alt);
499 StgExpr body = stgPrimAltBody(alt);
500 AsmSp altBegin = asmBeginAlt(bco);
501 map1Proc(cgBind,bco,pats);
502 testPrimPats(bco,root,pats,body);
503 asmEndAlt(bco,altBegin);
505 /* if we got this far and didn't match, panic! */
509 /* ToDo: implement this code... */
511 /* asmPushRet(bco,delayPrimAlt( stgPrimCaseVars(e),
512 stgPrimCaseBody(e))); */
513 /* cgExpr( bco,root,scrut ); */
517 case STGAPP: /* Tail call */
519 AsmSp env = asmBeginEnter(bco);
520 map1Proc(pushAtom,bco,reverse(stgAppArgs(e)));
521 pushAtom(bco,stgAppFun(e));
522 asmEndEnter(bco,env,root);
526 case NAME: /* Tail call (with no args) */
528 AsmSp env = asmBeginEnter(bco);
529 /* JRS 000112: next line used to be: pushVar(bco,name(e).stgVar); */
531 asmEndEnter(bco,env,root);
534 case STGVAR: /* Tail call (with no args), plus unboxed return */
540 AsmSp env = asmBeginEnter(bco);
541 pushAtom /*pushVar*/ (bco,e);
542 asmEndEnter(bco,env,root);
547 /* cgTailCall(bco,singleton(e)); */
548 /* asmReturnInt(bco); */
551 internal("cgExpr StgVar");
554 case STGPRIM: /* Tail call again */
556 AsmSp beginPrim = asmBeginPrim(bco);
557 map1Proc(pushAtom,bco,reverse(stgPrimArgs(e)));
558 asmEndPrim(bco,(AsmPrim*)name(e).primop,beginPrim);
559 /* map1Proc(cgBind,bco,rs_vars); */
560 assert(0); /* asmReturn_retty(); */
564 fprintf(stderr,"\nYoiks2: "); printExp(stderr,e);
569 /* allocate space for top level variable
570 * any change requires a corresponding change in 'build'.
572 static Void alloc( AsmBCO bco, StgVar v )
574 StgRhs rhs = stgVarBody(v);
577 printf("alloc: ");ppStgExpr(v);
579 switch (whatIs(rhs)) {
582 StgDiscr con = stgConCon(rhs);
583 List args = stgConArgs(rhs);
584 if (isBoxingCon(con)) {
585 pushAtom(bco,hd(args));
586 setPos(v,asmBox(bco,boxingConRep(con)));
588 setPos(v,asmAllocCONSTR(bco,stgConInfo(con)));
594 List bs = stgAppArgs(rhs);
595 for (; nonNull(bs); bs=tl(bs)) {
596 if (isName(hd(bs))) {
599 ASSERT(whatIs(hd(bs))==STGVAR);
600 totSizeW += asmRepSizeW( charOf(stgVarRep(hd(bs))) );
603 setPos(v,asmAllocAP(bco,totSizeW));
606 case LAMBDA: /* optimisation */
607 setObj(v,cgLambda(rhs));
610 setPos(v,asmAllocAP(bco,0));
615 static Void build( AsmBCO bco, StgVar v )
617 StgRhs rhs = stgVarBody(v);
620 switch (whatIs(rhs)) {
623 StgDiscr con = stgConCon(rhs);
624 List args = stgConArgs(rhs);
625 if (isBoxingCon(con)) {
626 doNothing(); /* already done in alloc */
628 AsmSp start = asmBeginPack(bco);
629 map1Proc(pushAtom,bco,reverse(args));
630 asmEndPack(bco,getPos(v),start,stgConInfo(con));
637 StgVar fun = stgAppFun(rhs);
638 List args = stgAppArgs(rhs);
641 itsaPAP = name(fun).arity > length(args);
645 if (nonNull(stgVarBody(fun))
646 && whatIs(stgVarBody(fun)) == LAMBDA
647 && length(stgLambdaArgs(stgVarBody(fun))) > length(args)
652 internal("build: STGAPP");
654 Looks like a hack to me.
656 if (nonNull(name(fun).closure))
657 fun = name(fun).closure; else
658 fun = cptrFromName(fun);
662 assert(isName(fun0));
663 itsaPAP = name(fun0).arity > length(args);
665 fprintf ( stderr, "nativeCall: name %s, arity %d, args %d\n",
666 nameFromOPtr(cptrOf(fun)), name(fun0).arity,
671 if (nonNull(stgVarBody(fun))
672 && whatIs(stgVarBody(fun)) == LAMBDA
673 && length(stgLambdaArgs(stgVarBody(fun))) > length(args)
680 AsmSp start = asmBeginMkPAP(bco);
681 map1Proc(pushAtom,bco,reverse(args));
683 asmEndMkPAP(bco,getPos(v),start); /* optimisation */
685 AsmSp start = asmBeginMkAP(bco);
686 map1Proc(pushAtom,bco,reverse(args));
688 asmEndMkAP(bco,getPos(v),start);
692 case LAMBDA: /* optimisation */
693 doNothing(); /* already pushed in alloc */
696 /* These two cases look almost identical to the default but they're really
697 * special cases of STGAPP. The essential thing here is that we can't call
698 * cgRhs(rhs) because that expects the rhs to have no free variables when,
699 * in fact, the rhs is _always_ a free variable.
701 * ToDo: a simple optimiser would eliminate all examples
702 * of this except "let x = x in ..."
707 AsmSp start = asmBeginMkAP(bco);
709 asmEndMkAP(bco,getPos(v),start);
714 AsmSp start = asmBeginMkAP(bco); /* make it updateable! */
715 asmPushRefObject(bco,cgRhs(rhs));
716 asmEndMkAP(bco,getPos(v),start);
722 /* --------------------------------------------------------------------------
723 * Top level variables
725 * ToDo: these should be handled by allocating a dynamic unentered CAF
726 * for each top level variable - this should be simpler!
727 * ------------------------------------------------------------------------*/
729 /* allocate AsmObject for top level variables
730 * any change requires a corresponding change in endTop
732 static void beginTop( StgVar v )
738 switch (whatIs(rhs)) {
740 setObj(v,asmBeginCon(stgConInfo(stgConCon(rhs))));
743 # ifdef CRUDE_PROFILING
744 setObj(v,asmBeginBCO(currentTop));
746 setObj(v,asmBeginBCO(rhs));
750 setObj(v,asmBeginCAF());
755 static AsmObject endTop( StgVar v )
757 StgRhs rhs = stgVarBody(v);
759 switch (whatIs(rhs)) {
761 List as = stgConArgs(rhs);
762 AsmCon con = (AsmCon)getObj(v);
763 for ( ; nonNull(as); as=tl(as)) {
767 /* should be a delayed combinator! */
768 asmAddRefObject(con,(AsmObject)getObj(a));
771 StgVar var = name(a).closure;
772 cgAddPtrToObject(con,var);
775 # if !USE_ADDR_FOR_STRINGS
777 asmAddPtr(con,asmStringObj(textToStr(textOf(a))));
781 /* asmAddPtr(con,??); */
789 case LAMBDA: { /* optimisation */
790 /* ToDo: merge this code with cgLambda */
791 AsmBCO bco = (AsmBCO)getObj(v);
792 AsmSp root = asmBeginArgCheck(bco);
793 map1Proc(cgBind,bco,reverse(stgLambdaArgs(rhs)));
794 asmEndArgCheck(bco,root);
796 cgExpr(bco,root,stgLambdaBody(rhs));
801 default: { /* updateable caf */
802 AsmCAF caf = (AsmCAF)getObj(v);
803 asmAddRefObject ( caf, cgRhs(rhs) );
811 /* --------------------------------------------------------------------------
812 * The external entry points for the code generator.
813 * ------------------------------------------------------------------------*/
815 Void cgModule ( Module mod )
821 /* Lambda-lift, by traversing the code list of this module.
822 This creates more name-table entries, which are duly added
823 to the module's code list.
827 /* Initialise the BCO linker subsystem. */
830 /* Generate BCOs, CAFs and Constructors into mallocville.
831 At this point, the .closure values of the names/tycons on
832 the codelist contain StgVars, ie trees. The call to beginTop
833 converts them to MPtrs to AsmObjects.
835 for (cl=module(mod).codeList; nonNull(cl); cl=tl(cl)) {
836 c = getNameOrTupleClosure(hd(cl));
837 if (isCPtr(c)) continue;
839 if (isName(hd(cl))) {
840 printStg( stdout, name(hd(cl)).closure ); printf( "\n\n");
846 for (cl=module(mod).codeList; nonNull(cl); cl=tl(cl)) {
847 c = getNameOrTupleClosure(hd(cl));
848 if (isCPtr(c)) continue;
850 if (isName(hd(cl))) {
851 printStg( stdout, name(hd(cl)).closure ); printf( "\n\n");
854 setNameOrTupleClosure ( hd(cl), mkMPtr(endTop(c)) );
857 //fprintf ( stderr, "\nstarting sanity check\n" );
858 for (cl = module(mod).codeList; nonNull(cl); cl=tl(cl)) {
860 ASSERT(isName(c) || isTuple(c));
861 c = getNameOrTupleClosure(c);
862 ASSERT(isMPtr(c) || isCPtr(c));
864 //fprintf ( stderr, "completed sanity check\n" );
867 /* Figure out how big each object will be in the evaluator's heap,
868 and allocate space to put each in, but don't copy yet. Record
869 the heap address in the object. Assumes that GC doesn't happen;
870 reasonable since we use allocate().
872 asmAllocateHeapSpace();
874 /* Update name/tycon table closure entries with these new heap addrs. */
875 for (cl = module(mod).codeList; nonNull(cl); cl=tl(cl)) {
876 c = getNameOrTupleClosure(hd(cl));
878 setNameOrTupleClosureCPtr (
879 hd(cl), asmGetClosureOfObject(mptrOf(c)) );
882 /* Copy out of mallocville into the heap, resolving references on
887 /* Free up the malloc'd memory. */
892 /* --------------------------------------------------------------------------
893 * Code Generator control:
894 * ------------------------------------------------------------------------*/
908 /*-------------------------------------------------------------------------*/