[Mono-list] RE: Questions

Paolo Molaro lupus@ximian.com
Mon, 5 Jul 2004 19:17:01 +0200

On 07/02/04 Neale.Ferguson@SoftwareAG-USA.com wrote:
>                 try{
>                         try {
>                                 throw new NotImplementedException();
>                         }
>                         finally {
>                                 i++;
>                                 Console.WriteLine("Finally called");
>                         }
>                 } catch(NotImplementedException){
>                         i++;
>                         Console.WriteLine("Exception ignored");
>                 }
>                 if (i != 2)
>                         return 1;
>                 return 0;
>         }
> }
> If the exception handler gets involved, discovers the catch, calls the
> call_filter routine which branches to the finally after restoring the
> registers to the context of Main. Now, this finally requires access to
> its entire stack frame as it references the variable "i". However, it
> then issues a Console.WriteLine which means new stack frames appearing
> after its own. Therefore, when it returns to the "call_filter" routine
> it's possible that the stack frame of this routine has been trashed by
> the calls made in the finally stuff.
> Now considering this works for all the other platforms then I must be
> doing something incorrectly or the ABI for S/390 makes things problematic
> for me.

There are two registers that are commonly used to address the stack:
the stack pointer and the frame pointer. On x86 the are esp and ebp.
The stack pointer is the actual pointer to the start of the stack.
The frame pointer is the base pointer used to access local vars and
arguments on the stack.
Now, on ppc and s390 the call convention allows us to use the same
hardware register for both the stack and the frame pointer (r1 on ppc)
in most cases (we could do the same on x86 but it complicates some code
and makes for a bigger icache footprint, so currently it's avoided).
There are two cases where this optimization can't be done: methods that
use localloc and methods with exception blocks that need to be executed
during exception handling. In these cases you need to use another
register as frame pointer, so when the finally or filter is executed
local vars in the method are accessed correctly while the stack register
can still be expanded to allow for calls to happen.
So, when allocating variables, you use the frame pointer register instead 
of the stack register in these cases and in the prolog of the method you
simply copy the stack pointer to the frame pointer. The frame register
choosen should be the same used by the C compiler in the platform in
functions that use the C alloca () function (it should be a callee-saves
register and it should not be included in the set of registers used for
global register allocation for that method, obviously).


lupus@debian.org                                     debian/rules
lupus@ximian.com                             Monkeys do it better