diff options
author | H. Peter Anvin <hpa@zytor.com> | 2007-09-18 16:39:03 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2007-09-18 16:39:03 -0700 |
commit | 141d7cf68d60f6c77c078fea7ff85526db668c6f (patch) | |
tree | e5605799603d43038a2a4d095471a2e3f3a35a76 | |
parent | 510a2508e6cb1298fa4640d6083e73c1c7ead06e (diff) | |
download | nasm-141d7cf68d60f6c77c078fea7ff85526db668c6f.tar.gz |
Support 16-bit IEEE floating point; used in SSE5
SSE5 supports standard IEEE 16-bit floating point, so we should
support that too.
-rw-r--r-- | float.c | 71 | ||||
-rw-r--r-- | parser.c | 24 |
2 files changed, 82 insertions, 13 deletions
@@ -213,6 +213,7 @@ static int ieee_round(uint16_t *mant, int i) #define put(a,b) ( (*(a)=(b)), ((a)[1]=(b)>>8) ) +/* 64-bit format with 52-bit mantissa and 11-bit exponent */ static int to_double(char *str, int32_t sign, uint8_t *result, efunc error) { @@ -275,6 +276,7 @@ static int to_double(char *str, int32_t sign, uint8_t *result, return 1; /* success */ } +/* 32-bit format with 23-bit mantissa and 8-bit exponent */ static int to_float(char *str, int32_t sign, uint8_t *result, efunc error) { @@ -330,6 +332,64 @@ static int to_float(char *str, int32_t sign, uint8_t *result, return 1; } +/* 16-bit format with 10-bit mantissa and 5-bit exponent. + Defined in IEEE 754r. Used in SSE5. See the AMD SSE5 manual, AMD + document number 43479. */ +static int to_float16(char *str, int32_t sign, uint8_t *result, + efunc error) +{ + uint16_t mant[MANT_WORDS]; + int32_t exponent; + + sign = (sign < 0 ? 0x8000L : 0L); + + ieee_flconvert(str, mant, &exponent, error); + if (mant[0] & 0x8000) { + /* + * Non-zero. + */ + exponent--; + if (exponent >= -14 && exponent <= 16) { + /* + * Normalised. + */ + exponent += 15; + ieee_shr(mant, 5); + ieee_round(mant, 1); + if (mant[0] & 0x800) /* did we scale up by one? */ + ieee_shr(mant, 1), exponent++; + mant[0] &= 0x3FF; /* remove leading one */ + put(result + 0, (exponent << 7) | mant[0] | sign); + } else if (exponent < -14 && exponent >= -24) { + /* + * Denormal. + */ + int shift = -(exponent + 8); + int sh = shift % 16, wds = shift / 16; + ieee_shr(mant, sh); + if (ieee_round(mant, 1 - wds) + || (sh > 0 && (mant[0] & (0x8000 >> (sh - 1))))) { + ieee_shr(mant, 1); + if (sh == 0) + mant[0] |= 0x8000; + exponent++; + } + put(result + 0, (wds == 0 ? mant[0] : 0) | sign); + } else { + if (exponent > 0) { + error(ERR_NONFATAL, "overflow in floating-point constant"); + return 0; + } else + memset(result, 0, 2); + } + } else { + memset(result, 0, 2); + } + return 1; +} + +/* 80-bit format with 64-bit mantissa *including an explicit integer 1* + and 15-bit exponent. */ static int to_ldoub(char *str, int32_t sign, uint8_t *result, efunc error) { @@ -394,13 +454,16 @@ static int to_ldoub(char *str, int32_t sign, uint8_t *result, int float_const(char *number, int32_t sign, uint8_t *result, int bytes, efunc error) { - if (bytes == 4) + switch (bytes) { + case 2: + return to_float16(number, sign, result, error); + case 4: return to_float(number, sign, result, error); - else if (bytes == 8) + case 8: return to_double(number, sign, result, error); - else if (bytes == 10) + case 10: return to_ldoub(number, sign, result, error); - else { + default: error(ERR_PANIC, "strange value %d passed to float_const", bytes); return 0; } @@ -230,30 +230,36 @@ insn *parse_line(int pass, char *buffer, insn * result, if (i == TOKEN_FLOAT) { eop->type = EOT_DB_STRING; result->eops_float = TRUE; - if (result->opcode == I_DD) + switch (result->opcode) { + case I_DW: + eop->stringlen = 2; + break; + case I_DD: eop->stringlen = 4; - else if (result->opcode == I_DQ) + break; + case I_DQ: eop->stringlen = 8; - else if (result->opcode == I_DT) + break; + case I_DT: eop->stringlen = 10; - else if (result->opcode == I_DO) - eop->stringlen = 16; - else { + break; + default: error(ERR_NONFATAL, "floating-point constant" - " encountered in `D%c' instruction", - result->opcode == I_DW ? 'W' : 'B'); + " encountered in `d%c' instruction" + ? (result->opcode == I_DO) ? 'o' : 'b'); /* * fix suggested by Pedro Gimeno... original line * was: * eop->type = EOT_NOTHING; */ eop->stringlen = 0; + break; } eop = nasm_realloc(eop, sizeof(extop) + eop->stringlen); tail = &eop->next; *fixptr = eop; eop->stringval = (char *)eop + sizeof(extop); - if (eop->stringlen < 4 || + if (!eop->stringlen || !float_const(tokval.t_charptr, sign, (uint8_t *)eop->stringval, eop->stringlen, error)) |