[WIP/RFC] MIPS registers overhaul

Andrew Cagney ac131313@redhat.com
Sun Jun 15 17:23:00 GMT 2003


>> 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.




More information about the Gdb-patches mailing list