@@ -38,15 +38,167 @@ struct input {
};
enum precision {
+ PREC_HALF,
PREC_FLOAT,
PREC_DOUBLE,
PREC_QUAD,
+ /* Integers */
+ PREC_INT16,
+ PREC_INT32,
+ PREC_INT64,
+ PREC_UINT16,
+ PREC_UINT32,
+ PREC_UINT64,
+ /* Float to Float conversions */
+ PREC_HALF_TO_FLOAT,
+ PREC_HALF_TO_DOUBLE,
+ PREC_FLOAT_TO_HALF,
PREC_FLOAT_TO_DOUBLE,
+ PREC_DOUBLE_TO_HALF,
+ PREC_DOUBLE_TO_FLOAT,
+ /* Float to Int conversions */
+ PREC_HALF_TO_INT16,
+ PREC_HALF_TO_INT32,
+ PREC_HALF_TO_INT64,
+ PREC_FLOAT_TO_INT16,
+ PREC_FLOAT_TO_INT32,
+ PREC_FLOAT_TO_INT64,
+ PREC_DOUBLE_TO_INT16,
+ PREC_DOUBLE_TO_INT32,
+ PREC_DOUBLE_TO_INT64,
+ /* Float to unsigned int conversions */
+ PREC_HALF_TO_UINT16,
+ PREC_HALF_TO_UINT32,
+ PREC_HALF_TO_UINT64,
+ PREC_FLOAT_TO_UINT16,
+ PREC_FLOAT_TO_UINT32,
+ PREC_FLOAT_TO_UINT64,
+ PREC_DOUBLE_TO_UINT16,
+ PREC_DOUBLE_TO_UINT32,
+ PREC_DOUBLE_TO_UINT64,
+ /* Int to float conversions */
+ PREC_INT16_TO_HALF,
+ PREC_INT16_TO_FLOAT,
+ PREC_INT16_TO_DOUBLE,
+ PREC_INT32_TO_HALF,
+ PREC_INT32_TO_FLOAT,
+ PREC_INT32_TO_DOUBLE,
+ PREC_INT64_TO_HALF,
+ PREC_INT64_TO_FLOAT,
+ PREC_INT64_TO_DOUBLE,
+ /* Unsigned int to float conversions */
+ PREC_UINT16_TO_HALF,
+ PREC_UINT16_TO_FLOAT,
+ PREC_UINT16_TO_DOUBLE,
+ PREC_UINT32_TO_HALF,
+ PREC_UINT32_TO_FLOAT,
+ PREC_UINT32_TO_DOUBLE,
+ PREC_UINT64_TO_HALF,
+ PREC_UINT64_TO_FLOAT,
+ PREC_UINT64_TO_DOUBLE,
};
+static enum precision get_input_prec(enum precision prec)
+{
+ /* Map conversions to input precision */
+ if (prec >= PREC_HALF_TO_FLOAT) {
+ switch (prec) {
+ case PREC_HALF_TO_FLOAT:
+ case PREC_HALF_TO_DOUBLE:
+ case PREC_HALF_TO_INT16:
+ case PREC_HALF_TO_INT32:
+ case PREC_HALF_TO_INT64:
+ case PREC_HALF_TO_UINT16:
+ case PREC_HALF_TO_UINT32:
+ case PREC_HALF_TO_UINT64:
+ return PREC_HALF;
+ case PREC_FLOAT_TO_HALF:
+ case PREC_FLOAT_TO_DOUBLE:
+ case PREC_FLOAT_TO_INT16:
+ case PREC_FLOAT_TO_INT32:
+ case PREC_FLOAT_TO_INT64:
+ case PREC_FLOAT_TO_UINT16:
+ case PREC_FLOAT_TO_UINT32:
+ case PREC_FLOAT_TO_UINT64:
+ return PREC_FLOAT;
+ case PREC_DOUBLE_TO_HALF:
+ case PREC_DOUBLE_TO_FLOAT:
+ case PREC_DOUBLE_TO_INT16:
+ case PREC_DOUBLE_TO_INT32:
+ case PREC_DOUBLE_TO_INT64:
+ case PREC_DOUBLE_TO_UINT16:
+ case PREC_DOUBLE_TO_UINT32:
+ case PREC_DOUBLE_TO_UINT64:
+ return PREC_DOUBLE;
+ default:
+ assert(false);
+ }
+ }
+
+ return prec;
+}
+
+static enum precision get_output_prec(enum precision prec)
+{
+ /* Map conversions to input precision */
+ if (prec >= PREC_HALF_TO_FLOAT) {
+ switch (prec) {
+ case PREC_FLOAT_TO_HALF:
+ case PREC_DOUBLE_TO_HALF:
+ case PREC_INT16_TO_HALF:
+ case PREC_INT32_TO_HALF:
+ case PREC_INT64_TO_HALF:
+ case PREC_UINT16_TO_HALF:
+ case PREC_UINT32_TO_HALF:
+ case PREC_UINT64_TO_HALF:
+ return PREC_HALF;
+ case PREC_HALF_TO_FLOAT:
+ case PREC_DOUBLE_TO_FLOAT:
+ return PREC_FLOAT;
+ case PREC_HALF_TO_DOUBLE:
+ case PREC_FLOAT_TO_DOUBLE:
+ return PREC_DOUBLE;
+ case PREC_HALF_TO_INT16:
+ case PREC_FLOAT_TO_INT16:
+ case PREC_DOUBLE_TO_INT16:
+ return PREC_INT16;
+ case PREC_HALF_TO_INT32:
+ case PREC_FLOAT_TO_INT32:
+ case PREC_DOUBLE_TO_INT32:
+ return PREC_INT32;
+ case PREC_HALF_TO_INT64:
+ case PREC_FLOAT_TO_INT64:
+ case PREC_DOUBLE_TO_INT64:
+ return PREC_INT64;
+ case PREC_HALF_TO_UINT16:
+ case PREC_FLOAT_TO_UINT16:
+ case PREC_DOUBLE_TO_UINT16:
+ return PREC_UINT16;
+ case PREC_HALF_TO_UINT32:
+ case PREC_FLOAT_TO_UINT32:
+ case PREC_DOUBLE_TO_UINT32:
+ return PREC_UINT32;
+ case PREC_HALF_TO_UINT64:
+ case PREC_FLOAT_TO_UINT64:
+ case PREC_DOUBLE_TO_UINT64:
+ return PREC_UINT64;
+ default:
+ assert(false);
+ }
+ }
+
+ return prec;
+}
+
+typedef struct {
+ char *opstr;
+ enum precision prec;
+} map_to_prec;
+
struct op_desc {
const char * const name;
int n_operands;
+ map_to_prec *decode_tbl;
};
enum op {
@@ -62,9 +214,46 @@ enum op {
OP_ABS,
OP_IS_NAN,
OP_IS_INF,
- OP_FLOAT_TO_DOUBLE,
+ /* All above are conversions */
+ OP_FLOAT_TO_FLOAT,
+ OP_FLOAT_TO_INT,
+ OP_FLOAT_TO_UINT,
+ OP_INT_TO_FLOAT,
+ OP_UINT_TO_FLOAT
};
+map_to_prec float_to_float[] = { {"b16b32", PREC_HALF_TO_FLOAT},
+ {"b16b64", PREC_HALF_TO_DOUBLE},
+ {"b32b16", PREC_FLOAT_TO_HALF},
+ {"b32b64", PREC_FLOAT_TO_DOUBLE},
+ {"b64b16", PREC_DOUBLE_TO_HALF},
+ {"b64b32", PREC_DOUBLE_TO_FLOAT},
+ { NULL, 0} };
+
+map_to_prec float_to_int[] = { {"b16b16", PREC_HALF_TO_INT16},
+ {"b16b32", PREC_HALF_TO_INT16},
+ {"b16b64", PREC_HALF_TO_INT16},
+ {"b32b16", PREC_FLOAT_TO_INT16},
+ {"b32b32", PREC_FLOAT_TO_INT32},
+ {"b32b64", PREC_FLOAT_TO_INT64},
+ {"b64b16", PREC_DOUBLE_TO_INT16},
+ {"b64b32", PREC_DOUBLE_TO_INT32},
+ {"b64b64", PREC_DOUBLE_TO_INT64},
+ { NULL, 0} };
+
+static enum precision decode_map_table(map_to_prec *tbl, char *opstr)
+{
+ while (tbl->opstr) {
+ if (strncmp(tbl->opstr, opstr, strlen(tbl->opstr)) == 0) {
+ return tbl->prec;
+ }
+ tbl++;
+ }
+
+ /* lookup failed */
+ assert(false);
+}
+
static const struct op_desc ops[] = {
[OP_ADD] = { "+", 2 },
[OP_SUB] = { "-", 2 },
@@ -78,7 +267,11 @@ static const struct op_desc ops[] = {
[OP_ABS] = { "A", 1 },
[OP_IS_NAN] = { "?N", 1 },
[OP_IS_INF] = { "?i", 1 },
- [OP_FLOAT_TO_DOUBLE] = { "cff", 1 },
+ [OP_FLOAT_TO_FLOAT] = { "cff", 1, float_to_float },
+ [OP_FLOAT_TO_INT] = { "cfi", 1, float_to_int },
+ [OP_FLOAT_TO_UINT] = { "cfu", 1 },
+ [OP_INT_TO_FLOAT] = { "cif", 1 },
+ [OP_UINT_TO_FLOAT] = { "cuf", 1 },
};
/*
@@ -269,6 +462,159 @@ static enum error tester_check(const struct test_op *t, uint64_t res64,
return err;
}
+static float get_float(struct operand op)
+{
+ switch (op.type) {
+ case OP_TYPE_QNAN:
+ return __builtin_nanf("");
+ case OP_TYPE_SNAN:
+ return __builtin_nansf("");
+ default:
+ return u64_to_float(op.val);
+ }
+}
+
+static double get_double(struct operand op)
+{
+ switch (op.type) {
+ case OP_TYPE_QNAN:
+ return __builtin_nanf("");
+ case OP_TYPE_SNAN:
+ return __builtin_nansf("");
+ default:
+ return u64_to_double(op.val);
+ }
+}
+
+static enum error host_tester_cff(struct test_op *t)
+{
+ float in32, res32;
+ double in64, res64;
+ bool result_is_nan;
+ uint8_t flags = 0;
+
+ assert(t->op == OP_FLOAT_TO_FLOAT);
+
+ switch (t->prec) {
+ case PREC_HALF_TO_FLOAT:
+ case PREC_HALF_TO_DOUBLE:
+ return ERROR_NOT_HANDLED;
+ case PREC_FLOAT_TO_HALF:
+ return ERROR_NOT_HANDLED;
+ case PREC_FLOAT_TO_DOUBLE:
+ {
+ in32 = get_float(t->operands[0]);
+ t->expected_result.val = double_to_u64(get_double(t->expected_result));
+ res64 = (double) in32;
+ break;
+ }
+ case PREC_DOUBLE_TO_HALF:
+ return ERROR_NOT_HANDLED;
+ case PREC_DOUBLE_TO_FLOAT:
+ in64 = get_double(t->operands[0]);
+ t->expected_result.val = float_to_u64(get_float(t->expected_result));
+ res32 = (float) in64;
+ break;
+ default:
+ return ERROR_NOT_HANDLED;
+ }
+
+ flags = host_get_exceptions();
+
+ switch (t->prec) {
+ case PREC_HALF_TO_DOUBLE:
+ case PREC_FLOAT_TO_DOUBLE:
+ result_is_nan = isnan(res64);
+ return tester_check(t, res64, result_is_nan, flags);
+ case PREC_HALF_TO_FLOAT:
+ case PREC_DOUBLE_TO_FLOAT:
+ result_is_nan = isnan(res32);
+ return tester_check(t, res32, result_is_nan, flags);
+ default:
+ assert(false);
+ }
+
+ return ERROR_NOT_HANDLED;
+}
+
+static enum error host_tester_cfi(struct test_op *t)
+{
+ uint8_t flags = 0;
+ float in32;
+ double in64;
+ uint64_t res;
+
+ assert(t->op == OP_FLOAT_TO_INT || t->op == OP_FLOAT_TO_UINT);
+
+ switch (get_input_prec(t->prec)) {
+ case PREC_HALF:
+ return ERROR_NOT_HANDLED;
+ case PREC_FLOAT:
+ in32 = get_float(t->operands[0]);
+ break;
+ case PREC_DOUBLE:
+ in64 = get_double(t->operands[0]);
+ break;
+ default:
+ assert(false);
+ }
+
+
+ switch (t->prec) {
+ case PREC_HALF_TO_INT16:
+ case PREC_HALF_TO_INT32:
+ case PREC_HALF_TO_INT64:
+ return ERROR_NOT_HANDLED;
+ case PREC_FLOAT_TO_INT16:
+ {
+ int16_t oi16 = (int16_t) in32;
+ res = (uint64_t) oi16;
+ break;
+ }
+ case PREC_FLOAT_TO_INT32:
+ {
+ int32_t oi32 = (int32_t) in32;
+ res = (uint64_t) oi32;
+ break;
+ }
+ case PREC_FLOAT_TO_INT64:
+ {
+ int64_t oi64 = (int64_t) in32;
+ res = (uint64_t) oi64;
+ break;
+ }
+ case PREC_DOUBLE_TO_INT16:
+ {
+ int16_t oi16 = (int16_t) in64;
+ res = (uint64_t) oi16;
+ break;
+ }
+ case PREC_DOUBLE_TO_INT32:
+ {
+ int32_t oi32 = (int32_t) in64;
+ res = (uint64_t) oi32;
+ break;
+ }
+ case PREC_DOUBLE_TO_INT64:
+ {
+ int64_t oi64 = (int64_t) in64;
+ res = (uint64_t) oi64;
+ break;
+ }
+ default:
+ assert(false);
+ }
+
+ flags = host_get_exceptions();
+
+ return tester_check(t, res, false, flags);
+}
+
+static enum error host_tester_cif(struct test_op *t)
+{
+ return ERROR_NOT_HANDLED;
+}
+
static enum error host_tester(struct test_op *t)
{
uint64_t res64;
@@ -280,6 +626,20 @@ static enum error host_tester(struct test_op *t)
host_set_exceptions(default_exceptions);
}
+ /* Handle conversions first */
+ switch (t->op) {
+ case OP_FLOAT_TO_FLOAT:
+ return host_tester_cff(t);
+ case OP_FLOAT_TO_INT:
+ case OP_FLOAT_TO_UINT:
+ return host_tester_cfi(t);
+ case OP_INT_TO_FLOAT:
+ case OP_UINT_TO_FLOAT:
+ return host_tester_cif(t);
+ default:
+ break;
+ }
+
if (t->prec == PREC_FLOAT) {
float a, b, c;
float *in[] = { &a, &b, &c };
@@ -396,40 +756,68 @@ static enum error host_tester(struct test_op *t)
flags = host_get_exceptions();
res64 = double_to_u64(res);
result_is_nan = isnan(res);
- } else if (t->prec == PREC_FLOAT_TO_DOUBLE) {
- float a;
- double res;
-
- if (t->operands[0].type == OP_TYPE_QNAN) {
- a = __builtin_nanf("");
- } else if (t->operands[0].type == OP_TYPE_SNAN) {
- a = __builtin_nansf("");
- } else {
- a = u64_to_float(t->operands[0].val);
- }
-
- if (t->expected_result.type == OP_TYPE_QNAN) {
- t->expected_result.val = double_to_u64(__builtin_nan(""));
- } else if (t->expected_result.type == OP_TYPE_SNAN) {
- t->expected_result.val = double_to_u64(__builtin_nans(""));
- }
-
- switch (t->op) {
- case OP_FLOAT_TO_DOUBLE:
- res = a;
- break;
- default:
- return ERROR_NOT_HANDLED;
- }
- flags = host_get_exceptions();
- res64 = double_to_u64(res);
- result_is_nan = isnan(res);
} else {
return ERROR_NOT_HANDLED; /* XXX */
}
return tester_check(t, res64, result_is_nan, flags);
}
+static enum error soft_tester_cff(struct test_op *t, float_status *s)
+{
+ float in32, res32;
+ double in64, res64;
+ bool result_is_nan;
+
+ assert(t->op == OP_FLOAT_TO_FLOAT);
+
+ switch (t->prec) {
+ case PREC_HALF_TO_FLOAT:
+ case PREC_HALF_TO_DOUBLE:
+ return ERROR_NOT_HANDLED;
+ case PREC_FLOAT_TO_HALF:
+ return ERROR_NOT_HANDLED;
+ case PREC_FLOAT_TO_DOUBLE:
+ {
+ in32 = get_float(t->operands[0]);
+ t->expected_result.val = double_to_u64(get_double(t->expected_result));
+ res64 = float32_to_float64(in32, s);
+ break;
+ }
+ case PREC_DOUBLE_TO_HALF:
+ return ERROR_NOT_HANDLED;
+ case PREC_DOUBLE_TO_FLOAT:
+ in64 = get_double(t->operands[0]);
+ t->expected_result.val = float_to_u64(get_float(t->expected_result));
+ res32 = float64_to_float32(in64, s);
+ break;
+ default:
+ return ERROR_NOT_HANDLED;
+ }
+
+ switch (t->prec) {
+ case PREC_HALF_TO_DOUBLE:
+ case PREC_FLOAT_TO_DOUBLE:
+ result_is_nan = isnan(res64);
+ return tester_check(t, res64, result_is_nan, s->float_exception_flags);
+ case PREC_HALF_TO_FLOAT:
+ case PREC_DOUBLE_TO_FLOAT:
+ result_is_nan = isnan(res32);
+ return tester_check(t, res32, result_is_nan, s->float_exception_flags);
+ default:
+ assert(false);
+ }
+}
+
+static enum error soft_tester_cfi(struct test_op *t, float_status *s)
+{
+ return ERROR_NOT_HANDLED;
+}
+
+static enum error soft_tester_cif(struct test_op *t, float_status *s)
+{
+ return ERROR_NOT_HANDLED;
+}
+
static enum error soft_tester(struct test_op *t)
{
float_status *s = &soft_status;
@@ -440,6 +828,20 @@ static enum error soft_tester(struct test_op *t)
s->float_rounding_mode = t->round;
s->float_exception_flags = default_exceptions;
+ /* Handle conversions first */
+ switch (t->op) {
+ case OP_FLOAT_TO_FLOAT:
+ return soft_tester_cff(t, s);
+ case OP_FLOAT_TO_INT:
+ case OP_FLOAT_TO_UINT:
+ return soft_tester_cfi(t, s);
+ case OP_INT_TO_FLOAT:
+ case OP_UINT_TO_FLOAT:
+ return soft_tester_cif(t, s);
+ default:
+ break;
+ }
+
if (t->prec == PREC_FLOAT) {
float32 a, b, c;
float32 *in[] = { &a, &b, &c };
@@ -558,17 +960,6 @@ static enum error soft_tester(struct test_op *t)
return ERROR_NOT_HANDLED;
}
result_is_nan = isnan(*(double *)&res64);
- } else if (t->prec == PREC_FLOAT_TO_DOUBLE) {
- float32 a = t->operands[0].val;
-
- switch (t->op) {
- case OP_FLOAT_TO_DOUBLE:
- res64 = float32_to_float64(a, s);
- break;
- default:
- return ERROR_NOT_HANDLED;
- }
- result_is_nan = isnan(*(double *)&res64);
} else {
return ERROR_NOT_HANDLED; /* XXX */
}
@@ -752,23 +1143,41 @@ ibm_fp_hex(const char *p, enum precision prec, struct operand *ret)
return 0;
}
return 0;
- } else if (!strcmp(p, "0x0")) {
- if (prec == PREC_FLOAT) {
- ret->val = float_to_u64(0.0);
- } else if (prec == PREC_DOUBLE) {
- ret->val = double_to_u64(0.0);
- } else {
- assert(false);
+ } else if (strncmp("0x", p, 2) == 0) {
+ unsigned long long result;
+ char *end;
+
+ result = strtoull(p, &end, 16);
+ if (result == 0 && end == p) {
+ /* not a number */
+ return 1;
+ } else if (result == ULLONG_MAX && errno) {
+ /* value does not fit in unsigned long long */
+ return 1;
+ } else if (*end) {
+ /* began with a number but has junk left over at the end */
+ return 1;
}
- return 0;
- } else if (!strcmp(p, "0x1")) {
- if (prec == PREC_FLOAT) {
- ret->val = float_to_u64(1.0);
- } else if (prec == PREC_DOUBLE) {
- ret->val = double_to_u64(1.0);
- } else {
+
+ switch (prec) {
+ case PREC_FLOAT:
+ ret->val = float_to_u64(result);
+ break;
+ case PREC_DOUBLE:
+ ret->val = double_to_u64(result);
+ break;
+ case PREC_INT16:
+ case PREC_INT32:
+ case PREC_INT64:
+ case PREC_UINT16:
+ case PREC_UINT32:
+ case PREC_UINT64:
+ ret->val = result;
+ break;
+ default:
assert(false);
}
+
return 0;
}
return 1;
@@ -814,11 +1223,14 @@ static enum error ibm_test_line(const char *line)
if (unlikely(strlen(p) < 4)) {
return ERROR_INPUT;
}
- if (strcmp("b32b64cff", p) == 0) {
- t.prec = PREC_FLOAT_TO_DOUBLE;
+ /* Conversions are of the form bXXbYYcZZ */
+ if (p[0] == 'b' && p[3] == 'b' && p[6] == 'c') {
if (find_op(&p[6], &t.op)) {
+ fprintf(stderr, "%s: unhandled conversion %s\n", __func__, p);
return ERROR_NOT_HANDLED;
}
+ assert(ops[t.op].decode_tbl);
+ t.prec = decode_map_table(ops[t.op].decode_tbl, p);
} else {
if (strncmp("b32", p, 3) == 0) {
t.prec = PREC_FLOAT;
@@ -855,9 +1267,7 @@ static enum error ibm_test_line(const char *line)
}
for (i = 0; i < ops[t.op].n_operands; i++) {
- enum precision prec = t.prec == PREC_FLOAT_TO_DOUBLE ?
- PREC_FLOAT : t.prec;
-
+ enum precision prec = get_input_prec(t.prec);
p = s[field++];
if (ibm_fp_hex(p, prec, &t.operands[i])) {
return ERROR_INPUT;
@@ -873,8 +1283,7 @@ static enum error ibm_test_line(const char *line)
if (unlikely(strcmp("#", p) == 0)) {
t.expected_result_is_valid = false;
} else {
- enum precision prec = t.prec == PREC_FLOAT_TO_DOUBLE ?
- PREC_DOUBLE : t.prec;
+ enum precision prec = get_output_prec(t.prec);
if (ibm_fp_hex(p, prec, &t.expected_result)) {
return ERROR_INPUT;
@@ -943,7 +1352,7 @@ static void test_file(const char *filename)
enum error err;
i++;
- if (unlikely(line_is_whitelisted(line))) {
+ if (whitelist.n > 0 && unlikely(line_is_whitelisted(line))) {
test_stats[ERROR_WHITELISTED]++;
continue;
}
new file mode 100644
@@ -0,0 +1,7 @@
+QEMU Floating point regression tests
+------------------------------------
+
+b32b32cfi =0 -Inf -> 0xffffffff80000000 i
+b32b32cfi =0 +Inf -> 0xffffffff80000000 i
+b32b64cfi =0 -Inf -> 0x8000000000000000 i
+b32b64cfi =0 +Inf -> 0x8000000000000000 i
This adds support for the various FOO-to-BAR conversions to fp-test. It also defines PREC_HALF although currently that is not used and will need compile time detection for _Float16 support. I've added a small test file for testing against regressions. Signed-off-by: Alex Bennée <alex.bennee@linaro.org> --- tests/fp/fp-test.c | 535 +++++++++++++++++++++++--- tests/fp/qemu/regression-tests.fptest | 7 + 2 files changed, 479 insertions(+), 63 deletions(-) create mode 100644 tests/fp/qemu/regression-tests.fptest -- 2.17.0