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]

[PATCH, ARM] ARM EHABI support for Objective-C (libobjc)


Hi,

This patch implements exception-handling support for Objective-C on ARM
EABI Linux systems, using the ARM-defined EHABI. This patch gets things
working fairly well, though at least one issue remains -- but this is a
start, at least.

Test results are clean, apart from the following:

FAIL: objc/execute/exceptions/foward-1.m execution,  -O3
-fomit-frame-pointer -funroll-loops  -fgnu-runtime

FAIL: objc/execute/exceptions/foward-1.m execution,  -O3
-fomit-frame-pointer -funroll-all-loops -finline-functions
-fgnu-runtime

AFAICT, these seem to be caused by data-flow information being corrupted
somehow: the failure happens during the regrename pass, but I don't
think the underlying cause is there. On entry to the landing pad:

  @catch (id error)
  {
    i++;
    [error free];
  }

The "error" pointer gets scrambled, because the register it should be
passed in, r0, is not marked as live-on-entry to the associated block,
so register-renaming thinks it's safe to overwrite that register.

On the 4.3 branch, defining the EH_USES macro in arm.h as follows fixed
this issue:

/* r0 may be used by catch handlers.  Make sure it's marked live on
   entry to those.  */
#define EH_USES(N) ((N) == 0)

But, this solution doesn't seem to work on mainline. Does anyone know
why that might be?

Is the attached patch OK for mainline, despite the above shortcoming?
(Tested with cross to ARM EABI Linux).

Cheers,

Julian

ChangeLog

    * configure.ac (arm*-*-linux-gnueabi): Don't disable building
    of libobjc for ARM EABI Linux.
    * configure: Regenerate.

    libobjc/
    * exception.c (__objc_exception_class): Initialise as constant
    array for ARM EABI. Change macro to static const for non-ARM EABI.
    (ObjcException): Add note about structure layout. Remove landingPad
    and handlerSwitchValue for ARM EABI.
    (get_ttype_entry): Add __ARM_EABI_UNWINDER__ version
    of function.
    (CONTINUE_UNWINDING): Define for ARM EABI/otherwise cases.
    (PERSONALITY_FUNCTION): Use ARM EABI-specific arguments, and add
    ARM EABI unwinding support.
    (objc_exception_throw): Use memcpy to initialise exception class.
--- .pc/libobjc-arm-eabi-support/configure.ac	2008-04-30 05:15:45.000000000 -0700
+++ configure.ac	2008-05-06 05:41:51.000000000 -0700
@@ -538,7 +538,6 @@ case "${target}" in
     ;;
   arm*-*-linux-gnueabi)
     noconfigdirs="$noconfigdirs target-qthreads"
-    noconfigdirs="$noconfigdirs target-libobjc"
     case ${with_newlib} in
       no) noconfigdirs="$noconfigdirs target-newlib target-libgloss"
     esac
--- .pc/libobjc-arm-eabi-support/libobjc/exception.c	2008-04-30 05:15:45.000000000 -0700
+++ libobjc/exception.c	2008-05-06 05:41:51.000000000 -0700
@@ -31,16 +31,25 @@ Boston, MA 02110-1301, USA.  */
 #include "unwind-pe.h"
 
 
+#ifdef __ARM_EABI_UNWINDER__
+
+const _Unwind_Exception_Class __objc_exception_class
+  = {'G', 'N', 'U', 'C', 'O', 'B', 'J', 'C'};
+  
+#else
+
 /* This is the exception class we report -- "GNUCOBJC".  */
-#define __objc_exception_class			\
-  ((((((((_Unwind_Exception_Class) 'G'		\
-         << 8 | (_Unwind_Exception_Class) 'N')	\
-        << 8 | (_Unwind_Exception_Class) 'U')	\
-       << 8 | (_Unwind_Exception_Class) 'C')	\
-      << 8 | (_Unwind_Exception_Class) 'O')	\
-     << 8 | (_Unwind_Exception_Class) 'B')	\
-    << 8 | (_Unwind_Exception_Class) 'J')	\
-   << 8 | (_Unwind_Exception_Class) 'C')
+static const _Unwind_Exception_Class __objc_exception_class
+  = ((((((((_Unwind_Exception_Class) 'G'
+            << 8 | (_Unwind_Exception_Class) 'N')
+           << 8 | (_Unwind_Exception_Class) 'U')
+          << 8 | (_Unwind_Exception_Class) 'C')
+         << 8 | (_Unwind_Exception_Class) 'O')
+        << 8 | (_Unwind_Exception_Class) 'B')
+       << 8 | (_Unwind_Exception_Class) 'J')
+      << 8 | (_Unwind_Exception_Class) 'C');
+
+#endif
 
 /* This is the object that is passed around by the Objective C runtime
    to represent the exception in flight.  */
@@ -50,12 +59,18 @@ struct ObjcException
   /* This bit is needed in order to interact with the unwind runtime.  */
   struct _Unwind_Exception base;
 
-  /* The actual object we want to throw.  */
+  /* The actual object we want to throw. Note: must come immediately after
+     unwind header.  */
   id value;
 
+#ifdef __ARM_EABI_UNWINDER__
+  /* Note: we use the barrier cache defined in the unwind control block for
+     ARM EABI.  */
+#else
   /* Cache some internal unwind data between phase 1 and phase 2.  */
   _Unwind_Ptr landingPad;
   int handlerSwitchValue;
+#endif
 };
 
 
@@ -106,6 +121,24 @@ parse_lsda_header (struct _Unwind_Contex
   return p;
 }
 
+#ifdef __ARM_EABI_UNWINDER__
+
+static Class
+get_ttype_entry (struct lsda_header_info *info, _uleb128_t i)
+{
+  _Unwind_Ptr ptr;
+  
+  ptr = (_Unwind_Ptr) (info->TType - (i * 4));
+  ptr = _Unwind_decode_target2 (ptr);
+  
+  if (ptr)
+    return objc_get_class ((const char *) ptr);
+  else
+    return 0;
+}
+
+#else
+
 static Class
 get_ttype_entry (struct lsda_header_info *info, _Unwind_Word i)
 {
@@ -122,6 +155,8 @@ get_ttype_entry (struct lsda_header_info
     return 0;
 }
 
+#endif
+
 /* Like unto the method of the same name on Object, but takes an id.  */
 /* ??? Does this bork the meta-type system?  Can/should we look up an
    isKindOf method on the id?  */
@@ -150,12 +185,32 @@ isKindOf (id value, Class target)
 #define PERSONALITY_FUNCTION	__gnu_objc_personality_v0
 #endif
 
+#ifdef __ARM_EABI_UNWINDER__
+
+#define CONTINUE_UNWINDING \
+  do								\
+    {								\
+      if (__gnu_unwind_frame(ue_header, context) != _URC_OK)	\
+	return _URC_FAILURE;					\
+      return _URC_CONTINUE_UNWIND;				\
+    }								\
+  while (0)
+
+_Unwind_Reason_Code
+PERSONALITY_FUNCTION (_Unwind_State state,
+		      struct _Unwind_Exception *ue_header,
+		      struct _Unwind_Context *context)
+#else
+
+#define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
+
 _Unwind_Reason_Code
 PERSONALITY_FUNCTION (int version,
 		      _Unwind_Action actions,
 		      _Unwind_Exception_Class exception_class,
 		      struct _Unwind_Exception *ue_header,
 		      struct _Unwind_Context *context)
+#endif
 {
   struct ObjcException *xh = (struct ObjcException *) ue_header;
 
@@ -165,19 +220,65 @@ PERSONALITY_FUNCTION (int version,
   const unsigned char *p;
   _Unwind_Ptr landing_pad, ip;
   int handler_switch_value;
-  int saw_cleanup = 0, saw_handler;
+  int saw_cleanup = 0, saw_handler, foreign_exception;
   void *return_object;
+  int ip_before_insn = 0;
+
+#ifdef __ARM_EABI_UNWINDER__
+  _Unwind_Action actions;
+  
+  switch (state & _US_ACTION_MASK)
+    {
+    case _US_VIRTUAL_UNWIND_FRAME:
+      actions = _UA_SEARCH_PHASE;
+      break;
+
+    case _US_UNWIND_FRAME_STARTING:
+      actions = _UA_CLEANUP_PHASE;
+      if (!(state & _US_FORCE_UNWIND)
+	  && ue_header->barrier_cache.sp == _Unwind_GetGR (context, 13))
+	actions |= _UA_HANDLER_FRAME;
+      break;
+
+    case _US_UNWIND_FRAME_RESUME:
+      CONTINUE_UNWINDING;
+      break;
+
+    default:
+      abort();
+    }
+  actions |= state & _US_FORCE_UNWIND;
 
+  /* TODO: Foreign exceptions need some attention (e.g. rethrowing doesn't
+     work).  */
+  foreign_exception = 0;
+
+  /* The dwarf unwinder assumes the context structure holds things like the
+     function and LSDA pointers.  The ARM implementation caches these in
+     the exception header (UCB).  To avoid rewriting everything we make the
+     virtual IP register point at the UCB.  */
+  ip = (_Unwind_Ptr) ue_header;
+  _Unwind_SetGR (context, 12, ip);
+
+#else  /* !__ARM_EABI_UNWINDER.  */
   /* Interface version check.  */
   if (version != 1)
     return _URC_FATAL_PHASE1_ERROR;
+  
+  foreign_exception = (exception_class != __objc_exception_class);
+#endif
 
   /* Shortcut for phase 2 found handler for domestic exception.  */
   if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
-      && exception_class == __objc_exception_class)
+      && !foreign_exception)
     {
+#ifdef __ARM_EABI_UNWINDER__
+      handler_switch_value = (int) ue_header->barrier_cache.bitpattern[1];
+      landing_pad = (_Unwind_Ptr) ue_header->barrier_cache.bitpattern[3];
+#else
       handler_switch_value = xh->handlerSwitchValue;
       landing_pad = xh->landingPad;
+#endif
       goto install_context;
     }
 
@@ -186,12 +287,18 @@ PERSONALITY_FUNCTION (int version,
 
   /* If no LSDA, then there are no handlers or cleanups.  */
   if (! language_specific_data)
-    return _URC_CONTINUE_UNWIND;
+    CONTINUE_UNWINDING;
 
   /* Parse the LSDA header.  */
   p = parse_lsda_header (context, language_specific_data, &info);
   info.ttype_base = base_of_encoded_value (info.ttype_encoding, context);
+#ifdef HAVE_GETIPINFO
+  ip = _Unwind_GetIPInfo (context, &ip_before_insn);
+#else
   ip = _Unwind_GetIP (context) - 1;
+#endif
+  if (!ip_before_insn)
+    --ip;
   landing_pad = 0;
   action_record = 0;
   handler_switch_value = 0;
@@ -250,7 +357,7 @@ PERSONALITY_FUNCTION (int version,
   /* If ip is not present in the table, C++ would call terminate.  */
   /* ??? As with Java, it's perhaps better to tweek the LSDA to
      that no-action is mapped to no-entry.  */
-  return _URC_CONTINUE_UNWIND;
+  CONTINUE_UNWINDING;
 
  found_something:
   saw_cleanup = 0;
@@ -287,8 +394,7 @@ PERSONALITY_FUNCTION (int version,
 
 	  /* During forced unwinding, we only run cleanups.  With a
 	     foreign exception class, we have no class info to match.  */
-	  else if ((actions & _UA_FORCE_UNWIND)
-		   || exception_class != __objc_exception_class)
+	  else if ((actions & _UA_FORCE_UNWIND) || foreign_exception)
 	    ;
 
 	  else if (ar_filter > 0)
@@ -318,18 +424,24 @@ PERSONALITY_FUNCTION (int version,
     }
 
   if (! saw_handler && ! saw_cleanup)
-    return _URC_CONTINUE_UNWIND;
+    CONTINUE_UNWINDING;
 
   if (actions & _UA_SEARCH_PHASE)
     {
       if (!saw_handler)
-	return _URC_CONTINUE_UNWIND;
+	CONTINUE_UNWINDING;
 
       /* For domestic exceptions, we cache data from phase 1 for phase 2.  */
-      if (exception_class == __objc_exception_class)
+      if (!foreign_exception)
         {
+#ifdef __ARM_EABI_UNWINDER__
+	  ue_header->barrier_cache.sp = _Unwind_GetGR (context, 13);
+	  ue_header->barrier_cache.bitpattern[1] = (_uw) handler_switch_value;
+	  ue_header->barrier_cache.bitpattern[3] = (_uw) landing_pad;
+#else
           xh->handlerSwitchValue = handler_switch_value;
           xh->landingPad = landing_pad;
+#endif
 	}
       return _URC_HANDLER_FOUND;
     }
@@ -361,7 +473,9 @@ void
 objc_exception_throw (id value)
 {
   struct ObjcException *header = calloc (1, sizeof (*header));
-  header->base.exception_class = __objc_exception_class;
+  
+  memcpy (&header->base.exception_class, &__objc_exception_class,
+	  sizeof (__objc_exception_class));
   header->base.exception_cleanup = __objc_exception_cleanup;
   header->value = value;
 

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