diff options
Diffstat (limited to 'bcc/assign.c')
-rw-r--r-- | bcc/assign.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/bcc/assign.c b/bcc/assign.c new file mode 100644 index 0000000..ebbc1fc --- /dev/null +++ b/bcc/assign.c @@ -0,0 +1,387 @@ +/* assign.c - assignment and casting operations for bcc */ + +/* Copyright (C) 1992 Bruce Evans */ + +#include "const.h" +#include "types.h" +#include "byteord.h" +#include "gencode.h" +#include "reg.h" +#include "sc.h" +#include "sizes.h" +#include "type.h" + +FORWARD void blockmove P((struct symstruct *source, struct symstruct *target)); +FORWARD void call3 P((char *funcname, struct symstruct *target, + struct symstruct *source, uoffset_t size)); +FORWARD void fconvert P((struct symstruct *source, struct typestruct *type)); + +PUBLIC void assign(source, target) +struct symstruct *source; +struct symstruct *target; +{ + store_pt regpushed; + store_pt sourcereg; + scalar_t tscalar; + + if (target->type->constructor & (ARRAY | FUNCTION) || + target->flags == TEMP || target->flags == (LABELLED | STRING) || + (target->indcount == 0 && target->flags != REGVAR)) + { + bugerror("botched lvalue"); + return; + } + if (source->storage != target->storage + || source->indcount != target->indcount + || source->level != target->level + || source->offset.offi != target->offset.offi /* kludge union cmp */ + || source->type != target->type + || ((source->storage & (LOCAL | GLOBAL) + || source->level == OFFKLUDGELEVEL) + && ((source->flags ^ target->flags) & LABELLED + || (source->flags & LABELLED + && source->name.label != target->name.label) + || (!(source->flags & LABELLED) + && strcmp(source->name.namep, target->name.namep) != 0)))) + { + tscalar = target->type->scalar; + if (tscalar & CHAR && source->storage == CONSTANT) + { + source->offset.offv &= CHMASKTO; + source->type = target->type; + } + regpushed = preslval(source, target); + if (!(tscalar & CHAR) || source->flags != TEMP || + source->offset.offi != sp || source->type->typesize > itypesize) + cast(target->type, source); + if (tscalar & RSCALAR) + { + if (source->storage == CONSTANT && (!reguse & doubleregs)) + load(source, doubleregs & ~DREG); + if (source->storage != CONSTANT && source->indcount == 0) + { + /* XXX - 386 only */ + storereg(DREG, target); + if (tscalar & DOUBLE) + { + target->indcount = 1; /* XXX - outnnadr clobbers this */ + target->offset.offi += accregsize; + storereg(doubleregs & ~DREG, target); + } + target->storage = source->storage; + target->offset.offi = 0; + } + else if (f_indirect(source) && tscalar & DOUBLE + && (!(reguse & OPREG) || target->storage == OPREG)) + { + struct symstruct temptarg; + + temptarg = *target; + pointat(&temptarg); + call("Fpull"); + outntypechar(temptarg.type); + sp += dtypesize; + } + else + blockmove(source, target); + } + else if (target->type->constructor & STRUCTU) + blockmove(source, target); + else + { + if (tscalar & CHAR) + load(source, DREG); + else if (target->indcount == 0 && target->storage & regregs) + load(source, target->storage); + else + loadany(source); + if (tscalar & DLONG) + { + storereg(DREG, target); + target->indcount = 1; + target->offset.offi += accregsize; + } + if ((store_t) (sourcereg = source->storage) == DREG && + target->type->scalar & CHAR) + sourcereg = BREG; + storereg(sourcereg, target); + if ((store_t) regpushed != 0) + { + /* DLONG */ + target->indcount = 1; + target->offset.offi -= accregsize; + recovlist(regpushed); + } + else + { + target->storage = sourcereg; + target->offset.offi = 0; + if (target->level == OFFKLUDGELEVEL) + target->level = EXPRLEVEL; + } + } + } +} + +/* block move assumes itypesize == accregsize && BREG size == 1 */ + +PRIVATE void blockmove(source, target) +struct symstruct *source; +struct symstruct *target; +{ + struct symstruct oldtarget; + uoffset_t typesize; + struct symstruct worksource; + + oldtarget = *target; + if ((typesize = target->type->typesize) >= 16 * itypesize || + source->indcount + target->indcount != 2) + { + address(source); + address(target); + call3("_memcpy", target, source, typesize); + } + else + { + if (source->level == OFFKLUDGELEVEL) + addoffset(source); /* else kludge is lost and offsets big */ + if (target->level == OFFKLUDGELEVEL) + addoffset(target); + worksource = *source; + for (; typesize >= itypesize; typesize -= itypesize) + { + loadreg(source, DREG); + worksource.offset.offi += itypesize; + *source = worksource; + storereg(DREG, target); + target->indcount = 1; + target->offset.offi += accregsize; + } + while (typesize-- != 0) + { + outload(); + outregname(BREG); + outopsep(); + outadr(source); + worksource.offset.offi += 1; + *source = worksource; + storereg(BREG, target); + target->indcount = 1; + target->offset.offi += 1; + } + } + *target = oldtarget; +} + +PRIVATE void call3(funcname, target, source, size) +char *funcname; +struct symstruct *target; +struct symstruct *source; +uoffset_t size; +{ + store_pt regpushed; + offset_t spmark; + struct symstruct *length; + + pushlist(regpushed = reguse & ~calleemask); + spmark = sp; + length = constsym((value_t) size); + length->type = uitype; + push(length); + push(source); + push(target); + call(funcname); + outnl(); + if (regpushed) + { + modstk(spmark); + recovlist(regpushed); + } +} + +PUBLIC void cast(type, target) +struct typestruct *type; +struct symstruct *target; +{ + scalar_t newscalar; + uoffset_t newsize; + scalar_t oldscalar; + uoffset_t oldsize; + store_pt targreg; + + if (type->constructor & (ARRAY | FUNCTION) + || (type->constructor & STRUCTU && target->type != type)) + { + bugerror("botched implicit cast"); + return; + } + if (target->type == type) + return; + if (target->type->constructor == ARRAY) + oldsize = ptypesize; + else + oldsize = target->type->typesize; + newscalar = type->scalar; + oldscalar = target->type->scalar; + if ((newsize = type->typesize) == oldsize && + !((newscalar | oldscalar) & RSCALAR)) + ; + else if (newsize == ctypesize) /* char only */ + { + if (oldscalar & RSCALAR) + fconvert(target, type); + else if (target->indcount == 1) + { +#if DYNAMIC_LONG_ORDER + if (long_big_endian) +#endif +#if DYNAMIC_LONG_ORDER || LONG_BIG_ENDIAN +# if BIG_ENDIAN + target->offset.offi += oldsize - ctypesize; +# else + { + if (oldscalar & DLONG) + target->offset.offi += itypesize; /* discard msword */ + } +# endif +#endif +#if DYNAMIC_LONG_ORDER + else +#endif +#if DYNAMIC_LONG_ORDER || LONG_BIG_ENDIAN == 0 +# if BIG_ENDIAN + target->offset.offi += ctypesize; +# else + ; +# endif +#endif + } + else if (target->storage != CONSTANT) + { + load(target, DREG); + target->storage = BREG; + } + } + else if ((newscalar & (SHORT | INT | LONG) && !(newscalar & DLONG)) + || type->constructor & POINTER) + { + if (oldscalar & RSCALAR) + fconvert(target, type); + else if (oldsize < newsize) + extend(target); + else if (target->indcount == 1) + { +#if DYNAMIC_LONG_ORDER + if (long_big_endian) +#endif +#if DYNAMIC_LONG_ORDER || LONG_BIG_ENDIAN + { + if (oldscalar & DLONG) + target->offset.offi += itypesize; /* discard msword */ + } +#endif + } + else + load(target, DREG); + } + else if (newscalar & DLONG) + { + if (oldscalar & RSCALAR) + fconvert(target, type); + else if (target->storage != CONSTANT) + { + extend(target); + load(target, DREG); + target->storage = targreg = getindexreg(); + if (oldscalar & (UNSIGNED | CHAR) || + target->type->constructor & (ARRAY | FUNCTION | POINTER)) + uitol(targreg); + else + itol(targreg); + } + } + else if (newscalar & RSCALAR) + { + saveopreg(); /* XXX */ + if (oldscalar & (ISCALAR | FLOAT)) + fpush(target); + if (newscalar & FLOAT) + { + pointat(target); + call("dto"); + outntypechar(type); + justpushed(target); /* XXX - sets dtype wrong (harmless) and + * wastes (dtypesize - ftypesize) stack */ + } + restoreopreg(); + } + target->type = type; +} + +/* extend char or short to int (unsigned if from unsigned) */ + +PUBLIC void extend(target) +struct symstruct *target; +{ + scalar_t tscalar; + + if ((tscalar = target->type->scalar) & (CHAR | SHORT)) + { + if (target->storage != CONSTANT && + target->type->typesize < itypesize) + { + load(target, DREG); + if (target->type == sctype) + sctoi(); +#ifdef I8088 + else if (tscalar & SHORT) + { + if (tscalar & UNSIGNED) + ustoi(); + else + stoi(); + } +#endif + else + ctoi(); + target->storage = DREG; + } + if (tscalar & UNSIGNED) + target->type = uitype; + else + target->type = itype; + } +} + +PRIVATE void fconvert(source, type) +struct symstruct *source; +struct typestruct *type; +{ + offset_t spmark; + + pushlist(reguse & OPREG); + spmark = sp; + pointat(source); + if (source->type->scalar & DOUBLE) + call("dto"); + else + call("fto"); + if (type->scalar & UNSIGNED) + outbyte('u'); + outntypechar(type); + if (type->scalar & DLONG) + { + if (reguse & OPREG) + bugerror("loading long into used reg"); + source->storage = OPREG; + } + else if (type->scalar & CHAR) + source->storage = BREG; + else + source->storage = DREG; + source->offset.offi = + source->flags = + source->indcount = 0; + modstk(spmark); /* could adjust later (load instead of pop) */ + poplist(reguse & OPREG); +} |