In my last post I gave an overview of the DebuggableAttribute, what values the C# compiler gives it, and how the CLR uses those values. I mentioned that with /debug+, the C# compiler sets the IgnoreSymbolStoreSequencePoints DebuggingModes bit, but I didn’t describe what this bit does. Understanding sequence points and ths IgnoreSymbolStoreSequencePoints bit is important for anyone writing a compiler for .NET (including one that uses Reflection.Emit).
A sequence point is used to mark a spot in the IL code that corresponds to a specific location in the original source. For Reflection.Emit, they are emitted by the ILGenerator.MarkSequencePoint method. This serves two purposes. First to allow debuggers (and the StackTrace class) to map IL offsets back to source and line information. Secondly to tell the JIT compiler that it must preserve this location uniquely in the generated native code so that a user could, for example, set a breakpoint on it (see Mike’s blog entry for more details).
Since they’re only needed for debugging scenarios, the location of sequence points, and their mapping back to source locations, are stored in the PDB file. Unfortunately this means that the JIT compiler has to open the PDB file and read this data any time it compiles a method with optimizations disabled (if JIT optimizations are enabled, then it doesn’t care about sequence points in order to get the best codegen possible). The PDB file format is quite complicated, and has a long legacy. So as you might imagine, there is an unfortunate performance penalty here.
Now in addition to the sequence points specified in the PDB file (“explicit sequence points”), the JIT also infers additional sequence points based on the IL code (“implicit sequence points”). For example, it adds an implicit sequence at every point where the IL evaluation stack is empty, or on any “nop” instruction. So in Whidbey we added the IgnoreSymbolStoreSequencePoints bit to allow compilers to indicate to the JIT that they are content with the JITs rules for implicit sequence points, and there is no need for the JIT to look in the PDB file for more.
So our official recommendation is that all compilers (including those using Reflection.Emit) set the IgnoreSymbolStoreSequencePoints bit and rely on “nop” instructions to ensure the JIT places sequence points at the right place. This can make a nice JIT performance improvement, and also has some other benefits related to not needing to read the PDB file at JIT time (eg. it may not be available). The Whidbey C# and VB compilers do this, but unfortunately the managed C++ compiler does not.