Re: [WIP/RFC] MIPS registers overhaul

Andrew Cagney
2003-06-15T17:23:29Z

>> For instance, do a 64 bit FP restore then clear the FR bit; the reverse;
>> some other variant; ...?
>
>
> So, if the process is running with FR=0, then the save/restore should
> ("must" i believe) be done with FR=0.
>
> When FR=0, there are two options as to how to do it:
>
> for (i = 0; i < 32; i++)
> move/store word from c1 reg $i (i.e., dmfc1/sdc1)

mfc1/sc1

> OR:
>
> for (i = 0; i < 32; i += 2)
> move/store dword from c1 reg $i (i.e., dmfc1/sdc1)

OK, I'm going to go out on a limb here. I don't think the two are
equivalent, and I think the second is wrong. For big-endian, the second
would store fp[n+0] ||| fp[n+1] backwards.

When extracting a double from a MIPS register pair, GDB does the rough
equivalent of:

if (big-endian && type == double)
memcpy (dest + 0, &fp[regnum + 1], 4);
memcpy (dest + 4, &fp[regnum + 0], 4);
else if (little-endian && type == double)
memcpy (dest + 0, &fp[regnum + 0], 4);
memcpy (dest + 4, &fp[regnum + 1], 4);

(See register_to_type. The code dates back to ~92 and was added to fix
IRIX double value display problems.) The effect is to always get the
least significant part of a double value from fp[n+0], and the most
significant from fp[n+1].

Given MIPS xor endian sillyness, it's hard to see this is happening,
however looking at (MIPS IV ref 3.2) DMFC1, given a 32 bit FGR, it does:

GPR[rt] = FGR[fs+1] ||| FGR[fs+0]

A double word store of that GPR would then order the value as:

0: FGR[fs+0]
1: FGR[fs+1]

little endian, and

0: FGR[fs+1]
1: FGR[fs+0]

big endian.

If you stare at LDC1 long enough, remembering that LoadMemory contains
the horrible XOR magic, you'll realise that it has the same effect.

Anyway, this, I believe, means that any implementation of:

union {
float flt;
double dbl;
int32 i32;
int64 i64;
} $fp0

is going to need, for BE, a big-byte little-word DOUBLE, and a similar
INT64. Otherwize, $f0.int32 would modify the wrong part of the double
register. Alternatively, some of those union values could be given
magic offsets. Looking at kevin's patch:

+ t = init_composite_type ("__gdb_mips_type_float_double_register_big",
+ TYPE_CODE_UNION);
+ append_composite_type_field (t, "i", builtin_type_uint64);
+ append_composite_type_field (t, "f", builtin_type_ieee_single_big);
+ f = &(TYPE_FIELDS (t))[TYPE_NFIELDS (t) - 1];
+ FIELD_BITPOS (f[0]) = 32;
+ append_composite_type_field (t, "d", builtin_type_ieee_double_big);
+
+ TYPE_NAME (t) = "mips_type_float_double_register_big";

It appears to have taken the second option.

Andrew

> (move to / load for the state restore, of course.)
>
> (of course, these will typically be written in assembly code, and
> "fully unrolled" -- the pseudo-C-code is to demonstrate the concept
> only.)
>
> either one is valid, though all implementations that I know of choose
> the latter because it's fewer instructions and almost certainly more
> efficient.
>
>
> the linux kernel presents that to o32 userland (o32 ptrace syscall) as
> an array of 32 32-bit values, but IIRC it's stored internally as (8
> byte reg, 8 byte pad) * 16.