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;
    }
}
Beispiel #2
0
/* 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;
}
Beispiel #3
0
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);
}
Beispiel #5
0
/* 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;
		}
	}
}