[Mono-devel-list] mcs code generation
Ben Maurer
bmaurer at users.sourceforge.net
Sun Mar 28 11:35:55 EST 2004
Hello,
You address two separate issues, as such, I will respond separately.
On Sun, 2004-03-28 at 08:57, Andre 'Ilu' Seidelt wrote:
> given the following simple pice of code:
> try {
> return new Object().ToString();
> } catch(NullReferenceException e) {
>
> why does mcs generate the leave twice for each block?
Lets look at a different code example to aid the explanation:
using System;
class T {
static int Main () {
try {
return 0;
} catch {
Console.WriteLine ("uhoh!");
}
Console.WriteLine ("ok, so we failed the test :-(");
return 1;
}
}
The code generate for main (I have cleaned this up by hand):
[header blah blah blah]
.try {
ldc.i4.0
stloc.0
leave RETURN_LABEL
leave EXCEPTION_END
}
catch [mscorlib]System.Object {
pop
ldstr "uhoh!"
call void class [mscorlib]'System.Console'::'WriteLine'(string)
leave EXCEPTION_END
}
ldstr "ok, so we failed the test :-("
call void class [mscorlib]'System.Console'::'WriteLine'(string)
ldc.i4.1
ret
RETURN_LABEL: ldloc.0
ret
So, now that we have a bit of a easier sample to explain, lets look at
the rationale for each leave statement:
> leave RETURN_LABEL
According to the CLI spec:
The ret instruction cannot be used to transfer control out of a
try, filter, catch, or finally block. From within a try or
catch, use the leave instruction with a destination of a ret
instruction that is outside all enclosing exception blocks.
So, we comply with this.
Now, for the less obvious one:
> leave EXCEPTION_END
The rationale behind this instruction is that you may never `fall out
of' an exception handling block. Therefore, it is necessary to provide a
leave statement at the end.
`Now wait!' you say, `You can't fall thru because of that other leave
instruction'. Well, the issue is that System.Reflection.Emit takes care
of this for us:
> private void InternalEndClause ()
> {
> switch (ex_handlers [cur_block].LastClauseType ()) {
> case ILExceptionBlock.CATCH:
> // how could we optimize code size here?
> Emit (OpCodes.Leave, ex_handlers [cur_block].end);
> break;
> case ILExceptionBlock.FAULT:
> case ILExceptionBlock.FINALLY:
> Emit (OpCodes.Endfinally);
> break;
> case ILExceptionBlock.FILTER:
> Emit (OpCodes.Endfilter);
> break;
> }
> }
(note that a `try' block goes under the Catch switch statement here).
So, we *always* get a leave instruction that makes it look like we are
falling thru to the end of the block, even if we dont ask for it.
I am not really sure there is a good way to fix this in the context of
Sys.Ref.Emit.
> they even generate a leave.s instead of leave.
That is because we do not do any second-pass optimizing on the IL, so we
can not know that the target of the leave instruction is actually within
the range of leave.s.
Please note that `fixing' this would only help the size of the compiled
IL code. The runtime can kill that extra leave instruction. Also, the
leave.s has no effect on code generation.
-- Ben
More information about the Mono-devel-list
mailing list