Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug flags produced .NET SDK compiler differ from C# #4365

Closed
dsyme opened this issue Feb 20, 2018 · 2 comments
Closed

Debug flags produced .NET SDK compiler differ from C# #4365

dsyme opened this issue Feb 20, 2018 · 2 comments

Comments

@dsyme
Copy link
Contributor

dsyme commented Feb 20, 2018

The F# .NET SDK compiler produces incorrect flags in binaries for default settings of Debug and Release code compared to C#.

Below are the flags produced for default settings of dotnet new console. There are several discrepancies. The missing flags may affect JIT/CLR performance, but regardless of that we should just do exactly the same thing as C# by default in all cases.

  • /debug:portable /optimize+ incorrectly sets the jitTracking flag. This is regardless of whether targeting netcoreapp2.0, netstandard2.0 or net461. This may cause a performance hit for jitted code.
  • /debug:full /optimize- and /debug:pdbonly /optimize+ for the F# compiler doesn't set the ignoreSymbolStoreSequencePoints. We should fix likely that.
  • CompilationRelaxationsAttribute is always missing in F# code. We should add it.
  • /debug:fulldoesn't set the EnC enabled flag. That's ok since we don't support EnC.

C# Release mode (all compilers, whether /debug:portable /optimize+ or /debug:pdbonly /optimize+):

CompilerServices.CompilationRelaxationsAttribute::.ctor(..) = ( 01 00 08 00 00 00 00 00 ) 
Diagnostics.DebuggableAttribute::.ctor(..) = ( 01 00 02 00 00 00 00 00 ) = ignoreSymbolStoreSequencePoints

F# Release mode (dotnet SDK defaults, /debug:portable /optimize+):

no CompilerServices.CompilationRelaxationsAttribute
Diagnostics.DebuggableAttribute::.ctor(..) = ( 01 00 03 00 00 00 00 00 )  = jitTracking + ignoreSymbolStoreSequencePoints

F# Release mode (VS2017 defaults, /debug:pdbonly /optimize+)

no CompilerServices.CompilationRelaxationsAttribute
Diagnostics.DebuggableAttribute::.ctor(..) = ( 01 00 00 00 00 00 00 00 ) = nothing

C# Debug mode (all compilers, whether /debug:portable /optimize- or /debug:full /optimize-)

CompilerServices.CompilationRelaxationsAttribute::.ctor(..) = ( 01 00 08 00 00 00 00 00 ) 
Diagnostics.DebuggableAttribute::.ctor(..) = ( 01 00 07 01 00 00 00 00 )  = jitTracking + ignoreSymbolStoreSequencePoints + enableEnC

F# Debug mode (dotnet SDK defaults, /debug:portable /optimize-)

no CompilerServices.CompilationRelaxationsAttribute
Diagnostics.DebuggableAttribute::.ctor(..) = ( 01 00 03 01 00 00 00 00 ) = jitTracking + ignoreSymbolStoreSequencePoints

F# Debug mode (VS2017 defaults, /debug:full /optimize-)

no CompilerServices.CompilationRelaxationsAttribute
Diagnostics.DebuggableAttribute::.ctor(..) = ( 01 00 01 01 00 00 00 00 ) = jitTracking
@KevinRansom
Copy link
Member

Okay: These attributes are all super old, and the supporting code and designs have evolved a bunch.

CompilationRelaxationsAttribute

The old relaxations are no longer supported ... or more likely the default behavior. The one relaxation identified in the enumeration: NoStringInterning value:8 is added by default to C# assemblies.

What it is used for is when ngening a set off assemblies, ngen has to do some work to ensure that strings are interned and hold the correct 'interned' invariant. This attribute and value turns it off, allowing for ngened apps, non-interned string literals.

https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.compilationrelaxations(v=vs.110).aspx

The reason for this relaxation is that maintaining strict interning has some cost. The case for the need for this strict interning in the ngen case was regarded as a corner case, and so the C# language, relaxed the interning invariant that in version 2.0 of the C# language.

Technically, emitting this relaxation in assemblies now would be a breaking change. We would go from the intern guarantee to not a guarantee.

However, I believe with that non ngened assemblies the interning guarantee is still maintained by the JIT. So, I am kind of confused about why this even exists. The best I can suggest is that it offers a minor start up perf gain for ngened F# assemblies, with a potential behavior difference between the ngen and jitted code in some rare and arbitrary string identity scenarios.

JitTracking

Was implemented to improve the debug-ability of managed code, not for performance reasons. Debugging optimized code has always been tough and by default we disable optimizations when building debug code. Release builds do not specify this value.

This value is no longer supported, and is certainly ignored by the coreclr and desktop jit-compiler, which always generates tracking information.

There is some documentation that suggests that ngen doesn't "always generate this information" and it is possible to enable it in the ngen.ini for assemblies without this enabled.

ignoreSymbolStoreSequencePoints

This tells the debugger to not use the sequence points in the PDBs but to instead infer them by examining the il code and stack, to figure out where break points can be set. This is not a tested F# scenario, I imagine that we would have to fix a number of codegen bugs to add NOPs and the like to ensure that the Jit can infer Sequence Points at the correct spot.

Summary:

We can make the proposed changes or not make them ... the priority has to be pretty low though.
I think:

  • If we choose to make the CompilationRelaxationsAttribute change we need to think about it a lot. Since there is a, albeit remote, that a program may change behaviour mysteriously when ngened.
  • JitTracking has no practical scenario where it is needed.
  • ignoreSymbolStoreSequencePoints might be a good thing to add, if we plan on testing it, and amending our codegen to enable the Jit to find spots for SPs. However, right now, pdbs are our preferred, and tested debugging scenario.

Does that make sense?

Kevin

@dsyme
Copy link
Contributor Author

dsyme commented Feb 21, 2018

Yes, your answer makes sense. I'm happy to close this for now given the above analysis. It’s good to have all this analysed and written down in detail.

It was necessary to double-check this. It was just surprising to me to see that, unlike C#, the F# compiler produces slightly different flags for

/debug:portable /optimize+ 

and

/debug:full /optimize+ 

After your reply I am confident that we can recommend to users that /debug:portable is an adequate replacement for /debug:full, even for old .NET Framework compilation.

@dsyme dsyme closed this as completed Feb 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants