[llvm-commits] Enable exceptions in JIT for Darwin

Evan Cheng evan.cheng at apple.com
Tue Aug 26 23:43:06 PDT 2008


On Aug 24, 2008, at 2:21 AM, Nicolas Geoffray wrote:

> Hi Evan,
>
> Evan Cheng wrote:
>>
>> +  // Allocate a new LibgccObject to represent this frame.
>> Deallocation of this
>> +  // object may be impossible: libgcc does not seem to deallocate  
>> it,
>> even with
>> +  // __deregister_frame. Deallocating it manually may also cause
>> libgcc to
>> +  // crash.
>> +  struct LibgccObject* ob = (struct LibgccObject*)
>> +    malloc(sizeof(struct LibgccObject));
>>
>> Are you sure you can't deallocate it when the JIT is teared down?
>>
>
> Not really sure. I can add a table of LibgccObject* to the LLVM JIT,  
> and
> deallocate them when the JIT is destroyed. However, libgcc will  
> probably
> dislike this if exceptions still occur after the JIT is destroyed. It
> may be feasible to remove the exception tables from libgcc's linked
> list, but libgcc may cache the tables some places I don't know.
>
> A deep understanding of how things work in libgcc is needed :). And I
> double-checked, my comment is actually wrong, the __deregister_frame
> function does deallocate the objects. However, it does not work with  
> the
> darwin code for libgcc...

After looking into this a bit, I think this is ok.

Nick, do you know anything about this?

Thanks,

Evan

>
>
> Nicolas
>
>
>> Evan
>>
>>
>> On Aug 21, 2008, at 1:20 AM, Nicolas Geoffray wrote:
>>
>>> Hi Evan,
>>>
>>> New patch with new un-gcc documentation :)
>>>
>>> Thanks for reviewing!
>>>
>>> Nicolas
>>>
>>> Evan Cheng wrote:
>>>> Hi Nicolas,
>>>>
>>>> Comments in line.
>>>>
>>>> On Aug 19, 2008, at 8:04 AM, Nicolas Geoffray wrote:
>>>>
>>>>
>>>>> Dear all,
>>>>>
>>>>> Here's a patch that enables exception handling on Darwin with the
>>>>> JIT. It's really ugly, it's a workaround to work with libgcc which
>>>>> is doing crazy stuff when unwinding the stack. The
>>>>> __register_frame  function was sufficient enough for Linux, but
>>>>> unfortunately, the  code for darwin erases by default the
>>>>> registered frames. In a  perfect world, I think libgcc should be
>>>>> modified....
>>>>>
>>>>>
>>>>> I'd be totally OK if you don't want this patch in, as it's an ugly
>>>>> workaround. I can move the patch to vmkit, and that would be fine
>>>>> by  me. However, this makes lli work out of the box with  
>>>>> exceptions
>>>>> and  darwin, so it's still an improvement.
>>>>>
>>>>> Nicolas
>>>>> Index: lib/ExecutionEngine/JIT/JIT.cpp
>>>>> = 
>>>>> ==================================================================
>>>>> --- lib/ExecutionEngine/JIT/JIT.cpp (revision 54920)
>>>>> +++ lib/ExecutionEngine/JIT/JIT.cpp (working copy)
>>>>> @@ -64,10 +64,73 @@
>>>>> }
>>>>> }
>>>>>
>>>>> +
>>>>> #if defined (__GNUC__)
>>>>> extern "C" void __register_frame(void*);
>>>>> -#endif
>>>>>
>>>>> +#if defined (__APPLE__)
>>>>> +struct object {
>>>>> +  void *pc_begin;
>>>>> +  void *tbase;
>>>>> +  void *dbase;
>>>>> +  void* real_begin;
>>>>> +
>>>>> +    union {
>>>>> +    struct {
>>>>> +      unsigned long sorted : 1;
>>>>> +      unsigned long from_array : 1;
>>>>> +      unsigned long mixed_encoding : 1;
>>>>> +      unsigned long encoding : 8;
>>>>> +      unsigned long count : 21;
>>>>> +    } b;
>>>>> +    size_t i;
>>>>> +  } s;
>>>>>
>>>>
>>>> Please pick more descriptive names than object, etc. Also, please
>>>> try  to match the naming convention used in the file. Some
>>>> documentation is  definitely welcome. :-)
>>>>
>>>>> +
>>>>> +  // Be pessimistic, include this field even if GCC
>>>>> +  // may not have it.
>>>>> +  char *fde_end;
>>>>> +
>>>>> +  struct object *next;
>>>>> +};
>>>>> +
>>>>> +extern "C" void _keymgr_set_and_unlock_processwide_ptr (int,  
>>>>> void *);
>>>>> +extern "C" void *_keymgr_get_and_lock_processwide_ptr (int);
>>>>> +#define KEYMGR_GCC3_DW2_OBJ_LIST        302     /* Dwarf2 object
>>>>> list  */
>>>>> +
>>>>> +struct km_object_info {
>>>>> +  struct object* seen_objects;
>>>>> +  struct object* unseen_objects;
>>>>> +  unsigned spare[2];
>>>>> +};
>>>>> +
>>>>> +extern "C" void darwin_register_frame(void* begin) {
>>>>> +  struct km_object_info* the_obj_info = (struct km_object_info*)
>>>>> +    _keymgr_get_and_lock_processwide_ptr  
>>>>> (KEYMGR_GCC3_DW2_OBJ_LIST);
>>>>> +
>>>>> +  struct object* ob = (struct object*)malloc(sizeof(struct  
>>>>> object));
>>>>>
>>>>
>>>> Who frees this?
>>>>
>>>>
>>>>> +  ob->pc_begin = (void *)-1;
>>>>> +  ob->tbase = 0;
>>>>> +  ob->dbase = 0;
>>>>> +  ob->real_begin = begin;
>>>>> +  ob->s.i = 0;
>>>>> +  //ob->s.b.encoding = DW_EH_PE_omit;
>>>>>
>>>>
>>>> Why is this commented out?
>>>>
>>>>
>>>>> +  ob->s.b.encoding = 0xff;
>>>>> +
>>>>> +  // Put the info on both places, as libgcc uses the first or the
>>>>> +  // the second field.
>>>>> +  ob->fde_end = (char*)the_obj_info->unseen_objects;
>>>>> +  ob->next = (struct object*)the_obj_info->unseen_objects;
>>>>>
>>>>
>>>> Are the casting necessary? Why not just define the types correctly.
>>>>
>>>>
>>>>> +
>>>>> +  the_obj_info->unseen_objects = ob;
>>>>> +
>>>>> +  _keymgr_set_and_unlock_processwide_ptr  
>>>>> (KEYMGR_GCC3_DW2_OBJ_LIST,
>>>>> +                                          the_obj_info);
>>>>> +
>>>>> +}
>>>>>
>>>>
>>>> Function level documentation please.
>>>>
>>>> Thanks!
>>>>
>>>> Evan
>>>>
>>>>
>>>>> +
>>>>> +#endif // __APPLE__
>>>>> +#endif // __GNUC__
>>>>> +
>>>>> /// createJIT - This is the factory method for creating a JIT for
>>>>> the current
>>>>> /// machine, it does not fall back to the interpreter.  This takes
>>>>> ownership
>>>>> /// of the module provider.
>>>>> @@ -108,8 +171,20 @@
>>>>>
>>>>> // Register routine for informing unwinding runtime about new EH
>>>>> frames
>>>>> #if defined(__GNUC__)
>>>>> +#if defined(__APPLE__)
>>>>> +  struct km_object_info* the_obj_info = (struct km_object_info*)
>>>>> +    _keymgr_get_and_lock_processwide_ptr  
>>>>> (KEYMGR_GCC3_DW2_OBJ_LIST);
>>>>> +
>>>>> +  if (!the_obj_info) {
>>>>> +    the_obj_info = (km_object_info*)malloc(sizeof(struct
>>>>> km_object_info));
>>>>> +    _keymgr_set_and_unlock_processwide_ptr  
>>>>> (KEYMGR_GCC3_DW2_OBJ_LIST,
>>>>> +                                            the_obj_info);
>>>>> +  }
>>>>> +  InstallExceptionTableRegister(darwin_register_frame);
>>>>> +#else
>>>>> InstallExceptionTableRegister(__register_frame);
>>>>> -#endif
>>>>> +#endif // __APPLE__
>>>>> +#endif // __GNUC__
>>>>>
>>>>>
>>>>> // Initialize passes.
>>>>> PM.doInitialization();
>>>>> _______________________________________________
>>>>> llvm-commits mailing list
>>>>> llvm-commits at cs.uiuc.edu <mailto:llvm-commits at cs.uiuc.edu>
>>>>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>>>>>
>>>>
>>>> _______________________________________________
>>>> llvm-commits mailing list
>>>> llvm-commits at cs.uiuc.edu <mailto:llvm-commits at cs.uiuc.edu>
>>>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>>>>
>>>
>>> Index: lib/ExecutionEngine/JIT/JIT.cpp
>>> ===================================================================
>>> --- lib/ExecutionEngine/JIT/JIT.cpp (revision 54988)
>>> +++ lib/ExecutionEngine/JIT/JIT.cpp (working copy)
>>> @@ -64,10 +64,128 @@
>>>  }
>>> }
>>>
>>> +
>>> #if defined (__GNUC__)
>>> +
>>> +// libgcc defines the __register_frame function to dynamically
>>> register new
>>> +// dwarf frames for exception handling. This functionality is not
>>> portable
>>> +// across compilers and is only provided by GCC. We use the
>>> __register_frame
>>> +// function here so that code generated by the JIT cooperates with
>>> the unwinding
>>> +// runtime of libgcc. When JITting with exception handling  
>>> enable, LLVM
>>> +// generates dwarf frames and registers it to libgcc with
>>> __register_frame.
>>> +//
>>> +// The __register_frame function works with Linux.
>>> +//
>>> +// Unfortunately, this functionality seems to be in libgcc after  
>>> the
>>> unwinding
>>> +// library of libgcc for darwin was written. The code for darwin
>>> overwrites the
>>> +// value updated by __register_frame with a value fetched with  
>>> "keymgr".
>>> +// "keymgr" is an obsolete functionality, which should be rewritten
>>> some day.
>>> +// In the meantime, since "keymgr" is on all libgccs shipped with
>>> apple-gcc, we
>>> +// need a workaround in LLVM which uses the "keymgr" to dynamically
>>> modify the
>>> +// values of an opaque key, used by libgcc to find dwarf tables.
>>> +
>>> extern "C" void __register_frame(void*);
>>> -#endif
>>>
>>> +#if defined (__APPLE__)
>>> +
>>> +namespace {
>>> +
>>> +// LibgccObject - This is the structure defined in libgcc. There is
>>> no #include
>>> +// provided for this structure, so we also define it here. libgcc
>>> calls it
>>> +// "struct object". The structure is undocumented in libgcc.
>>> +struct LibgccObject {
>>> +  void *unused1;
>>> +  void *unused2;
>>> +  void *unused3;
>>> +
>>> +  /// frame - Pointer to the exception table.
>>> +  void *frame;
>>> +
>>> +  /// encoding -  The encoding of the object?
>>> +  union {
>>> +    struct {
>>> +      unsigned long sorted : 1;
>>> +      unsigned long from_array : 1;
>>> +      unsigned long mixed_encoding : 1;
>>> +      unsigned long encoding : 8;
>>> +      unsigned long count : 21;
>>> +    } b;
>>> +    size_t i;
>>> +  } encoding;
>>> +
>>> +  /// fde_end - libgcc defines this field only if some macro is
>>> defined. We
>>> +  /// include this field even if it may not there, to make libgcc  
>>> happy.
>>> +  char *fde_end;
>>> +
>>> +  /// next - At least we know it's a chained list!
>>> +  struct LibgccObject *next;
>>> +};
>>> +
>>> +// "kemgr" stuff. Apparently, all frame tables are stored there.
>>> +extern "C" void _keymgr_set_and_unlock_processwide_ptr(int, void  
>>> *);
>>> +extern "C" void *_keymgr_get_and_lock_processwide_ptr(int);
>>> +#define KEYMGR_GCC3_DW2_OBJ_LIST        302     /* Dwarf2 object
>>> list  */
>>> +
>>> +/// LibgccObjectInfo - libgcc defines this struct as  
>>> km_object_info. It
>>> +/// probably contains all dwarf tables that are loaded.
>>> +struct LibgccObjectInfo {
>>> +
>>> +  /// seenObjects - LibgccObjects already parsed by the unwinding
>>> runtime.
>>> +  ///
>>> +  struct LibgccObject* seenObjects;
>>> +
>>> +  /// unseenObjects - LibgccObjects not parsed yet by the unwinding
>>> runtime.
>>> +  ///
>>> +  struct LibgccObject* unseenObjects;
>>> +
>>> +  unsigned unused[2];
>>> +};
>>> +
>>> +// for DW_EH_PE_omit
>>> +#include "llvm/Support/Dwarf.h"
>>> +
>>> +/// darwin_register_frame - Since __register_frame does not work
>>> with darwin's
>>> +/// libgcc,we provide our own function, which "tricks" libgcc by
>>> modifying the
>>> +/// "Dwarf2 object list" key.
>>> +void DarwinRegisterFrame(void* FrameBegin) {
>>> +  // Get the key.
>>> +  struct LibgccObjectInfo* LOI = (struct LibgccObjectInfo*)
>>> +    _keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
>>> +
>>> +  // Allocate a new LibgccObject to represent this frame.
>>> Deallocation of this
>>> +  // object may be impossible: libgcc does not seem to deallocate
>>> it, even with
>>> +  // __deregister_frame. Deallocating it manually may also cause
>>> libgcc to
>>> +  // crash.
>>> +  struct LibgccObject* ob = (struct LibgccObject*)
>>> +    malloc(sizeof(struct LibgccObject));
>>> +
>>> +  // Do like libgcc for the values of the field.
>>> +  ob->unused1 = (void *)-1;
>>> +  ob->unused2 = 0;
>>> +  ob->unused3 = 0;
>>> +  ob->frame = FrameBegin;
>>> +  ob->encoding.i = 0;
>>> +  ob->encoding.b.encoding = llvm::dwarf::DW_EH_PE_omit;
>>> +
>>> +  // Put the info on both places, as libgcc uses the first or the
>>> the second
>>> +  // field. Note that we rely on having two pointers here. If
>>> fde_end was a
>>> +  // char, things would get complicated.
>>> +  ob->fde_end = (char*)LOI->unseenObjects;
>>> +  ob->next = LOI->unseenObjects;
>>> +
>>> +  // Update the key's unseenObjects list.
>>> +  LOI->unseenObjects = ob;
>>> +
>>> +  // Finally update the "key". Apparently, libgcc requires it.
>>> +  _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST,
>>> +                                         LOI);
>>> +
>>> +}
>>> +
>>> +}
>>> +#endif // __APPLE__
>>> +#endif // __GNUC__
>>> +
>>> /// createJIT - This is the factory method for creating a JIT for  
>>> the
>>> current
>>> /// machine, it does not fall back to the interpreter.  This takes
>>> ownership
>>> /// of the module provider.
>>> @@ -108,8 +226,23 @@
>>>
>>>  // Register routine for informing unwinding runtime about new EH  
>>> frames
>>> #if defined(__GNUC__)
>>> +#if defined(__APPLE__)
>>> +  struct LibgccObjectInfo* LOI = (struct LibgccObjectInfo*)
>>> +    _keymgr_get_and_lock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST);
>>> +
>>> +  // The key is created on demand, and libgcc creates it the first
>>> time an
>>> +  // exception occurs. Since we need the key to register frames, we
>>> create
>>> +  // it now.
>>> +  if (!LOI) {
>>> +    LOI = (LibgccObjectInfo*)malloc(sizeof(struct  
>>> LibgccObjectInfo));
>>> +     
>>> _keymgr_set_and_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST,
>>> +                                           LOI);
>>> +  }
>>> +  InstallExceptionTableRegister(DarwinRegisterFrame);
>>> +#else
>>>  InstallExceptionTableRegister(__register_frame);
>>> -#endif
>>> +#endif // __APPLE__
>>> +#endif // __GNUC__
>>>
>>>  // Initialize passes.
>>>  PM.doInitialization();
>>> _______________________________________________
>>> llvm-commits mailing list
>>> llvm-commits at cs.uiuc.edu <mailto:llvm-commits at cs.uiuc.edu>
>>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>>
>> ------------------------------------------------------------------------
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at cs.uiuc.edu
>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits




More information about the llvm-commits mailing list