tinymux/tests/dbt/test_interp.cpp
Stephen Dennis a38e7ab39a test(dbt): FCVT conformance against qemu-derived golden values
580 cases: every float->int convert (W/WU/L/LU) against every static
rounding mode (RNE/RTZ/RDN/RUP/RMM) over 29 inputs -- zeroes of both
signs, values that round in each direction, exact ties in both parities,
quiet and signalling NaNs, both infinities, and the representable values
either side of each type's range boundary.

Deliberately not differential.  The fuzzer next door compares the
interpreter against the DBT, which by construction cannot see anything the
two get wrong in the same way, and both bugs fixed in this branch were of
exactly that kind: both routes truncated instead of rounding, and both
returned 0 for NaN in the unsigned converts.  The fuzzer reported those
runs clean.

So the expected values come from qemu-riscv64 executing the same
instructions, not from anything in this tree.  There is a comment on the
table saying so, because the tempting way to "fix" a failure here is to
paste in what dbt_interp currently returns, and that would throw away the
only independent opinion the test has.

Negative control: against the unfixed interpreter this fails 105 of the
580 -- the rounding cases in every mode but RTZ, the NaN unsigned cases,
and the round-then-range-check boundary at 2147483647.5.

The test runs each case as a real guest program (ADDI/FLD/FCVT/ECALL)
rather than calling the conversion helper, so decode, the rm field and the
range checks are all on the path.  Note the DATA address must fit a signed
12-bit immediate: 0x800 encodes as -2048, every load returns zero, and the
whole table then reads as "the interpreter converts everything to 0" --
which is how the first draft of this test failed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 23:18:03 -06:00

296 lines
28 KiB
C++

/*! \file test_dbt_interp.cpp
* \brief Guest memory bounds checks for the RV64 interpreter route.
*
* mem_check() gates every guest read and write in dbt_interp.cpp. It used
* to be `addr + len <= mem->size`, which is unsigned 64-bit arithmetic and
* wraps: for an addr within len of UINT64_MAX the sum becomes a small
* number, compares below size, and the caller then indexes mem->data with
* the unwrapped addr (#1292).
*
* mem_check and the accessors are file-static, so this includes the
* translation unit directly rather than linking against it. That is also
* why there are no stubs here -- dbt_interp.cpp depends only on
* dbt_interp.h, dbt_decoder.h and the standard library.
*
* The wrap cases are the ones that discriminate: every other case below
* behaves identically under both the old and new check, so a run that
* passes them all proves nothing on its own. Reverting mem_check to
* `addr + len <= mem->size` must fail exactly the WRAP cases.
*/
// No standard headers here. dbt_interp.cpp already includes <cstdio>,
// <cstring>, <cmath>, <climits> and <cfloat>, and pulling any of them in
// alongside the engine source breaks <type_traits> in this arrangement.
// Everything below uses only what the engine TU already provides.
//
#include "dbt_interp.cpp"
static int g_pass = 0;
static int g_fail = 0;
static void expect(bool cond, const char *what) {
if (cond) {
g_pass++;
} else {
g_fail++;
printf("FAIL: %s\n", what);
}
}
// ---------------------------------------------------------------
// FCVT.{W,WU,L,LU}.D conformance
// ---------------------------------------------------------------
//
// This is deliberately NOT a differential test against the DBT. The
// differential fuzzer in this directory compares the interpreter with the
// translator and reports where they disagree, which by construction cannot
// see anything they get wrong in the same way -- and two such bugs lived
// here: every convert truncated instead of honouring the rounding mode, and
// NaN converted to 0 rather than the destination type's maximum in the
// unsigned forms. Both routes agreed, so the fuzzer called it clean.
//
// The expected values below therefore come from qemu-riscv64 running the
// same instructions, not from either implementation in this tree. Do not
// "fix" a failure here by copying what dbt_interp currently returns; that
// discards the only independent opinion the test has.
//
// Columns are the five static rounding modes, in rm-field order:
// RNE, RTZ, RDN, RUP, RMM.
//
struct fcvt_row_t {
uint64_t in; // input double, as bits
uint8_t sel; // rs2: 0=W 1=WU 2=L 3=LU
uint64_t expect[5]; // by rounding mode
};
static const fcvt_row_t FCVT_GOLDEN[] = {
{ 0x0000000000000000ULL, 0, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // +0.0 fcvt.w.d
{ 0x0000000000000000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // +0.0 fcvt.wu.d
{ 0x0000000000000000ULL, 2, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // +0.0 fcvt.l.d
{ 0x0000000000000000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // +0.0 fcvt.lu.d
{ 0x8000000000000000ULL, 0, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -0.0 fcvt.w.d
{ 0x8000000000000000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -0.0 fcvt.wu.d
{ 0x8000000000000000ULL, 2, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -0.0 fcvt.l.d
{ 0x8000000000000000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -0.0 fcvt.lu.d
{ 0x3FF0000000000000ULL, 0, { 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL } }, // 1.0 fcvt.w.d
{ 0x3FF0000000000000ULL, 1, { 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL } }, // 1.0 fcvt.wu.d
{ 0x3FF0000000000000ULL, 2, { 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL } }, // 1.0 fcvt.l.d
{ 0x3FF0000000000000ULL, 3, { 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000001ULL } }, // 1.0 fcvt.lu.d
{ 0xBFF0000000000000ULL, 0, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // -1.0 fcvt.w.d
{ 0xBFF0000000000000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -1.0 fcvt.wu.d
{ 0xBFF0000000000000ULL, 2, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // -1.0 fcvt.l.d
{ 0xBFF0000000000000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -1.0 fcvt.lu.d
{ 0x3FE0000000000000ULL, 0, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000001ULL, 0x0000000000000001ULL } }, // 0.5 fcvt.w.d
{ 0x3FE0000000000000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000001ULL, 0x0000000000000001ULL } }, // 0.5 fcvt.wu.d
{ 0x3FE0000000000000ULL, 2, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000001ULL, 0x0000000000000001ULL } }, // 0.5 fcvt.l.d
{ 0x3FE0000000000000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000001ULL, 0x0000000000000001ULL } }, // 0.5 fcvt.lu.d
{ 0x3FF8000000000000ULL, 0, { 0x0000000000000002ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000002ULL, 0x0000000000000002ULL } }, // 1.5 fcvt.w.d
{ 0x3FF8000000000000ULL, 1, { 0x0000000000000002ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000002ULL, 0x0000000000000002ULL } }, // 1.5 fcvt.wu.d
{ 0x3FF8000000000000ULL, 2, { 0x0000000000000002ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000002ULL, 0x0000000000000002ULL } }, // 1.5 fcvt.l.d
{ 0x3FF8000000000000ULL, 3, { 0x0000000000000002ULL, 0x0000000000000001ULL, 0x0000000000000001ULL, 0x0000000000000002ULL, 0x0000000000000002ULL } }, // 1.5 fcvt.lu.d
{ 0x4004000000000000ULL, 0, { 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000003ULL, 0x0000000000000003ULL } }, // 2.5 fcvt.w.d
{ 0x4004000000000000ULL, 1, { 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000003ULL, 0x0000000000000003ULL } }, // 2.5 fcvt.wu.d
{ 0x4004000000000000ULL, 2, { 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000003ULL, 0x0000000000000003ULL } }, // 2.5 fcvt.l.d
{ 0x4004000000000000ULL, 3, { 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000003ULL, 0x0000000000000003ULL } }, // 2.5 fcvt.lu.d
{ 0x4004CCCCCCCCCCCDULL, 0, { 0x0000000000000003ULL, 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000003ULL, 0x0000000000000003ULL } }, // 2.6 fcvt.w.d
{ 0x4004CCCCCCCCCCCDULL, 1, { 0x0000000000000003ULL, 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000003ULL, 0x0000000000000003ULL } }, // 2.6 fcvt.wu.d
{ 0x4004CCCCCCCCCCCDULL, 2, { 0x0000000000000003ULL, 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000003ULL, 0x0000000000000003ULL } }, // 2.6 fcvt.l.d
{ 0x4004CCCCCCCCCCCDULL, 3, { 0x0000000000000003ULL, 0x0000000000000002ULL, 0x0000000000000002ULL, 0x0000000000000003ULL, 0x0000000000000003ULL } }, // 2.6 fcvt.lu.d
{ 0xC004000000000000ULL, 0, { 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL, 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL } }, // -2.5 fcvt.w.d
{ 0xC004000000000000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2.5 fcvt.wu.d
{ 0xC004000000000000ULL, 2, { 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL, 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL } }, // -2.5 fcvt.l.d
{ 0xC004000000000000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2.5 fcvt.lu.d
{ 0xC004CCCCCCCCCCCDULL, 0, { 0xFFFFFFFFFFFFFFFDULL, 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL, 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL } }, // -2.6 fcvt.w.d
{ 0xC004CCCCCCCCCCCDULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2.6 fcvt.wu.d
{ 0xC004CCCCCCCCCCCDULL, 2, { 0xFFFFFFFFFFFFFFFDULL, 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL, 0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFDULL } }, // -2.6 fcvt.l.d
{ 0xC004CCCCCCCCCCCDULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2.6 fcvt.lu.d
{ 0x400C000000000000ULL, 0, { 0x0000000000000004ULL, 0x0000000000000003ULL, 0x0000000000000003ULL, 0x0000000000000004ULL, 0x0000000000000004ULL } }, // 3.5 fcvt.w.d
{ 0x400C000000000000ULL, 1, { 0x0000000000000004ULL, 0x0000000000000003ULL, 0x0000000000000003ULL, 0x0000000000000004ULL, 0x0000000000000004ULL } }, // 3.5 fcvt.wu.d
{ 0x400C000000000000ULL, 2, { 0x0000000000000004ULL, 0x0000000000000003ULL, 0x0000000000000003ULL, 0x0000000000000004ULL, 0x0000000000000004ULL } }, // 3.5 fcvt.l.d
{ 0x400C000000000000ULL, 3, { 0x0000000000000004ULL, 0x0000000000000003ULL, 0x0000000000000003ULL, 0x0000000000000004ULL, 0x0000000000000004ULL } }, // 3.5 fcvt.lu.d
{ 0xBFE0000000000000ULL, 0, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL, 0x0000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL } }, // -0.5 fcvt.w.d
{ 0xBFE0000000000000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -0.5 fcvt.wu.d
{ 0xBFE0000000000000ULL, 2, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL, 0x0000000000000000ULL, 0xFFFFFFFFFFFFFFFFULL } }, // -0.5 fcvt.l.d
{ 0xBFE0000000000000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -0.5 fcvt.lu.d
{ 0x7FF0000000000000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // +inf fcvt.w.d
{ 0x7FF0000000000000ULL, 1, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // +inf fcvt.wu.d
{ 0x7FF0000000000000ULL, 2, { 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL } }, // +inf fcvt.l.d
{ 0x7FF0000000000000ULL, 3, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // +inf fcvt.lu.d
{ 0xFFF0000000000000ULL, 0, { 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL } }, // -inf fcvt.w.d
{ 0xFFF0000000000000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -inf fcvt.wu.d
{ 0xFFF0000000000000ULL, 2, { 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL } }, // -inf fcvt.l.d
{ 0xFFF0000000000000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -inf fcvt.lu.d
{ 0x7FF8000000000000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // qNaN fcvt.w.d
{ 0x7FF8000000000000ULL, 1, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // qNaN fcvt.wu.d
{ 0x7FF8000000000000ULL, 2, { 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL } }, // qNaN fcvt.l.d
{ 0x7FF8000000000000ULL, 3, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // qNaN fcvt.lu.d
{ 0xFFF8000000000000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // -qNaN fcvt.w.d
{ 0xFFF8000000000000ULL, 1, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // -qNaN fcvt.wu.d
{ 0xFFF8000000000000ULL, 2, { 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL } }, // -qNaN fcvt.l.d
{ 0xFFF8000000000000ULL, 3, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // -qNaN fcvt.lu.d
{ 0x7FF0000000000001ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // sNaN fcvt.w.d
{ 0x7FF0000000000001ULL, 1, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // sNaN fcvt.wu.d
{ 0x7FF0000000000001ULL, 2, { 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL } }, // sNaN fcvt.l.d
{ 0x7FF0000000000001ULL, 3, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // sNaN fcvt.lu.d
{ 0x41DFFFFFFFC00000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^31-1 fcvt.w.d
{ 0x41DFFFFFFFC00000ULL, 1, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^31-1 fcvt.wu.d
{ 0x41DFFFFFFFC00000ULL, 2, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^31-1 fcvt.l.d
{ 0x41DFFFFFFFC00000ULL, 3, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^31-1 fcvt.lu.d
{ 0x41E0000000000000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^31 fcvt.w.d
{ 0x41E0000000000000ULL, 1, { 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL } }, // 2^31 fcvt.wu.d
{ 0x41E0000000000000ULL, 2, { 0x0000000080000000ULL, 0x0000000080000000ULL, 0x0000000080000000ULL, 0x0000000080000000ULL, 0x0000000080000000ULL } }, // 2^31 fcvt.l.d
{ 0x41E0000000000000ULL, 3, { 0x0000000080000000ULL, 0x0000000080000000ULL, 0x0000000080000000ULL, 0x0000000080000000ULL, 0x0000000080000000ULL } }, // 2^31 fcvt.lu.d
{ 0xC1E0000000000000ULL, 0, { 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL } }, // -2^31 fcvt.w.d
{ 0xC1E0000000000000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2^31 fcvt.wu.d
{ 0xC1E0000000000000ULL, 2, { 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL } }, // -2^31 fcvt.l.d
{ 0xC1E0000000000000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2^31 fcvt.lu.d
{ 0xC1E0000000200000ULL, 0, { 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL } }, // -2^31-1 fcvt.w.d
{ 0xC1E0000000200000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2^31-1 fcvt.wu.d
{ 0xC1E0000000200000ULL, 2, { 0xFFFFFFFF7FFFFFFFULL, 0xFFFFFFFF7FFFFFFFULL, 0xFFFFFFFF7FFFFFFFULL, 0xFFFFFFFF7FFFFFFFULL, 0xFFFFFFFF7FFFFFFFULL } }, // -2^31-1 fcvt.l.d
{ 0xC1E0000000200000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2^31-1 fcvt.lu.d
{ 0x41DFFFFFFFE00000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^31-0.5 fcvt.w.d
{ 0x41DFFFFFFFE00000ULL, 1, { 0xFFFFFFFF80000000ULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL } }, // 2^31-0.5 fcvt.wu.d
{ 0x41DFFFFFFFE00000ULL, 2, { 0x0000000080000000ULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x0000000080000000ULL, 0x0000000080000000ULL } }, // 2^31-0.5 fcvt.l.d
{ 0x41DFFFFFFFE00000ULL, 3, { 0x0000000080000000ULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x0000000080000000ULL, 0x0000000080000000ULL } }, // 2^31-0.5 fcvt.lu.d
{ 0x41EFFFFFFFE00000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^32-1 fcvt.w.d
{ 0x41EFFFFFFFE00000ULL, 1, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // 2^32-1 fcvt.wu.d
{ 0x41EFFFFFFFE00000ULL, 2, { 0x00000000FFFFFFFFULL, 0x00000000FFFFFFFFULL, 0x00000000FFFFFFFFULL, 0x00000000FFFFFFFFULL, 0x00000000FFFFFFFFULL } }, // 2^32-1 fcvt.l.d
{ 0x41EFFFFFFFE00000ULL, 3, { 0x00000000FFFFFFFFULL, 0x00000000FFFFFFFFULL, 0x00000000FFFFFFFFULL, 0x00000000FFFFFFFFULL, 0x00000000FFFFFFFFULL } }, // 2^32-1 fcvt.lu.d
{ 0x41F0000000000000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^32 fcvt.w.d
{ 0x41F0000000000000ULL, 1, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // 2^32 fcvt.wu.d
{ 0x41F0000000000000ULL, 2, { 0x0000000100000000ULL, 0x0000000100000000ULL, 0x0000000100000000ULL, 0x0000000100000000ULL, 0x0000000100000000ULL } }, // 2^32 fcvt.l.d
{ 0x41F0000000000000ULL, 3, { 0x0000000100000000ULL, 0x0000000100000000ULL, 0x0000000100000000ULL, 0x0000000100000000ULL, 0x0000000100000000ULL } }, // 2^32 fcvt.lu.d
{ 0x43E0000000000000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^63 fcvt.w.d
{ 0x43E0000000000000ULL, 1, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // 2^63 fcvt.wu.d
{ 0x43E0000000000000ULL, 2, { 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL } }, // 2^63 fcvt.l.d
{ 0x43E0000000000000ULL, 3, { 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL } }, // 2^63 fcvt.lu.d
{ 0xC3E0000000000000ULL, 0, { 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL } }, // -2^63 fcvt.w.d
{ 0xC3E0000000000000ULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2^63 fcvt.wu.d
{ 0xC3E0000000000000ULL, 2, { 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL } }, // -2^63 fcvt.l.d
{ 0xC3E0000000000000ULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -2^63 fcvt.lu.d
{ 0x43F0000000000000ULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 2^64 fcvt.w.d
{ 0x43F0000000000000ULL, 1, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // 2^64 fcvt.wu.d
{ 0x43F0000000000000ULL, 2, { 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL } }, // 2^64 fcvt.l.d
{ 0x43F0000000000000ULL, 3, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // 2^64 fcvt.lu.d
{ 0x7E37E43C8800759CULL, 0, { 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL, 0x000000007FFFFFFFULL } }, // 1e300 fcvt.w.d
{ 0x7E37E43C8800759CULL, 1, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // 1e300 fcvt.wu.d
{ 0x7E37E43C8800759CULL, 2, { 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL } }, // 1e300 fcvt.l.d
{ 0x7E37E43C8800759CULL, 3, { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL } }, // 1e300 fcvt.lu.d
{ 0xFE37E43C8800759CULL, 0, { 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL, 0xFFFFFFFF80000000ULL } }, // -1e300 fcvt.w.d
{ 0xFE37E43C8800759CULL, 1, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -1e300 fcvt.wu.d
{ 0xFE37E43C8800759CULL, 2, { 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL, 0x8000000000000000ULL } }, // -1e300 fcvt.l.d
{ 0xFE37E43C8800759CULL, 3, { 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL, 0x0000000000000000ULL } }, // -1e300 fcvt.lu.d
};
static int fcvt_halt_ecall(rv64_state_t *, void *) { return 1; }
// Run one FCVT through the real interpreter and return the destination
// register. A guest program rather than a direct call to the conversion
// helper, so the decode, the rounding-mode field and the range checks are
// all covered.
static uint64_t run_one_fcvt(rv64_memory_t *mem, uint64_t in,
uint8_t sel, uint8_t rm) {
// Must fit a *signed* 12-bit immediate, because the address is built
// with a single ADDI: 0x800 would encode as -2048 and every load would
// come back zero, which reads as "the interpreter converted to 0".
const uint64_t DATA = 0x400;
memcpy(mem->data + DATA, &in, 8);
// ADDI x9, x0, DATA ; FLD f1, 0(x9) ; FCVT x5, f1 ; ECALL
const uint32_t code[4] = {
(uint32_t)(0x13u | (9u << 7) | (0u << 12) | (0u << 15)
| ((DATA & 0xFFFu) << 20)),
(uint32_t)(0x07u | (1u << 7) | (3u << 12) | (9u << 15)),
(uint32_t)(0xC2000000u | (5u << 7) | ((uint32_t)rm << 12)
| (1u << 15) | ((uint32_t)sel << 20) | 0x53u),
0x00000073u
};
memcpy(mem->data, code, sizeof(code));
rv64_state_t st;
memset(&st, 0, sizeof(st));
st.pc = 0;
st.x[2] = 0x40000;
rv64_interp_run(&st, mem, fcvt_halt_ecall, nullptr);
return st.x[5];
}
static void test_fcvt_conformance(rv64_memory_t *mem) {
static const char *RM_NAME[5] = { "rne", "rtz", "rdn", "rup", "rmm" };
static const char *CVT_NAME[4] = { "fcvt.w.d", "fcvt.wu.d",
"fcvt.l.d", "fcvt.lu.d" };
const size_t n = sizeof(FCVT_GOLDEN) / sizeof(FCVT_GOLDEN[0]);
char desc[160];
for (size_t i = 0; i < n; i++) {
const fcvt_row_t &row = FCVT_GOLDEN[i];
for (uint8_t rm = 0; rm < 5; rm++) {
uint64_t got = run_one_fcvt(mem, row.in, row.sel, rm);
snprintf(desc, sizeof(desc),
"%s %s in=0x%016llX -> 0x%016llX (got 0x%016llX)",
CVT_NAME[row.sel], RM_NAME[rm],
(unsigned long long)row.in,
(unsigned long long)row.expect[rm],
(unsigned long long)got);
expect(got == row.expect[rm], desc);
}
}
}
int main() {
printf("=== DBT interpreter guest memory bounds ===\n\n");
// A plain buffer rather than std::vector: this TU includes engine
// source, whose headers collide with <vector>.
//
static const size_t SIZE = 1u << 20; // 1 MB guest image
static uint8_t backing[SIZE];
memset(backing, 0xAB, SIZE);
rv64_memory_t mem;
mem.data = backing;
mem.size = SIZE;
// --- legitimate accesses still work (guards over-tightening) ---
mem_write8(&mem, 0x1000, 0x5A);
expect(mem_read8(&mem, 0x1000) == 0x5A, "in-range byte round-trips");
mem_write64(&mem, SIZE - 8, 0x0123456789ABCDEFull);
expect(mem_read64(&mem, SIZE - 8) == 0x0123456789ABCDEFull,
"8-byte access ending exactly at the limit is allowed");
expect(mem_check(&mem, SIZE, 0), "zero-length access at the limit is allowed");
expect(mem_check(&mem, 0, SIZE), "whole-image access is allowed");
// --- ordinary out-of-range is refused (unchanged by #1292) ---
expect(!mem_check(&mem, SIZE - 7, 8), "8 bytes ending one past the limit is refused");
expect(!mem_check(&mem, SIZE + 4096, 8), "far out-of-range is refused");
expect(!mem_check(&mem, SIZE, 1), "one byte at the limit is refused");
// --- WRAP: the #1292 cases. These are the only ones that
// discriminate against the pre-fix check. ---
expect(!mem_check(&mem, 0xFFFFFFFFFFFFFFFFull, 2),
"WRAP addr=2^64-1 len=2 must be refused");
expect(!mem_check(&mem, 0xFFFFFFFFFFFFFFFCull, 8),
"WRAP addr=2^64-4 len=8 must be refused");
expect(!mem_check(&mem, 0xFFFFFFFFFFFFF000ull, 8192),
"WRAP addr near 2^64 with a large len must be refused");
// A wrapping read must also return the safe value rather than index
// the backing store, which is what the check exists to prevent.
expect(mem_read8(&mem, 0xFFFFFFFFFFFFFFFFull) == 0,
"WRAP read8 returns 0 rather than indexing out of bounds");
expect(mem_read64(&mem, 0xFFFFFFFFFFFFFFFCull) == 0,
"WRAP read64 returns 0 rather than indexing out of bounds");
// A wrapping write must leave the image untouched. Byte 0 is the
// one an unwrapped index would be most likely to reach.
const uint8_t before = backing[0];
mem_write64(&mem, 0xFFFFFFFFFFFFFFFCull, 0xDEADBEEFDEADBEEFull);
expect(backing[0] == before, "WRAP write64 must not modify guest memory");
// --- FCVT conformance against qemu-derived golden values ---
memset(backing, 0, SIZE);
test_fcvt_conformance(&mem);
printf("\n=== dbt interp: %d passed, %d failed ===\n", g_pass, g_fail);
return (g_fail > 0) ? 1 : 0;
}