[v3,13/19] RISC-V: Add the RV32 libm-test-ulps

Message ID 6f6f811cb3453c1de41c8cb542aace23162f90a0.1594568655.git.alistair.francis@wdc.com
State Committed
Headers
Series glibc port for 32-bit RISC-V (RV32) |

Commit Message

Alistair Francis July 12, 2020, 3:47 p.m. UTC
  Add a libm-test-ulps for RV32, this is the same as the RV64 one.

This dosn't match what is generated by running `make regen-ulps` on RV32
QEMU, but the current in tree RV64 doesn't match that either.
---
 sysdeps/riscv/rv32/rvd/libm-test-ulps      | 1402 ++++++++++++++++++++
 sysdeps/riscv/rv32/rvd/libm-test-ulps-name |    1 +
 2 files changed, 1403 insertions(+)
 create mode 100644 sysdeps/riscv/rv32/rvd/libm-test-ulps
 create mode 100644 sysdeps/riscv/rv32/rvd/libm-test-ulps-name
  

Comments

Maciej W. Rozycki July 13, 2020, 5:14 p.m. UTC | #1
On Sun, 12 Jul 2020, Alistair Francis via Libc-alpha wrote:

> Add a libm-test-ulps for RV32, this is the same as the RV64 one.
> 
> This dosn't match what is generated by running `make regen-ulps` on RV32
> QEMU, but the current in tree RV64 doesn't match that either.

 For the record RV32 and RV64 QEMU ULPS results (obtained in the Linux 
user emulation mode) match each other in my setup.  However those don't 
match RV64 results obtained with my HiFive Unleashed hardware, and then 
those don't match our existing RV64 results, so I guess they will have to 
be regenerated for the upcoming release.

 I'll leave this change up to Joseph to ack, as he's the expert in this 
area.

  Maciej
  
Alistair Francis July 13, 2020, 5:32 p.m. UTC | #2
On Mon, Jul 13, 2020 at 10:23 AM Maciej W. Rozycki via Libc-alpha
<libc-alpha@sourceware.org> wrote:
>
> On Sun, 12 Jul 2020, Alistair Francis via Libc-alpha wrote:
>
> > Add a libm-test-ulps for RV32, this is the same as the RV64 one.
> >
> > This dosn't match what is generated by running `make regen-ulps` on RV32
> > QEMU, but the current in tree RV64 doesn't match that either.
>
>  For the record RV32 and RV64 QEMU ULPS results (obtained in the Linux
> user emulation mode) match each other in my setup.  However those don't
> match RV64 results obtained with my HiFive Unleashed hardware, and then
> those don't match our existing RV64 results, so I guess they will have to
> be regenerated for the upcoming release.

Yep, so the current RV64 in tree doesn't match what I generate in QEMU
softmmu (full system) emulation for RV64.

I kind of assumed that what is in tree matches the Unleased.

>
>  I'll leave this change up to Joseph to ack, as he's the expert in this
> area.

Let me know if you want it re-generated. I was assuming that what is
in tree works as it has been in a few releases now.

Alistair

>
>   Maciej
  
Maciej W. Rozycki July 13, 2020, 7:19 p.m. UTC | #3
On Mon, 13 Jul 2020, Alistair Francis wrote:

> >  For the record RV32 and RV64 QEMU ULPS results (obtained in the Linux
> > user emulation mode) match each other in my setup.  However those don't
> > match RV64 results obtained with my HiFive Unleashed hardware, and then
> > those don't match our existing RV64 results, so I guess they will have to
> > be regenerated for the upcoming release.
> 
> Yep, so the current RV64 in tree doesn't match what I generate in QEMU
> softmmu (full system) emulation for RV64.
> 
> I kind of assumed that what is in tree matches the Unleased.

 For the record here's my RV64 diff to the Unleashed.

  Maciej

Automatic regeneration of ULPs complete.
Difference between the current baseline and the new baseline is:
--- ../sysdeps/riscv/rv64/rvd/libm-test-ulps	2020-06-24 19:09:06.149618620 +0100
+++ .../obj/glibc/lp64d/math/NewUlps	2020-07-13 17:38:39.366490448 +0100
@@ -61,7 +61,7 @@
 
 Function: "asinh":
 double: 1
-float: 1
+float: 2
 ldouble: 3
 
 Function: "asinh_downward":
@@ -633,6 +633,7 @@
 
 Function: "cos":
 double: 1
+float: 1
 ldouble: 1
 
 Function: "cos_downward":
@@ -652,7 +653,7 @@
 
 Function: "cosh":
 double: 1
-float: 1
+float: 2
 ldouble: 1
 
 Function: "cosh_downward":
@@ -947,6 +948,7 @@
 ldouble: 5
 
 Function: "exp":
+float: 1
 ldouble: 1
 
 Function: "exp10":
@@ -1055,7 +1057,7 @@
 
 Function: "j0":
 double: 2
-float: 2
+float: 8
 ldouble: 2
 
 Function: "j0_downward":
@@ -1064,23 +1066,23 @@
 ldouble: 4
 
 Function: "j0_towardzero":
-double: 2
-float: 1
+double: 5
+float: 6
 ldouble: 2
 
 Function: "j0_upward":
-double: 3
-float: 2
+double: 4
+float: 5
 ldouble: 5
 
 Function: "j1":
-double: 1
-float: 2
+double: 2
+float: 8
 ldouble: 4
 
 Function: "j1_downward":
 double: 3
-float: 2
+float: 5
 ldouble: 4
 
 Function: "j1_towardzero":
@@ -1224,6 +1226,7 @@
 
 Function: "sin":
 double: 1
+float: 1
 ldouble: 1
 
 Function: "sin_downward":
@@ -1321,27 +1324,27 @@
 
 Function: "tgamma":
 double: 5
-float: 4
+float: 8
 ldouble: 4
 
 Function: "tgamma_downward":
 double: 5
-float: 5
+float: 7
 ldouble: 5
 
 Function: "tgamma_towardzero":
 double: 5
-float: 4
+float: 7
 ldouble: 5
 
 Function: "tgamma_upward":
 double: 4
-float: 4
+float: 8
 ldouble: 4
 
 Function: "y0":
 double: 2
-float: 1
+float: 6
 ldouble: 3
 
 Function: "y0_downward":
Copy .../obj/glibc/lp64d/math/NewUlps to ../sysdeps/riscv/rv64/rvd/libm-test-ulps (relative to source).

  Maciej
  
Carlos O'Donell July 13, 2020, 7:38 p.m. UTC | #4
On 7/13/20 3:19 PM, Maciej W. Rozycki via Libc-alpha wrote:
> On Mon, 13 Jul 2020, Alistair Francis wrote:
> 
>>>  For the record RV32 and RV64 QEMU ULPS results (obtained in the Linux
>>> user emulation mode) match each other in my setup.  However those don't
>>> match RV64 results obtained with my HiFive Unleashed hardware, and then
>>> those don't match our existing RV64 results, so I guess they will have to
>>> be regenerated for the upcoming release.
>>
>> Yep, so the current RV64 in tree doesn't match what I generate in QEMU
>> softmmu (full system) emulation for RV64.
>>
>> I kind of assumed that what is in tree matches the Unleased.
> 
>  For the record here's my RV64 diff to the Unleashed.
> 
>   Maciej

That's close enough that you should commit this blind to the
development branch that way developers don't get build failures.

We do this routinely for i686 and older hardware being used Fedora Koji
builders.

 
> Automatic regeneration of ULPs complete.
> Difference between the current baseline and the new baseline is:
> --- ../sysdeps/riscv/rv64/rvd/libm-test-ulps	2020-06-24 19:09:06.149618620 +0100
> +++ .../obj/glibc/lp64d/math/NewUlps	2020-07-13 17:38:39.366490448 +0100
> @@ -61,7 +61,7 @@
>  
>  Function: "asinh":
>  double: 1
> -float: 1
> +float: 2
>  ldouble: 3
>  
>  Function: "asinh_downward":
> @@ -633,6 +633,7 @@
>  
>  Function: "cos":
>  double: 1
> +float: 1
>  ldouble: 1
>  
>  Function: "cos_downward":
> @@ -652,7 +653,7 @@
>  
>  Function: "cosh":
>  double: 1
> -float: 1
> +float: 2
>  ldouble: 1
>  
>  Function: "cosh_downward":
> @@ -947,6 +948,7 @@
>  ldouble: 5
>  
>  Function: "exp":
> +float: 1
>  ldouble: 1
>  
>  Function: "exp10":
> @@ -1055,7 +1057,7 @@
>  
>  Function: "j0":
>  double: 2
> -float: 2
> +float: 8
>  ldouble: 2
>  
>  Function: "j0_downward":
> @@ -1064,23 +1066,23 @@
>  ldouble: 4
>  
>  Function: "j0_towardzero":
> -double: 2
> -float: 1
> +double: 5
> +float: 6
>  ldouble: 2
>  
>  Function: "j0_upward":
> -double: 3
> -float: 2
> +double: 4
> +float: 5
>  ldouble: 5
>  
>  Function: "j1":
> -double: 1
> -float: 2
> +double: 2
> +float: 8
>  ldouble: 4
>  
>  Function: "j1_downward":
>  double: 3
> -float: 2
> +float: 5
>  ldouble: 4
>  
>  Function: "j1_towardzero":
> @@ -1224,6 +1226,7 @@
>  
>  Function: "sin":
>  double: 1
> +float: 1
>  ldouble: 1
>  
>  Function: "sin_downward":
> @@ -1321,27 +1324,27 @@
>  
>  Function: "tgamma":
>  double: 5
> -float: 4
> +float: 8
>  ldouble: 4
>  
>  Function: "tgamma_downward":
>  double: 5
> -float: 5
> +float: 7
>  ldouble: 5
>  
>  Function: "tgamma_towardzero":
>  double: 5
> -float: 4
> +float: 7
>  ldouble: 5
>  
>  Function: "tgamma_upward":
>  double: 4
> -float: 4
> +float: 8
>  ldouble: 4
>  
>  Function: "y0":
>  double: 2
> -float: 1
> +float: 6
>  ldouble: 3
>  
>  Function: "y0_downward":
> Copy .../obj/glibc/lp64d/math/NewUlps to ../sysdeps/riscv/rv64/rvd/libm-test-ulps (relative to source).
> 
>   Maciej
>
  
Joseph Myers July 13, 2020, 9:26 p.m. UTC | #5
On Mon, 13 Jul 2020, Maciej W. Rozycki via Libc-alpha wrote:

> On Sun, 12 Jul 2020, Alistair Francis via Libc-alpha wrote:
> 
> > Add a libm-test-ulps for RV32, this is the same as the RV64 one.
> > 
> > This dosn't match what is generated by running `make regen-ulps` on RV32
> > QEMU, but the current in tree RV64 doesn't match that either.
> 
>  For the record RV32 and RV64 QEMU ULPS results (obtained in the Linux 
> user emulation mode) match each other in my setup.  However those don't 
> match RV64 results obtained with my HiFive Unleashed hardware, and then 
> those don't match our existing RV64 results, so I guess they will have to 
> be regenerated for the upcoming release.

If results on QEMU and on hardware (for the same binaries) don't match, 
that indicates a bug in one or the other, which you could find by 
identifying the exact instruction at which different floating-point values 
first appear.  (You can get different results from compilation with 
different options or compiler versions, however, without any such bug, 
because of e.g. changes in when the compiler chose to contract an 
expression to use fused multiply-add.)
  
Carlos O'Donell July 13, 2020, 9:30 p.m. UTC | #6
On 7/13/20 5:26 PM, Joseph Myers wrote:
> On Mon, 13 Jul 2020, Maciej W. Rozycki via Libc-alpha wrote:
> 
>> On Sun, 12 Jul 2020, Alistair Francis via Libc-alpha wrote:
>>
>>> Add a libm-test-ulps for RV32, this is the same as the RV64 one.
>>>
>>> This dosn't match what is generated by running `make regen-ulps` on RV32
>>> QEMU, but the current in tree RV64 doesn't match that either.
>>
>>  For the record RV32 and RV64 QEMU ULPS results (obtained in the Linux 
>> user emulation mode) match each other in my setup.  However those don't 
>> match RV64 results obtained with my HiFive Unleashed hardware, and then 
>> those don't match our existing RV64 results, so I guess they will have to 
>> be regenerated for the upcoming release.
> 
> If results on QEMU and on hardware (for the same binaries) don't match, 
> that indicates a bug in one or the other, which you could find by 
> identifying the exact instruction at which different floating-point values 
> first appear.  (You can get different results from compilation with 
> different options or compiler versions, however, without any such bug, 
> because of e.g. changes in when the compiler chose to contract an 
> expression to use fused multiply-add.)

Is it really a "bug?" QEMU as an emulator could be taking liberties that
the hardware does not for the express purpose of improving emulated
performance? Is it ever QEMU's contract that it will operate identically
to hardware given the same software configuration?
  
Joseph Myers July 13, 2020, 9:59 p.m. UTC | #7
On Mon, 13 Jul 2020, Carlos O'Donell via Libc-alpha wrote:

> > If results on QEMU and on hardware (for the same binaries) don't match, 
> > that indicates a bug in one or the other, which you could find by 
> > identifying the exact instruction at which different floating-point values 
> > first appear.  (You can get different results from compilation with 
> > different options or compiler versions, however, without any such bug, 
> > because of e.g. changes in when the compiler chose to contract an 
> > expression to use fused multiply-add.)
> 
> Is it really a "bug?" QEMU as an emulator could be taking liberties that
> the hardware does not for the express purpose of improving emulated
> performance? Is it ever QEMU's contract that it will operate identically
> to hardware given the same software configuration?

I believe the semantics of the RISC-V floating-point instructions are 
fully specified (including details such as choice of NaN results) and so 
it's a bug for either hardware or emulator to produce results other than 
those in the architecture specification.

Sometimes architecture specifications do not fully specify results for all 
floating-point instructions.  For example, the Power instructions to 
estimate reciprocal or reciprocal square root have accuracy bounds in the 
architecture specification, but the exact result is not specified, so it 
would be legitimate for hardware and emulation to produce different 
results for those instructions.  But AArch64 has an instruction to 
estimate reciprocal square root whose exact semantics are fully specified 
in the architecture specification, meaning it's a bug if emulation fails 
to produce a result with exactly the same bit-pattern as hardware.  My 
understanding is that all the RISC-V floating-point instructions are fully 
specified, as on AArch64.
  
Andrew Waterman July 13, 2020, 10:26 p.m. UTC | #8
On Mon, Jul 13, 2020 at 2:59 PM Joseph Myers <joseph@codesourcery.com> wrote:
>
> On Mon, 13 Jul 2020, Carlos O'Donell via Libc-alpha wrote:
>
> > > If results on QEMU and on hardware (for the same binaries) don't match,
> > > that indicates a bug in one or the other, which you could find by
> > > identifying the exact instruction at which different floating-point values
> > > first appear.  (You can get different results from compilation with
> > > different options or compiler versions, however, without any such bug,
> > > because of e.g. changes in when the compiler chose to contract an
> > > expression to use fused multiply-add.)
> >
> > Is it really a "bug?" QEMU as an emulator could be taking liberties that
> > the hardware does not for the express purpose of improving emulated
> > performance? Is it ever QEMU's contract that it will operate identically
> > to hardware given the same software configuration?
>
> I believe the semantics of the RISC-V floating-point instructions are
> fully specified (including details such as choice of NaN results) and so
> it's a bug for either hardware or emulator to produce results other than
> those in the architecture specification.
>
> Sometimes architecture specifications do not fully specify results for all
> floating-point instructions.  For example, the Power instructions to
> estimate reciprocal or reciprocal square root have accuracy bounds in the
> architecture specification, but the exact result is not specified, so it
> would be legitimate for hardware and emulation to produce different
> results for those instructions.  But AArch64 has an instruction to
> estimate reciprocal square root whose exact semantics are fully specified
> in the architecture specification, meaning it's a bug if emulation fails
> to produce a result with exactly the same bit-pattern as hardware.  My
> understanding is that all the RISC-V floating-point instructions are fully
> specified, as on AArch64.

This is indeed the case (at least for now; the forthcoming vector
extension adds an unordered sum reduction whose result can vary by
implementation).  I think it would be good to confirm whether the
exact same binaries were used for the QEMU and Unleashed experiments,
and if not, whether the disparity remains when that's rectified.

>
> --
> Joseph S. Myers
> joseph@codesourcery.com
  
Maciej W. Rozycki July 14, 2020, midnight UTC | #9
On Mon, 13 Jul 2020, Andrew Waterman wrote:

> > Sometimes architecture specifications do not fully specify results for all
> > floating-point instructions.  For example, the Power instructions to
> > estimate reciprocal or reciprocal square root have accuracy bounds in the
> > architecture specification, but the exact result is not specified, so it
> > would be legitimate for hardware and emulation to produce different
> > results for those instructions.  But AArch64 has an instruction to
> > estimate reciprocal square root whose exact semantics are fully specified
> > in the architecture specification, meaning it's a bug if emulation fails
> > to produce a result with exactly the same bit-pattern as hardware.  My
> > understanding is that all the RISC-V floating-point instructions are fully
> > specified, as on AArch64.
> 
> This is indeed the case (at least for now; the forthcoming vector
> extension adds an unordered sum reduction whose result can vary by
> implementation).  I think it would be good to confirm whether the
> exact same binaries were used for the QEMU and Unleashed experiments,
> and if not, whether the disparity remains when that's rectified.

 Well, yes.  I just ran `make regen-ulps' consecutively with different 
`test-wrapper*' variables each time so as to redirect execution either to 
my HiFive Unleashed board or (user-mode) QEMU.  I have double-checked the 
logs now and no rebuild was made for subsequent runs, so the very same 
executables and DSOs were used.

 I tend to trust hardware with these matters and only use QEMU for quick 
comparative verification where hardware is slow, or where hardware is 
unavailable.  A silicon erratum cannot be excluded in this case of course, 
but I would check QEMU first.

  Maciej
  
Joseph Myers July 14, 2020, 5:24 p.m. UTC | #10
On Tue, 14 Jul 2020, Maciej W. Rozycki via Libc-alpha wrote:

>  Well, yes.  I just ran `make regen-ulps' consecutively with different 
> `test-wrapper*' variables each time so as to redirect execution either to 
> my HiFive Unleashed board or (user-mode) QEMU.  I have double-checked the 
> logs now and no rebuild was made for subsequent runs, so the very same 
> executables and DSOs were used.

So picking one function and setting its ulps back to the lower value, then 
rerunning the test on whichever of hardware and QEMU gave the higher 
value, should result in a test log showing a specific input to a specific 
function (in a specific rounding mode) for which the results on hardware 
and QEMU differ.  After confirming the difference with a minimal test just 
making that one function call, it should be possible to trace the 
execution of the function, whether in a debugger or inserting printf 
calls, to find the exact instruction for which hardware and QEMU produce 
different results with the same inputs.
  

Patch

diff --git a/sysdeps/riscv/rv32/rvd/libm-test-ulps b/sysdeps/riscv/rv32/rvd/libm-test-ulps
new file mode 100644
index 0000000000..8e2971ce66
--- /dev/null
+++ b/sysdeps/riscv/rv32/rvd/libm-test-ulps
@@ -0,0 +1,1402 @@ 
+# Begin of automatic generation
+
+# Maximal error of functions:
+Function: "acos":
+float: 1
+ldouble: 1
+
+Function: "acos_downward":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "acos_towardzero":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "acos_upward":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "acosh":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "acosh_downward":
+double: 2
+float: 2
+ldouble: 3
+
+Function: "acosh_towardzero":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "acosh_upward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "asin":
+float: 1
+ldouble: 1
+
+Function: "asin_downward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "asin_towardzero":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "asin_upward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "asinh":
+double: 1
+float: 1
+ldouble: 3
+
+Function: "asinh_downward":
+double: 3
+float: 3
+ldouble: 4
+
+Function: "asinh_towardzero":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "asinh_upward":
+double: 3
+float: 3
+ldouble: 4
+
+Function: "atan":
+float: 1
+ldouble: 1
+
+Function: "atan2":
+float: 1
+ldouble: 1
+
+Function: "atan2_downward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: "atan2_towardzero":
+double: 1
+float: 2
+ldouble: 3
+
+Function: "atan2_upward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "atan_downward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: "atan_towardzero":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "atan_upward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: "atanh":
+double: 2
+float: 2
+ldouble: 3
+
+Function: "atanh_downward":
+double: 3
+float: 3
+ldouble: 4
+
+Function: "atanh_towardzero":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "atanh_upward":
+double: 3
+float: 3
+ldouble: 4
+
+Function: "cabs":
+double: 1
+ldouble: 1
+
+Function: "cabs_downward":
+double: 1
+ldouble: 1
+
+Function: "cabs_towardzero":
+double: 1
+ldouble: 1
+
+Function: "cabs_upward":
+double: 1
+ldouble: 1
+
+Function: Real part of "cacos":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "cacos":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Real part of "cacos_downward":
+double: 3
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "cacos_downward":
+double: 5
+float: 3
+ldouble: 6
+
+Function: Real part of "cacos_towardzero":
+double: 3
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "cacos_towardzero":
+double: 4
+float: 2
+ldouble: 5
+
+Function: Real part of "cacos_upward":
+double: 2
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "cacos_upward":
+double: 5
+float: 5
+ldouble: 7
+
+Function: Real part of "cacosh":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "cacosh":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Real part of "cacosh_downward":
+double: 4
+float: 2
+ldouble: 5
+
+Function: Imaginary part of "cacosh_downward":
+double: 3
+float: 3
+ldouble: 4
+
+Function: Real part of "cacosh_towardzero":
+double: 4
+float: 2
+ldouble: 5
+
+Function: Imaginary part of "cacosh_towardzero":
+double: 3
+float: 2
+ldouble: 3
+
+Function: Real part of "cacosh_upward":
+double: 4
+float: 3
+ldouble: 6
+
+Function: Imaginary part of "cacosh_upward":
+double: 3
+float: 2
+ldouble: 4
+
+Function: "carg":
+float: 1
+ldouble: 2
+
+Function: "carg_downward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: "carg_towardzero":
+double: 1
+float: 2
+ldouble: 3
+
+Function: "carg_upward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: Real part of "casin":
+double: 1
+float: 1
+ldouble: 2
+
+Function: Imaginary part of "casin":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Real part of "casin_downward":
+double: 3
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "casin_downward":
+double: 5
+float: 3
+ldouble: 6
+
+Function: Real part of "casin_towardzero":
+double: 3
+float: 1
+ldouble: 3
+
+Function: Imaginary part of "casin_towardzero":
+double: 4
+float: 2
+ldouble: 5
+
+Function: Real part of "casin_upward":
+double: 3
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "casin_upward":
+double: 5
+float: 5
+ldouble: 7
+
+Function: Real part of "casinh":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "casinh":
+double: 1
+float: 1
+ldouble: 2
+
+Function: Real part of "casinh_downward":
+double: 5
+float: 3
+ldouble: 6
+
+Function: Imaginary part of "casinh_downward":
+double: 3
+float: 2
+ldouble: 3
+
+Function: Real part of "casinh_towardzero":
+double: 4
+float: 2
+ldouble: 5
+
+Function: Imaginary part of "casinh_towardzero":
+double: 3
+float: 1
+ldouble: 3
+
+Function: Real part of "casinh_upward":
+double: 5
+float: 5
+ldouble: 7
+
+Function: Imaginary part of "casinh_upward":
+double: 3
+float: 2
+ldouble: 3
+
+Function: Real part of "catan":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Imaginary part of "catan":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Real part of "catan_downward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "catan_downward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Real part of "catan_towardzero":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "catan_towardzero":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Real part of "catan_upward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: Imaginary part of "catan_upward":
+double: 2
+float: 2
+ldouble: 3
+
+Function: Real part of "catanh":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Imaginary part of "catanh":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Real part of "catanh_downward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "catanh_downward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Real part of "catanh_towardzero":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "catanh_towardzero":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Real part of "catanh_upward":
+double: 4
+float: 4
+ldouble: 4
+
+Function: Imaginary part of "catanh_upward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "cbrt":
+double: 3
+float: 1
+ldouble: 1
+
+Function: "cbrt_downward":
+double: 4
+float: 1
+ldouble: 1
+
+Function: "cbrt_towardzero":
+double: 3
+float: 1
+ldouble: 1
+
+Function: "cbrt_upward":
+double: 5
+float: 1
+ldouble: 1
+
+Function: Real part of "ccos":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Imaginary part of "ccos":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Real part of "ccos_downward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: Imaginary part of "ccos_downward":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Real part of "ccos_towardzero":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "ccos_towardzero":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Real part of "ccos_upward":
+double: 1
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "ccos_upward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Real part of "ccosh":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Imaginary part of "ccosh":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Real part of "ccosh_downward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "ccosh_downward":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Real part of "ccosh_towardzero":
+double: 2
+float: 3
+ldouble: 2
+
+Function: Imaginary part of "ccosh_towardzero":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Real part of "ccosh_upward":
+double: 1
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "ccosh_upward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Real part of "cexp":
+double: 2
+float: 1
+ldouble: 1
+
+Function: Imaginary part of "cexp":
+double: 1
+float: 2
+ldouble: 1
+
+Function: Real part of "cexp_downward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "cexp_downward":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Real part of "cexp_towardzero":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "cexp_towardzero":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Real part of "cexp_upward":
+double: 1
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "cexp_upward":
+double: 3
+float: 2
+ldouble: 3
+
+Function: Real part of "clog":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Imaginary part of "clog":
+float: 1
+ldouble: 1
+
+Function: Real part of "clog10":
+double: 3
+float: 4
+ldouble: 2
+
+Function: Imaginary part of "clog10":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Real part of "clog10_downward":
+double: 5
+float: 5
+ldouble: 3
+
+Function: Imaginary part of "clog10_downward":
+double: 2
+float: 4
+ldouble: 3
+
+Function: Real part of "clog10_towardzero":
+double: 5
+float: 5
+ldouble: 4
+
+Function: Imaginary part of "clog10_towardzero":
+double: 2
+float: 4
+ldouble: 3
+
+Function: Real part of "clog10_upward":
+double: 6
+float: 5
+ldouble: 4
+
+Function: Imaginary part of "clog10_upward":
+double: 2
+float: 4
+ldouble: 3
+
+Function: Real part of "clog_downward":
+double: 4
+float: 3
+ldouble: 3
+
+Function: Imaginary part of "clog_downward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Real part of "clog_towardzero":
+double: 4
+float: 4
+ldouble: 3
+
+Function: Imaginary part of "clog_towardzero":
+double: 1
+float: 3
+ldouble: 2
+
+Function: Real part of "clog_upward":
+double: 4
+float: 3
+ldouble: 4
+
+Function: Imaginary part of "clog_upward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: "cos":
+double: 1
+ldouble: 1
+
+Function: "cos_downward":
+double: 1
+float: 1
+ldouble: 3
+
+Function: "cos_towardzero":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "cos_upward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "cosh":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "cosh_downward":
+double: 2
+float: 1
+ldouble: 2
+
+Function: "cosh_towardzero":
+double: 2
+float: 1
+ldouble: 2
+
+Function: "cosh_upward":
+double: 2
+float: 2
+ldouble: 3
+
+Function: Real part of "cpow":
+double: 2
+float: 5
+ldouble: 4
+
+Function: Imaginary part of "cpow":
+float: 2
+ldouble: 1
+
+Function: Real part of "cpow_downward":
+double: 5
+float: 8
+ldouble: 6
+
+Function: Imaginary part of "cpow_downward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Real part of "cpow_towardzero":
+double: 5
+float: 8
+ldouble: 6
+
+Function: Imaginary part of "cpow_towardzero":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Real part of "cpow_upward":
+double: 4
+float: 1
+ldouble: 3
+
+Function: Imaginary part of "cpow_upward":
+double: 1
+float: 2
+ldouble: 2
+
+Function: Real part of "csin":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Imaginary part of "csin":
+ldouble: 1
+
+Function: Real part of "csin_downward":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Imaginary part of "csin_downward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: Real part of "csin_towardzero":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Imaginary part of "csin_towardzero":
+double: 1
+float: 1
+ldouble: 2
+
+Function: Real part of "csin_upward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "csin_upward":
+double: 1
+float: 2
+ldouble: 3
+
+Function: Real part of "csinh":
+float: 1
+ldouble: 1
+
+Function: Imaginary part of "csinh":
+double: 1
+float: 1
+ldouble: 1
+
+Function: Real part of "csinh_downward":
+double: 2
+float: 1
+ldouble: 2
+
+Function: Imaginary part of "csinh_downward":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Real part of "csinh_towardzero":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "csinh_towardzero":
+double: 3
+float: 3
+ldouble: 2
+
+Function: Real part of "csinh_upward":
+double: 1
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "csinh_upward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Real part of "csqrt":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Imaginary part of "csqrt":
+double: 2
+float: 2
+ldouble: 2
+
+Function: Real part of "csqrt_downward":
+double: 5
+float: 4
+ldouble: 4
+
+Function: Imaginary part of "csqrt_downward":
+double: 4
+float: 3
+ldouble: 3
+
+Function: Real part of "csqrt_towardzero":
+double: 4
+float: 3
+ldouble: 3
+
+Function: Imaginary part of "csqrt_towardzero":
+double: 4
+float: 3
+ldouble: 3
+
+Function: Real part of "csqrt_upward":
+double: 5
+float: 4
+ldouble: 4
+
+Function: Imaginary part of "csqrt_upward":
+double: 3
+float: 3
+ldouble: 3
+
+Function: Real part of "ctan":
+double: 1
+float: 1
+ldouble: 3
+
+Function: Imaginary part of "ctan":
+double: 2
+float: 2
+ldouble: 3
+
+Function: Real part of "ctan_downward":
+double: 6
+float: 5
+ldouble: 4
+
+Function: Imaginary part of "ctan_downward":
+double: 2
+float: 2
+ldouble: 5
+
+Function: Real part of "ctan_towardzero":
+double: 5
+float: 2
+ldouble: 4
+
+Function: Imaginary part of "ctan_towardzero":
+double: 2
+float: 2
+ldouble: 5
+
+Function: Real part of "ctan_upward":
+double: 2
+float: 4
+ldouble: 5
+
+Function: Imaginary part of "ctan_upward":
+double: 2
+float: 2
+ldouble: 5
+
+Function: Real part of "ctanh":
+double: 2
+float: 2
+ldouble: 3
+
+Function: Imaginary part of "ctanh":
+double: 2
+float: 1
+ldouble: 3
+
+Function: Real part of "ctanh_downward":
+double: 4
+float: 2
+ldouble: 5
+
+Function: Imaginary part of "ctanh_downward":
+double: 6
+float: 5
+ldouble: 4
+
+Function: Real part of "ctanh_towardzero":
+double: 2
+float: 2
+ldouble: 5
+
+Function: Imaginary part of "ctanh_towardzero":
+double: 5
+float: 2
+ldouble: 3
+
+Function: Real part of "ctanh_upward":
+double: 2
+float: 2
+ldouble: 5
+
+Function: Imaginary part of "ctanh_upward":
+double: 2
+float: 3
+ldouble: 5
+
+Function: "erf":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "erf_downward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "erf_towardzero":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "erf_upward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "erfc":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "erfc_downward":
+double: 4
+float: 4
+ldouble: 5
+
+Function: "erfc_towardzero":
+double: 3
+float: 3
+ldouble: 4
+
+Function: "erfc_upward":
+double: 4
+float: 4
+ldouble: 5
+
+Function: "exp":
+ldouble: 1
+
+Function: "exp10":
+double: 2
+ldouble: 2
+
+Function: "exp10_downward":
+double: 3
+float: 1
+ldouble: 3
+
+Function: "exp10_towardzero":
+double: 3
+float: 1
+ldouble: 3
+
+Function: "exp10_upward":
+double: 2
+float: 1
+ldouble: 3
+
+Function: "exp2":
+double: 1
+ldouble: 1
+
+Function: "exp2_downward":
+double: 1
+ldouble: 1
+
+Function: "exp2_towardzero":
+double: 1
+ldouble: 1
+
+Function: "exp2_upward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "exp_downward":
+double: 1
+float: 1
+
+Function: "exp_towardzero":
+double: 1
+float: 1
+
+Function: "exp_upward":
+double: 1
+float: 1
+
+Function: "expm1":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "expm1_downward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "expm1_towardzero":
+double: 1
+float: 2
+ldouble: 4
+
+Function: "expm1_upward":
+double: 1
+float: 1
+ldouble: 3
+
+Function: "gamma":
+double: 3
+float: 3
+ldouble: 5
+
+Function: "gamma_downward":
+double: 4
+float: 4
+ldouble: 8
+
+Function: "gamma_towardzero":
+double: 4
+float: 3
+ldouble: 5
+
+Function: "gamma_upward":
+double: 4
+float: 5
+ldouble: 8
+
+Function: "hypot":
+double: 1
+ldouble: 1
+
+Function: "hypot_downward":
+double: 1
+ldouble: 1
+
+Function: "hypot_towardzero":
+double: 1
+ldouble: 1
+
+Function: "hypot_upward":
+double: 1
+ldouble: 1
+
+Function: "j0":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "j0_downward":
+double: 2
+float: 4
+ldouble: 4
+
+Function: "j0_towardzero":
+double: 2
+float: 1
+ldouble: 2
+
+Function: "j0_upward":
+double: 3
+float: 2
+ldouble: 5
+
+Function: "j1":
+double: 1
+float: 2
+ldouble: 4
+
+Function: "j1_downward":
+double: 3
+float: 2
+ldouble: 4
+
+Function: "j1_towardzero":
+double: 3
+float: 2
+ldouble: 4
+
+Function: "j1_upward":
+double: 3
+float: 4
+ldouble: 3
+
+Function: "jn":
+double: 4
+float: 4
+ldouble: 7
+
+Function: "jn_downward":
+double: 4
+float: 5
+ldouble: 8
+
+Function: "jn_towardzero":
+double: 4
+float: 5
+ldouble: 8
+
+Function: "jn_upward":
+double: 5
+float: 4
+ldouble: 7
+
+Function: "lgamma":
+double: 3
+float: 3
+ldouble: 5
+
+Function: "lgamma_downward":
+double: 4
+float: 4
+ldouble: 8
+
+Function: "lgamma_towardzero":
+double: 4
+float: 3
+ldouble: 5
+
+Function: "lgamma_upward":
+double: 4
+float: 5
+ldouble: 8
+
+Function: "log":
+ldouble: 1
+
+Function: "log10":
+double: 2
+float: 2
+ldouble: 1
+
+Function: "log10_downward":
+double: 2
+float: 3
+ldouble: 1
+
+Function: "log10_towardzero":
+double: 2
+float: 1
+ldouble: 1
+
+Function: "log10_upward":
+double: 2
+float: 2
+ldouble: 1
+
+Function: "log1p":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "log1p_downward":
+double: 1
+float: 2
+ldouble: 3
+
+Function: "log1p_towardzero":
+double: 2
+float: 2
+ldouble: 3
+
+Function: "log1p_upward":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "log2":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "log2_downward":
+double: 3
+ldouble: 3
+
+Function: "log2_towardzero":
+double: 2
+ldouble: 1
+
+Function: "log2_upward":
+double: 3
+ldouble: 1
+
+Function: "log_downward":
+ldouble: 1
+
+Function: "log_towardzero":
+ldouble: 2
+
+Function: "log_upward":
+double: 1
+ldouble: 2
+
+Function: "pow":
+double: 1
+ldouble: 2
+
+Function: "pow_downward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "pow_towardzero":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "pow_upward":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "sin":
+double: 1
+ldouble: 1
+
+Function: "sin_downward":
+double: 1
+float: 1
+ldouble: 3
+
+Function: "sin_towardzero":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "sin_upward":
+double: 1
+float: 1
+ldouble: 3
+
+Function: "sincos":
+double: 1
+ldouble: 1
+
+Function: "sincos_downward":
+double: 1
+float: 1
+ldouble: 3
+
+Function: "sincos_towardzero":
+double: 1
+float: 1
+ldouble: 2
+
+Function: "sincos_upward":
+double: 1
+float: 1
+ldouble: 3
+
+Function: "sinh":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "sinh_downward":
+double: 3
+float: 3
+ldouble: 3
+
+Function: "sinh_towardzero":
+double: 2
+float: 2
+ldouble: 3
+
+Function: "sinh_upward":
+double: 3
+float: 3
+ldouble: 4
+
+Function: "tan":
+float: 1
+ldouble: 1
+
+Function: "tan_downward":
+double: 1
+float: 2
+ldouble: 1
+
+Function: "tan_towardzero":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "tan_upward":
+double: 1
+float: 1
+ldouble: 1
+
+Function: "tanh":
+double: 2
+float: 2
+ldouble: 2
+
+Function: "tanh_downward":
+double: 3
+float: 3
+ldouble: 4
+
+Function: "tanh_towardzero":
+double: 2
+float: 2
+ldouble: 3
+
+Function: "tanh_upward":
+double: 3
+float: 3
+ldouble: 3
+
+Function: "tgamma":
+double: 5
+float: 4
+ldouble: 4
+
+Function: "tgamma_downward":
+double: 5
+float: 5
+ldouble: 5
+
+Function: "tgamma_towardzero":
+double: 5
+float: 4
+ldouble: 5
+
+Function: "tgamma_upward":
+double: 4
+float: 4
+ldouble: 4
+
+Function: "y0":
+double: 2
+float: 1
+ldouble: 3
+
+Function: "y0_downward":
+double: 3
+float: 4
+ldouble: 4
+
+Function: "y0_towardzero":
+double: 3
+float: 3
+ldouble: 3
+
+Function: "y0_upward":
+double: 2
+float: 5
+ldouble: 3
+
+Function: "y1":
+double: 3
+float: 2
+ldouble: 2
+
+Function: "y1_downward":
+double: 3
+float: 2
+ldouble: 4
+
+Function: "y1_towardzero":
+double: 3
+float: 2
+ldouble: 2
+
+Function: "y1_upward":
+double: 5
+float: 2
+ldouble: 5
+
+Function: "yn":
+double: 3
+float: 3
+ldouble: 5
+
+Function: "yn_downward":
+double: 3
+float: 4
+ldouble: 5
+
+Function: "yn_towardzero":
+double: 3
+float: 3
+ldouble: 5
+
+Function: "yn_upward":
+double: 4
+float: 5
+ldouble: 5
+
+# end of automatic generation
diff --git a/sysdeps/riscv/rv32/rvd/libm-test-ulps-name b/sysdeps/riscv/rv32/rvd/libm-test-ulps-name
new file mode 100644
index 0000000000..827fcdca19
--- /dev/null
+++ b/sysdeps/riscv/rv32/rvd/libm-test-ulps-name
@@ -0,0 +1 @@ 
+RISC-V