This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

iWMMXt unwind support


This patch adds support to the ARM EABI unwinder for restoring iWMMXt
registers.  Tested arm-linux-gnueabi -mcpu=iwmmxt.  OK to commit?

One possible problem is that the unwind support code in libunwind.S
uses ldc2 and stc2 instructions which are ARM v5 instructions.  They
only ever get executed on a CPU supporting iWMMXt (because only on
such a CPU will there have been iWMMXt registers saved that need
restoring), but cause the object file to be marked as v5, which causes
problems if you are building libgcc for v4.  To avoid this
.object_arch is used to mark the object file as v4 if building for
v4.  This feature was added to binutils by
<http://sourceware.org/ml/binutils/2006-09/msg00054.html> and so isn't
in binutils 2.17.  Is it OK for GCC trunk to require unreleased CVS
binutils to build for ARM EABI targets for v4, or is it necessary to
have configure checks to disable the .object_arch use (and remove the
problem instructions) for older binutils?

(To use the new unwind support at runtime you also need my binutils
patch <http://sourceware.org/ml/binutils/2006-08/msg00207.html>, again
not in 2.17, so that the assembler generates correct unwind
information for iWMMXt register saves; without this the code will
compile but the unwind information will be wrong and so unwinding will
go wrong at runtime.)

gcc:
2007-01-02  Paul Brook  <paul@codesourcery.com>

	* config/arm/libunwind.S: Add .arch/.object_arch for armv4 builds.

gcc:
2007-01-02  Joseph Myers  <joseph@codesourcery.com>

	* config/arm/unwind-arm.c (struct wmmxd_regs, struct wmmxc_regs):
	New.
	(phase1_vrs): Use them.
	(DEMAND_SAVE_WMMXD, DEMAND_SAVE_WMMXC): New.
	(__gnu_Unwind_Save_WMMXD, __gnu_Unwind_Restore_WMMXD,
	__gnu_Unwind_Save_WMMXC, __gnu_Unwind_Restore_WMMXC): Declare.
	(restore_non_core_regs): Call __gnu_Unwind_Restore_WMMXD and
	__gnu_Unwind_Restore_WMMXC if required.
	(_Unwind_VRS_Pop): Implement iWMMXt support.
	* config/arm/libunwind.S (gnu_Unwind_Restore_WMMXD,
	gnu_Unwind_Save_WMMXD, gnu_Unwind_Restore_WMMXC,
	gnu_Unwind_Save_WMMXC): Define.

gcc/testsuite:
2007-01-02  Joseph Myers  <joseph@codesourcery.com>

	* g++.dg/eh/arm-iwmmxt-unwind.C: New test.

Index: gcc/testsuite/g++.dg/eh/arm-iwmmxt-unwind.C
===================================================================
--- gcc/testsuite/g++.dg/eh/arm-iwmmxt-unwind.C	(revision 0)
+++ gcc/testsuite/g++.dg/eh/arm-iwmmxt-unwind.C	(revision 0)
@@ -0,0 +1,47 @@
+/* Test unwinding of iWMMXt register saves.  */
+/* Origin: Joseph Myers <joseph@codesourcery.com> */
+
+/* { dg-do run } */
+/* { dg-require-effective-target arm32 } */
+
+#ifdef __IWMMXT__
+extern "C" void abort (void);
+extern "C" void exit (int);
+
+void
+foo (void)
+{
+  register long long wr10 asm("wr10") = 0;
+  register long long wr11 asm("wr11") = 1;
+  register long long wr12 asm("wr12") = 2;
+  register long long wr14 asm("wr14") = 4;
+  register long long wr15 asm("wr15") = 5;
+  asm volatile ("" : "+y" (wr10), "+y" (wr11), "+y" (wr12), "+y" (wr14), "+y" (wr15));
+  throw "";
+}
+
+int
+main (void)
+{
+  register long long wr10 asm("wr10") = 10;
+  register long long wr11 asm("wr11") = 11;
+  register long long wr12 asm("wr12") = 12;
+  register long long wr13 asm("wr13") = 13;
+  register long long wr14 asm("wr14") = 14;
+  register long long wr15 asm("wr15") = 15;
+  asm volatile ("" : "+y" (wr10), "+y" (wr11), "+y" (wr12), "+y" (wr13), "+y" (wr14), "+y" (wr15));
+  try {
+    foo ();
+  } catch (...) {
+    asm volatile ("" : "+y" (wr10), "+y" (wr11), "+y" (wr12), "+y" (wr13), "+y" (wr14), "+y" (wr15));
+    if (wr10 != 10 || wr11 != 11 || wr12 != 12 || wr13 != 13 || wr14 != 14 || wr15 != 15)
+      abort ();
+  }
+  exit (0);
+}
+#else
+int
+main (void)
+{
+}
+#endif
Index: gcc/config/arm/libunwind.S
===================================================================
--- gcc/config/arm/libunwind.S	(revision 120317)
+++ gcc/config/arm/libunwind.S	(working copy)
@@ -41,6 +41,18 @@
 	EQUIV SYM (\name), SYM (__\name)
 .endm
 
+#if (__ARM_ARCH__ == 4) && !defined(__thumb2__)
+/* Some coprocessors require armv5.  We know this code will never be run on
+   other cpus.  Tell gas to allow armv5, but only mark the objects as armv4.
+ */
+.arch armv5t
+#ifdef __ARM_ARCH_4T__
+.object_arch armv4t
+#else
+.object_arch armv4
+#endif
+#endif
+
 /* r0 points to a 16-word block.  Upload these values to the actual core
    state.  */
 ARM_FUNC_START restore_core_regs
@@ -108,6 +120,66 @@
 	stcl  p11,cr0,[r0],{0x20} /* vstm r0, {d16-d31} */
 	RET
 
+ARM_FUNC_START gnu_Unwind_Restore_WMMXD
+	/* Use the generic coprocessor form so that gas doesn't complain
+	   on non-iWMMXt targets.  */
+	ldcl  p1, cr0, [r0], #8 /* wldrd wr0, [r0], #8 */
+	ldcl  p1, cr1, [r0], #8 /* wldrd wr1, [r0], #8 */
+	ldcl  p1, cr2, [r0], #8 /* wldrd wr2, [r0], #8 */
+	ldcl  p1, cr3, [r0], #8 /* wldrd wr3, [r0], #8 */
+	ldcl  p1, cr4, [r0], #8 /* wldrd wr4, [r0], #8 */
+	ldcl  p1, cr5, [r0], #8 /* wldrd wr5, [r0], #8 */
+	ldcl  p1, cr6, [r0], #8 /* wldrd wr6, [r0], #8 */
+	ldcl  p1, cr7, [r0], #8 /* wldrd wr7, [r0], #8 */
+	ldcl  p1, cr8, [r0], #8 /* wldrd wr8, [r0], #8 */
+	ldcl  p1, cr9, [r0], #8 /* wldrd wr9, [r0], #8 */
+	ldcl  p1, cr10, [r0], #8 /* wldrd wr10, [r0], #8 */
+	ldcl  p1, cr11, [r0], #8 /* wldrd wr11, [r0], #8 */
+	ldcl  p1, cr12, [r0], #8 /* wldrd wr12, [r0], #8 */
+	ldcl  p1, cr13, [r0], #8 /* wldrd wr13, [r0], #8 */
+	ldcl  p1, cr14, [r0], #8 /* wldrd wr14, [r0], #8 */
+	ldcl  p1, cr15, [r0], #8 /* wldrd wr15, [r0], #8 */
+	RET
+
+ARM_FUNC_START gnu_Unwind_Save_WMMXD
+	/* Use the generic coprocessor form so that gas doesn't complain
+	   on non-iWMMXt targets.  */
+	stcl  p1, cr0, [r0], #8 /* wstrd wr0, [r0], #8 */
+	stcl  p1, cr1, [r0], #8 /* wstrd wr1, [r0], #8 */
+	stcl  p1, cr2, [r0], #8 /* wstrd wr2, [r0], #8 */
+	stcl  p1, cr3, [r0], #8 /* wstrd wr3, [r0], #8 */
+	stcl  p1, cr4, [r0], #8 /* wstrd wr4, [r0], #8 */
+	stcl  p1, cr5, [r0], #8 /* wstrd wr5, [r0], #8 */
+	stcl  p1, cr6, [r0], #8 /* wstrd wr6, [r0], #8 */
+	stcl  p1, cr7, [r0], #8 /* wstrd wr7, [r0], #8 */
+	stcl  p1, cr8, [r0], #8 /* wstrd wr8, [r0], #8 */
+	stcl  p1, cr9, [r0], #8 /* wstrd wr9, [r0], #8 */
+	stcl  p1, cr10, [r0], #8 /* wstrd wr10, [r0], #8 */
+	stcl  p1, cr11, [r0], #8 /* wstrd wr11, [r0], #8 */
+	stcl  p1, cr12, [r0], #8 /* wstrd wr12, [r0], #8 */
+	stcl  p1, cr13, [r0], #8 /* wstrd wr13, [r0], #8 */
+	stcl  p1, cr14, [r0], #8 /* wstrd wr14, [r0], #8 */
+	stcl  p1, cr15, [r0], #8 /* wstrd wr15, [r0], #8 */
+	RET
+
+ARM_FUNC_START gnu_Unwind_Restore_WMMXC
+	/* Use the generic coprocessor form so that gas doesn't complain
+	   on non-iWMMXt targets.  */
+	ldc2  p1, cr8, [r0], #4 /* wldrw wcgr0, [r0], #4 */
+	ldc2  p1, cr9, [r0], #4 /* wldrw wcgr1, [r0], #4 */
+	ldc2  p1, cr10, [r0], #4 /* wldrw wcgr2, [r0], #4 */
+	ldc2  p1, cr11, [r0], #4 /* wldrw wcgr3, [r0], #4 */
+	RET
+
+ARM_FUNC_START gnu_Unwind_Save_WMMXC
+	/* Use the generic coprocessor form so that gas doesn't complain
+	   on non-iWMMXt targets.  */
+	stc2  p1, cr8, [r0], #4 /* wstrw wcgr0, [r0], #4 */
+	stc2  p1, cr9, [r0], #4 /* wstrw wcgr1, [r0], #4 */
+	stc2  p1, cr10, [r0], #4 /* wstrw wcgr2, [r0], #4 */
+	stc2  p1, cr11, [r0], #4 /* wstrw wcgr3, [r0], #4 */
+	RET
+
 /* Wrappers to save core registers, then call the real routine.   */
 
 .macro  UNWIND_WRAPPER name nargs
Index: gcc/config/arm/unwind-arm.c
===================================================================
--- gcc/config/arm/unwind-arm.c	(revision 120317)
+++ gcc/config/arm/unwind-arm.c	(working copy)
@@ -91,6 +91,16 @@
   struct fpa_reg f[8];
 };
 
+struct wmmxd_regs
+{
+  _uw64 wd[16];
+};
+
+struct wmmxc_regs
+{
+  _uw wc[4];
+};
+
 /* Unwind descriptors.  */
 
 typedef struct
@@ -123,12 +133,18 @@
   struct vfp_regs vfp;
   struct vfpv3_regs vfp_regs_16_to_31;
   struct fpa_regs fpa;
+  struct wmmxd_regs wmmxd;
+  struct wmmxc_regs wmmxc;
 } phase1_vrs;
 
 #define DEMAND_SAVE_VFP 1	/* VFP state has been saved if not set */
 #define DEMAND_SAVE_VFP_D 2	/* VFP state is for FLDMD/FSTMD if set */
 #define DEMAND_SAVE_VFP_V3 4    /* VFPv3 state for regs 16 .. 31 has
                                    been saved if not set */
+#define DEMAND_SAVE_WMMXD 8	/* iWMMXt data registers have been
+				   saved if not set.  */
+#define DEMAND_SAVE_WMMXC 16	/* iWMMXt control registers have been
+				   saved if not set.  */
 
 /* This must match the structure created by the assembly wrappers.  */
 typedef struct
@@ -157,6 +173,10 @@
 /* Routines for FLDMX/FSTMX format...  */
 void __gnu_Unwind_Save_VFP (struct vfp_regs * p);
 void __gnu_Unwind_Restore_VFP (struct vfp_regs * p);
+void __gnu_Unwind_Save_WMMXD (struct wmmxd_regs * p);
+void __gnu_Unwind_Restore_WMMXD (struct wmmxd_regs * p);
+void __gnu_Unwind_Save_WMMXC (struct wmmxc_regs * p);
+void __gnu_Unwind_Restore_WMMXC (struct wmmxc_regs * p);
 
 /* ...and those for FLDMD/FSTMD format...  */
 void __gnu_Unwind_Save_VFP_D (struct vfp_regs * p);
@@ -181,6 +201,11 @@
 
   if ((vrs->demand_save_flags & DEMAND_SAVE_VFP_V3) == 0)
     __gnu_Unwind_Restore_VFP_D_16_to_31 (&vrs->vfp_regs_16_to_31);
+
+  if ((vrs->demand_save_flags & DEMAND_SAVE_WMMXD) == 0)
+    __gnu_Unwind_Restore_WMMXD (&vrs->wmmxd);
+  if ((vrs->demand_save_flags & DEMAND_SAVE_WMMXC) == 0)
+    __gnu_Unwind_Restore_WMMXC (&vrs->wmmxc);
 }
 
 /* A better way to do this would probably be to compare the absolute address
@@ -421,10 +446,81 @@
       return _UVRSR_OK;
 
     case _UVRSC_FPA:
+      return _UVRSR_NOT_IMPLEMENTED;
+
     case _UVRSC_WMMXD:
+      {
+	_uw start = discriminator >> 16;
+	_uw count = discriminator & 0xffff;
+	struct wmmxd_regs tmp;
+	_uw *sp;
+	_uw *dest;
+
+	if ((representation != _UVRSD_UINT64) || start + count > 16)
+	  return _UVRSR_FAILED;
+
+	if (vrs->demand_save_flags & DEMAND_SAVE_WMMXD)
+	  {
+	    /* Demand-save resisters for stage1.  */
+	    vrs->demand_save_flags &= ~DEMAND_SAVE_WMMXD;
+	    __gnu_Unwind_Save_WMMXD (&vrs->wmmxd);
+	  }
+
+	/* Restore the registers from the stack.  Do this by saving the
+	   current WMMXD registers to a memory area, moving the in-memory
+	   values into that area, and restoring from the whole area.  */
+	__gnu_Unwind_Save_WMMXD (&tmp);
+
+	/* The stack address is only guaranteed to be word aligned, so
+	   we can't use doubleword copies.  */
+	sp = (_uw *) vrs->core.r[R_SP];
+	dest = (_uw *) &tmp.wd[start];
+	count *= 2;
+	while (count--)
+	  *(dest++) = *(sp++);
+
+	/* Set the new stack pointer.  */
+	vrs->core.r[R_SP] = (_uw) sp;
+
+	/* Reload the registers.  */
+	__gnu_Unwind_Restore_WMMXD (&tmp);
+      }
+      return _UVRSR_OK;
+
     case _UVRSC_WMMXC:
-      return _UVRSR_NOT_IMPLEMENTED;
+      {
+	int i;
+	struct wmmxc_regs tmp;
+	_uw *sp;
 
+	if ((representation != _UVRSD_UINT32) || discriminator > 16)
+	  return _UVRSR_FAILED;
+
+	if (vrs->demand_save_flags & DEMAND_SAVE_WMMXC)
+	  {
+	    /* Demand-save resisters for stage1.  */
+	    vrs->demand_save_flags &= ~DEMAND_SAVE_WMMXC;
+	    __gnu_Unwind_Save_WMMXC (&vrs->wmmxc);
+	  }
+
+	/* Restore the registers from the stack.  Do this by saving the
+	   current WMMXC registers to a memory area, moving the in-memory
+	   values into that area, and restoring from the whole area.  */
+	__gnu_Unwind_Save_WMMXC (&tmp);
+
+	sp = (_uw *) vrs->core.r[R_SP];
+	for (i = 0; i < 4; i++)
+	  if (discriminator & (1 << i))
+	    tmp.wc[i] = *(sp++);
+
+	/* Set the new stack pointer.  */
+	vrs->core.r[R_SP] = (_uw) sp;
+
+	/* Reload the registers.  */
+	__gnu_Unwind_Restore_WMMXC (&tmp);
+      }
+      return _UVRSR_OK;
+
     default:
       return _UVRSR_FAILED;
     }

-- 
Joseph S. Myers
joseph@codesourcery.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]