int bool_expr(boolean_t op, oprtype *addr) { oprtype x; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; INCREMENT_EXPR_DEPTH; if (!eval_expr(&x)) { DECREMENT_EXPR_DEPTH; return FALSE; } assert(TRIP_REF == x.oprclass); coerce(&x, OCT_BOOL); bx_tail(x.oprval.tref, op, addr); DECREMENT_EXPR_DEPTH; return TRUE; }
int bool_expr(bool op,oprtype *addr) { oprtype x; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (!(TREF(expr_depth))++) TREF(expr_start) = TREF(expr_start_orig) = NULL; if (!eval_expr(&x)) { TREF(expr_depth) = 0; return FALSE; } coerce(&x, OCT_BOOL); if (!(--(TREF(expr_depth)))) TREF(shift_side_effects) = FALSE; assert(x.oprclass == TRIP_REF); bx_tail(x.oprval.tref, op, addr); return TRUE; }
void bx_boolop(triple *t, boolean_t jmp_type_one, boolean_t jmp_to_next, boolean_t sense, oprtype *addr) { boolean_t expr_fini; oprtype *adj_addr, *i, *p; tbp *tripbp; triple *ref0, *ref1, *ref2, *t0, *t1; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(((1 & sense) == sense) && ((1 & jmp_to_next) == jmp_to_next) && ((1 & jmp_type_one) == jmp_type_one)); assert((TRIP_REF == t->operand[0].oprclass) && (TRIP_REF == t->operand[1].oprclass)); if (jmp_to_next) { p = (oprtype *)mcalloc(SIZEOF(oprtype)); *p = put_tjmp(t); } else p = addr; if (GTM_BOOL == TREF(gtm_fullbool) || !TREF(saw_side_effect)) { /* nice simple short circuit */ assert(NULL == TREF(boolchain_ptr)); bx_tail(t->operand[0].oprval.tref, jmp_type_one, p); bx_tail(t->operand[1].oprval.tref, sense, addr); t->opcode = OC_NOOP; t->operand[0].oprclass = t->operand[1].oprclass = NOCLASS; return; } /* got a side effect and don't want them short circuited */ /* This code violates info hiding big-time and relies on the original technique of setting up a jump ladder * then it changes the jumps into stotemps and creates a new ladder using the saved evaluations * for the relocated jumps to use for controlling conditional transfers, When the stotemps reference mvals, * they are optimized away when possible. The most interesting part is getting the addresses for the new jump * operands (targets) - see comment below. In theory we could turn this technique on and off around each side effect, * but that's even more complicated, requiring additional instructions, and we don't predict the typical boolean * expression has enough subexpressions to justify the extra trouble, although the potential pay-back would be to * avoid unnecessary global references - again, not expecting that many in a typical boolean expresion. */ assert(TREF(shift_side_effects)); if (expr_fini = (NULL == TREF(boolchain_ptr))) /* NOTE assignment */ { /* initialize work on boolean section of the AST */ TREF(boolchain_ptr) = &(TREF(boolchain)); dqinit(TREF(boolchain_ptr), exorder); t0 = t->exorder.fl; if (NULL == TREF(bool_targ_ptr)) { /* first time - set up anchor */ TREF(bool_targ_ptr) = &(TREF(bool_targ_anchor)); /* mcalloc won't persist over multiple complies */ dqinit(TREF(bool_targ_ptr), que); } else /* queue should be empty */ assert((TREF(bool_targ_ptr) == (TREF(bool_targ_ptr))->que.fl) && (TREF(bool_targ_ptr) == (TREF(bool_targ_ptr))->que.bl)); /* ex_tail wraps bools that produce a value with OC_BOOLINIT (clr) and OC_BOOLFINI (set) */ assert((OC_BOOLFINI != t0->opcode) || ((OC_COMVAL == t0->exorder.fl->opcode) && (TRIP_REF == t0->operand[0].oprclass))); } for (i = t->operand; i < ARRAYTOP(t->operand); i++) { assert(NULL != TREF(boolchain_ptr)); t1 = i->oprval.tref; if (&(t->operand[0]) == i) bx_tail(t1, jmp_type_one, p); /* do normal transform */ else { /* operand[1] */ bx_tail(t1, sense, addr); /* do normal transform */ if (!expr_fini) break; /* only need to relocate last operand[1] */ } if (OC_NOOP == t1->opcode) { /* the technique of sprinkling noops means fishing around for the actual instruction */ do { t1 = t1->exorder.bl; assert(TREF(curtchain) != t1->exorder.bl); } while (OC_NOOP == t1->opcode); if ((oc_tab[t1->opcode].octype & OCT_JUMP) && (OC_JMPTSET != t1->opcode) && (OC_JMPTCLR != t1->opcode)) t1 = t1->exorder.bl; if (OC_NOOP == t1->opcode) { for (t1 = i->oprval.tref; OC_NOOP == t1->opcode; t1 = t1->exorder.fl) assert(TREF(curtchain) != t1->exorder.fl); } } assert(OC_NOOP != t1->opcode); assert((oc_tab[t1->exorder.fl->opcode].octype & OCT_JUMP) ||(OC_JMPTSET != t1->exorder.fl->opcode) || (OC_JMPTCLR != t1->exorder.fl->opcode)); ref0 = maketriple(t1->opcode); /* copy operation for place in new ladder */ ref1 = (TREF(boolchain_ptr))->exorder.bl; /* common setup for above op insert */ switch (t1->opcode) { /* time to subvert original jump ladder entry */ case OC_COBOOL: /* insert COBOOL and copy of following JMP in boolchain; overlay them with STOTEMP and NOOP */ assert(TRIP_REF == t1->operand[0].oprclass); dqins(ref1, exorder, ref0); if (oc_tab[t1->operand[0].oprval.tref->opcode].octype & OCT_MVAL) { /* do we need a STOTEMP? */ switch (t1->operand[0].oprval.tref->opcode) { case OC_INDGLVN: /* indirect actions not happy without STOTEMP */ case OC_INDNAME: case OC_VAR: /* variable could change so must save it */ t1->opcode = OC_STOTEMP; ref0->operand[0] = put_tref(t1);/* new COBOOL points to this OC_STOTEMP */ break; default: /* else no temporary if it's mval */ ref0->operand[0] = put_tref(t1->operand[0].oprval.tref); t1->opcode = OC_NOOP; t1->operand[0].oprclass = NOCLASS; } } else { /* make it an mval instead of COBOOL now */ t1->opcode = OC_COMVAL; ref0->operand[0] = put_tref(t1); /* new COBOOL points to this OC_COMVAL */ } t1 = t1->exorder.fl; ref0 = maketriple(t1->opcode); /* create new jmp on result of coerce */ ref0->operand[0] = t1->operand[0]; t1->opcode = OC_NOOP; /* wipe out original jmp */ t1->operand[0].oprclass = NOCLASS; break; case OC_CONTAIN: case OC_EQU: case OC_FOLLOW: case OC_NUMCMP: case OC_PATTERN: case OC_SORTS_AFTER: /* insert copies of orig OC and following JMP in boolchain & overly originals with STOTEMPs */ assert(TRIP_REF == t1->operand[0].oprclass); assert(TRIP_REF == t1->operand[1].oprclass); dqins(ref1, exorder, ref0); if (OC_VAR == t1->operand[0].oprval.tref->opcode) { /* VAR could change so must save it */ t1->opcode = OC_STOTEMP; /* overlay the original op with a STOTEMP */ ref0->operand[0] = put_tref(t1); /* new op points to thi STOTEMP */ } else { /* no need for a temporary unless it's a VAR */ ref0->operand[0] = put_tref(t1->operand[0].oprval.tref); t1->opcode = OC_NOOP; } ref1 = t1; t1 = t1->exorder.fl; ref2 = maketriple(t1->opcode); /* copy jmp */ ref2->operand[0] = t1->operand[0]; if (OC_VAR == ref1->operand[1].oprval.tref->opcode) { /* VAR could change so must save it */ ref0->operand[1] = put_tref(t1); /* new op points to STOTEMP overlaying the jmp */ t1->operand[0] = ref1->operand[1]; t1->opcode = OC_STOTEMP; /* overlay jmp with 2nd STOTEMP */ } else { /* no need for a temporary unless it's a VAR */ ref0->operand[1] = put_tref(ref1->operand[1].oprval.tref); t1->opcode = OC_NOOP; t1->operand[0].oprclass = NOCLASS; } if (OC_NOOP == ref1->opcode) /* does op[0] need cleanup? */ ref1->operand[0].oprclass = ref1->operand[1].oprclass = NOCLASS; ref0 = ref2; break; case OC_JMPTSET: case OC_JMPTCLR: /* move copy of jmp to boolchain and NOOP it */ ref0->operand[0] = t1->operand[0]; /* new jmpt gets old target */ ref2 = maketriple(OC_NOOP); /* insert a NOOP in new chain inplace of COBOOL */ dqins(ref1, exorder, ref2); t1->opcode = OC_NOOP; /* wipe out original jmp */ t1->operand[0].oprclass = NOCLASS; break; default: assertpro(FALSE); } assert((OC_STOTEMP == t1->opcode) || (OC_NOOP == t1->opcode) || (OC_COMVAL == t1->opcode)); assert(oc_tab[ref0->opcode].octype & OCT_JUMP); ref1 = (TREF(boolchain_ptr))->exorder.bl; dqins(ref1, exorder, ref0); /* common insert for new jmp */ } assert(oc_tab[t->opcode].octype & OCT_BOOL); t->opcode = OC_NOOP; /* wipe out the original boolean op */ t->operand[0].oprclass = t->operand[1].oprclass = NOCLASS; tripbp = &t->jmplist; /* borrow jmplist to track jmp targets */ assert(NULL == tripbp->bpt); assert((tripbp == tripbp->que.fl) && (tripbp == tripbp->que.bl)); tripbp->bpt = jmp_to_next ? (TREF(boolchain_ptr))->exorder.bl : ref0; /* point op triple at op[1] position or op[0] */ dqins(TREF(bool_targ_ptr), que, tripbp); /* queue jmplist for clean-up */ if (!expr_fini) return; /* time to deal with new jump ladder */ assert(NULL != TREF(boolchain_ptr)); assert(NULL != TREF(bool_targ_ptr)); assert(TREF(bool_targ_ptr) != (TREF(bool_targ_ptr))->que.fl); assert(t0->exorder.bl == t); assert(t0 == t->exorder.fl); dqadd(t, TREF(boolchain_ptr), exorder); /* insert the new jump ladder */ ref0 = (TREF(boolchain_ptr))->exorder.bl->exorder.fl; t0 = t->exorder.fl; if (ref0 == TREF(curtchain)) { /* add a safe target */ newtriple(OC_NOOP); ref0 = (TREF(curtchain))->exorder.bl; } assert((OC_COBOOL == t0->opcode) ||(OC_JMPTSET != t0->opcode) || (OC_JMPTCLR != t0->opcode)) ; t0 = t0->exorder.fl; assert(oc_tab[t0->opcode].octype & OCT_JUMP); for (; (t0 != ref0) && oc_tab[t0->opcode].octype & OCT_JUMP; t0 = t0->exorder.fl) { /* process replacement jmps */ adj_addr = &t0->operand[0]; assert(INDR_REF == adj_addr->oprclass); if (NULL != (t1 = (adj_addr = adj_addr->oprval.indr)->oprval.tref)) { /* need to adjust target; NOTE assignments above */ if (OC_BOOLFINI != t1->opcode) { /* not past the end of the new chain */ assert(TJMP_REF == adj_addr->oprclass); if ((t == t1) || (t1 == ref0)) ref1 = ref0; /* adjust to end of boolean expression */ else { /* old target should have jmplist entry */ /* from the jmp jmplisted in the old target we move past the next * test (or NOOP) and jmp which correspond to the old target and pick * the subsequent test (or NOOP) and jmp which correspond to those that originally followed * the logic after the old target and are therefore the appropriate new target for this jmp */ assert(OC_NOOP == t1->opcode); assert(&(t1->jmplist) != t1->jmplist.que.fl); assert(NULL != t1->jmplist.bpt); assert(oc_tab[t1->jmplist.bpt->opcode].octype & OCT_JUMP); ref1 = t1->jmplist.bpt->exorder.fl; assert((oc_tab[ref1->opcode].octype & OCT_BOOL) || (OC_NOOP == ref1->opcode)); assert(oc_tab[ref1->exorder.fl->opcode].octype & OCT_JUMP); ref1 = ref1->exorder.fl->exorder.fl; assert((oc_tab[ref1->opcode].octype & OCT_BOOL) || (OC_BOOLFINI == ref1->opcode) || ((OC_NOOP == ref1->opcode) && ((OC_JMPTCLR == ref1->exorder.fl->opcode) || (OC_JMPTSET == ref1->exorder.fl->opcode) || (TREF(curtchain) == ref1->exorder.fl)))); } t0->operand[0] = put_tjmp(ref1); /* no indrection simplifies later interations */ } } t0 = t0->exorder.fl; if ((OC_BOOLFINI == t0->opcode) || (TREF(curtchain) == t0->exorder.fl)) break; assert((oc_tab[t0->opcode].octype & OCT_BOOL) || (OC_JMPTSET == t0->exorder.fl->opcode) || (OC_JMPTCLR == t0->exorder.fl->opcode)); } dqloop(TREF(bool_targ_ptr), que, tripbp) /* clean up borrowed jmplist entries */ { dqdel(tripbp, que); tripbp->bpt = NULL; }
int bool_expr(boolean_t sense, oprtype *addr) /* * invoked to resolve expresions that are by definition coerced to Boolean, which include * IF arguments, $SELECT() arguments, and postconditionals for both commands and arguments * IF is the only one that comes in with the "TRUE" sense * *addr winds up as an pointer to a jump operand, which the caller fills in */ { boolean_t is_com, tv; uint4 bexprs; opctype c; oprtype x; triple *bitrip, *t, *t0, *t1, *t2; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; INCREMENT_EXPR_DEPTH; if (!eval_expr(&x)) { DECREMENT_EXPR_DEPTH; return FALSE; } UNARY_TAIL(&x); if (OC_LIT == (x.oprval.tref)->opcode) { /* if its just a literal don't waste time */ DECREMENT_EXPR_DEPTH; return TRUE; } assert(TRIP_REF == x.oprclass); coerce(&x, OCT_BOOL); t = x.oprval.tref; for (t1 = t; ; t1 = t2) { assert(TRIP_REF == t1->operand[0].oprclass); t2 = t1->operand[0].oprval.tref; if (!(oc_tab[t2->opcode].octype & OCT_BOOL)) break; } if (OC_INDGLVN == t2->opcode) t1 = t2; /* because of how we process indirection, can't insert a NOOP between COBOOL and INDGLGN */ bitrip = maketriple(OC_BOOLINIT); /* a marker we'll delete later */ dqins(t1->exorder.bl, exorder, bitrip); assert(TREF(curtchain) == t->exorder.fl); (TREF(curtchain))->operand[0] = put_tref(bitrip); bx_tail(t, sense, addr); (TREF(curtchain))->operand[0].oprclass = NO_REF; assert(t == x.oprval.tref); DECREMENT_EXPR_DEPTH; for (bexprs = 0, t0 = t; bitrip != t0; t0 = t0->exorder.bl) { if (OCT_JUMP & oc_tab[c = t0->opcode].octype) /* WARNING assignment */ { switch (t0->opcode) { case OC_JMPFALSE: case OC_JMPTRUE: assert(INDR_REF == t0->operand[0].oprclass); t0->opcode = (OC_JMPTRUE == t0->opcode) ? OC_NOOP : OC_JMP; t0->operand[0].oprclass = (OC_NOOP == t0->opcode) ? NO_REF : INDR_REF; if (!bexprs++) t = t0; break; default: bexprs += 2; } } } bitrip->opcode = OC_NOOP; /* ditch it after it served us */ if (1 == bexprs) { /* if there is just a one JMP TRUE / FALSE turn it into a literal */ assert((OC_NOOP == t->opcode) || (OC_JMP == t->opcode)); PUT_LITERAL_TRUTH((OC_NOOP == t->opcode) ^ sense, t); t->opcode = OC_LIT; } else if (!bexprs && (OC_COBOOL == t->opcode) && (OC_LIT == (t0 = t->operand[0].oprval.tref)->opcode) && ((OC_JMPEQU == t->exorder.fl->opcode) || (OC_JMPNEQ == t->exorder.fl->opcode))) { /* just one jump based on a literal, so resolve it */ t->opcode = OC_NOOP; t->operand[0].oprclass = NO_REF; t = t->exorder.fl; dqdel(t, exorder); unuse_literal(&t0->operand[0].oprval.mlit->v); tv = (((0 == t0->operand[0].oprval.mlit->v.m[1]) ? OC_JMPNEQ : OC_JMPEQU) == t->opcode) ^ sense; PUT_LITERAL_TRUTH(tv, t0); } return TRUE; }
void bx_tail(triple *t, boolean_t sense, oprtype *addr) /* * triple *t; triple to be processed *boolean_t sense; code to be generated is jmpt or jmpf *oprtype *addr; address to jmp */ { triple *ref; oprtype *p; assert((1 & sense) == sense); assert(oc_tab[t->opcode].octype & OCT_BOOL); assert(TRIP_REF == t->operand[0].oprclass); assert((TRIP_REF == t->operand[1].oprclass) || (NOCLASS == t->operand[1].oprclass)); switch (t->opcode) { case OC_COBOOL: ex_tail(&t->operand[0]); if (OC_GETTRUTH == t->operand[0].oprval.tref->opcode) { dqdel(t->operand[0].oprval.tref, exorder); t->opcode = sense ? OC_JMPTSET : OC_JMPTCLR; t->operand[0] = put_indr(addr); return; } ref = maketriple(sense ? OC_JMPNEQ : OC_JMPEQU); ref->operand[0] = put_indr(addr); dqins(t, exorder, ref); return; case OC_COM: bx_tail(t->operand[0].oprval.tref, !sense, addr); t->opcode = OC_NOOP; t->operand[0].oprclass = 0; return; case OC_NEQU: sense = !sense; /* caution: fall through */ case OC_EQU: bx_relop(t, OC_EQU, sense ? OC_JMPNEQ : OC_JMPEQU, addr); break; case OC_NPATTERN: sense = !sense; /* caution: fall through */ case OC_PATTERN: bx_relop(t, OC_PATTERN, sense ? OC_JMPNEQ : OC_JMPEQU, addr); break; case OC_NFOLLOW: sense = !sense; /* caution: fall through */ case OC_FOLLOW: bx_relop(t, OC_FOLLOW, sense ? OC_JMPGTR : OC_JMPLEQ, addr); break; case OC_NSORTS_AFTER: sense = !sense; /* caution: fall through */ case OC_SORTS_AFTER: bx_relop(t, OC_SORTS_AFTER, sense ? OC_JMPGTR : OC_JMPLEQ, addr); break; case OC_NCONTAIN: sense = !sense; /* caution: fall through */ case OC_CONTAIN: bx_relop(t, OC_CONTAIN, sense ? OC_JMPNEQ : OC_JMPEQU, addr); break; case OC_NGT: sense = !sense; /* caution: fall through */ case OC_GT: bx_relop(t, OC_NUMCMP, sense ? OC_JMPGTR : OC_JMPLEQ, addr); break; case OC_NLT: sense = !sense; /* caution: fall through */ case OC_LT: bx_relop(t, OC_NUMCMP, sense ? OC_JMPLSS : OC_JMPGEQ, addr); break; case OC_NAND: sense = !sense; /* caution: fall through */ case OC_AND: bx_boolop(t, FALSE, sense, sense, addr); return; case OC_NOR: sense = !sense; /* caution: fall through */ case OC_OR: bx_boolop(t, TRUE, !sense, sense, addr); return; default: GTMASSERT; } for (p = t->operand ; p < ARRAYTOP(t->operand); p++) if (TRIP_REF == p->oprclass) ex_tail(p); return; }