Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / CLR / March 2007

Tip: Looking for answers? Try searching our database.

Redundant IL generated by the compiler

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Anthony Paul - 11 Jan 2007 23:40 GMT
Hello everyone,

I've been teaching myself IL and when compiling a very simple example I
found what I believe to be redundant code generated by the C# compiler.
Here is my code :

using System;

namespace TestIL
    {
    class Program
        {
        static int val;

        static void Main(string[] args)
            {
            bool res;

            do
                {
                Console.WriteLine("Enter a number");
                string s = Console.ReadLine();
                res = int.TryParse(s, out val);

                if (res)
                    {
                    if ((val & 1) != 0)
                        s = "odd!";
                    else
                        s = "even!";
                    }
                else
                    s = "How Rude!";

                Console.WriteLine(s);
                }
            while (res);
            }
        }
    }

and the IL code generated is :

.method private hidebysig static void  Main(string[] args) cil managed
{
 .entrypoint
 // Code size       72 (0x48)
 .maxstack  2
 .locals init ([0] bool res,
          [1] string s)
 IL_0000:  ldstr      "Enter a number"
 IL_0005:  call       void [mscorlib]System.Console::WriteLine(string)
 IL_000a:  call       string [mscorlib]System.Console::ReadLine()
 IL_000f:  stloc.1   // <-- redundant?
 IL_0010:  ldloc.1  // <-- redundant?
 IL_0011:  ldsflda    int32 TestIL.Program::val
 IL_0016:  call       bool [mscorlib]System.Int32::TryParse(string,
                                                            int32&)
 IL_001b:  stloc.0
 IL_001c:  ldloc.0
 IL_001d:  brfalse.s  IL_0038
 IL_001f:  ldsfld     int32 TestIL.Program::val
 IL_0024:  ldc.i4.1
 IL_0025:  and
 IL_0026:  brfalse.s  IL_0030
 IL_0028:  ldstr      "odd!"
 IL_002d:  stloc.1   // <-- redundant?
 IL_002e:  br.s       IL_003e
 IL_0030:  ldstr      "even!"
 IL_0035:  stloc.1   // <-- redundant?
 IL_0036:  br.s       IL_003e
 IL_0038:  ldstr      "How Rude!"
 IL_003d:  stloc.1   // <-- redundant?
 IL_003e:  ldloc.1   // <-- redundant?
 IL_003f:  call       void [mscorlib]System.Console::WriteLine(string)
 IL_0044:  ldloc.0
 IL_0045:  brtrue.s   IL_0000
 IL_0047:  ret
} // end of method Program::Main

Please correct me if I'm wrong, but in the above IL code I commented
the lines that I believe are redundant; I just can't see any use for
them. Would anyone happen to know why they are being generated?

Cheers!

Anthony
Jon Shemitz - 11 Jan 2007 23:51 GMT
>   IL_000f:  stloc.1   // <-- redundant?
>   IL_0010:  ldloc.1  // <-- redundant?

>   IL_0028:  ldstr      "odd!"
>   IL_002d:  stloc.1   // <-- redundant?

>   IL_0030:  ldstr      "even!"
>   IL_0035:  stloc.1   // <-- redundant?

>   IL_0038:  ldstr      "How Rude!"
>   IL_003d:  stloc.1   // <-- redundant?
>   IL_003e:  ldloc.1   // <-- redundant?

> Please correct me if I'm wrong, but in the above IL code I commented
> the lines that I believe are redundant; I just can't see any use for
> them. Would anyone happen to know why they are being generated?

Perhaps debugger cues; perhaps just because it's easier to write the
code generator that way.

In any case, I wouldn't pay much attention to it - as you (presumably)
know, CIL is just the source code to the jitter, and the jitter is
quite capable of optimizing out unnecessary saves and loads.

Signature

.NET 2.0 for Delphi Programmers
www.midnightbeach.com/.net

Ben Voigt - 11 Jan 2007 23:51 GMT
> Please correct me if I'm wrong, but in the above IL code I commented
> the lines that I believe are redundant; I just can't see any use for
> them. Would anyone happen to know why they are being generated?

Yes, you'll see that a lot.  Did you enable release build?  Even then, most
optimization is done by the JIT converting from MSIL to machine code, so the
MSIL tends to be poorly optimized (and therefore .NET assemblies are larger
than they should be, by probably some 10-25% judging from the frequency of
occurence of that particular useless pattern).

The why, is because the parser constructs an abstract syntax tree, the
compiler does name binding and overload resolution, and then converts the
results to MSIL.  The AST representation has to be very flexible, so in most
situations there's some redundancy.

> Cheers!
>
> Anthony
Anthony Paul - 12 Jan 2007 00:22 GMT
> The why, is because the parser constructs an abstract syntax tree, the
> compiler does name binding and overload resolution, and then converts the
> results to MSIL.  The AST representation has to be very flexible, so in most
> situations there's some redundancy.

Hmm... I don't see how AST generation would result it the kind of
redundancy exhibited in the IL above but I'm no expert in that field so
I'll take your word for it. Also, you would think that the
post-processor would eliminate them... seems so simple. Anyway, I got
my answer, thanks to all for replying!

Cheers!

Anthony

p.s. Oh, and yes, I targetted it for a release build as soon as I saw
all the NOP's generated in the debug build, lol! I read somewhere that
they were intentionally inserted to help with debugging.
Ben Voigt - 12 Jan 2007 17:04 GMT
>> The why, is because the parser constructs an abstract syntax tree, the
>> compiler does name binding and overload resolution, and then converts the
[quoted text clipped - 7 lines]
> post-processor would eliminate them... seems so simple. Anyway, I got
> my answer, thanks to all for replying!

As Jon Shemitz mentioned, the expression trees can't be merged, to maintain
line number information the AST for each line needs to be independently
output.

Also, the MS developers are constantly blogging that the JIT recognizes
certain patterns and performs special handling for them... so I fear that
hand-optimized MSIL, though substantially smaller, may cause the JIT
optimizer to miss certain optimizations.

> Cheers!
>
[quoted text clipped - 3 lines]
> all the NOP's generated in the debug build, lol! I read somewhere that
> they were intentionally inserted to help with debugging.
Jon Skeet [C# MVP] - 12 Jan 2007 00:08 GMT
> I've been teaching myself IL and when compiling a very simple example I
> found what I believe to be redundant code generated by the C# compiler.
> Here is my code :

<snip>

Is your point that the value in local 1 is only ever used immediately
after it's stored, and that therefore it could just stay on the stack
instead of being copied into the variable at all?

Consider what you'd see in a debugger if you didn't have the stloc and
ldloc instructions - s would never have a value.

Of course, I may have missed your point entirely - I'm somewhat sleepy.

The last three stloc.1 calls certainly could have been amalgamated into
one though, by branching before storing instead of after. That may
violate some other rules though...

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Anthony Paul - 12 Jan 2007 00:31 GMT
> Is your point that the value in local 1 is only ever used immediately
> after it's stored, and that therefore it could just stay on the stack
> instead of being copied into the variable at all?

My point is that, given the IL code in my original post, if you were to
strip it of the lines which I marked as being redundant, the code will
compile and execute just fine and as intended. The answer to my
question happens to be that the compiler is inneficient and is apt to
produce some bloat that the JIT will eventually sort out.

Cheers!

Anthony
Jon Skeet [C# MVP] - 12 Jan 2007 22:37 GMT
> > Is your point that the value in local 1 is only ever used immediately
> > after it's stored, and that therefore it could just stay on the stack
[quoted text clipped - 5 lines]
> question happens to be that the compiler is inneficient and is apt to
> produce some bloat that the JIT will eventually sort out.

It will compile and execute - but if you try to use it in a debugger,
you'll never see a value in s, I believe. When you're debugging, do you
really want to not see assignments to locals?

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Anthony Paul - 12 Jan 2007 23:13 GMT
I would love to have that information *IF* I compile a debug build. A
release build should never contain code to ease debugging IMHO.
However, I don't think that these lines of IL code were generated by
the compiler with ease--of-debugging in mind.

Cheers!

Anthony

> It will compile and execute - but if you try to use it in a debugger,
> you'll never see a value in s, I believe. When you're debugging, do you
[quoted text clipped - 4 lines]
> http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
> If replying to the group, please do not mail me too
Jon Skeet [C# MVP] - 12 Jan 2007 23:29 GMT
> I would love to have that information *IF* I compile a debug build. A
> release build should never contain code to ease debugging IMHO.
> However, I don't think that these lines of IL code were generated by
> the compiler with ease--of-debugging in mind.

If the C# compiler team knows (for certain) that the JIT will optimise
away those statements when there is no debugger attached, what's the
downside of making it easier to debug even in release mode?

I would imagine that it makes for simpler compiler code - and given
that you'll end up with multiple high level language compilers but only
one JIT, I'd rather the optimisation went into the JIT than the high
level language compilers.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Anthony Paul - 13 Jan 2007 00:33 GMT
I agree with you that the emphasis should be on the JIT when it comes
to optimization; however, I'm a firm believer of doing things right the
first time around. I've read another thread (in which you've been
involved) that was rather interesting regarding this very subject and
am aware that perhaps the MSIL shouldn't be highly optimized (ie. loop
unrolling) because the JIT would do a better job of it seeing that it's
aware of the underlying platform. I can see that now and though I'm not
in the position to agree or disagree I see that there may be undesired
consequences and that's good enough for me. However, in this specific
case I see redundancy in the code that has nothing to do with
optimization... it's just redundant, dead code that does nothing but
contribute to bloat. It's a minor thing really, maybe I'm just being
anal about it but this is probably due to my having learned how to
program in a time when one would drool all over 32K of memory, 1 mhz
processors were the "bomb", and data was stored on tape or floppies.
Ahhh, the good ol' days!! :D

Cheers!

Anthony

p.s. Oh, and shame on you! The C# compiler team should NEVER make any
assumptions! :D

> If the C# compiler team knows (for certain) that the JIT will optimise
> away those statements when there is no debugger attached, what's the
[quoted text clipped - 9 lines]
> http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
> If replying to the group, please do not mail me too
Jon Skeet [C# MVP] - 13 Jan 2007 19:13 GMT
<snip>

> p.s. Oh, and shame on you! The C# compiler team should NEVER make any
> assumptions! :D

Except that you're asking them to make the assumption that you don't
want the value of the local variable to be updated in the IL when it
*was* updated in the code :)

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Ciaran O''Donnell - 12 Feb 2007 12:05 GMT
I agree in doing things right the first time to if you wanted this done
'correctly' in IL, perhaps you should have coded it that way as you knew that
you didnt need the s variable. If you remove the first assignment to s and
put the readline call as the param to the parse call then the IL is removed.
The compiler is doing what you told it to do.
So if you are that concerned with doing it right first time and not wasting
time compiling those statements and storing them, you need to optimise your
code. The compiler cant as you could still attach debuigging components and
expect to see a value in the field s.

Signature

Ciaran O''Donnell
http://wannabedeveloper.spaces.live.com

> I agree with you that the emphasis should be on the JIT when it comes
> to optimization; however, I'm a firm believer of doing things right the
[quoted text clipped - 33 lines]
> > http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
> > If replying to the group, please do not mail me too
Anthony Paul - 23 Feb 2007 15:20 GMT
Hello Ciaran!

Thanks for replying!

> code. The compiler cant as you could still attach debuigging components and
> expect to see a value in the field s.

But see, that's my point... debugging was explicitly disabled. At this
point there should be no assumptions as to whether I want to see
debugging info or not (in this case seeing the value for 's'). IMHO a
release build should be completely devoid of any debugging information
whatsoever for performance reasons and compactness. However, I believe
my question has been answered... the reason why redundant IL code is
generated is for debugging purposes, even in a release build.

Anthony
Willy Denoyette [MVP] - 12 Jan 2007 11:23 GMT
> Hello everyone,
>
[quoted text clipped - 83 lines]
>
> Anthony

Did you actually try to remove what you believe to be redundant and run peverify.exe on the
resultant assembly?

Willy.
Anthony Paul - 12 Jan 2007 13:09 GMT
> Did you actually try to remove what you believe to be redundant and run peverify.exe on the
> resultant assembly?
>
> Willy.

I hadn't dont that before since I was unaware of the peverify tool, but
I just ran it and everything is kosher.

Cheers!

Anthony
Link_M - 27 Mar 2007 16:50 GMT
IL_0030:  ldstr      "even!"
 IL_0035:  stloc.1   // <-- redundant?
 IL_0036:  br.s       IL_003e
 IL_0038:  ldstr      "How Rude!"
 IL_003d:  stloc.1   // <-- redundant?
 IL_003e:  ldloc.1   // <-- redundant?
 IL_003f:  call       void [mscorlib]System.Console::WriteLine(string)
 IL_0044:  ldloc.0
 IL_0045:  brtrue.s   IL_0000
 IL_0047:  ret

In this case i don´t think is redundant, because the IL is saving the variable S on stack.

but you can create your il code, by copying that text generated and run on ILDasm, and put ou delete code, and see what is happening!

I start to see il, but i think is like this!

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.