void count_cases(SNODE *stmt, struct cases *cs) { while (stmt) { switch(stmt->stype) { case st_tryblock: break; case st_throw: break; case st_return: case st_expr: break; case st_while: case st_do: count_cases(stmt->s1,cs); break; case st_for: count_cases(stmt->s1,cs); break; case st_if: count_cases(stmt->s1,cs); count_cases(stmt->s2,cs); break; case st_switch: break; case st_block: count_cases(stmt->exp,cs); break; case st_asm: break; case st_case: if (!stmt->s2) { cs->count++; if (stmt->switchid < cs->bottom) cs->bottom = stmt->switchid; if (stmt->switchid > cs->top) cs->top = stmt->switchid; } break; } stmt = stmt->next; } }
/* Allocate the structures for handling a sequence of expressions * to be combined with AND. Expand the structure into a series of * cases. Do not start the computations yet, but prepare them. * The sequence of expressions is considered a NULL-terminated * array of NUL-terminated strings. * * This routine returns NULL in case of error, and will log the * expression that caused the failure. In case of success, an * expanded expression is returned, that should be cleaned up * with free() as soon as it is ready. */ static struct valexp *allocate_valexp (char **and_expressions) { int allcases = 1; char **andexp; struct valexp *retval = NULL; int memsz; // // Count the individual expressions' sizes, and compute the total for (andexp=and_expressions; (*andexp) != NULL; andexp++) { int explen = strlen (*andexp); int parsed; int newcases = count_cases (*andexp, explen, 0, &parsed); if ((newcases == -1) || (parsed != explen)) { //TODO// Expression will not be the same anymore tlog (TLOG_USER, LOG_NOTICE, "Syntax error in logic expression, treat as False: %s", *andexp); return NULL; } allcases = allcases * newcases; } // // Allocate memory for the overal expression // Since all cases are initialised to all-zeroes, they represent // TRUE in all these cases. Expansion should add restrictions. memsz = sizeof (struct valexp) + sizeof (struct valexp_case) * (allcases - 1); retval = (struct valexp *) malloc (memsz); if (retval == NULL) { //TODO// Expression will not be the same anymore tlog (TLOG_TLS, LOG_NOTICE, "Out of memory expanding logic expressions"); return NULL; } memset (retval, 0, memsz); retval->numcases = allcases; retval->numcases_incomplete = allcases; // // Expand the expressions to the form (OR (AND ... ~...) (AND ...) ...) // This relies on: // - De Morgan to fold inversion inward // - Distribution laws for AND / OR // - Rewriting of ? to a combination and AND / OR // - No cases as the zero element of OR to represent FALSE // - A complete case to represent TRUE // Note that the and_expressions are pushed into each' OR caselist // Note that all cases are initialised AND-ready (they're all TRUE) for (andexp=and_expressions; *andexp != NULL; andexp++) { //TODO// EXPAND_EXPRESSION_INTO_EXISTING_WITH_AND } // // Return successfully. return retval; }
void genxswitch(STATEMENT *stmt, SYMBOL *funcsp) /* * analyze and generate best switch statement. */ { int oldbreak, i; struct cases cs; IMODE *ap, *ap3; #ifdef USE_LONGLONG ULLONG_TYPE a = 1; #endif oldbreak = breaklab; breaklab = stmt->breaklabel; memset(&cs,0,sizeof(cs)); #ifndef USE_LONGLONG cs.top = INT_MIN; cs.bottom = INT_MAX; #else cs.top = (a << 63); /* LLONG_MIN*/ cs.bottom = cs.top - 1; /* LLONG_MAX*/ #endif count_cases(stmt->cases,&cs) ; cs.top++; ap3 = gen_expr(funcsp, stmt->select, F_VOL | F_NOVALUE, ISZ_UINT); ap = LookupLoadTemp(NULL, ap3); if (ap != ap3) { IMODE *barrier; if (stmt->select->isatomic) { barrier = doatomicFence(funcsp, stmt->select, NULL); } gen_icode(i_assn, ap, ap3, NULL); if (stmt->select->isatomic) { doatomicFence(funcsp, stmt->select, barrier); } } gen_icode2(i_coswitch, make_immed(ISZ_NONE,cs.count), ap, make_immed(ISZ_NONE,cs.top - cs.bottom), stmt->label); gather_cases(stmt->cases,&cs); qsort(cs.ptrs, cs.count, sizeof(cs.ptrs[0]), gcs_compare); for (i = 0; i < cs.count; i++) { gen_icode2(i_swbranch,0,make_immed(ISZ_NONE,cs.ptrs[i].id),0,cs.ptrs[i].label); } breaklab = oldbreak; }
int analyzeswitch(SNODE *stmt, struct cases *cs) { int size = natural_size(stmt->exp); count_cases(stmt->s1,cs) ; cs->top++; if (cs->count == 0) return (0); if (cs->count < 5) return 3; if (cs->count *10 / (cs->top - cs->bottom) >= 8) return (1); // do a simple switch instead of a binary if it is a long long if (size == BESZ_QWORD || size == - BESZ_QWORD) return 3; return (2); }
/* Count the number of cases in a validation expression; that is, after * folding all the | and ? to the outside for fastest-possible evaluation * caused by the shortest computation path to a positive outcome. We * care less about refusals -- they may take time. That'll teach them :) * * This routine returns -1 if the syntax or anything else in the * validation expression is not in order; otherwise it returns a * case count >= 0. As a few (not so) special cases, the logic value * FALSE written as 0 or 1~ is returned as zero cases, because FALSE is * the zero element for logical OR; and the logic value TRUE, which * is written as 1 or 0~, is returned as one case that will be * setup during expression expansion to have succeeded immediately, * namely as a case with nothing left to be computed, and with no * negative or positive actions that were not satisfied. Since TRUE * is the zero element for logical AND, it is often used as a default * in AND compositions, and will then combine with further cases when * it the validation expressions are being expanded. It makes sense * in algebra, and so it makes sense in real life. * * While counting, some useful information is collected and stored by * overwriting binary operators with a byte that has the high bit set * to form a recognisable OPC_xxx code. There are flags OPF_xxx to * indicate trivia found while counting, namely whether left/right * branches have zero cases, and whether zero cases trickle up. This * information is of great use for the interleaving algorithm. This * also means that count_cases() needs to run before expand_cases_rec(). * * Even though the tree is modified, count_cases() can still pass * through it, provided that it did not return -1 before. This means * that it should not be made to work on constant strings, but it can * be made to work on the same (global/static) variables repeatedly or * even concurrently; it is designed to be idempotent and re-entrant. * * On success, the function fills the parsed value with the number * of characters that were parsed (in a range of 0 up to vallen) * starting from the end of the expression -- since it is reverse * Polish notation. The top-call to this function should return in * the output parameter parsed the same value as the vallen it was * called with. Otherwise, the validation expression did not accept * the entire string passed to it as valexp / vallen. */ static int count_cases (char *valexpstr, int vallen, int invert, int *parsed) { int case0p, case0n, case1, case2; int pars0, pars1, pars2; uint8_t *opcp; uint8_t opc; uint8_t zero_1st = invert? OPF_ONE_FIRST: OPF_ZERO_FIRST; uint8_t zero_2nd = invert? OPF_ONE_SECOND: OPF_ZERO_SECOND; int retval = -1; // // Ensure that the validation expression is non-empty if (vallen <= 0) { return -1; } vallen--; opcp = &valexpstr [vallen]; opc = *opcp; switch (opc) { case '&': opc = OPC_AND; break; case '|': opc = OPC_OR; break; case '?': opc = OPC_IF; break; default: if (opc & 0x80) { opc = opc & OPC_MASK; } break; } // // Find one of | or & or ~ or ? switch (opc) { case OPC_OR: case OPC_AND: case1 = count_cases (valexpstr, vallen, invert, &pars1); vallen -= pars1; case2 = count_cases (valexpstr, vallen, invert, &pars2); *parsed = 1 + pars1 + pars2; if ((case1 == -1) || (case2 == -1)) { *opcp = ERR_BINOP; return -1; } else if (opc == (invert? OPC_AND: OPC_OR)) { retval = case1 + case2; } else { retval = case1 * case2; } *opcp = opc; if (case1 == 0) { *opcp |= zero_1st; } if (case2 == 0) { *opcp |= zero_2nd; } return retval; case OPC_IF: // Count the test case twice; once positive and once negative // "eti?" reads as "IF i THEN t ELSE e" or like in C, "i?t:e" case0p = count_cases (valexpstr, vallen, 0, &pars0); case0n = count_cases (valexpstr, vallen, 1, &pars0); vallen -= pars0; case1 = count_cases (valexpstr, vallen, invert, &pars1); vallen -= pars1; case2 = count_cases (valexpstr, vallen, invert, &pars2); *parsed = 1 + pars0 + pars1 + pars2; // Recombine "eti?" as "et&ti&ei~&||" // Note the test is not inverted, but the case's outcomes are if ((case0p == -1) || (case0n == -1) || (case1 == -1) || (case2 == -1)) { *opcp = ERR_TERNOP; return -1; } *opcp = OPC_IF; if (case1 == 0) { *opcp |= zero_1st; } if (case2 == 0) { *opcp |= zero_2nd; } retval = (case1 * case0p) + (case2 * case0n) + (case1 * case2); return retval; case ERR_BINOP: *opcp = ERR_BINOP; return -1; case ERR_TERNOP: *opcp = ERR_TERNOP; return -1; case '~': case1 = count_cases (valexpstr, vallen, !invert, &pars1); *parsed = 1 + pars1; return case1; case '0': case '1': *parsed = 1; if (opc == (invert? '1': '0')) { // strings like 0 and 1~ --> FALSE // expands to no case at all return 0; } else { // strings like 1 and 0~ --> TRUE // expands to a single, already-fulfilled case return 1; } default: if (VALEXP_OPERAND (valexpstr [vallen])) { *parsed = 1; return 1; } else { // Syntax error return -1; } } }