tests/tcg/arm: add fcvt test cases for AArch32/64
This runs through the usual float to float conversions and crucially also runs with ARM Alternative Half Precision Format. Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Tested-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
This commit is contained in:
parent
29e0436e3d
commit
8ec8a55e3f
19
tests/tcg/aarch64/Makefile.target
Normal file
19
tests/tcg/aarch64/Makefile.target
Normal file
@ -0,0 +1,19 @@
|
||||
# -*- Mode: makefile -*-
|
||||
#
|
||||
# AArch64 specific tweaks
|
||||
|
||||
AARCH64_SRC=$(SRC_PATH)/tests/tcg/aarch64
|
||||
VPATH += $(AARCH64_SRC)
|
||||
|
||||
# we don't build any of the ARM tests
|
||||
AARCH64_TESTS=$(filter-out $(ARM_TESTS), $(TESTS))
|
||||
AARCH64_TESTS+=fcvt
|
||||
TESTS:=$(AARCH64_TESTS)
|
||||
|
||||
fcvt: LDFLAGS+=-lm
|
||||
|
||||
run-fcvt: fcvt
|
||||
$(call quiet-command, \
|
||||
$(QEMU) $< > fcvt.out && \
|
||||
diff -u $(AARCH64_SRC)/fcvt.ref fcvt.out, \
|
||||
"TEST", "$< (default) on $(TARGET_NAME)")
|
3268
tests/tcg/aarch64/fcvt.ref
Normal file
3268
tests/tcg/aarch64/fcvt.ref
Normal file
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,9 @@ ARM_SRC=$(SRC_PATH)/tests/tcg/arm
|
||||
# Set search path for all sources
|
||||
VPATH += $(ARM_SRC)
|
||||
|
||||
TESTS += hello-arm test-arm-iwmmxt
|
||||
ARM_TESTS=hello-arm test-arm-iwmmxt
|
||||
|
||||
TESTS += $(ARM_TESTS) fcvt
|
||||
|
||||
hello-arm: CFLAGS+=-marm -ffreestanding
|
||||
hello-arm: LDFLAGS+=-nostdlib
|
||||
@ -17,5 +19,16 @@ test-arm-iwmmxt: CFLAGS+=-marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16
|
||||
test-arm-iwmmxt: test-arm-iwmmxt.S
|
||||
$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
|
||||
|
||||
ifeq ($(TARGET_NAME), arm)
|
||||
fcvt: LDFLAGS+=-lm
|
||||
# fcvt: CFLAGS+=-march=armv8.2-a+fp16 -mfpu=neon-fp-armv8
|
||||
|
||||
run-fcvt: fcvt
|
||||
$(call quiet-command, \
|
||||
$(QEMU) $< > fcvt.out && \
|
||||
diff -u $(ARM_SRC)/fcvt.ref fcvt.out, \
|
||||
"TEST", "$< (default) on $(TARGET_NAME)")
|
||||
endif
|
||||
|
||||
# On ARM Linux only supports 4k pages
|
||||
EXTRA_RUNS+=run-test-mmap-4096
|
||||
|
458
tests/tcg/arm/fcvt.c
Normal file
458
tests/tcg/arm/fcvt.c
Normal file
@ -0,0 +1,458 @@
|
||||
/*
|
||||
* Test Floating Point Conversion
|
||||
*/
|
||||
|
||||
/* we want additional float type definitions */
|
||||
#define __STDC_WANT_IEC_60559_BFP_EXT__
|
||||
#define __STDC_WANT_IEC_60559_TYPES_EXT__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <fenv.h>
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
static char flag_str[256];
|
||||
|
||||
static char *get_flag_state(int flags)
|
||||
{
|
||||
if (flags) {
|
||||
snprintf(flag_str, sizeof(flag_str), "%s %s %s %s %s",
|
||||
flags & FE_OVERFLOW ? "OVERFLOW" : "",
|
||||
flags & FE_UNDERFLOW ? "UNDERFLOW" : "",
|
||||
flags & FE_DIVBYZERO ? "DIV0" : "",
|
||||
flags & FE_INEXACT ? "INEXACT" : "",
|
||||
flags & FE_INVALID ? "INVALID" : "");
|
||||
} else {
|
||||
snprintf(flag_str, sizeof(flag_str), "OK");
|
||||
}
|
||||
|
||||
return flag_str;
|
||||
}
|
||||
|
||||
static void print_double_number(int i, double num)
|
||||
{
|
||||
uint64_t double_as_hex = *(uint64_t *) #
|
||||
int flags = fetestexcept(FE_ALL_EXCEPT);
|
||||
char *fstr = get_flag_state(flags);
|
||||
|
||||
printf("%02d DOUBLE: %02.20e / %#020" PRIx64 " (%#x => %s)\n",
|
||||
i, num, double_as_hex, flags, fstr);
|
||||
}
|
||||
|
||||
static void print_single_number(int i, float num)
|
||||
{
|
||||
uint32_t single_as_hex = *(uint32_t *) #
|
||||
int flags = fetestexcept(FE_ALL_EXCEPT);
|
||||
char *fstr = get_flag_state(flags);
|
||||
|
||||
printf("%02d SINGLE: %02.20e / %#010x (%#x => %s)\n",
|
||||
i, num, single_as_hex, flags, fstr);
|
||||
}
|
||||
|
||||
static void print_half_number(int i, uint16_t num)
|
||||
{
|
||||
int flags = fetestexcept(FE_ALL_EXCEPT);
|
||||
char *fstr = get_flag_state(flags);
|
||||
|
||||
printf("%02d HALF: %#04x (%#x => %s)\n",
|
||||
i, num, flags, fstr);
|
||||
}
|
||||
|
||||
static void print_int64(int i, int64_t num)
|
||||
{
|
||||
uint64_t int64_as_hex = *(uint64_t *) #
|
||||
int flags = fetestexcept(FE_ALL_EXCEPT);
|
||||
char *fstr = get_flag_state(flags);
|
||||
|
||||
printf("%02d INT64: %20" PRId64 "/%#020" PRIx64 " (%#x => %s)\n",
|
||||
i, num, int64_as_hex, flags, fstr);
|
||||
}
|
||||
|
||||
#ifndef SNANF
|
||||
/* Signaling NaN macros, if supported. */
|
||||
# if __GNUC_PREREQ(3, 3)
|
||||
# define SNANF (__builtin_nansf (""))
|
||||
# define SNAN (__builtin_nans (""))
|
||||
# define SNANL (__builtin_nansl (""))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
float single_numbers[] = { -SNANF,
|
||||
-NAN,
|
||||
-INFINITY,
|
||||
-FLT_MAX,
|
||||
-1.111E+31,
|
||||
-1.111E+30,
|
||||
-1.08700982e-12,
|
||||
-1.78051176e-20,
|
||||
-FLT_MIN,
|
||||
0.0,
|
||||
FLT_MIN,
|
||||
2.98023224e-08,
|
||||
5.96046E-8, /* min positive FP16 subnormal */
|
||||
6.09756E-5, /* max subnormal FP16 */
|
||||
6.10352E-5, /* min positive normal FP16 */
|
||||
1.0,
|
||||
1.0009765625, /* smallest float after 1.0 FP16 */
|
||||
2.0,
|
||||
M_E, M_PI,
|
||||
65503.0,
|
||||
65504.0, /* max FP16 */
|
||||
65505.0,
|
||||
131007.0,
|
||||
131008.0, /* max AFP */
|
||||
131009.0,
|
||||
1.111E+30,
|
||||
FLT_MAX,
|
||||
INFINITY,
|
||||
NAN,
|
||||
SNANF };
|
||||
|
||||
static void convert_single_to_half(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Converting single-precision to half-precision\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(single_numbers); ++i) {
|
||||
float input = single_numbers[i];
|
||||
|
||||
feclearexcept(FE_ALL_EXCEPT);
|
||||
|
||||
print_single_number(i, input);
|
||||
#if defined(__arm__)
|
||||
uint32_t output;
|
||||
asm("vcvtb.f16.f32 %0, %1" : "=t" (output) : "x" (input));
|
||||
#else
|
||||
uint16_t output;
|
||||
asm("fcvt %h0, %s1" : "=w" (output) : "x" (input));
|
||||
#endif
|
||||
print_half_number(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_single_to_double(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Converting single-precision to double-precision\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(single_numbers); ++i) {
|
||||
float input = single_numbers[i];
|
||||
/* uint64_t output; */
|
||||
double output;
|
||||
|
||||
feclearexcept(FE_ALL_EXCEPT);
|
||||
|
||||
print_single_number(i, input);
|
||||
#if defined(__arm__)
|
||||
asm("vcvt.f64.f32 %P0, %1" : "=w" (output) : "t" (input));
|
||||
#else
|
||||
asm("fcvt %d0, %s1" : "=w" (output) : "x" (input));
|
||||
#endif
|
||||
print_double_number(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_single_to_integer(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Converting single-precision to integer\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(single_numbers); ++i) {
|
||||
float input = single_numbers[i];
|
||||
int64_t output;
|
||||
|
||||
feclearexcept(FE_ALL_EXCEPT);
|
||||
|
||||
print_single_number(i, input);
|
||||
#if defined(__arm__)
|
||||
/* asm("vcvt.s32.f32 %s0, %s1" : "=t" (output) : "t" (input)); */
|
||||
output = input;
|
||||
#else
|
||||
asm("fcvtzs %0, %s1" : "=r" (output) : "w" (input));
|
||||
#endif
|
||||
print_int64(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
/* This allows us to initialise some doubles as pure hex */
|
||||
typedef union {
|
||||
double d;
|
||||
uint64_t h;
|
||||
} test_doubles;
|
||||
|
||||
test_doubles double_numbers[] = {
|
||||
{SNAN},
|
||||
{-NAN},
|
||||
{-INFINITY},
|
||||
{-DBL_MAX},
|
||||
{-FLT_MAX-1.0},
|
||||
{-FLT_MAX},
|
||||
{-1.111E+31},
|
||||
{-1.111E+30}, /* half prec */
|
||||
{-2.0}, {-1.0},
|
||||
{-DBL_MIN},
|
||||
{-FLT_MIN},
|
||||
{0.0},
|
||||
{FLT_MIN},
|
||||
{2.98023224e-08},
|
||||
{5.96046E-8}, /* min positive FP16 subnormal */
|
||||
{6.09756E-5}, /* max subnormal FP16 */
|
||||
{6.10352E-5}, /* min positive normal FP16 */
|
||||
{1.0},
|
||||
{1.0009765625}, /* smallest float after 1.0 FP16 */
|
||||
{DBL_MIN},
|
||||
{1.3789972848607228e-308},
|
||||
{1.4914738736681624e-308},
|
||||
{1.0}, {2.0},
|
||||
{M_E}, {M_PI},
|
||||
{65503.0},
|
||||
{65504.0}, /* max FP16 */
|
||||
{65505.0},
|
||||
{131007.0},
|
||||
{131008.0}, /* max AFP */
|
||||
{131009.0},
|
||||
{.h = 0x41dfffffffc00000 }, /* to int = 0x7fffffff */
|
||||
{FLT_MAX},
|
||||
{FLT_MAX + 1.0},
|
||||
{DBL_MAX},
|
||||
{INFINITY},
|
||||
{NAN},
|
||||
{.h = 0x7ff0000000000001}, /* SNAN */
|
||||
{SNAN},
|
||||
};
|
||||
|
||||
static void convert_double_to_half(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Converting double-precision to half-precision\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) {
|
||||
double input = double_numbers[i].d;
|
||||
uint16_t output;
|
||||
|
||||
feclearexcept(FE_ALL_EXCEPT);
|
||||
|
||||
print_double_number(i, input);
|
||||
|
||||
/* as we don't have _Float16 support */
|
||||
#if defined(__arm__)
|
||||
/* asm("vcvtb.f16.f64 %0, %P1" : "=t" (output) : "x" (input)); */
|
||||
output = input;
|
||||
#else
|
||||
asm("fcvt %h0, %d1" : "=w" (output) : "x" (input));
|
||||
#endif
|
||||
print_half_number(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_double_to_single(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Converting double-precision to single-precision\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) {
|
||||
double input = double_numbers[i].d;
|
||||
uint32_t output;
|
||||
|
||||
feclearexcept(FE_ALL_EXCEPT);
|
||||
|
||||
print_double_number(i, input);
|
||||
|
||||
#if defined(__arm__)
|
||||
asm("vcvt.f32.f64 %0, %P1" : "=w" (output) : "x" (input));
|
||||
#else
|
||||
asm("fcvt %s0, %d1" : "=w" (output) : "x" (input));
|
||||
#endif
|
||||
|
||||
print_single_number(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_double_to_integer(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Converting double-precision to integer\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) {
|
||||
double input = double_numbers[i].d;
|
||||
int64_t output;
|
||||
|
||||
feclearexcept(FE_ALL_EXCEPT);
|
||||
|
||||
print_double_number(i, input);
|
||||
#if defined(__arm__)
|
||||
/* asm("vcvt.s32.f32 %s0, %s1" : "=t" (output) : "t" (input)); */
|
||||
output = input;
|
||||
#else
|
||||
asm("fcvtzs %0, %d1" : "=r" (output) : "w" (input));
|
||||
#endif
|
||||
print_int64(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
/* no handy defines for these numbers */
|
||||
uint16_t half_numbers[] = {
|
||||
0xffff, /* -NaN / AHP -Max */
|
||||
0xfcff, /* -NaN / AHP */
|
||||
0xfc01, /* -NaN / AHP */
|
||||
0xfc00, /* -Inf */
|
||||
0xfbff, /* -Max */
|
||||
0xc000, /* -2 */
|
||||
0xbc00, /* -1 */
|
||||
0x8001, /* -MIN subnormal */
|
||||
0x8000, /* -0 */
|
||||
0x0000, /* +0 */
|
||||
0x0001, /* MIN subnormal */
|
||||
0x3c00, /* 1 */
|
||||
0x7bff, /* Max */
|
||||
0x7c00, /* Inf */
|
||||
0x7c01, /* NaN / AHP */
|
||||
0x7cff, /* NaN / AHP */
|
||||
0x7fff, /* NaN / AHP +Max*/
|
||||
};
|
||||
|
||||
static void convert_half_to_double(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Converting half-precision to double-precision\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(half_numbers); ++i) {
|
||||
uint16_t input = half_numbers[i];
|
||||
double output;
|
||||
|
||||
feclearexcept(FE_ALL_EXCEPT);
|
||||
|
||||
print_half_number(i, input);
|
||||
#if defined(__arm__)
|
||||
/* asm("vcvtb.f64.f16 %P0, %1" : "=w" (output) : "t" (input)); */
|
||||
output = input;
|
||||
#else
|
||||
asm("fcvt %d0, %h1" : "=w" (output) : "x" (input));
|
||||
#endif
|
||||
print_double_number(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_half_to_single(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Converting half-precision to single-precision\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(half_numbers); ++i) {
|
||||
uint16_t input = half_numbers[i];
|
||||
float output;
|
||||
|
||||
feclearexcept(FE_ALL_EXCEPT);
|
||||
|
||||
print_half_number(i, input);
|
||||
#if defined(__arm__)
|
||||
asm("vcvtb.f32.f16 %0, %1" : "=w" (output) : "x" ((uint32_t)input));
|
||||
#else
|
||||
asm("fcvt %s0, %h1" : "=w" (output) : "x" (input));
|
||||
#endif
|
||||
print_single_number(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_half_to_integer(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Converting half-precision to integer\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(half_numbers); ++i) {
|
||||
uint16_t input = half_numbers[i];
|
||||
int64_t output;
|
||||
|
||||
feclearexcept(FE_ALL_EXCEPT);
|
||||
|
||||
print_half_number(i, input);
|
||||
#if defined(__arm__)
|
||||
/* asm("vcvt.s32.f16 %0, %1" : "=t" (output) : "t" (input)); v8.2*/
|
||||
output = input;
|
||||
#else
|
||||
asm("fcvt %s0, %h1" : "=w" (output) : "x" (input));
|
||||
#endif
|
||||
print_int64(i, output);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int flag;
|
||||
char *desc;
|
||||
} float_mapping;
|
||||
|
||||
float_mapping round_flags[] = {
|
||||
{ FE_TONEAREST, "to nearest" },
|
||||
{ FE_UPWARD, "upwards" },
|
||||
{ FE_DOWNWARD, "downwards" },
|
||||
{ FE_TOWARDZERO, "to zero" }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[argc])
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("#### Enabling IEEE Half Precision\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(round_flags); ++i) {
|
||||
fesetround(round_flags[i].flag);
|
||||
printf("### Rounding %s\n", round_flags[i].desc);
|
||||
convert_single_to_half();
|
||||
convert_single_to_double();
|
||||
convert_double_to_half();
|
||||
convert_double_to_single();
|
||||
convert_half_to_single();
|
||||
convert_half_to_double();
|
||||
}
|
||||
|
||||
/* convert to integer */
|
||||
convert_single_to_integer();
|
||||
convert_double_to_integer();
|
||||
convert_half_to_integer();
|
||||
|
||||
/* And now with ARM alternative FP16 */
|
||||
#if defined(__arm__)
|
||||
/* See glibc sysdeps/arm/fpu_control.h */
|
||||
asm("mrc p10, 7, r1, cr1, cr0, 0\n\t"
|
||||
"orr r1, r1, %[flags]\n\t"
|
||||
"mcr p10, 7, r1, cr1, cr0, 0\n\t"
|
||||
: /* no output */ : [flags] "n" (1 << 26) : "r1" );
|
||||
#else
|
||||
asm("mrs x1, fpcr\n\t"
|
||||
"orr x1, x1, %[flags]\n\t"
|
||||
"msr fpcr, x1\n\t"
|
||||
: /* no output */ : [flags] "n" (1 << 26) : "x1" );
|
||||
#endif
|
||||
|
||||
printf("#### Enabling ARM Alternative Half Precision\n");
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(round_flags); ++i) {
|
||||
fesetround(round_flags[i].flag);
|
||||
printf("### Rounding %s\n", round_flags[i].desc);
|
||||
convert_single_to_half();
|
||||
convert_single_to_double();
|
||||
convert_double_to_half();
|
||||
convert_double_to_single();
|
||||
convert_half_to_single();
|
||||
convert_half_to_double();
|
||||
}
|
||||
|
||||
/* convert to integer */
|
||||
convert_single_to_integer();
|
||||
convert_double_to_integer();
|
||||
convert_half_to_integer();
|
||||
|
||||
return 0;
|
||||
}
|
3268
tests/tcg/arm/fcvt.ref
Normal file
3268
tests/tcg/arm/fcvt.ref
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user