[Mono-list] Announce: A .NET assembly -> nativecodegenerationtool (ala ngen for MONO)

Zoltan.2.Varga@nokia.com Zoltan.2.Varga@nokia.com
Mon, 29 Jul 2002 13:23:02 +0200


				Hi,


> -----Original Message-----
> From: ext Dietmar Maurer [mailto:dietmar@ximian.com]
> Sent: 29. July 2002 13:00
> To: Varga Zoltan.2 (NMP/Budapest)
> Cc: Miguel de Icaza; Mono List
> Subject: RE: [Mono-list] Announce: A .NET assembly ->
> nativecodegenerationtool (ala ngen for MONO)
> 
> 
> On Mon, 2002-07-29 at 12:40, Zoltan.2.Varga@nokia.com wrote:
> > 
> > 					Hi,
> > 
> > > -----Original Message-----
> > > From: ext Dietmar Maurer [mailto:dietmar@ximian.com]
> > > Sent: 29. July 2002 12:10
> > > To: Varga Zoltan.2 (NMP/Budapest)
> > > Cc: Miguel de Icaza; Mono List
> > > Subject: RE: [Mono-list] Announce: A .NET assembly -> native
> > > codegenerationtool (ala ngen for MONO)
> > > 
> > > 
> > > On Mon, 2002-07-29 at 11:12, Zoltan.2.Varga@nokia.com wrote:
> > > > > 
> > > > >         1. It does not work with exceptions: The 
> current code is
> > > > >            incorrect because it does not save/restore 
> the LMF when
> > > > >            calling precompiled methods - but 
> > > saving/restoring the LMF
> > > > >            would lead to serious performance problems.
> > > > 
> > > > Can you explain what the LMF is? I looked at the code, but 
> > > I can't understand what it is.
> > > 
> > > Sure. The Problem is that unmanaged code can raise 
> exception, and we
> > > must be able to recover from such exception and print a stack 
> > > trace for
> > > example (you can find some docu in mono/docs/exceptions). One 
> > > (compiler
> > > independent) way to do that is to save a data structure with all
> > > necessary info on the stack each time you call an unmanaged 
> > > method - we
> > > call that info Last Managed Frame (MonoLMF).
> > > 
> > > If a exception occurs in unmanaged code we simply use the 
> data in the
> > > LMF to unwind the stack.
> > > 
> > 
> > Since the precompiled code does not contain exception 
> handlers, it only needs to propagate exceptions, which means restoring
> > callee saved registers. This is done by examining the 
> prelude of the function to determine which registers are saved and
> > in which order, then restoring these registers from stack 
> counting back from EBP. This works because the prelude generated
> > by gcc has a simple structure (after you turn off certain 
> optimizations). So the current code can handle exceptions without
> > saving/restoring an LMF.
> 
> How do you know what code raised the exception without an 
> LMF? Also you
> cant rely on unmanaged code.
> 

I try to treat the precompiled code the same as the JIT generated code, i.e. I put it into the jit_info_table. So the
exception handling code can find the precompiled method the same way it can find the JIT compiled method. The is_ngen
flag in the MonoJitInfo structure tells that it is a precompiled method so the code which needs to distinguish between
precompiled/JIT code (like the exception handler) can do so.
The unmanaged code still needs to conform to the x86 ABI, so it must save the callee saved registers etc. The only hackish
part is the register restore code, which depends on the code generated by gcc. I can't prove that gcc will allways generate
these code sequences but this seems to happen in practice. You have to turn off the optimisations which reorder code too much (-fno-schedule-insns2).

> > > > 
> > > > BTW: I started this as a hobby project to learn about 
> > > .NET/compilers etc. I wouldn't be suprised if it turned 
> to be unusable
> > > > due to problems such as the one above...
> > > 
> > > Such code is always usable. If it turns out that we cant solve all
> > > problems we simply need to find another Solution. But I 
> > > imagine you have
> > > learned a lot about mono when you wrote that code!
> > > 
> > > > >         2. Array bound checking: There is no array bound 
> > > > > checking at the
> > > > >            moment and maybe that is the only reason why 
> > > it speeds up
> > > > >            pnetmark? gcc is unable to do bound check 
> > > removal, so array
> > > > >            access will be slow (or you have to remove that 
> > > > > checks before
> > > > >            you emit C code).
> > > > 
> > > > You are right. I somehow throught that mono does not do 
> > > bound checking (perhaps an earlier version didn't) so I put that
> > > > on the TODO list instead of implementing it right away.
> > > > 
> > > > 
> > > > >         3. I wonder if gcc is really able to optimize the 
> > > emitted C#
> > > > >            very much.
> > > > > 
> > > > 
> > > > gcc is designed to optimize procedural code, so of course 
> > > it has problems with virtual calls, bound checks, delegates etc. 
> > > > But every program contains a mix of high level and low 
> > > level code, so some performance gains can be expected ever for
> > > > programs written in an object-oriented style such as mcs.
> > > 
> > > Is it difficult to implement array bound checking? If not 
> I would like
> > > to have some real benchmark results (including bound checking).
> > 
> > Not at all. I added bound checking to the latest snapshot 
> and put it onto the web. I re-ran pnetmark and the mcs self
> > compiling tests, but there was no noticable slowdown.
> 
> So why is it that much faster? Maybe we can improve the JIT?
> 

The better pnetmark score for gcc comes from the good score on the 'loops' and 'logic' tests. These are the tests where an optimizing compiler usually shines. A JIT compiler must be fast, so it can't do the extensive optimizations that a command
line compiler can. On the other hand, the current compiler is absolutely unusable for development (compiling mcs to native
code takes approx. 2 minutes). So it can be used to compile assemblies to native code at, for example installation time, but not much else.

> - Dietmar
> 
> 

						bye

							Zoltan