diff options
Diffstat (limited to 'bcc/function.c')
-rw-r--r-- | bcc/function.c | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/bcc/function.c b/bcc/function.c new file mode 100644 index 0000000..1d48a19 --- /dev/null +++ b/bcc/function.c @@ -0,0 +1,413 @@ +/* function.c - function call protocol for bcc */ + +/* Copyright (C) 1992 Bruce Evans */ + +#include "const.h" +#include "types.h" +#include "align.h" +#include "byteord.h" +#include "gencode.h" +#include "parse.h" +#include "reg.h" +#include "sc.h" +#include "table.h" +#include "type.h" + +#ifdef I8088 +# define ADJUSTLONGRETURN +# define CANHANDLENOFRAME +# undef CANHANDLENOFRAME +# define STUPIDFRAME +#endif + +FORWARD void out_callstring P((void)); + +/* call a named (assembly interface) procedure, don't print newline after */ + +PUBLIC void call(name) +char *name; +{ + out_callstring(); + outstr(name); +} + +PUBLIC void function(source) +struct symstruct *source; +{ + if (source->indcount == 0 && source->storage == GLOBAL && + !(source->flags & LABELLED) && *source->name.namep != 0) + { + out_callstring(); + outnccname(source->name.namep); + } + else + { +#ifdef XENIX_AS + if (source->indcount == 0) /* fix call fixed address */ + out_callstring(); + else +#endif + outcalladr(); +#ifdef MC6809 + if (source->indcount == 1) + ++source->indcount; /* fake for outadr */ +#endif + outadr(source); + } + source->type = source->type->nexttype; +#ifdef LONGRETSPECIAL /* LONGRETURNREGS!=RETURNREG && RETURNREG==LONGREG2 */ + if (source->type->scalar & DLONG) + { +# ifdef ADJUSTLONGRETURN +# if DYNAMIC_LONG_ORDER + if (long_big_endian) +# endif +# if DYNAMIC_LONG_ORDER || LONG_BIG_ENDIAN + { + regexchange(LONGREG2, LONGRETURNREGS & ~LONGREG2); + regexchange(LONGREG2, DXREG); + } +# endif +# if DYNAMIC_LONG_ORDER + else +# endif +# if DYNAMIC_LONG_ORDER || LONG_BIG_ENDIAN == 0 + regtransfer(DXREG, LONGRETURNREGS & ~LONGREG2); +# endif +# endif + source->storage = LONGRETURNREGS & ~LONGREG2; + } + else +#endif + if (source->type->scalar & CHAR) + { +#if RETURNREG != DREG + transfer(source, DREG); +#endif + source->storage = BREG; + } + else if (source->type->scalar & DOUBLE) + source->storage = doublreturnregs & ~DREG; +#if 0 + else if (source->type->scalar & FLOAT) + source->storage = floatreturnregs /* XXX? & ~DREG */; +#endif + else + source->storage = RETURNREG; + source->offset.offi = source->indcount = 0; + if (source->level == OFFKLUDGELEVEL) + source->level = EXPRLEVEL; + if (source->type->constructor & STRUCTU) + { + transfer(source, getindexreg()); /* so it can be indirected + * and/or preserved in blockmove() */ + source->indcount = 1; + source->flags = TEMP; /* kludge so blockpush can be avoided */ + } +} + +PUBLIC void ldregargs() +{ + register struct symstruct *symptr; + store_pt targreg; + struct symstruct temptarg; + + for (symptr = &locsyms[0]; symptr < locptr && symptr->level == ARGLEVEL; + symptr = (struct symstruct *) + align(&symptr->name.namea[strlen(symptr->name.namea) + 1])) + { + if ((store_t) (targreg = symptr->storage) & allregs) + { + + /* load() is designed to work on expression symbols, so don't + * trust it on reg variables although it almost works. + */ + temptarg = *symptr; + if (arg1inreg && symptr == &locsyms[0]) + { + temptarg.storage = ARGREG; + temptarg.offset.offi = 0; + } + else + { + temptarg.storage = LOCAL; + temptarg.indcount = 1; + } + load(&temptarg, targreg); + symptr->offset.offi = 0; + } + } + regarg = FALSE; +} + +PUBLIC void loadretexpression() +{ + if (returntype->constructor & STRUCTU) + { + struct nodestruct *etmark; + struct nodestruct *exp; + struct symstruct *exprmark; + struct symstruct *structarg; + + etmark = etptr; + exprmark = exprptr; + exp = expression(); + makeleaf(exp); + structarg = constsym((value_t) 0); + structarg->type = pointype(returntype); + onstack(structarg); + indirec(structarg); + structarg->flags = 0; /* assign() doesn't like TEMP even for indir */ + structarg->offset.offi = returnadrsize; + assign(exp->left.symptr, structarg); + etptr = etmark; + exprptr = exprmark; + } +#ifdef LONGRETSPECIAL /* LONGRETURNREGS!=RETURNREG && RETURNREG==LONGREG2 */ + else if (returntype->scalar & DLONG) + { + loadexpression(LONGRETURNREGS & ~LONGREG2, returntype); +# ifdef ADJUSTLONGRETURN +# if DYNAMIC_LONG_ORDER + if (long_big_endian) +# endif +# if DYNAMIC_LONG_ORDER || LONG_BIG_ENDIAN + { + regexchange(LONGREG2, DXREG); + regexchange(LONGREG2, LONGRETURNREGS & ~LONGREG2); + } +# endif +# if DYNAMIC_LONG_ORDER + else +# endif +# if DYNAMIC_LONG_ORDER || LONG_BIG_ENDIAN == 0 + regtransfer(LONGRETURNREGS & ~LONGREG2, DXREG); +# endif +# endif + } + else +#endif + if (returntype->scalar & DOUBLE) + loadexpression(doublreturnregs & ~DREG, returntype); +#if 0 + else if (returntype->scalar & FLOAT) + loadexpression(floatreturnregs /* XXX? & ~DREG */, returntype); +#endif + else + loadexpression(RETURNREG, returntype); +} + +PUBLIC void listo(target, lastargsp) +struct symstruct *target; +offset_t lastargsp; +{ + extend(target); + push(target); + if (lastargsp != 0 && sp != lastargsp - target->type->typesize) + { + loadany(target); + modstk(lastargsp); + push(target); + if (sp != lastargsp - target->type->typesize) + { + bugerror("botched push of arg"); +#ifdef DEBUG + outstr("arg type is "); + dbtype(target->type); + outnl(); +#endif + } + } +} + +PUBLIC void listroot(target) +struct symstruct *target; +{ + extend(target); + /* necessary regs are free since they were saved for function */ + if (target->type->scalar & DLONG) + load(target, LONGARGREGS & ~LONGREG2); + else + load(target, ARGREG); +} + +PRIVATE void out_callstring() +{ + outop3str(callstring); +#ifdef I8088 + if (i386_32) + bumplc2(); +#endif +} + +#ifdef FRAMEPOINTER + +PUBLIC void popframe() +{ + poplist(frame1list); +} + +#endif + +/* reserve storage for locals if necessary */ +/* also push 1st function arg and load register args if necessary */ + +PUBLIC void reslocals() +{ +#ifdef FRAMEPOINTER +# ifndef STUPIDFRAME + bool_t loadframe = FALSE; + +# endif +#endif + + if (switchnow != NULL) + { +#ifdef FRAMEPOINTER + if (framep == 0 && softsp != sp) + bugerror("local variables in switch statement messed up, sorry"); +#else + if (sp != softsp) + bugerror("local variables in switch statement don't work, sorry"); +#endif + if (lowsp > softsp) + lowsp = softsp; + sp = softsp; + return; + } +#ifdef FRAMEPOINTER + if (framep == 0) + { +# ifdef STUPIDFRAME + pushreg(FRAMEREG); + regtransfer(STACKREG, FRAMEREG); + framep = sp; + pushlist(callee1mask); +# else /* not STUPIDFRAME */ +# ifdef CANHANDLENOFRAME + if (stackarg || softsp != -frameregsize) /* args or locals */ +# endif + { + pushlist(frame1list); + loadframe = TRUE; + } +# endif /* not STUPIDFRAME */ + } +#else + if (sp == 0) + pushlist(callee1mask); +#endif /* FRAMEPOINTER */ + if (arg1size) + { + switch ((fastin_t) arg1size) + { + case 8: + pushlist(doubleargregs); + break; + case 4: +# ifdef I8088 + if (!i386_32) +# endif + { + pushlist(LONGARGREGS); + break; + } + case 2: +# ifdef I8088 + pushlist(ARGREG); +# endif +# ifdef MC6809 + switch (sp - softsp) + { + case 3: + pushlist(LOC1REGS | ARGREG); + break; + case 4: + pushlist(LOC2REGS | ARGREG); + break; + case 5: + pushlist(LOC3REGS | ARGREG); + break; + case 6: + pushlist(LOC4REGS | ARGREG); + break; + default: + pushlist(ARGREG); + break; + } +# endif /* MC6809 */ + } + arg1size = 0; /* show 1st arg allocated */ + } +#ifdef FRAMEPOINTER +# ifndef STUPIDFRAME /* else this moved above for compat with Xenix cc frame */ + if (loadframe || softsp != -frameregsize) + modstk(softsp); + /* else avoid modstk() because softsp holds space for frame pointer only) */ + /* but pointer has not been pushed (must keep softsp for later levels) */ + if (loadframe) + { + regtransfer(STACKREG, FRAMEREG); + framep = sp; + } +# else /* STUPIDFRAME */ + modstk(softsp); +# endif /* STUPIDFRAME */ +#else /* no FRAMEPOINTER */ + modstk(softsp); +#endif /* FRAMEPOINTER */ + if (regarg) + ldregargs(); +} + +/* clean up stack and return from a function */ + +PUBLIC void ret() +{ +#ifdef FRAMEPOINTER + offset_t newsp; + + if (framep != 0) + { + newsp = -(offset_t) func1saveregsize; + if (switchnow != NULL || newsp - sp >= 0x80) + changesp(newsp, TRUE); + else + modstk(newsp); + popframe(); + } + outreturn(); +#else /* no FRAMEPOINTER */ +# ifdef MC6809 + store_pt reglist; + + switch (sp) + { + case -1: + reglist = JUNK1REGS | PCREG; + break; + case -2: + reglist = JUNK2REGS | PCREG; + break; + case -3: + reglist = JUNK3REGS | PCREG; + break; + case -4: + reglist = JUNK4REGS | PCREG; + break; + default: + modstk(0); + outreturn(); + return; + } + poplist(reglist); +#else + if (sp != 0) + { + modstk(-(offset_t) func1saveregsize); + poplist(callee1mask); + } + outreturn(); +# endif /* no MC6809 */ +#endif /* no FRAMEPOINTER */ +} |