+/****************************************************************************/
+/* TYPESTACK FUNCTIONS */
+/****************************************************************************/
+
+#define TYPESTACK_IS_RETURNADDRESS(sptr) \
+ TYPE_IS_RETURNADDRESS((sptr)->type,(sptr)->typeinfo)
+
+#define TYPESTACK_IS_REFERENCE(sptr) \
+ TYPE_IS_REFERENCE((sptr)->type,(sptr)->typeinfo)
+
+#define TYPESTACK_RETURNADDRESSSET(sptr) \
+ ((typeinfo_retaddr_set*)TYPEINFO_RETURNADDRESS((sptr)->typeinfo))
+
+#define RETURNADDRESSSET_SEEK(set,pos) \
+ do {int i; for (i=pos;i--;) set=set->alt;} while(0)
+
+static void
+typestack_copy(stackptr dst,stackptr y,typevector *selected)
+{
+ typevector *sel;
+ typeinfo_retaddr_set *sety;
+ typeinfo_retaddr_set *new;
+ typeinfo_retaddr_set **next;
+ int k;
+
+ for (;dst; dst=dst->prev, y=y->prev) {
+ if (!y) panic("Stack depth mismatch");
+ if (dst->type != y->type)
+ panic("Stack type mismatch");
+ LOG3("copy %p -> %p (type %d)",y,dst,dst->type);
+ if (dst->type == TYPE_ADDRESS) {
+ if (TYPEINFO_IS_PRIMITIVE(y->typeinfo)) {
+ /* We copy the returnAddresses from the selected
+ * states only. */
+
+ LOG("copying returnAddress");
+ sety = TYPESTACK_RETURNADDRESSSET(y);
+ next = &new;
+ for (k=0,sel=selected; sel; sel=sel->alt) {
+ LOG1("selected k=%d",sel->k);
+ while (k<sel->k) {
+ sety = sety->alt;
+ k++;
+ }
+ *next = DNEW(typeinfo_retaddr_set);
+ (*next)->addr = sety->addr;
+ next = &((*next)->alt);
+ }
+ *next = NULL;
+ TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,new);
+ }
+ else {
+ TYPEINFO_CLONE(y->typeinfo,dst->typeinfo);
+ }
+ }
+ }
+ if (y) panic("Stack depth mismatch");
+}
+
+static void
+typestack_put_retaddr(stackptr dst,void *retaddr,typevector *loc)
+{
+#ifdef TYPECHECK_DEBUG
+ if (dst->type != TYPE_ADDRESS)
+ panic("Internal error: Storing returnAddress in non-address slot");
+#endif
+
+
+ TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,NULL);
+ for (;loc; loc=loc->alt) {
+ typeinfo_retaddr_set *set = DNEW(typeinfo_retaddr_set);
+ set->addr = retaddr;
+ set->alt = TYPESTACK_RETURNADDRESSSET(dst);
+ TYPEINFO_INIT_RETURNADDRESS(dst->typeinfo,set);
+ }
+}
+
+static bool
+typestack_canmerge(stackptr a,stackptr b)
+{
+ for (; a; a = a->prev, b = b->prev) {
+ if (!b) return false;
+ if (a->type != b->type) return false;
+ if (a->type == TYPE_ADDRESS) {
+ if (((TYPEINFO_IS_PRIMITIVE(a->typeinfo)) ? 1 : 0)
+ ^
+ ((TYPEINFO_IS_PRIMITIVE(b->typeinfo)) ? 1 : 0))
+ return false;
+ }
+ }
+ if (b) return false;
+ return true;
+}
+
+static void
+typestack_collapse(stackptr dst)
+{
+ for (; dst; dst = dst->prev) {
+ if (TYPESTACK_IS_RETURNADDRESS(dst))
+ TYPESTACK_RETURNADDRESSSET(dst)->alt = NULL;
+ }
+}
+
+/* 'dst' and 'y' are assumed to have passed typestack_canmerge! */
+static bool
+typestack_merge(stackptr dst,stackptr y)
+{
+ bool changed = false;
+ for (; dst; dst = dst->prev, y=y->prev) {
+ if (TYPESTACK_IS_REFERENCE(dst))
+ changed |= typeinfo_merge(&(dst->typeinfo),&(y->typeinfo));
+ }
+ return changed;
+}
+
+static void
+typestack_add(stackptr dst,stackptr y,int ky)
+{
+ typeinfo_retaddr_set *setd;
+ typeinfo_retaddr_set *sety;
+
+ for (; dst; dst = dst->prev, y=y->prev) {
+ if (TYPESTACK_IS_RETURNADDRESS(dst)) {
+ setd = TYPESTACK_RETURNADDRESSSET(dst);
+ sety = TYPESTACK_RETURNADDRESSSET(y);
+ RETURNADDRESSSET_SEEK(sety,ky);
+ while (setd->alt)
+ setd=setd->alt;
+ setd->alt = DNEW(typeinfo_retaddr_set);
+ setd->alt->addr = sety->addr;
+ setd->alt->alt = NULL;
+ }
+ }
+}
+
+/* 'a' and 'b' are assumed to have passed typestack_canmerge! */
+static bool
+typestack_separable_with(stackptr a,stackptr b,int kb)
+{
+ typeinfo_retaddr_set *seta;
+ typeinfo_retaddr_set *setb;
+
+ for (; a; a = a->prev, b = b->prev) {
+#ifdef TYPECHECK_DEBUG
+ if (!b) panic("Internal error: typestack_separable_from: different depth");
+#endif
+ if (TYPESTACK_IS_RETURNADDRESS(a)) {
+#ifdef TYPECHECK_DEBUG
+ if (!TYPESTACK_IS_RETURNADDRESS(b))
+ panic("Internal error: typestack_separable_from: unmergable stacks");
+#endif
+ seta = TYPESTACK_RETURNADDRESSSET(a);
+ setb = TYPESTACK_RETURNADDRESSSET(b);
+ RETURNADDRESSSET_SEEK(setb,kb);
+
+ for (;seta;seta=seta->alt)
+ if (seta->addr != setb->addr) return true;
+ }
+ }
+#ifdef TYPECHECK_DEBUG
+ if (b) panic("Internal error: typestack_separable_from: different depth");
+#endif
+ return false;
+}
+
+/* 'a' and 'b' are assumed to have passed typestack_canmerge! */
+static bool
+typestack_separable_from(stackptr a,int ka,stackptr b,int kb)
+{
+ typeinfo_retaddr_set *seta;
+ typeinfo_retaddr_set *setb;
+ int i;
+
+ for (; a; a = a->prev, b = b->prev) {
+#ifdef TYPECHECK_DEBUG
+ if (!b) panic("Internal error: typestack_separable_from: different depth");
+#endif
+ if (TYPESTACK_IS_RETURNADDRESS(a)) {
+#ifdef TYPECHECK_DEBUG
+ if (!TYPESTACK_IS_RETURNADDRESS(b))
+ panic("Internal error: typestack_separable_from: unmergable stacks");
+#endif
+ seta = TYPESTACK_RETURNADDRESSSET(a);
+ setb = TYPESTACK_RETURNADDRESSSET(b);
+ RETURNADDRESSSET_SEEK(seta,ka);
+ RETURNADDRESSSET_SEEK(setb,kb);
+
+ if (seta->addr != setb->addr) return true;
+ }
+ }
+#ifdef TYPECHECK_DEBUG
+ if (b) panic("Internal error: typestack_separable_from: different depth");
+#endif
+ return false;
+}
+
+/****************************************************************************/
+/* TYPESTATE FUNCTIONS */
+/****************************************************************************/
+
+static bool
+typestate_merge(stackptr deststack,typevector *destloc,
+ stackptr ystack,typevector *yloc,
+ /* int retindex,void *retaddr, */
+ int locsize)
+{
+ typevector *dvec,*yvec;
+ int kd,ky;
+ bool changed = false;
+
+#ifdef TYPECHECK_DEBUG
+ /* Do some sanity checks */
+ /*
+ if (retindex < -1 || retindex >= locsize || (retindex >= 0 && !retaddr))
+ panic("Internal error: typestate_merge: invalid arguments");
+ */
+#endif
+
+ LOG("merge:");
+ LOGSTR("dstack: "); DOLOG(typestack_print(get_logfile(),deststack)); LOGNL;
+ LOGSTR("ystack: "); DOLOG(typestack_print(get_logfile(),ystack)); LOGNL;
+ LOGSTR("dloc : "); DOLOG(typevectorset_print(get_logfile(),destloc,locsize)); LOGNL;
+ LOGSTR("yloc : "); DOLOG(typevectorset_print(get_logfile(),yloc,locsize)); LOGNL;
+
+ /* Check if the stack types of deststack and ystack match */
+
+ /* XXX move this check into typestack_merge? */
+ if (!typestack_canmerge(deststack,ystack))
+ panic("Stack depth or stack type mismatch");
+
+ /* The stack is always merged. If there are returnAddresses on
+ * the stack they are ignored in this step. */
+
+ changed |= typestack_merge(deststack,ystack);
+
+ for (yvec=yloc; yvec; yvec=yvec->alt) {
+ ky = yvec->k;
+
+ /* If retindex >= 0 we select only those states (ystack,yvec)
+ * with returnAddress retaddr in variable no. retindex. */
+
+ /*
+ if (retindex >= 0) {
+ if (!TYPEDESC_IS_RETURNADDRESS(yvec->td[retindex]))
+ panic("Illegal instruction: RET on non-returnAddress");
+ if (TYPEINFO_RETURNADDRESS(yvec->td[retindex].info)
+ != retaddr)
+ continue;
+ }
+ */
+
+ /* Check if the typestates (deststack,destloc) will be
+ * separable when (ystack,yvec) is added. */
+
+ if (!typestack_separable_with(deststack,ystack,ky)
+ && !typevectorset_separable_with(destloc,yvec,locsize))
+ {
+ /* No, the resulting set won't be separable, thus we
+ * may merge all states in (deststack,destloc) and
+ * (ystack,yvec). */
+
+ typestack_collapse(deststack);
+ typevectorset_collapse(destloc,locsize);
+ typevector_merge(destloc,yvec,locsize);
+ }
+ else {
+ /* Yes, the resulting set will be separable. Thus we check
+ * if we may merge (ystack,yvec) with a single state in
+ * (deststack,destloc). */
+
+ for (dvec=destloc,kd=0; dvec; dvec=dvec->alt, kd++) {
+ if (!typestack_separable_from(ystack,ky,deststack,kd)
+ && !typevector_separable_from(yvec,dvec,locsize))
+ {
+ /* The typestate (ystack,yvec) is not separable from
+ * (deststack,dvec) by any returnAddress. Thus we may
+ * merge the states. */
+
+ changed |= typevector_merge(dvec,yvec,locsize);
+
+ goto merged;
+ }
+ }
+
+ /* The typestate (ystack,yvec) is separable from all typestates
+ * (deststack,destloc). Thus we must add this state to the
+ * result set. */
+
+ typestack_add(deststack,ystack,ky);
+ typevectorset_add(destloc,yvec,locsize);
+ changed = true;
+ }
+
+ merged:
+
+ }
+
+ LOG("result:");
+ LOGSTR("dstack: "); DOLOG(typestack_print(get_logfile(),deststack)); LOGNL;
+ LOGSTR("dloc : "); DOLOG(typevectorset_print(get_logfile(),destloc,locsize)); LOGNL;
+
+ return changed;
+}
+
+/* Globals used:
+ * block
+ */
+static bool
+typestate_reach(void *localbuf,
+ basicblock *current,
+ basicblock *destblock,
+ stackptr ystack,typevector *yloc,
+ int locsize)
+{
+ typevector *yvec;
+ typevector *destloc;
+ int destidx;
+ bool repeat = false;
+ bool changed = false;
+
+ destidx = destblock - block;
+ destloc = MGET_TYPEVECTOR(localbuf,destidx,locsize);
+
+ if (destblock->flags == BBTYPECHECK_UNDEF) {
+ /* The destblock has never been reached before */
+
+ LOG1("block (index %04d) reached first time",destidx);
+
+ typestack_copy(destblock->instack,ystack,yloc);
+ COPY_TYPEVECTORSET(yloc,destloc,locsize);
+ changed = true;
+ }
+ else {
+ /* The destblock has already been reached before */
+
+ LOG1("block (index %04d) reached before",destidx);
+
+ changed = typestate_merge(destblock->instack,destloc,
+ ystack,yloc,locsize);
+ }
+
+ if (changed) {
+ LOG("changed!");
+ destblock->flags = BBTYPECHECK_REACHED;
+ if (destblock <= current) {repeat = true; LOG("REPEAT!");}
+ }
+ return repeat;
+}
+
+/* Globals used:
+ * see typestate_reach
+ */
+static bool
+typestate_ret(void *localbuf,
+ basicblock *current,
+ stackptr ystack,typevector *yloc,
+ int retindex,int locsize)
+{
+ typevector *yvec;
+ typevector *selected;
+ basicblock *destblock;
+ bool repeat = false;
+
+ for (yvec=yloc; yvec; ) {
+ if (!TYPEDESC_IS_RETURNADDRESS(yvec->td[retindex]))
+ panic("Illegal instruction: RET on non-returnAddress");
+
+ destblock = (basicblock*) TYPEINFO_RETURNADDRESS(yvec->td[retindex].info);
+
+ selected = typevectorset_select(&yvec,retindex,destblock);
+
+ repeat |= typestate_reach(localbuf,current,destblock,
+ ystack,selected,locsize);
+ }
+ return repeat;
+}
+
+static bool
+typestate_jsr(void *localbuf,
+ basicblock *current,basicblock *destblock,
+ stackptr ystack,typevector *yloc,
+ int locsize)
+{
+ typestack_put_retaddr(ystack,current+1,yloc);
+ return typestate_reach(localbuf,current,destblock,ystack,yloc,locsize);
+}
+