Watch, Follow, &
Connect with Us

Please visit our new home
community.embarcadero.com.


Welcome, Guest
Guest Settings
Help

Thread: Why does the use of the delete statement trigger an automatic catch frame?


This question is not answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 3 - Last Post: Aug 12, 2015 2:05 AM Last Post By: Vladimir Ulchenko
Jan Dijkstra

Posts: 206
Registered: 11/4/99
Why does the use of the delete statement trigger an automatic catch frame?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 5, 2015 5:29 AM
Like subject states.

I'm using compile-to-assembly to inspect (and optimise) the code that is generated for my source code. I'm especially keen on eliminating automatic catch frames as much as possible for low level library functions that I know will be called a lot. Even to the point of moving a portion of a function to a separate one to ring fence the catch frame, if that portion is not unconditional in the parent function. That way, when the condition is not hit, no catch frame entry and exit code is executed.

I'm now working on a function where I introduce a local stack object to guard a counter, so I fully expected to see the catch frame code in the generated assembly source. Which is indeed there, as expected.

However, to my surprise I not only found references to my local guard object (both it's creation and destruction points), but I found a third reference as well. This one to a data type I only use as a pointer inside the function. Initially I thought it was a reference to the function's parameter (as one of the parameters is a pointer to this type), but after a bit of further digging (using trial and error with commenting out sections of code inside the function), I could trace it to the use of the delete statement.

The class, of which this function is a member, has a private data field, which holds a pointer to an object, called FTemplate

If I use the statement:

delete FTemplate;

then a try catch frame is generated by the compiler. If I comment that statement out, the try catch frame disappears from the generated code.

My question is, why does the compiler generate a catch frame for a delete statement? It's not an actual local object whose destruction needs to be guaranteed. delete is issued on a dumb pointer variable, which does not alter it's value after delete has done it's job. So why does it need to be guarded by a catch frame?
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Why does the use of the delete statement trigger an automatic catchframe?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 5, 2015 11:31 AM   in response to: Jan Dijkstra in response to: Jan Dijkstra
Jan wrote:

My question is, why does the compiler generate a catch frame
for a delete statement?

That is really hard to answer without seeing what the rest of your code looks
like. What is FTemplate actually declared as? What does the catch frame
look like, and what is it actually doing? Where exactly is the 'delete'
statement being called from? Is it inside a function that has a throw()
or __declspec(nothrow) specifier attached to it, by chance?

--
Remy Lebeau (TeamB)
Jan Dijkstra

Posts: 206
Registered: 11/4/99
Re: Why does the use of the delete statement trigger an automatic catchframe?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 6, 2015 3:37 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Jan wrote:

My question is, why does the compiler generate a catch frame
for a delete statement?

That is really hard to answer without seeing what the rest of your code looks
like. What is FTemplate actually declared as? What does the catch frame
look like, and what is it actually doing? Where exactly is the 'delete'
statement being called from? Is it inside a function that has a throw()
or __declspec(nothrow) specifier attached to it, by chance?

--
Remy Lebeau (TeamB)

Below is the full code for the function. As you can see, there is no explicit try ... catch anywhere in it.

void __fastcall TLicProvider::ListNotificationSink (TObject *sender,
                                                    TLicListItem *item,
                                                    TListNotification notification
                                                   )
{
  // It is guaranteed that any changes to the contents of the list (sorting,
  // adding, removing and such), are preceded by an lnChanging notification,
  // and are followed by an lnChanged notification after the list is again
  // in a stable state.
  //
  // We'll use this to keep track of our current item.
  if (item)
  {
    // Notifications on individual items
    switch (notification)
    {
    case lnRemoving:
      // item is about to be removed from the list
      if (FNewCurrent == item)
      {
        // The item to be removed is the item designated to become the new
        // current. Move the new current according to the specified action.
        int index = -1;
        if (FDelAction != ldaSetNoCurrent && FItems->Count > 1)
        {
          index = FItems->IndexOf (item);
          if (index == 0)
          {
            index++;
          }
          else if (index == FItems->LastIndex)
          {
            index--;
          }
          else if (FDelAction == ldaSetNextCurrent)
          {
            index++;
          }
          else
          {
            index--;
          }
        }
        FNewCurrent = index >= 0 ? FItems->Items [index] : NULL;
      }
 
      if (FCurrent == item)
      {
        FCurrent      = NULL;
        FCurrentIndex = -1;
        FCurDeleted   = true;
      }
 
      if (FSelected == item) FSelected = NULL;
      if (FModified == item) FModified = NULL;
 
      FListChanged = true;
      break;
 
    case lnAdded:
      // item has been added to the list
      if (FAddAction == laaSetCurrent || (FAddAction == laaSetWhenNoCurrent && ! FNewCurrent))
      {
        FNewCurrent = item;
      }
      FListChanged = true;
      break;
 
    case lnChanged:
      // contents of the item has been altered
      if (FCurrent == item) FCurModified = true;
      if (! FMultiModified && FModified != item)
      {
        if (FModified)
        {
          FMultiModified = true;
          FModified      = NULL;
        }
        else
        {
          FModified = item;
        }
      }
      break;
 
    case lnSelected:
      if (! FMultiSelected && FSelected != item)
      {
        if (FSelected)
        {
          FMultiSelected = true;
          FSelected      = NULL;
        }
        else
        {
          FSelected = item;
        }
      }
      break;
    }
 
    ItemNotification (item, notification);
  }
  else
  {
    // Notifications on list level
    switch (notification)
    {
    case lnChanging:
      // Start of alterations to the list and/or it's items. It fires just
      // before the first change is made.
      FCurrent       = FCurrentIndex >= 0 ? FItems->Items [FCurrentIndex] : NULL;
      FNewCurrent    = FCurrent;
      FSelected      = NULL;
      FModified      = NULL;
      FCurDeleted    = false; // tracks if the current item is removed.
      FCurModified   = false; // tracks changes to the current item.
      FListChanged   = false; // tracks changes that could invalidate the current index.
      FMultiModified = false; // tracks if more than one item is modified.
      FMultiSelected = false; // tracks if more than one item is (de)selected.
      break;
 
    case lnChanged:
      {
        // End of alterations to the list. It only fires if actual changes have
        // been made. Here is where we figure out what changes, and which
        // notifications to fire.
        //
        // First update the current index. The current item pointer is only used
        // between the lnChanging notification and this one.
        if (FLinkedTo && FCurrentLinked)
        {
          // We're a proxy, with a current synchronised to the parent. Override
          // whatever has been setup with what is now the current item in the
          // parent.
          FNewCurrent = FLinkedTo->Current;
        }
 
        // Don't send out a single item contents change if the rows have been
        // rearranged in any way.
        if (FListChanged)
        {
          FModified     = NULL;
          FCurrentIndex = -1;
        }
 
        int  index    = FItems->IndexOf (FNewCurrent);
        int  prev     = FCurrentIndex;
        int  sIndex   = FItems->IndexOf (FSelected);
        int  mIndex   = FItems->IndexOf (FModified);
        bool cChanged = FCurDeleted || FCurrent != FNewCurrent;
        bool iChanged = FCurDeleted || index != FCurrentIndex;
        bool mChanged = cChanged || FCurModified;
        bool lChanged = FListChanged || FMultiModified || FModified;
        bool sChanged = FMultiSelected || FSelected;
 
        FCurrentIndex = index;
 
        // Now send out the notifications.
        if (lChanged) ContentsChanged (mIndex, FModified);
        if (cChanged) CurrentChanged (FCurrent, FNewCurrent);
        if (iChanged) IndexChanged (prev, index);
        if (mChanged) CurrentModified (FCurrentIndex, FNewCurrent);
        if (sChanged) SelectionChanged (sIndex, FSelected);
        Changed ();
      }
      break;
 
    case lnDestructing:
      // The list of items is about to go away entirely.
      if (FPropagated)
      {
        FPropagated (sender, item, notification);
        FPropagated = NULL;
      }
 
      if (FCurrent)
      {
        FCurDeleted = true;
        FCurrent    = NULL;
      }
 
      delete FTemplate;
 
      FTemplate      = NULL;
      FItems         = NULL;
      FDelItems      = NULL;
      FNewCurrent    = NULL;
      FListChanged   = true;
      FMultiSelected = false;
      FMultiModified = false;
      FModified      = NULL;
      FSelected      = NULL;
      break;
 
    case lnSorted:
      FListChanged = true;
      break;
    }
  }
 
  // Proxies linked to ourselves aren't notified through the notification list.
  // Instead, we're maintaining a list of proxies, and we'll signal their
  // counterparts of this list notification directly, just as they would receive
  // it if their items lists were still locally allocated.
  if (FProxyList)
  {
    int index = 0;
    int count = FProxyList->Count;
 
    while (index < count)
    {
      TLicProvider *proxy = dynamic_cast<TLicProvider *> (FProxyList->Items [index]);
      if (proxy) proxy->ListNotificationSink (sender, item, notification);
 
      index++;
    }
  }
 
  if (FPropagated)
  {
    // This is the place where we propagate the notification to the original
    // event handler that was in place before we coupled with the external
    // items list.
    FPropagated (sender, item, notification);
  }
}


FTemplate is declared as a TLicListItem *, and TLicListItem itself is derived from TPersistent

The generated machine code for this function is as follows (I only include the relevant bits, as it's rather large ...)

_DATA	segment dword public use32 'DATA'
@_$DCDU$@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	segment virtual
	align	2
@@_$DCDU$@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	label	dword
	dd	@@$xt$a2$p22licSystem@TLicListItem
	dd	20495
	dd	-8
	dd	0
@_$DCDU$@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	ends
_DATA	ends
_DATA	segment dword public use32 'DATA'
@_$ECTEU$@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	segment virtual
	align	2
@@_$ECTEU$@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	label	dword
	dd	0
	dd	0
	dd	-44
	dw	0
	dw	5
	dd	0
	dd	0
	dw	12
	dw	5
	dd	0
	dd	@@_$DCDU$@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification
	dw	24
	dw	5
	dd	0
	dd	0
@_$ECTEU$@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	ends
_DATA	ends
_TEXT	segment dword public use32 'CODE'
@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	segment virtual
	align	2
@@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	proc	near
?live16460@0:
 ;	
 ;	void __fastcall TLicProvider::ListNotificationSink (TObject *sender,
 ;	
	?debug L 805
@433:
	push      ebp
	mov       ebp,esp
	add       esp,-64
	push      ebx
	push      esi
	push      edi
	mov       dword ptr [ebp-52],ecx
	mov       dword ptr [ebp-48],edx
	mov       ebx,eax
	mov       eax,offset @@_$ECTEU$@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification
	call      @__InitExceptBlockLDTC
 ;	
 ;	                                                    TLicListItem *item,
 ;	                                                    TListNotification notification
 ;	                                                   )
 ;	{
 ;	  // It is guaranteed that any changes to the contents of the list (sorting,
 ;	  // adding, removing and such), are preceded by an lnChanging notification,
 ;	  // and are followed by an lnChanged notification after the list is again
 ;	  // in a stable state.
 ;	  //
 ;	  // We'll use this to keep track of our current item.
 ;	  if (item)
 ;	
	?debug L 816
?live16460@16: ; EBX = this
	cmp       dword ptr [ebp-52],0
	je        @435


As can be seen, the generated function entry point code includes the initialisation of a try ... catch frame, before the code generated for the function's first statement. I've included the data segment the compiler has setup to track the progress through the guarded elements of the code.

And now the machine code generated with the line that trigger this auto-generated frame (the line with delete FTemplate;) commented out

_TEXT	segment dword public use32 'CODE'
@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	segment virtual
	align	2
@@licSystem@TLicProvider@ListNotificationSink$qqrp14System@TObjectp22licSystem@TLicListItem27licSystem@TListNotification	proc	near
?live16460@0:
 ;	
 ;	void __fastcall TLicProvider::ListNotificationSink (TObject *sender,
 ;	
	?debug L 805
@433:
	push      ebp
	mov       ebp,esp
	add       esp,-20
	push      ebx
	push      esi
	push      edi
	mov       dword ptr [ebp-8],ecx
	mov       dword ptr [ebp-4],edx
	mov       ebx,eax
 ;	
 ;	                                                    TLicListItem *item,
 ;	                                                    TListNotification notification
 ;	                                                   )
 ;	{
 ;	  // It is guaranteed that any changes to the contents of the list (sorting,
 ;	  // adding, removing and such), are preceded by an lnChanging notification,
 ;	  // and are followed by an lnChanged notification after the list is again
 ;	  // in a stable state.
 ;	  //
 ;	  // We'll use this to keep track of our current item.
 ;	  if (item)
 ;	
	?debug L 816
?live16460@16: ; EBX = this
	cmp       dword ptr [ebp-8],0
	je        @435


As you can see, the exception frame initialisation is gone, as is the extra data segment.
Vladimir Ulchenko

Posts: 248
Registered: 1/12/00
Re: Why does the use of the delete statement trigger an automatic catchframe?  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 12, 2015 2:05 AM   in response to: Jan Dijkstra in response to: Jan Dijkstra
On Thu, 6 Aug 2015 03:37:31 -0700, Jan Dijkstra <> wrote:

The generated machine code for this function is as follows (I only include the relevant bits, as it's rather large ...)

I think relevant bits would include disasm listing for invokation of destructor and epilogue

call @__InitExceptBlockLDTC

btw there is compiler option "Fast exception prologs (-xf)" exist allowing to inline the code of __InitExceptBlockLDTC

As can be seen, the generated function entry point code includes the initialisation of a try ... catch frame, before the code generated for the function's first statement. I've included the data segment the compiler has setup to track the progress through the guarded elements of the code.

And now the machine code generated with the line that trigger this auto-generated frame (the line with delete FTemplate;) commented out

EH is one of the trickiest parts and according to cpp standard compiler has to take some measures to ensure its rules, for example to invoke
termination handler during stack unwinding when destructor fails with the exception etc.

not to mention that eh is of one of the weakest and buggy points of bcc

--
Vladimir Ulchenko aka vavan
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02