diff options
| author | Alexander Viro <viro@www.linux.org.uk> | 2004-07-23 22:35:46 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-07 21:02:18 -0700 |
| commit | 9c2ebe3f27f19095e2822463aa713be4b8b523d2 (patch) | |
| tree | 8fc6a25bc864b026da78824d9c961e8536f06084 | |
| parent | 07bc6f3abd6f1b16d3b7476a84aa127b56bb1233 (diff) | |
| download | sparse-dev-9c2ebe3f27f19095e2822463aa713be4b8b523d2.tar.gz | |
[PATCH] FP handling
FP handling added, everything straightforward by now.
| -rw-r--r-- | evaluate.c | 138 | ||||
| -rw-r--r-- | expand.c | 198 | ||||
| -rw-r--r-- | expression.c | 40 | ||||
| -rw-r--r-- | expression.h | 4 | ||||
| -rw-r--r-- | inline.c | 1 | ||||
| -rw-r--r-- | linearize.c | 11 | ||||
| -rw-r--r-- | show-parse.c | 11 | ||||
| -rw-r--r-- | symbol.c | 2 |
8 files changed, 326 insertions, 79 deletions
@@ -203,12 +203,55 @@ static inline int is_int_type(struct symbol *type) type->ctype.base_type == &int_type; } +static inline int is_float_type(struct symbol *type) +{ + if (type->type == SYM_NODE) + type = type->ctype.base_type; + return type->ctype.base_type == &fp_type; +} + static struct symbol *bad_expr_type(struct expression *expr) { warn(expr->pos, "incompatible types for operation"); return NULL; } +static struct symbol *compatible_float_binop(struct expression **lp, struct expression **rp) +{ + struct expression *left = *lp, *right = *rp; + struct symbol *ltype = left->ctype, *rtype = right->ctype; + + if (ltype->type == SYM_NODE) + ltype = ltype->ctype.base_type; + if (rtype->type == SYM_NODE) + rtype = rtype->ctype.base_type; + if (is_float_type(ltype)) { + if (is_int_type(rtype)) + goto Left; + if (is_float_type(rtype)) { + unsigned long lmod = ltype->ctype.modifiers; + unsigned long rmod = rtype->ctype.modifiers; + lmod &= MOD_LONG | MOD_LONGLONG; + rmod &= MOD_LONG | MOD_LONGLONG; + if (lmod == rmod) + return ltype; + if (lmod & ~rmod) + goto Left; + else + goto Right; + } + return NULL; + } + if (!is_float_type(rtype) || !is_int_type(ltype)) + return NULL; +Right: + *lp = cast_to(left, rtype); + return rtype; +Left: + *rp = cast_to(right, ltype); + return ltype; +} + static struct symbol *compatible_integer_binop(struct expression **lp, struct expression **rp) { struct expression *left = *lp, *right = *rp; @@ -234,6 +277,8 @@ static struct symbol *compatible_integer_binop(struct expression **lp, struct ex static struct symbol *evaluate_arith(struct expression *expr, int float_ok) { struct symbol *ctype = compatible_integer_binop(&expr->left, &expr->right); + if (!ctype && float_ok) + ctype = compatible_float_binop(&expr->left, &expr->right); if (ctype) { expr->ctype = ctype; return ctype; @@ -313,7 +358,6 @@ static struct symbol *evaluate_add(struct expression *expr) if (is_ptr_type(rtype)) return evaluate_ptr_add(expr, right, left); - // FIXME! FP promotion return evaluate_arith(expr, 1); } @@ -545,7 +589,6 @@ static struct symbol *evaluate_sub(struct expression *expr) if (is_ptr_type(ltype)) return evaluate_ptr_sub(expr, left, right); - // FIXME! FP promotion return evaluate_arith(expr, 1); } @@ -563,8 +606,29 @@ static struct symbol *evaluate_conditional(struct expression **p) warn(expr->pos, "assignment expression in conditional"); ctype = evaluate_expression(expr); - if (ctype && is_safe_type(ctype)) - warn(expr->pos, "testing a 'safe expression'"); + if (ctype) { + if (is_safe_type(ctype)) + warn(expr->pos, "testing a 'safe expression'"); + if (is_float_type(ctype)) { + struct expression *comp; + /* + * It's easier to handle here, rather than deal with + * FP all over the place. Floating point in boolean + * context is rare enough (and very often wrong), + * so price of explicit comparison with appropriate + * FP zero is not too high. And it simplifies things + * elsewhere. + */ + comp = alloc_expression(expr->pos, EXPR_BINOP); + comp->op = SPECIAL_NOTEQUAL; + comp->left = expr; + comp->right = alloc_expression(expr->pos, EXPR_FVALUE); + comp->right->ctype = comp->left->ctype; + comp->right->fvalue = 0; + ctype = comp->ctype = &bool_ctype; + *p = comp; + } + } return ctype; } @@ -676,15 +740,11 @@ static struct symbol *evaluate_compare(struct expression *expr) expr->ctype = &bool_ctype; return &bool_ctype; } + ctype = compatible_float_binop(&expr->left, &expr->right); return bad_expr_type(expr); } -static int compatible_integer_types(struct symbol *ltype, struct symbol *rtype) -{ - return (is_int_type(ltype) && is_int_type(rtype)); -} - /* * FIXME!! This should do casts, array degeneration etc.. */ @@ -731,17 +791,22 @@ static struct symbol * evaluate_conditional_expression(struct expression *expr) ctype = ltype; typediff = type_difference(ltype, rtype, MOD_IGN, MOD_IGN); - if (typediff) { - ctype = compatible_integer_binop(&true, &expr->cond_false); - if (!ctype) { - ctype = compatible_ptr_type(true, expr->cond_false); - if (!ctype) { - warn(expr->pos, "incompatible types in conditional expression (%s)", typediff); - return NULL; - } - } - } + if (!typediff) + goto out; + + ctype = compatible_integer_binop(&true, &expr->cond_false); + if (ctype) + goto out; + ctype = compatible_ptr_type(true, expr->cond_false); + if (ctype) + goto out; + ctype = compatible_float_binop(&true, &expr->cond_false); + if (ctype) + goto out; + warn(expr->pos, "incompatible types in conditional expression (%s)", typediff); + return NULL; +out: expr->ctype = ctype; return ctype; } @@ -758,10 +823,22 @@ static int compatible_assignment_types(struct expression *expr, struct symbol *t if (!typediff) return 1; - if (compatible_integer_types(target, source)) { - if (target->bit_size != source->bit_size) - *rp = cast_to(*rp, target); - return 1; + if (is_int_type(target)) { + if (is_int_type(source)) { + if (target->bit_size != source->bit_size) + goto Cast; + return 1; + } + if (is_float_type(source)) + goto Cast; + } else if (is_float_type(target)) { + if (is_int_type(source)) + goto Cast; + if (is_float_type(source)) { + if (target->bit_size != source->bit_size) + goto Cast; + return 1; + } } /* Pointer destination? */ @@ -799,6 +876,9 @@ static int compatible_assignment_types(struct expression *expr, struct symbol *t info(expr->pos, " expected %s", show_typename(target)); info(expr->pos, " got %s", show_typename(source)); return 0; +Cast: + *rp = cast_to(*rp, target); + return 1; } /* @@ -1088,6 +1168,8 @@ static struct symbol *evaluate_sign(struct expression *expr) if (rtype->bit_size != ctype->bit_size) expr->unop = cast_to(expr->unop, rtype); ctype = rtype; + } else if (is_float_type(ctype) && expr->op != '%') { + /* no conversions needed */ } else { return bad_expr_type(expr); } @@ -1128,6 +1210,15 @@ static struct symbol *evaluate_preop(struct expression *expr) case '!': if (is_safe_type(ctype)) warn(expr->pos, "testing a 'safe expression'"); + if (is_float_type(ctype)) { + struct expression *arg = expr->unop; + expr->type = EXPR_BINOP; + expr->op = SPECIAL_EQUAL; + expr->left = arg; + expr->right = alloc_expression(expr->pos, EXPR_FVALUE); + expr->right->ctype = ctype; + expr->right->fvalue = 0; + } ctype = &bool_ctype; break; @@ -1660,6 +1751,7 @@ struct symbol *evaluate_expression(struct expression *expr) switch (expr->type) { case EXPR_VALUE: + case EXPR_FVALUE: warn(expr->pos, "value expression without a type"); return NULL; case EXPR_STRING: @@ -43,19 +43,32 @@ static void expand_symbol_expression(struct expression *expr) } } +static long long get_longlong(struct expression *expr) +{ + int no_expand = expr->ctype->ctype.modifiers & MOD_UNSIGNED; + long long mask = 1ULL << (expr->ctype->bit_size - 1); + long long value = expr->value; + long long ormask, andmask; + + if (!(value & mask)) + no_expand = 1; + andmask = mask | (mask-1); + ormask = ~andmask; + if (no_expand) + ormask = 0; + return (value & andmask) | ormask; +} + void cast_value(struct expression *expr, struct symbol *newtype, struct expression *old, struct symbol *oldtype) { int old_size = oldtype->bit_size; int new_size = newtype->bit_size; - long long value, mask, ormask, andmask; - int is_signed; + long long value, mask; - // FIXME! We don't handle FP casts of constant values yet - if (newtype->ctype.base_type == &fp_type) - return; - if (oldtype->ctype.base_type == &fp_type) - return; + if (newtype->ctype.base_type == &fp_type || + oldtype->ctype.base_type == &fp_type) + goto Float; // For pointers and integers, we can just move the value around expr->type = EXPR_VALUE; @@ -65,21 +78,34 @@ void cast_value(struct expression *expr, struct symbol *newtype, } // expand it to the full "long long" value - is_signed = !(oldtype->ctype.modifiers & MOD_UNSIGNED); - mask = 1ULL << (old_size-1); - value = old->value; - if (!(value & mask)) - is_signed = 0; - andmask = mask | (mask-1); - ormask = ~andmask; - if (!is_signed) - ormask = 0; - value = (value & andmask) | ormask; + value = get_longlong(old); +Int: // Truncate it to the new size mask = 1ULL << (new_size-1); mask = mask | (mask-1); expr->value = value & mask; + return; + +Float: + if (newtype->ctype.base_type != &fp_type) { + value = (long long)expr->fvalue; + expr->type = EXPR_VALUE; + goto Int; + } + + if (oldtype->ctype.base_type != &fp_type) + expr->fvalue = (long double)get_longlong(old); + else + expr->fvalue = old->value; + + if (!(newtype->ctype.modifiers & MOD_LONGLONG)) { + if ((newtype->ctype.modifiers & MOD_LONG)) + expr->fvalue = (double)expr->fvalue; + else + expr->fvalue = (float)expr->fvalue; + } + expr->type = EXPR_FVALUE; } static int check_shift_count(struct expression *expr, struct symbol *ctype, unsigned int count) @@ -95,7 +121,7 @@ static int check_shift_count(struct expression *expr, struct symbol *ctype, unsi * CAREFUL! We need to get the size and sign of the * result right! */ -static void simplify_int_binop(struct expression *expr, struct symbol *ctype) +static int simplify_int_binop(struct expression *expr, struct symbol *ctype) { struct expression *left = expr->left, *right = expr->right; unsigned long long v, l, r, mask; @@ -103,7 +129,7 @@ static void simplify_int_binop(struct expression *expr, struct symbol *ctype) int is_signed, shift; if (left->type != EXPR_VALUE || right->type != EXPR_VALUE) - return; + return 0; l = left->value; r = right->value; is_signed = !(ctype->ctype.modifiers & MOD_UNSIGNED); mask = 1ULL << (ctype->bit_size-1); @@ -120,27 +146,31 @@ static void simplify_int_binop(struct expression *expr, struct symbol *ctype) case '|': v = l | r; s = v; break; case '^': v = l ^ r; s = v; break; case '*': v = l * r; s = sl * sr; break; - case '/': if (!r) return; v = l / r; s = sl / sr; break; - case '%': if (!r) return; v = l % r; s = sl % sr; break; + case '/': if (!r) goto Div; v = l / r; s = sl / sr; break; + case '%': if (!r) goto Div; v = l % r; s = sl % sr; break; case SPECIAL_LEFTSHIFT: shift = check_shift_count(expr, ctype, r); v = l << shift; s = v; break; case SPECIAL_RIGHTSHIFT:shift = check_shift_count(expr, ctype, r); v = l >> shift; s = sl >> shift; break; - default: return; + default: return 0; } if (is_signed) v = s; mask = mask | (mask-1); expr->value = v & mask; expr->type = EXPR_VALUE; + return 1; +Div: + warn(expr->pos, "division by zero"); + return 0; } -static void simplify_cmp_binop(struct expression *expr, struct symbol *ctype) +static int simplify_cmp_binop(struct expression *expr, struct symbol *ctype) { struct expression *left = expr->left, *right = expr->right; unsigned long long l, r, mask; signed long long sl, sr; if (left->type != EXPR_VALUE || right->type != EXPR_VALUE) - return; + return 0; l = left->value; r = right->value; mask = 1ULL << (ctype->bit_size-1); sl = l; sr = r; @@ -161,11 +191,80 @@ static void simplify_cmp_binop(struct expression *expr, struct symbol *ctype) case SPECIAL_UNSIGNED_GTE:expr->value = l >= r; break; } expr->type = EXPR_VALUE; + return 1; } -static void expand_int_binop(struct expression *expr) +static int simplify_float_binop(struct expression *expr) { - simplify_int_binop(expr, expr->ctype); + struct expression *left = expr->left, *right = expr->right; + unsigned long mod = expr->ctype->ctype.modifiers; + long double l = left->fvalue; + long double r = right->fvalue; + long double res; + + if (left->type != EXPR_FVALUE || right->type != EXPR_FVALUE) + return 0; + if (mod & MOD_LONGLONG) { + switch (expr->op) { + case '+': res = l + r; break; + case '-': res = l - r; break; + case '*': res = l * r; break; + case '/': if (!r) goto Div; + res = l / r; break; + default: return 0; + } + } else if (mod & MOD_LONG) { + switch (expr->op) { + case '+': res = (double) l + (double) r; break; + case '-': res = (double) l - (double) r; break; + case '*': res = (double) l * (double) r; break; + case '/': if (!r) goto Div; + res = (double) l / (double) r; break; + default: return 0; + } + } else { + switch (expr->op) { + case '+': res = (float)l + (float)r; break; + case '-': res = (float)l - (float)r; break; + case '*': res = (float)l * (float)r; break; + case '/': if (!r) goto Div; + res = (float)l / (float)r; break; + default: return 0; + } + } + expr->type = EXPR_FVALUE; + expr->fvalue = res; + return 1; +Div: + warn(expr->pos, "division by zero"); + return 0; +} + +static int simplify_float_cmp(struct expression *expr, struct symbol *ctype) +{ + struct expression *left = expr->left, *right = expr->right; + long double l = left->fvalue, r = right->fvalue; + + if (left->type != EXPR_FVALUE || right->type != EXPR_FVALUE) + return 0; + + switch (expr->op) { + case '<': expr->value = l < r; break; + case '>': expr->value = l > r; break; + case SPECIAL_LTE: expr->value = l <= r; break; + case SPECIAL_GTE: expr->value = l >= r; break; + case SPECIAL_EQUAL: expr->value = l == r; break; + case SPECIAL_NOTEQUAL: expr->value = l != r; break; + } + expr->type = EXPR_VALUE; + return 1; +} + +static void expand_binop(struct expression *expr) +{ + if (simplify_int_binop(expr, expr->ctype)) + return; + simplify_float_binop(expr); } static void expand_logical(struct expression *expr) @@ -203,14 +302,9 @@ static void expand_logical(struct expression *expr) } } -static void expand_binop(struct expression *expr) -{ - expand_int_binop(expr); -} - static void expand_comma(struct expression *expr) { - if (expr->left->type == EXPR_VALUE) + if (expr->left->type == EXPR_VALUE || expr->left->type == EXPR_FVALUE) *expr = *expr->right; } @@ -246,7 +340,9 @@ static void expand_compare(struct expression *expr) expr->value = compare_types(op, left->symbol, right->symbol); return; } - simplify_cmp_binop(expr, expr->ctype); + if (simplify_cmp_binop(expr, left->ctype)) + return; + simplify_float_cmp(expr, left->ctype); } static void expand_conditional(struct expression *expr) @@ -298,31 +394,53 @@ static void expand_dereference(struct expression *expr) if (value->type == EXPR_VALUE) { expr->type = EXPR_VALUE; expr->value = value->value; + } else if (value->type == EXPR_FVALUE) { + expr->type = EXPR_FVALUE; + expr->fvalue = value->fvalue; } } } } } -static void simplify_preop(struct expression *expr) +static int simplify_preop(struct expression *expr) { struct expression *op = expr->unop; unsigned long long v, mask; if (op->type != EXPR_VALUE) - return; + return 0; v = op->value; switch (expr->op) { case '+': break; case '-': v = -v; break; case '!': v = !v; break; case '~': v = ~v; break; - default: return; + default: return 0; } mask = 1ULL << (expr->ctype->bit_size-1); mask = mask | (mask-1); expr->value = v & mask; expr->type = EXPR_VALUE; + return 1; +} + +static int simplify_float_preop(struct expression *expr) +{ + struct expression *op = expr->unop; + long double v; + + if (op->type != EXPR_FVALUE) + return 0; + v = op->fvalue; + switch (expr->op) { + case '+': break; + case '-': v = -v; break; + default: return 0; + } + expr->fvalue = v; + expr->type = EXPR_FVALUE; + return 1; } /* @@ -355,7 +473,9 @@ static void expand_preop(struct expression *expr) default: break; } - simplify_preop(expr); + if (simplify_preop(expr)) + return; + simplify_float_preop(expr); } static void expand_arguments(struct expression_list *head) @@ -374,7 +494,7 @@ static void expand_cast(struct expression *expr) expand_expression(target); /* Simplify normal integer casts.. */ - if (target->type == EXPR_VALUE) + if (target->type == EXPR_VALUE || target->type == EXPR_FVALUE) cast_value(expr, expr->ctype, target, target->ctype); } @@ -426,7 +546,7 @@ static void expand_expression(struct expression *expr) switch (expr->type) { case EXPR_VALUE: - return; + case EXPR_FVALUE: case EXPR_STRING: return; case EXPR_TYPE: diff --git a/expression.c b/expression.c index af1ed3d1..46ed9a34 100644 --- a/expression.c +++ b/expression.c @@ -8,6 +8,7 @@ * * This is the expression parsing part of parsing C. */ +#define _ISOC99_SOURCE #include <stdarg.h> #include <stdlib.h> #include <stdio.h> @@ -100,18 +101,6 @@ static struct token *string_expression(struct token *token, struct expression *e return next; } -static void get_fp_value(struct expression *expr, struct token *token) -{ - static int fp_warned; - - expr->ctype = &double_ctype; - expr->value = 0; - if (!fp_warned) { - warn(token->pos, "FP values not yet implemented"); - fp_warned = 1; - } -} - #ifndef ULLONG_MAX #define ULLONG_MAX (~0ULL) #endif @@ -129,7 +118,7 @@ static void get_number_value(struct expression *expr, struct token *token) errno = 0; value = strtoull(str, &end, 0); if (end == str) - goto Enoint; + goto Float; if (value == ULLONG_MAX && errno == ERANGE) overflow = 1; while (1) { @@ -146,7 +135,7 @@ static void get_number_value(struct expression *expr, struct token *token) end++; } } else - goto Enoint; + goto Float; if (modifiers & added) goto Enoint; modifiers |= added; @@ -213,8 +202,29 @@ got_it: Eoverflow: error(expr->pos, "constant %s is too big even for unsigned long long", show_token(token)); + return; +Float: + expr->fvalue = strtold(str, &end); + if (str == end) + goto Enoint; + + if (*end && end[1]) + goto Enoint; + + if (*end == 'f' || *end == 'F') + expr->ctype = &float_ctype; + else if (*end == 'l' || *end == 'L') + expr->ctype = &ldouble_ctype; + else if (!*end) + expr->ctype = &double_ctype; + else + goto Enoint; + + expr->type = EXPR_FVALUE; + return; + Enoint: - get_fp_value(expr, token); + error(expr->pos, "constant %s is not a valid number", show_token(token)); } struct token *primary_expression(struct token *token, struct expression **tree) diff --git a/expression.h b/expression.h index 5845e0f4..5d5612de 100644 --- a/expression.h +++ b/expression.h @@ -38,6 +38,7 @@ enum expression_type { EXPR_IDENTIFIER, // identifier in initializer EXPR_INDEX, // index in initializer EXPR_POS, // position in initializer + EXPR_FVALUE, }; struct expression { @@ -49,6 +50,9 @@ struct expression { // EXPR_VALUE unsigned long long value; + // EXPR_FVALUE + long double fvalue; + // EXPR_STRING struct string *string; @@ -77,6 +77,7 @@ static struct expression * copy_expression(struct expression *expr) /* Atomics, never change, just return the expression directly */ case EXPR_VALUE: case EXPR_STRING: + case EXPR_FVALUE: break; /* Unops: check if the subexpression is unique */ diff --git a/linearize.c b/linearize.c index 1fdb55bd..0992c6d1 100644 --- a/linearize.c +++ b/linearize.c @@ -94,6 +94,10 @@ static void show_instruction(struct instruction *insn) printf("\t%%r%d <- %lld\n", insn->target->nr, expr->value); break; + case EXPR_FVALUE: + printf("\t%%r%d <- %Lf\n", + insn->target->nr, expr->fvalue); + break; case EXPR_STRING: printf("\t%%r%d <- %s\n", insn->target->nr, show_string(expr->string)); @@ -411,6 +415,7 @@ static pseudo_t linearize_access(struct entrypoint *ep, struct expression *expr) return linearize_load_gen(ep, expr, addr); } +/* FIXME: FP */ static pseudo_t linearize_inc_dec(struct entrypoint *ep, struct expression *expr, int postop) { pseudo_t addr = linearize_address_gen(ep, expr->unop); @@ -631,6 +636,10 @@ pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, s case EXPR_VALUE: add_goto(ep, expr->value ? bb_true : bb_false); return VOID; + + case EXPR_FVALUE: + add_goto(ep, expr->fvalue ? bb_true : bb_false); + return VOID; case EXPR_LOGICAL: linearize_logical_branch(ep, expr, bb_true, bb_false); @@ -693,7 +702,7 @@ pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr) return VOID; switch (expr->type) { - case EXPR_VALUE: case EXPR_STRING: case EXPR_SYMBOL: + case EXPR_VALUE: case EXPR_STRING: case EXPR_SYMBOL: case EXPR_FVALUE: return add_setval(ep, expr->ctype, expr); case EXPR_STATEMENT: diff --git a/show-parse.c b/show-parse.c index 517719b7..efe55811 100644 --- a/show-parse.c +++ b/show-parse.c @@ -842,6 +842,15 @@ static int show_value(struct expression *expr) return new; } +static int show_fvalue(struct expression *expr) +{ + int new = new_pseudo(); + long double value = expr->fvalue; + + printf("\tmovf.%d\t\tv%d,$%Lf\n", expr->ctype->bit_size, new, value); + return new; +} + static int show_string_expr(struct expression *expr) { int new = new_pseudo(); @@ -969,6 +978,8 @@ int show_expression(struct expression *expr) return show_cast_expr(expr); case EXPR_VALUE: return show_value(expr); + case EXPR_FVALUE: + return show_fvalue(expr); case EXPR_STRING: return show_string_expr(expr); case EXPR_BITFIELD: @@ -345,7 +345,7 @@ static void expand_constant_p(struct expression *expr) int value = 1; FOR_EACH_PTR (arglist, arg) { - if (arg->type != EXPR_VALUE) + if (arg->type != EXPR_VALUE && arg->type != EXPR_VALUE) value = 0; } END_FOR_EACH_PTR; |
