diff options
| author | grischka <grischka> | 2014-05-06 18:24:41 +0200 |
|---|---|---|
| committer | grischka <grischka> | 2014-05-06 18:24:41 +0200 |
| commit | 899d26605c5276964ac2ae1f8238a75a5afeb480 (patch) | |
| tree | 21aa546c6b9e22dadb38066a97d0968260445607 | |
| parent | 5e56fb635a23484d8fda8b54a40900d0a54b0ba1 (diff) | |
| download | tinycc-899d26605c5276964ac2ae1f8238a75a5afeb480.tar.gz tinycc-899d26605c5276964ac2ae1f8238a75a5afeb480.tar.bz2 | |
Revert "update static void parse_number()"
because:
- Constructing fp numbers isn't quite trivial
- 3 additional calls to strchr per number is noticeable slow
Also: exclude abitest.c:ret_longdouble_test2 on _WIN32
for mixed gcc/tcc scenario
test case:
- make -k test (on win32):
-2.120000 0.500000 23000000000.000000
+2.120000 0.500000 22999999999.999996
...
ret_longdouble_test2... failure
This reverts 857f7dbfa65179e6690dbee7ab915fb4458cee11
and deaee6c2496ecb25858290405fef8ef79aece979
| -rw-r--r-- | tccpp.c | 382 | ||||
| -rw-r--r-- | tests/abitest.c | 3 |
2 files changed, 247 insertions, 138 deletions
@@ -58,6 +58,7 @@ static const int *unget_saved_macro_ptr; static int unget_saved_buffer[TOK_MAX_SIZE + 1]; static int unget_buffer_enabled; static TokenSym *hash_ident[TOK_HASH_SIZE]; +static char token_buf[STRING_MAX_SIZE + 1]; /* true if isid(c) || isnum(c) */ static unsigned char isidnum_table[256-CH_EOF]; @@ -1789,156 +1790,261 @@ static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long cstr_wccat(outstr, '\0'); } +/* we use 64 bit numbers */ +#define BN_SIZE 2 + +/* bn = (bn << shift) | or_val */ +static void bn_lshift(unsigned int *bn, int shift, int or_val) +{ + int i; + unsigned int v; + for(i=0;i<BN_SIZE;i++) { + v = bn[i]; + bn[i] = (v << shift) | or_val; + or_val = v >> (32 - shift); + } +} + +static void bn_zero(unsigned int *bn) +{ + int i; + for(i=0;i<BN_SIZE;i++) { + bn[i] = 0; + } +} + /* parse number in null terminated string 'p' and return it in the current token */ static void parse_number(const char *p) { - int b, t, c; + int b, t, shift, frac_bits, s, exp_val, ch; + char *q; + unsigned int bn[BN_SIZE]; + double d; - c = *p++; - t = *p++; + /* number */ + q = token_buf; + ch = *p++; + t = ch; + ch = *p++; + *q++ = t; b = 10; - if(c=='.'){ - --p; - goto float_frac_parse; - } - if(c == '0'){ - if (t == 'x' || t == 'X') { + if (t == '.') { + goto float_frac_parse; + } else if (t == '0') { + if (ch == 'x' || ch == 'X') { + q--; + ch = *p++; b = 16; - c = *p++; - } else if (tcc_ext && (t == 'b' || t == 'B')) { + } else if (tcc_ext && (ch == 'b' || ch == 'B')) { + q--; + ch = *p++; b = 2; - c = *p++; - }else{ - --p; - } - }else - --p; - if(strchr(p , '.') || (b == 10 && (strchr(p,'e') || strchr(p,'E'))) || - ((b == 2 || b == 16)&& (strchr(p,'p') || strchr(p,'P')))){ - long double ld, sh, fb; - int exp; - /* NOTE: strtox should support that for hexa numbers, but - non ISOC99 libcs do not support it, so we prefer to do - it by hand */ - /* hexadecimal or binary floats */ - /* XXX: handle overflows */ -float_frac_parse: - fb = 1.0L/b; - sh = b; - ld = 0.0; - - while(1){ - if (c == '\0') - break; - if (c >= 'a' && c <= 'f') - t = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - t = c - 'A' + 10; - else if(isnum(c)) - t = c - '0'; - else - break; - if (t >= b) - tcc_error("invalid digit"); - ld = ld * b + t; - c = *p++; - } - if (c == '.'){ - c = *p++; - sh = fb; - while (1){ - if (c == '\0') - break; - if (c >= 'a' && c <= 'f') - t = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - t = c - 'A' + 10; - else if (isnum(c)) - t =c - '0'; - else - break; - if (t >= b){ - if(b == 10 && (c == 'e' || c == 'E' || c == 'f' || c == 'F')) - break; - tcc_error("invalid digit"); - } - ld += sh*t; - sh*=fb; - c = *p++; - } - } - if ((b == 16 || b == 2) && c != 'p' && c != 'P') + } + } + /* parse all digits. cannot check octal numbers at this stage + because of floating point constants */ + while (1) { + if (ch >= 'a' && ch <= 'f') + t = ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + t = ch - 'A' + 10; + else if (isnum(ch)) + t = ch - '0'; + else + break; + if (t >= b) + break; + if (q >= token_buf + STRING_MAX_SIZE) { + num_too_long: + tcc_error("number too long"); + } + *q++ = ch; + ch = *p++; + } + if (ch == '.' || + ((ch == 'e' || ch == 'E') && b == 10) || + ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) { + if (b != 10) { + /* NOTE: strtox should support that for hexa numbers, but + non ISOC99 libcs do not support it, so we prefer to do + it by hand */ + /* hexadecimal or binary floats */ + /* XXX: handle overflows */ + *q = '\0'; + if (b == 16) + shift = 4; + else + shift = 2; + bn_zero(bn); + q = token_buf; + while (1) { + t = *q++; + if (t == '\0') { + break; + } else if (t >= 'a') { + t = t - 'a' + 10; + } else if (t >= 'A') { + t = t - 'A' + 10; + } else { + t = t - '0'; + } + bn_lshift(bn, shift, t); + } + frac_bits = 0; + if (ch == '.') { + ch = *p++; + while (1) { + t = ch; + if (t >= 'a' && t <= 'f') { + t = t - 'a' + 10; + } else if (t >= 'A' && t <= 'F') { + t = t - 'A' + 10; + } else if (t >= '0' && t <= '9') { + t = t - '0'; + } else { + break; + } + if (t >= b) + tcc_error("invalid digit"); + bn_lshift(bn, shift, t); + frac_bits += shift; + ch = *p++; + } + } + if (ch != 'p' && ch != 'P') expect("exponent"); - if(((c == 'e' || c == 'E') && b == 10) || - ((c == 'p' || c == 'P') && (b == 16 || b == 2))){ - c = *p++; - if(c == '+' || c == '-'){ - if (c == '-') - sh = fb; - c = *p++; - }else - sh = b; - if (!isnum(c)) - expect("exponent digits"); - exp = 0; - do{ - exp = exp * 10 + c - '0'; - c = *p++; - }while(isnum(c)); - while (exp != 0){ - if (exp & 1) - ld *= sh; - exp >>= 1; - sh *= sh; - } - } - t = toup(c); - if (t == 'F') { - c = *p++; - tok = TOK_CFLOAT; - tokc.f = (float)ld; - } else if (t == 'L') { - c = *p++; + ch = *p++; + s = 1; + exp_val = 0; + if (ch == '+') { + ch = *p++; + } else if (ch == '-') { + s = -1; + ch = *p++; + } + if (ch < '0' || ch > '9') + expect("exponent digits"); + while (ch >= '0' && ch <= '9') { + exp_val = exp_val * 10 + ch - '0'; + ch = *p++; + } + exp_val = exp_val * s; + + /* now we can generate the number */ + /* XXX: should patch directly float number */ + d = (double)bn[1] * 4294967296.0 + (double)bn[0]; + d = ldexp(d, exp_val - frac_bits); + t = toup(ch); + if (t == 'F') { + ch = *p++; + tok = TOK_CFLOAT; + /* float : should handle overflow */ + tokc.f = (float)d; + } else if (t == 'L') { + ch = *p++; +#ifdef TCC_TARGET_PE + tok = TOK_CDOUBLE; + tokc.d = d; +#else + tok = TOK_CLDOUBLE; + /* XXX: not large enough */ + tokc.ld = (long double)d; +#endif + } else { + tok = TOK_CDOUBLE; + tokc.d = d; + } + } else { + /* decimal floats */ + if (ch == '.') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + float_frac_parse: + while (ch >= '0' && ch <= '9') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + } + } + if (ch == 'e' || ch == 'E') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + if (ch == '-' || ch == '+') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + } + if (ch < '0' || ch > '9') + expect("exponent digits"); + while (ch >= '0' && ch <= '9') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + } + } + *q = '\0'; + t = toup(ch); + errno = 0; + if (t == 'F') { + ch = *p++; + tok = TOK_CFLOAT; + tokc.f = strtof(token_buf, NULL); + } else if (t == 'L') { + ch = *p++; #ifdef TCC_TARGET_PE - tok = TOK_CDOUBLE; - tokc.d = (double)ld; + tok = TOK_CDOUBLE; + tokc.d = strtod(token_buf, NULL); #else - tok = TOK_CLDOUBLE; - tokc.ld = ld; + tok = TOK_CLDOUBLE; + tokc.ld = strtold(token_buf, NULL); #endif - } else { - tok = TOK_CDOUBLE; - tokc.d = (double)ld; - } + } else { + tok = TOK_CDOUBLE; + tokc.d = strtod(token_buf, NULL); + } + } } else { - uint64_t n = 0, n1; - int warn = 1; + unsigned long long n, n1; int lcount, ucount; - if (b == 10 && c == '0') { + + /* integer number */ + *q = '\0'; + q = token_buf; + if (b == 10 && *q == '0') { b = 8; + q++; } - while(1){ - if (c == '\0') - break; - if (c >= 'a' && c <= 'f') - t = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - t = c - 'A' + 10; - else if(isnum(c)) - t = c - '0'; - else - break; - if (t >= b) - tcc_error("invalid digit"); - n1 = n; - n = n * b + t; - if (n < n1 && warn){ - tcc_warning("integer constant overflow"); - warn = 0; - } - c = *p++; - } + n = 0; + while(1) { + t = *q++; + /* no need for checks except for base 10 / 8 errors */ + if (t == '\0') { + break; + } else if (t >= 'a') { + t = t - 'a' + 10; + } else if (t >= 'A') { + t = t - 'A' + 10; + } else { + t = t - '0'; + if (t >= b) + tcc_error("invalid digit"); + } + n1 = n; + n = n * b + t; + /* detect overflow */ + /* XXX: this test is not reliable */ + if (n < n1) + tcc_error("integer constant overflow"); + } + /* XXX: not exactly ANSI compliant */ if ((n & 0xffffffff00000000LL) != 0) { if ((n >> 63) != 0) @@ -1953,7 +2059,7 @@ float_frac_parse: lcount = 0; ucount = 0; for(;;) { - t = toup(c); + t = toup(ch); if (t == 'L') { if (lcount >= 2) tcc_error("three 'l's in integer constant"); @@ -1968,7 +2074,7 @@ float_frac_parse: #if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE } #endif - c = *p++; + ch = *p++; } else if (t == 'U') { if (ucount >= 1) tcc_error("two 'u's in integer constant"); @@ -1977,7 +2083,7 @@ float_frac_parse: tok = TOK_CUINT; else if (tok == TOK_CLLONG) tok = TOK_CULLONG; - c = *p++; + ch = *p++; } else { break; } @@ -1987,7 +2093,7 @@ float_frac_parse: else tokc.ull = n; } - if (c) + if (ch) tcc_error("invalid number\n"); } diff --git a/tests/abitest.c b/tests/abitest.c index 3ad707a..e2978b0 100644 --- a/tests/abitest.c +++ b/tests/abitest.c @@ -486,7 +486,10 @@ int main(int argc, char **argv) { RUN_TEST(ret_2float_test); RUN_TEST(ret_2double_test); RUN_TEST(ret_longlong_test2); +#if !defined _WIN32 || !defined __GNUC__ + /* on win32, 'long double' is 10-byte with gcc, but is 'double' with tcc/msvc */ RUN_TEST(ret_longdouble_test2); +#endif RUN_TEST(reg_pack_test); RUN_TEST(reg_pack_longlong_test); RUN_TEST(sret_test); |
