I've spent the past few days of my free time learning CIL and was wondering about branching to a label (br ) vs calling a method (.method declaration).
I know that if you declare a method, you will be able to access that from outside the assembly, but what about making private methods labels and just using br
to branch to it? Is there any performance gain to be had with that?
To clear up the confusion, here is a simplified example (due to space and time restrictions):
// calling code
ldc.i4 5
call int32 testmethod(int32)
// other code
// method
.method public int32 testmethod(int32)
{
ldc.i4 10
add
ldc.i4 20
mul
ret
}
So instead of doing that method, I could do that with labels and branches:
ldc.i4 5
br testlabel
leftoff:
// remaining instructions
testlabel:
.lcd.i4 10
add
ldc.i4 20
mul
br leftoff
So the method/label testlabel takes and int32 and then adds 10 and multiplies that result by 20. Simple enough. I realize that the one thing that a drawback (that I didn't mention originally) is readability, but if this is generated by a compiler readability becomes less important. So by using the second example, would using the labels and branching to the code offer any performance benefits? If not, what about if I would be able to fit it in a short branch? (br.s)
What you are describing is basically method inlining. Although it is possible that inlining a method will improve performance, there are several reasons that you might choose not to inline a method:
UPDATE
Okay, given your concrete example, let me explain the connection that I see to inlining. If the method is called several times (e.g. calling testmethod
twice in a row), then your approach won't work, because you'd need to branch back to different locations depending on which call was being simulated, but there's no simple way to do this (you can add a local variable to track the additional state and then use a conditional branch, but that's complex enough that it would probably undo any performance gains). If it's only called once, then your transformation is basically equivalent to inlining, except with additional unconditional branches to and from the inlined method. I think that it would make more sense to actually inline the method and get rid of the branches. That is, given your example, it would look like this:
ldc.i4 5
// start of inlined call to testmethod
ldc.i4 10
add
ldc.i4 20
mul
// end of inlined call to testmethod
// other code from caller goes here
Then, my above comments about inlining apply.