提交 acee6134 编写于 作者: V VSadov

Merge pull request #5381 from VSadov/fix4103

Propagate debug+ to suppress optimizations that interfere with debugging
......@@ -56,6 +56,7 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, platform, generalDiagnosticOption, warningLevel,
specificDiagnosticOptions, concurrentBuild,
extendedCustomDebugInformation: true,
debugPlusMode: false,
xmlReferenceResolver: xmlReferenceResolver,
sourceReferenceResolver: sourceReferenceResolver,
metadataReferenceResolver: metadataReferenceResolver,
......@@ -95,6 +96,7 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, platform, generalDiagnosticOption, warningLevel,
specificDiagnosticOptions, concurrentBuild,
extendedCustomDebugInformation: true,
debugPlusMode: false,
xmlReferenceResolver: xmlReferenceResolver,
sourceReferenceResolver: sourceReferenceResolver,
metadataReferenceResolver: metadataReferenceResolver,
......@@ -125,6 +127,7 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
IEnumerable<KeyValuePair<string, ReportDiagnostic>> specificDiagnosticOptions,
bool concurrentBuild,
bool extendedCustomDebugInformation,
bool debugPlusMode,
XmlReferenceResolver xmlReferenceResolver,
SourceReferenceResolver sourceReferenceResolver,
MetadataReferenceResolver metadataReferenceResolver,
......@@ -133,7 +136,7 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
MetadataImportOptions metadataImportOptions)
: base(outputKind, reportSuppressedDiagnostics, moduleName, mainTypeName, scriptClassName, cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign, optimizationLevel, checkOverflow,
platform, generalDiagnosticOption, warningLevel, specificDiagnosticOptions.ToImmutableDictionaryOrEmpty(),
concurrentBuild, extendedCustomDebugInformation, xmlReferenceResolver, sourceReferenceResolver, metadataReferenceResolver, assemblyIdentityComparer,
concurrentBuild, extendedCustomDebugInformation, debugPlusMode, xmlReferenceResolver, sourceReferenceResolver, metadataReferenceResolver, assemblyIdentityComparer,
strongNameProvider, metadataImportOptions)
{
this.Usings = usings.AsImmutableOrEmpty();
......@@ -159,6 +162,7 @@ public sealed class CSharpCompilationOptions : CompilationOptions, IEquatable<CS
specificDiagnosticOptions: other.SpecificDiagnosticOptions,
concurrentBuild: other.ConcurrentBuild,
extendedCustomDebugInformation: other.ExtendedCustomDebugInformation,
debugPlusMode: other.DebugPlusMode,
xmlReferenceResolver: other.XmlReferenceResolver,
sourceReferenceResolver: other.SourceReferenceResolver,
metadataReferenceResolver: other.MetadataReferenceResolver,
......@@ -404,6 +408,16 @@ internal CSharpCompilationOptions WithExtendedCustomDebugInformation(bool extend
return new CSharpCompilationOptions(this) { ExtendedCustomDebugInformation_internal_protected_set = extendedCustomDebugInformation };
}
internal CSharpCompilationOptions WithDebugPlusMode(bool debugPlusMode)
{
if (debugPlusMode == this.DebugPlusMode)
{
return this;
}
return new CSharpCompilationOptions(this) { DebugPlusMode_internal_protected_set = debugPlusMode };
}
internal CSharpCompilationOptions WithMetadataImportOptions(MetadataImportOptions value)
{
if (value == this.MetadataImportOptions)
......
......@@ -30,7 +30,7 @@ internal sealed partial class CodeGenerator
private readonly ILBuilder _builder;
private readonly PEModuleBuilder _module;
private readonly DiagnosticBag _diagnostics;
private readonly OptimizationLevel _optimizations;
private readonly ILEmitStyle _ilEmitStyle;
private readonly bool _emitPdbSequencePoints;
private readonly HashSet<LocalSymbol> _stackLocals;
......@@ -61,6 +61,20 @@ private enum IndirectReturnState : byte
Emitted = 2, // return sequence has been emitted
}
private enum ILEmitStyle : byte
{
// no optimizations
// add additional debug specific emit
// like nops for sequence points mapping to no IL
Debug = 0,
// do optimizations that do not diminish debug experience
DebugFriendlyRelease = 1,
// do all optimizations
Release = 2,
}
private LocalDefinition _returnTemp;
public CodeGenerator(
......@@ -84,13 +98,28 @@ private enum IndirectReturnState : byte
_module = moduleBuilder;
_diagnostics = diagnostics;
// Always optimize synthesized methods that don't contain user code.
//
// Specifically, always optimize synthesized explicit interface implementation methods
// (aka bridge methods) with by-ref returns because peverify produces errors if we
// return a ref local (which the return local will be in such cases).
_optimizations = method.GenerateDebugInfo ? optimizations : OptimizationLevel.Release;
if (!method.GenerateDebugInfo)
{
// Always optimize synthesized methods that don't contain user code.
//
// Specifically, always optimize synthesized explicit interface implementation methods
// (aka bridge methods) with by-ref returns because peverify produces errors if we
// return a ref local (which the return local will be in such cases).
_ilEmitStyle = ILEmitStyle.Release;
}
else
{
if (optimizations == OptimizationLevel.Debug)
{
_ilEmitStyle = ILEmitStyle.Debug;
}
else
{
_ilEmitStyle = IsDebugPlus() ?
ILEmitStyle.DebugFriendlyRelease :
ILEmitStyle.Release;
}
}
// Emit sequence points unless
// - the PDBs are not being generated
......@@ -102,12 +131,17 @@ private enum IndirectReturnState : byte
_boundBody = Optimizer.Optimize(
boundBody,
debugFriendly: _optimizations != OptimizationLevel.Release,
debugFriendly: _ilEmitStyle != ILEmitStyle.Release,
stackLocals: out _stackLocals);
_methodBodySyntaxOpt = (method as SourceMethodSymbol)?.BodySyntax;
}
private bool IsDebugPlus()
{
return this._module.Compilation.Options.DebugPlusMode;
}
private LocalDefinition LazyReturnTemp
{
get
......@@ -118,7 +152,7 @@ private LocalDefinition LazyReturnTemp
Debug.Assert(!_method.ReturnsVoid, "returning something from void method?");
var bodySyntax = _methodBodySyntaxOpt;
if (_optimizations == OptimizationLevel.Debug && bodySyntax != null)
if (_ilEmitStyle == ILEmitStyle.Debug && bodySyntax != null)
{
int syntaxOffset = _method.CalculateLocalSyntaxOffset(bodySyntax.SpanStart, bodySyntax.SyntaxTree);
var localSymbol = new SynthesizedLocal(_method, _method.ReturnType, SynthesizedLocalKind.FunctionReturnValue, bodySyntax);
......@@ -133,7 +167,7 @@ private LocalDefinition LazyReturnTemp
constraints: LocalSlotConstraints.None,
isDynamic: false,
dynamicTransformFlags: ImmutableArray<TypedConstant>.Empty,
isSlotReusable: localSymbol.SynthesizedKind.IsSlotReusable(_optimizations));
isSlotReusable: false);
}
else
{
......@@ -303,7 +337,7 @@ private void EmitSequencePointStatement(BoundSequencePoint node)
instructionsEmitted = this.EmitStatementAndCountInstructions(statement);
}
if (instructionsEmitted == 0 && syntax != null && _optimizations == OptimizationLevel.Debug)
if (instructionsEmitted == 0 && syntax != null && _ilEmitStyle == ILEmitStyle.Debug)
{
// if there was no code emitted, then emit nop
// otherwise this point could get associated with some random statement, possibly in a wrong scope
......@@ -326,7 +360,7 @@ private void EmitSequencePointStatement(BoundSequencePointWithSpan node)
instructionsEmitted = this.EmitStatementAndCountInstructions(statement);
}
if (instructionsEmitted == 0 && span != default(TextSpan) && _optimizations == OptimizationLevel.Debug)
if (instructionsEmitted == 0 && span != default(TextSpan) && _ilEmitStyle == ILEmitStyle.Debug)
{
// if there was no code emitted, then emit nop
// otherwise this point could get associated with some random statement, possibly in a wrong scope
......
......@@ -1415,7 +1415,7 @@ private void EmitCallExpression(BoundCall call, bool used)
{
EmitPopIfUnused(used);
}
else if (_optimizations == OptimizationLevel.Debug)
else if (_ilEmitStyle == ILEmitStyle.Debug)
{
// The only void methods with usable return values are constructors and we represent those
// as BoundObjectCreationExpressions, not BoundCalls.
......
......@@ -110,7 +110,7 @@ private void EmitNoOpStatement(BoundNoOpStatement statement)
switch (statement.Flavor)
{
case NoOpStatementFlavor.Default:
if (_optimizations == OptimizationLevel.Debug)
if (_ilEmitStyle == ILEmitStyle.Debug)
{
_builder.EmitOpCode(ILOpCode.Nop);
}
......@@ -623,7 +623,7 @@ private bool ShouldUseIndirectReturn()
// ret
//
// Do not emit this pattern if the method doesn't include user code or doesn't have a block body.
return _optimizations == OptimizationLevel.Debug && _method.GenerateDebugInfo && _methodBodySyntaxOpt?.IsKind(SyntaxKind.Block) == true ||
return _ilEmitStyle == ILEmitStyle.Debug && _method.GenerateDebugInfo && _methodBodySyntaxOpt?.IsKind(SyntaxKind.Block) == true ||
_builder.InExceptionHandler;
}
......@@ -1404,7 +1404,7 @@ private LocalDefinition DefineLocal(LocalSymbol local, CSharpSyntaxNode syntaxNo
constraints: constraints,
isDynamic: isDynamicSourceLocal,
dynamicTransformFlags: transformFlags,
isSlotReusable: local.SynthesizedKind.IsSlotReusable(_optimizations));
isSlotReusable: local.SynthesizedKind.IsSlotReusable(_ilEmitStyle != ILEmitStyle.Release));
// If named, add it to the local debug scope.
if (localDef.Name != null)
......@@ -1437,7 +1437,7 @@ private string GetLocalDebugName(ILocalSymbolInternal local, out LocalDebugId lo
return null;
}
if (_optimizations == OptimizationLevel.Debug)
if (_ilEmitStyle == ILEmitStyle.Debug)
{
var syntax = local.GetDeclaratorSyntax();
int syntaxOffset = _method.CalculateLocalSyntaxOffset(syntax.SpanStart, syntax.SyntaxTree);
......@@ -1455,7 +1455,7 @@ private string GetLocalDebugName(ILocalSymbolInternal local, out LocalDebugId lo
private bool IsSlotReusable(LocalSymbol local)
{
return local.SynthesizedKind.IsSlotReusable(_optimizations);
return local.SynthesizedKind.IsSlotReusable(_ilEmitStyle != ILEmitStyle.Release);
}
/// <summary>
......
......@@ -16,6 +16,39 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeGen
{
internal class Optimizer
{
/// <summary>
/// Perform IL specific optiomizations (mostly reduction of local slots)
/// </summary>
/// <param name="src">Method body to optimize</param>
/// <param name="debugFriendly">
/// When set, do not perform aggressive optimizations that degrade debugging experience.
/// In particular we do not do the following:
///
/// 1) Do not elide any user defined locals, even if never read from.
/// Example:
/// {
/// var dummy = Foo(); // should not become just "Foo"
/// }
///
/// User might want to examine dummy in the debugger.
///
/// 2) Do not carry values on the stack between statements
/// Example:
/// {
/// var temp = Foo();
/// temp.ToString(); // should not become Foo().ToString();
/// }
///
/// User might want to examine temp in the debugger.
///
/// </param>
/// <param name="stackLocals">
/// Produced list of "ephemeral" locals.
/// Essentially, these locals do not need to leave the evaluation stack.
/// As such they do not require an allocation of a local slot and
/// their load/store operations are implemented trivially.
/// </param>
/// <returns></returns>
public static BoundStatement Optimize(
BoundStatement src, bool debugFriendly,
out HashSet<LocalSymbol> stackLocals)
......
......@@ -57,6 +57,7 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
bool allowUnsafe = false;
bool concurrentBuild = true;
bool emitPdb = false;
bool debugPlus = false;
string pdbPath = null;
bool noStdLib = false;
string outputDirectory = baseDirectory;
......@@ -524,6 +525,7 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
break;
emitPdb = true;
debugPlus = true;
continue;
case "debug-":
......@@ -531,6 +533,7 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
break;
emitPdb = false;
debugPlus = false;
continue;
case "o":
......@@ -1081,6 +1084,11 @@ public new CSharpCommandLineArguments Parse(IEnumerable<string> args, string bas
reportSuppressedDiagnostics: reportSuppressedDiagnostics
);
if (debugPlus)
{
options = options.WithDebugPlusMode(debugPlus);
}
var emitOptions = new EmitOptions
(
metadataOnly: false,
......
......@@ -1169,53 +1169,67 @@ public void Debug()
{
var parsedArgs = DefaultParse(new[] { "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug-", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug+", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(true, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug+", "/debug-", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:full", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:FULL", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:PDBONLY", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:full", "/debug:pdbonly", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "/debug:full", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.True(parsedArgs.EmitPdb);
Assert.Equal(DebugInformationFormat.Pdb, parsedArgs.EmitOptions.DebugInformationFormat);
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "/debug-", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.False(parsedArgs.EmitPdb);
Assert.Equal(DebugInformationFormat.Pdb, parsedArgs.EmitOptions.DebugInformationFormat);
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "/debug-", "/debug", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.True(parsedArgs.EmitPdb);
Assert.Equal(DebugInformationFormat.Pdb, parsedArgs.EmitOptions.DebugInformationFormat);
Assert.Equal(false, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:pdbonly", "/debug-", "/debug+", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify();
Assert.True(parsedArgs.EmitPdb);
Assert.Equal(DebugInformationFormat.Pdb, parsedArgs.EmitOptions.DebugInformationFormat);
Assert.Equal(true, parsedArgs.CompilationOptions.DebugPlusMode);
parsedArgs = DefaultParse(new[] { "/debug:", "a.cs" }, _baseDirectory);
parsedArgs.Errors.Verify(Diagnostic(ErrorCode.ERR_SwitchNeedsString).WithArguments("<text>", "debug"));
......
......@@ -66,6 +66,14 @@ public static void Main()
Assert.Equal(TypeKind.Struct, stateMachine.TypeKind);
}, expectedOutput: "123");
options = TestOptions.ReleaseDebugExe;
Assert.False(options.EnableEditAndContinue);
CompileAndVerify(c.WithOptions(options), symbolValidator: module =>
{
var stateMachine = module.GlobalNamespace.GetMember<NamedTypeSymbol>("Test").GetMember<NamedTypeSymbol>("<F>d__0");
Assert.Equal(TypeKind.Struct, stateMachine.TypeKind);
}, expectedOutput: "123");
options = TestOptions.DebugExe;
Assert.True(options.EnableEditAndContinue);
......@@ -2173,6 +2181,163 @@ .maxstack 2
");
}
[Fact]
public void AsyncStateMachineIL_Struct_TaskT_A()
{
var source = @"
using System;
using System.Threading.Tasks;
class Test
{
public static async Task<int> F()
{
return await Task.Factory.StartNew(() => 42);
}
public static void Main()
{
var t = F();
t.Wait();
Console.WriteLine(t.Result);
}
}";
var expected = @"
42
";
var c = CompileAndVerify(source, options: TestOptions.ReleaseDebugExe ,expectedOutput: expected);
c.VerifyIL("Test.F", @"
{
// Code size 49 (0x31)
.maxstack 2
.locals init (Test.<F>d__0 V_0,
System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> V_1)
IL_0000: ldloca.s V_0
IL_0002: call ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.Create()""
IL_0007: stfld ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__0.<>t__builder""
IL_000c: ldloca.s V_0
IL_000e: ldc.i4.m1
IL_000f: stfld ""int Test.<F>d__0.<>1__state""
IL_0014: ldloc.0
IL_0015: ldfld ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__0.<>t__builder""
IL_001a: stloc.1
IL_001b: ldloca.s V_1
IL_001d: ldloca.s V_0
IL_001f: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.Start<Test.<F>d__0>(ref Test.<F>d__0)""
IL_0024: ldloca.s V_0
IL_0026: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__0.<>t__builder""
IL_002b: call ""System.Threading.Tasks.Task<int> System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.Task.get""
IL_0030: ret
}
");
c.VerifyIL("Test.<F>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @"
{
// Code size 196 (0xc4)
.maxstack 3
.locals init (int V_0,
int V_1,
int V_2,
System.Runtime.CompilerServices.TaskAwaiter<int> V_3,
int V_4,
System.Exception V_5)
IL_0000: ldarg.0
IL_0001: ldfld ""int Test.<F>d__0.<>1__state""
IL_0006: stloc.0
.try
{
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0062
IL_000a: call ""System.Threading.Tasks.TaskFactory System.Threading.Tasks.Task.Factory.get""
IL_000f: ldsfld ""System.Func<int> Test.<>c.<>9__0_0""
IL_0014: dup
IL_0015: brtrue.s IL_002e
IL_0017: pop
IL_0018: ldsfld ""Test.<>c Test.<>c.<>9""
IL_001d: ldftn ""int Test.<>c.<F>b__0_0()""
IL_0023: newobj ""System.Func<int>..ctor(object, System.IntPtr)""
IL_0028: dup
IL_0029: stsfld ""System.Func<int> Test.<>c.<>9__0_0""
IL_002e: callvirt ""System.Threading.Tasks.Task<int> System.Threading.Tasks.TaskFactory.StartNew<int>(System.Func<int>)""
IL_0033: callvirt ""System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()""
IL_0038: stloc.3
IL_0039: ldloca.s V_3
IL_003b: call ""bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get""
IL_0040: brtrue.s IL_007e
IL_0042: ldarg.0
IL_0043: ldc.i4.0
IL_0044: dup
IL_0045: stloc.0
IL_0046: stfld ""int Test.<F>d__0.<>1__state""
IL_004b: ldarg.0
IL_004c: ldloc.3
IL_004d: stfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__0.<>u__1""
IL_0052: ldarg.0
IL_0053: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__0.<>t__builder""
IL_0058: ldloca.s V_3
IL_005a: ldarg.0
IL_005b: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Test.<F>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Test.<F>d__0)""
IL_0060: leave.s IL_00c3
IL_0062: ldarg.0
IL_0063: ldfld ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__0.<>u__1""
IL_0068: stloc.3
IL_0069: ldarg.0
IL_006a: ldflda ""System.Runtime.CompilerServices.TaskAwaiter<int> Test.<F>d__0.<>u__1""
IL_006f: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_0075: ldarg.0
IL_0076: ldc.i4.m1
IL_0077: dup
IL_0078: stloc.0
IL_0079: stfld ""int Test.<F>d__0.<>1__state""
IL_007e: ldloca.s V_3
IL_0080: call ""int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()""
IL_0085: stloc.s V_4
IL_0087: ldloca.s V_3
IL_0089: initobj ""System.Runtime.CompilerServices.TaskAwaiter<int>""
IL_008f: ldloc.s V_4
IL_0091: stloc.2
IL_0092: ldloc.2
IL_0093: stloc.1
IL_0094: leave.s IL_00af
}
catch System.Exception
{
IL_0096: stloc.s V_5
IL_0098: ldarg.0
IL_0099: ldc.i4.s -2
IL_009b: stfld ""int Test.<F>d__0.<>1__state""
IL_00a0: ldarg.0
IL_00a1: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__0.<>t__builder""
IL_00a6: ldloc.s V_5
IL_00a8: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetException(System.Exception)""
IL_00ad: leave.s IL_00c3
}
IL_00af: ldarg.0
IL_00b0: ldc.i4.s -2
IL_00b2: stfld ""int Test.<F>d__0.<>1__state""
IL_00b7: ldarg.0
IL_00b8: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__0.<>t__builder""
IL_00bd: ldloc.1
IL_00be: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetResult(int)""
IL_00c3: ret
}
");
c.VerifyIL("Test.<F>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.SetStateMachine", @"
{
// Code size 13 (0xd)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> Test.<F>d__0.<>t__builder""
IL_0006: ldarg.1
IL_0007: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int>.SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)""
IL_000c: ret
}
");
}
[Fact]
public void AsyncStateMachineIL_Class_TaskT()
{
......
......@@ -3093,6 +3093,65 @@ .maxstack 3
");
}
[Fact]
public void RefInstanceFieldA()
{
string source = @"
public class D
{
public class Moo
{
public int I;
public Moo()
{
}
}
public static void Main()
{
Moo obj = new Moo();
System.Console.Write(obj.I);
obj.I = 42;
System.Console.Write(obj.I);
obj.I = 7;
System.Console.Write(obj.I);
}
}
";
var compilation = CompileAndVerify(source, options: TestOptions.ReleaseDebugExe ,expectedOutput: "0427");
compilation.VerifyIL("D.Main",
@"
{
// Code size 55 (0x37)
.maxstack 2
.locals init (D.Moo V_0) //obj
IL_0000: newobj ""D.Moo..ctor()""
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldfld ""int D.Moo.I""
IL_000c: call ""void System.Console.Write(int)""
IL_0011: ldloc.0
IL_0012: ldc.i4.s 42
IL_0014: stfld ""int D.Moo.I""
IL_0019: ldloc.0
IL_001a: ldfld ""int D.Moo.I""
IL_001f: call ""void System.Console.Write(int)""
IL_0024: ldloc.0
IL_0025: ldc.i4.7
IL_0026: stfld ""int D.Moo.I""
IL_002b: ldloc.0
IL_002c: ldfld ""int D.Moo.I""
IL_0031: call ""void System.Console.Write(int)""
IL_0036: ret
}
");
}
[Fact]
public void RefStaticField()
{
......@@ -6807,6 +6866,66 @@ .maxstack 2
");
}
[Fact]
public void TemporariesA()
{
string source = @"
using System;
class Program
{
static void Main()
{
bool x = true;
int y = (x != true).GetType().GetHashCode() - x.GetType().GetHashCode(); // Temps involved
Console.Write((y + y).ToString()); // Temp involved
}
public void test()
{
this.bar(1).ToString(); // Temp involved
}
public int bar(int x)
{
return 0;
}
}";
var compilation = CompileAndVerify(source, options: TestOptions.ReleaseDebugExe ,expectedOutput: @"0");
compilation.VerifyIL("Program.Main",
@"
{
// Code size 56 (0x38)
.maxstack 2
.locals init (bool V_0, //x
int V_1, //y
int V_2)
IL_0000: ldc.i4.1
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: ldc.i4.0
IL_0004: ceq
IL_0006: box ""bool""
IL_000b: call ""System.Type object.GetType()""
IL_0010: callvirt ""int object.GetHashCode()""
IL_0015: ldloc.0
IL_0016: box ""bool""
IL_001b: call ""System.Type object.GetType()""
IL_0020: callvirt ""int object.GetHashCode()""
IL_0025: sub
IL_0026: stloc.1
IL_0027: ldloc.1
IL_0028: ldloc.1
IL_0029: add
IL_002a: stloc.2
IL_002b: ldloca.s V_2
IL_002d: call ""string int.ToString()""
IL_0032: call ""void System.Console.Write(string)""
IL_0037: ret
}
");
}
[Fact]
public void EmitObjectToStringOnSimpleType()
{
......@@ -8632,6 +8751,79 @@ .maxstack 4
");
}
[Fact]
public void PreIncrementUnusedA()
{
string source = @"
using System;
class A
{
public static void Main()
{
int[] x = new int[3];
x[0] = 1;
x[1] = 2;
x[2] = 3;
++x[0];
Console.WriteLine(x[0]);
Console.WriteLine(x[1]);
Console.WriteLine(x[2]);
}
}
";
var compilation = CompileAndVerify(source, options: TestOptions.ReleaseDebugExe, expectedOutput: @"2
2
3");
compilation.VerifyIL("A.Main",
@"
{
// Code size 56 (0x38)
.maxstack 3
.locals init (int[] V_0) //x
IL_0000: ldc.i4.3
IL_0001: newarr ""int""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldc.i4.0
IL_0009: ldc.i4.1
IL_000a: stelem.i4
IL_000b: ldloc.0
IL_000c: ldc.i4.1
IL_000d: ldc.i4.2
IL_000e: stelem.i4
IL_000f: ldloc.0
IL_0010: ldc.i4.2
IL_0011: ldc.i4.3
IL_0012: stelem.i4
IL_0013: ldloc.0
IL_0014: ldc.i4.0
IL_0015: ldelema ""int""
IL_001a: dup
IL_001b: ldind.i4
IL_001c: ldc.i4.1
IL_001d: add
IL_001e: stind.i4
IL_001f: ldloc.0
IL_0020: ldc.i4.0
IL_0021: ldelem.i4
IL_0022: call ""void System.Console.WriteLine(int)""
IL_0027: ldloc.0
IL_0028: ldc.i4.1
IL_0029: ldelem.i4
IL_002a: call ""void System.Console.WriteLine(int)""
IL_002f: ldloc.0
IL_0030: ldc.i4.2
IL_0031: ldelem.i4
IL_0032: call ""void System.Console.WriteLine(int)""
IL_0037: ret
}
");
}
[Fact]
public void PostIncrementUnusedStruct()
{
......@@ -8723,6 +8915,56 @@ .maxstack 4
");
}
[Fact]
public void PostIncrementUnusedStruct1a()
{
string source = @"
using System;
class A
{
public S1 x = new S1();
public static void Main()
{
var v = new A();
v.x.y+=42;
Console.WriteLine(v.x.y);
}
}
struct S1
{
public int y;
}
";
var compilation = CompileAndVerify(source, expectedOutput: @"42", options: TestOptions.ReleaseDebugExe);
compilation.VerifyIL("A.Main",
@"
{
// Code size 40 (0x28)
.maxstack 3
.locals init (A V_0) //v
IL_0000: newobj ""A..ctor()""
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: ldflda ""S1 A.x""
IL_000c: ldflda ""int S1.y""
IL_0011: dup
IL_0012: ldind.i4
IL_0013: ldc.i4.s 42
IL_0015: add
IL_0016: stind.i4
IL_0017: ldloc.0
IL_0018: ldflda ""S1 A.x""
IL_001d: ldfld ""int S1.y""
IL_0022: call ""void System.Console.WriteLine(int)""
IL_0027: ret
}
");
}
[Fact]
public void PostIncrementUnusedStruct2()
{
......
......@@ -80,6 +80,7 @@ public void Invariants()
TestProperty((old, value) => old.WithConcurrentBuild(value), opt => opt.ConcurrentBuild, false);
TestProperty((old, value) => old.WithExtendedCustomDebugInformation(value), opt => opt.ExtendedCustomDebugInformation, false);
TestProperty((old, value) => old.WithDebugPlusMode(value), opt => opt.DebugPlusMode, true);
TestProperty((old, value) => old.WithXmlReferenceResolver(value), opt => opt.XmlReferenceResolver, new XmlFileResolver(null));
TestProperty((old, value) => old.WithMetadataReferenceResolver(value), opt => opt.MetadataReferenceResolver, new TestMetadataReferenceResolver());
......@@ -346,6 +347,7 @@ private static CSharpCompilationOptions CreateCSharpCompilationOptions()
IEnumerable<KeyValuePair<string, ReportDiagnostic>> specificDiagnosticOptions = null;
bool concurrentBuild = false;
bool extendedCustomDebugInformation = true;
bool debugPlusMode = false;
XmlReferenceResolver xmlReferenceResolver = new XmlFileResolver(null);
SourceReferenceResolver sourceReferenceResolver = new SourceFileResolver(ImmutableArray<string>.Empty, null);
MetadataReferenceResolver metadataReferenceResolver = new MetadataReferenceResolverWithEquality();
......@@ -356,7 +358,7 @@ private static CSharpCompilationOptions CreateCSharpCompilationOptions()
return new CSharpCompilationOptions(OutputKind.ConsoleApplication, reportSuppressedDiagnostics, moduleName, mainTypeName, scriptClassName, usings,
optimizationLevel, checkOverflow, allowUnsafe, cryptoKeyContainer, cryptoKeyFile, cryptoPublicKey, delaySign,
platform, generalDiagnosticOption, warningLevel, specificDiagnosticOptions,
concurrentBuild, extendedCustomDebugInformation, xmlReferenceResolver, sourceReferenceResolver, metadataReferenceResolver,
concurrentBuild, extendedCustomDebugInformation, debugPlusMode, xmlReferenceResolver, sourceReferenceResolver, metadataReferenceResolver,
assemblyIdentityComparer, strongNameProvider, metadataImportOptions);
}
......
......@@ -28,6 +28,7 @@ public void TestFieldsForEqualsAndGetHashCode()
"EnableEditAndContinue",
"Errors",
"ExtendedCustomDebugInformation",
"DebugPlusMode",
"Features",
"GeneralDiagnosticOption",
"MainTypeName",
......
......@@ -141,6 +141,14 @@ public abstract class CompilationOptions
// TODO: change visibility of the ExtendedCustomDebugInformation setter to internal & protected
internal bool ExtendedCustomDebugInformation_internal_protected_set { set { ExtendedCustomDebugInformation = value; } }
/// <summary>
/// Emit mode that favors debuggability.
/// </summary>
internal bool DebugPlusMode { get; private set; }
// TODO: change visibility of the DebugPlusMode setter to internal & protected
internal bool DebugPlusMode_internal_protected_set { set { DebugPlusMode = value; } }
/// <summary>
/// Import internal/private members from all references regardless of "internals-visible-to" relationship.
/// </summary>
......@@ -233,6 +241,7 @@ protected set
ImmutableDictionary<string, ReportDiagnostic> specificDiagnosticOptions,
bool concurrentBuild,
bool extendedCustomDebugInformation,
bool debugPlusMode,
XmlReferenceResolver xmlReferenceResolver,
SourceReferenceResolver sourceReferenceResolver,
MetadataReferenceResolver metadataReferenceResolver,
......@@ -257,6 +266,7 @@ protected set
this.OptimizationLevel = optimizationLevel;
this.ConcurrentBuild = concurrentBuild;
this.ExtendedCustomDebugInformation = extendedCustomDebugInformation;
this.DebugPlusMode = debugPlusMode;
this.XmlReferenceResolver = xmlReferenceResolver;
this.SourceReferenceResolver = sourceReferenceResolver;
this.MetadataReferenceResolver = metadataReferenceResolver;
......@@ -433,6 +443,7 @@ protected bool EqualsHelper(CompilationOptions other)
this.CheckOverflow == other.CheckOverflow &&
this.ConcurrentBuild == other.ConcurrentBuild &&
this.ExtendedCustomDebugInformation == other.ExtendedCustomDebugInformation &&
this.DebugPlusMode == other.DebugPlusMode &&
string.Equals(this.CryptoKeyContainer, other.CryptoKeyContainer, StringComparison.Ordinal) &&
string.Equals(this.CryptoKeyFile, other.CryptoKeyFile, StringComparison.Ordinal) &&
this.CryptoPublicKey.SequenceEqual(other.CryptoPublicKey) &&
......@@ -464,6 +475,7 @@ protected int GetHashCodeHelper()
return Hash.Combine(this.CheckOverflow,
Hash.Combine(this.ConcurrentBuild,
Hash.Combine(this.ExtendedCustomDebugInformation,
Hash.Combine(this.DebugPlusMode,
Hash.Combine(this.CryptoKeyContainer != null ? StringComparer.Ordinal.GetHashCode(this.CryptoKeyContainer) : 0,
Hash.Combine(this.CryptoKeyFile != null ? StringComparer.Ordinal.GetHashCode(this.CryptoKeyFile) : 0,
Hash.Combine(Hash.CombineValues(this.CryptoPublicKey, 16),
......@@ -482,7 +494,7 @@ protected int GetHashCodeHelper()
Hash.Combine(this.XmlReferenceResolver,
Hash.Combine(this.SourceReferenceResolver,
Hash.Combine(this.StrongNameProvider,
Hash.Combine(this.AssemblyIdentityComparer, 0))))))))))))))))))))));
Hash.Combine(this.AssemblyIdentityComparer, 0)))))))))))))))))))))));
}
public static bool operator ==(CompilationOptions left, CompilationOptions right)
......
......@@ -236,7 +236,12 @@ public static bool MustSurviveStateMachineSuspension(this SynthesizedLocalKind k
public static bool IsSlotReusable(this SynthesizedLocalKind kind, OptimizationLevel optimizations)
{
if (optimizations == OptimizationLevel.Debug)
return kind.IsSlotReusable(optimizations != OptimizationLevel.Release);
}
public static bool IsSlotReusable(this SynthesizedLocalKind kind, bool isDebug)
{
if (isDebug)
{
// Don't reuse any long-lived locals in debug builds to provide good debugging experience
// for user-defined locals and to allow EnC.
......
......@@ -22,6 +22,14 @@ public static class TestOptions
public static readonly CSharpCompilationOptions ReleaseDll = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release).WithExtendedCustomDebugInformation(true);
public static readonly CSharpCompilationOptions ReleaseExe = new CSharpCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel: OptimizationLevel.Release).WithExtendedCustomDebugInformation(true);
public static readonly CSharpCompilationOptions ReleaseDebugDll = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release).
WithExtendedCustomDebugInformation(true).
WithDebugPlusMode(true);
public static readonly CSharpCompilationOptions ReleaseDebugExe = new CSharpCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel: OptimizationLevel.Release).
WithExtendedCustomDebugInformation(true).
WithDebugPlusMode(true);
public static readonly CSharpCompilationOptions DebugDll = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Debug).WithExtendedCustomDebugInformation(true);
public static readonly CSharpCompilationOptions DebugExe = new CSharpCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel: OptimizationLevel.Debug).WithExtendedCustomDebugInformation(true);
......
......@@ -10,6 +10,14 @@ Public Class TestOptions
Public Shared ReadOnly ReleaseDll As VisualBasicCompilationOptions = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel:=OptimizationLevel.Release).WithExtendedCustomDebugInformation(True)
Public Shared ReadOnly ReleaseExe As VisualBasicCompilationOptions = New VisualBasicCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel:=OptimizationLevel.Release).WithExtendedCustomDebugInformation(True)
Public Shared ReadOnly ReleaseDebugDll As VisualBasicCompilationOptions = New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel:=OptimizationLevel.Release).
WithExtendedCustomDebugInformation(True).
WithDebugPlusMode(True)
Public Shared ReadOnly ReleaseDebugExe As VisualBasicCompilationOptions = New VisualBasicCompilationOptions(OutputKind.ConsoleApplication, optimizationLevel:=OptimizationLevel.Release).
WithExtendedCustomDebugInformation(True).
WithDebugPlusMode(True)
Private Shared ReadOnly s_features As New Dictionary(Of String, String) ' No experimental features to enable at this time
Public Shared ReadOnly ExperimentalReleaseExe As New VisualBasicCompilationOptions(OutputKind.ConsoleApplication,
optimizationLevel:=OptimizationLevel.Release,
......
......@@ -118,6 +118,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
concurrentBuild,
suppressEmbeddedDeclarations:=False,
extendedCustomDebugInformation:=True,
debugPlusMode:=False,
xmlReferenceResolver:=xmlReferenceResolver,
sourceReferenceResolver:=sourceReferenceResolver,
metadataReferenceResolver:=metadataReferenceResolver,
......@@ -214,6 +215,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
concurrentBuild,
suppressEmbeddedDeclarations:=False,
extendedCustomDebugInformation:=True,
debugPlusMode:=False,
xmlReferenceResolver:=xmlReferenceResolver,
sourceReferenceResolver:=sourceReferenceResolver,
metadataReferenceResolver:=metadataReferenceResolver,
......@@ -249,6 +251,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
concurrentBuild As Boolean,
suppressEmbeddedDeclarations As Boolean,
extendedCustomDebugInformation As Boolean,
debugPlusMode As Boolean,
xmlReferenceResolver As XmlReferenceResolver,
sourceReferenceResolver As SourceReferenceResolver,
metadataReferenceResolver As MetadataReferenceResolver,
......@@ -274,6 +277,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
specificDiagnosticOptions:=specificDiagnosticOptions.ToImmutableDictionaryOrEmpty(CaseInsensitiveComparison.Comparer), ' Diagnostic ids must be processed in case-insensitive fashion.
concurrentBuild:=concurrentBuild,
extendedCustomDebugInformation:=extendedCustomDebugInformation,
debugPlusMode:=debugPlusMode,
xmlReferenceResolver:=xmlReferenceResolver,
sourceReferenceResolver:=sourceReferenceResolver,
metadataReferenceResolver:=metadataReferenceResolver,
......@@ -322,6 +326,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
specificDiagnosticOptions:=other.SpecificDiagnosticOptions,
concurrentBuild:=other.ConcurrentBuild,
extendedCustomDebugInformation:=other.ExtendedCustomDebugInformation,
debugPlusMode:=other.DebugPlusMode,
xmlReferenceResolver:=other.XmlReferenceResolver,
sourceReferenceResolver:=other.SourceReferenceResolver,
metadataReferenceResolver:=other.MetadataReferenceResolver,
......@@ -637,6 +642,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return New VisualBasicCompilationOptions(Me) With {.ExtendedCustomDebugInformation_internal_protected_set = extendedCustomDebugInformation}
End Function
''' <summary>
''' Creates a new VisualBasicCompilationOptions instance with a different extended custom debug information specified.
''' </summary>
''' <param name="debugPlusMode">The extended custom debug information setting. </param>
''' <returns>A new instance of VisualBasicCompilationOptions, if the extended custom debug information is different; otherwise current instance.</returns>
Friend Function WithDebugPlusMode(debugPlusMode As Boolean) As VisualBasicCompilationOptions
If debugPlusMode = Me.DebugPlusMode Then
Return Me
End If
Return New VisualBasicCompilationOptions(Me) With {.DebugPlusMode_internal_protected_set = debugPlusMode}
End Function
''' <summary>
''' Creates a new VisualBasicCompilationOptions instance with different embedded declaration suppression setting specified.
''' </summary>
......
......@@ -76,6 +76,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
TestProperty(Function(old, value) old.WithConcurrentBuild(value), Function(opt) opt.ConcurrentBuild, False)
TestProperty(Function(old, value) old.WithExtendedCustomDebugInformation(value), Function(opt) opt.ExtendedCustomDebugInformation, False)
TestProperty(Function(old, value) old.WithDebugPlusMode(value), Function(opt) opt.DebugPlusMode, True)
TestProperty(Function(old, value) old.WithXmlReferenceResolver(value), Function(opt) opt.XmlReferenceResolver, New XmlFileResolver(Nothing))
TestProperty(Function(old, value) old.WithSourceReferenceResolver(value), Function(opt) opt.SourceReferenceResolver, New SourceFileResolver(ImmutableArray(Of String).Empty, Nothing))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册