diff --git a/build/Targets/Packages.props b/build/Targets/Packages.props
index 0c09c9832edf5..97015cac4a252 100644
--- a/build/Targets/Packages.props
+++ b/build/Targets/Packages.props
@@ -47,7 +47,7 @@
3.13.8
15.0.26730-alpha
14.3.25407-alpha
- 1.0.0-beta1-61531-03
+ 1.0.0-beta1-62621-03
8.0.0.0-alpha
15.6.0-dev
diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
index 898d0d7e41b9b..94c800cbaf647 100644
--- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
+++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs
@@ -1346,7 +1346,7 @@ private static MethodBody GenerateMethodBody(
var localSlotManager = new LocalSlotManager(variableSlotAllocatorOpt);
var optimizations = compilation.Options.OptimizationLevel;
- ILBuilder builder = new ILBuilder(moduleBuilder, localSlotManager, optimizations);
+ ILBuilder builder = new ILBuilder(moduleBuilder, localSlotManager, optimizations, method.AreLocalsZeroed);
DiagnosticBag diagnosticsForThisMethod = DiagnosticBag.GetInstance();
try
{
@@ -1460,6 +1460,7 @@ private static MethodBody GenerateMethodBody(
builder.RealizedSequencePoints,
debugDocumentProvider,
builder.RealizedExceptionHandlers,
+ builder.AreLocalsZeroed,
builder.GetAllScopes(),
builder.HasDynamicLocal,
importScopeOpt,
diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs
index 8c7a64501ddc3..8b1d98575b8bd 100644
--- a/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/ErrorMethodSymbol.cs
@@ -230,6 +230,11 @@ public sealed override DllImportData GetDllImportData()
return null;
}
+ public override bool AreLocalsZeroed
+ {
+ get { throw ExceptionUtilities.Unreachable; }
+ }
+
internal sealed override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation
{
get { return null; }
diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs
index ba2678ebb3385..44f5ada21b8c9 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs
@@ -1153,6 +1153,11 @@ internal override void AddSynthesizedReturnTypeAttributes(PEModuleBuilder module
throw ExceptionUtilities.Unreachable;
}
+ public override bool AreLocalsZeroed
+ {
+ get { throw ExceptionUtilities.Unreachable; }
+ }
+
// perf, not correctness
internal override CSharpCompilation DeclaringCompilation => null;
diff --git a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs
index 9754e641644b1..e0c8f014d08c8 100644
--- a/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/MethodSymbol.cs
@@ -1221,6 +1221,11 @@ public virtual bool IsTupleMethod
}
}
+ ///
+ /// Returns true if locals are to be initialized
+ ///
+ public abstract bool AreLocalsZeroed { get; }
+
///
/// If this is a method of a tuple type, return corresponding underlying method from the
/// tuple underlying type. Otherwise, null.
diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs
index ce420b290a41b..dca22c2440627 100644
--- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs
@@ -183,6 +183,11 @@ public override DllImportData GetDllImportData()
return _reducedFrom.GetDllImportData();
}
+ public override bool AreLocalsZeroed
+ {
+ get { throw ExceptionUtilities.Unreachable; }
+ }
+
internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation
{
get { return _reducedFrom.ReturnValueMarshallingInformation; }
diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs
index 8a7067f3b3bae..03a083fd3a813 100644
--- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyMethodSymbol.cs
@@ -132,6 +132,8 @@ public SignatureOnlyMethodSymbol(
public override bool IsExtern { get { throw ExceptionUtilities.Unreachable; } }
+ public override bool AreLocalsZeroed { get { throw ExceptionUtilities.Unreachable; } }
+
public override AssemblySymbol ContainingAssembly { get { throw ExceptionUtilities.Unreachable; } }
internal override ModuleSymbol ContainingModule { get { throw ExceptionUtilities.Unreachable; } }
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs
index a4686aef4dcbd..0ce2b30f3606b 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs
@@ -136,6 +136,11 @@ public override DllImportData GetDllImportData()
return null;
}
+ public override bool AreLocalsZeroed
+ {
+ get { throw ExceptionUtilities.Unreachable; }
+ }
+
internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation
{
get { return null; }
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs
index 182b3db4ae931..a325df52960c8 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs
@@ -340,6 +340,8 @@ internal override TypeSymbol IteratorElementType
public override DllImportData GetDllImportData() => null;
+ public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable;
+
internal override ImmutableArray GetAppliedConditionalSymbols() => ImmutableArray.Empty;
internal override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false;
diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs
index 6066c790ae5d0..715c3337cc789 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs
@@ -358,6 +358,15 @@ public override Symbol AssociatedSymbol
}
}
+ public override bool AreLocalsZeroed
+ {
+ get
+ {
+ var data = this.GetDecodedWellKnownAttributeData();
+ return data != null ? !data.HasSkipLocalsInitAttribute : true;
+ }
+ }
+
#region Flags
public override bool ReturnsVoid
@@ -1128,6 +1137,10 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut
arguments.Diagnostics.Add(ErrorCode.ERR_SecurityCriticalOrSecuritySafeCriticalOnAsync, arguments.AttributeSyntaxOpt.Location, arguments.AttributeSyntaxOpt.GetErrorDisplayName());
}
}
+ else if (attribute.IsTargetAttribute(this, AttributeDescription.SkipLocalsInitAttribute))
+ {
+ arguments.GetOrCreateData().HasSkipLocalsInitAttribute = true;
+ }
else
{
var compilation = this.DeclaringCompilation;
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs
index adffc1b052b58..f3c0ba94d77fe 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs
@@ -241,6 +241,11 @@ public override DllImportData GetDllImportData()
return null;
}
+ public override bool AreLocalsZeroed
+ {
+ get { return true; }
+ }
+
internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation
{
get { return null; }
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs
index 29649964c4d94..ef5c986c517a3 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedGlobalMethodSymbol.cs
@@ -116,6 +116,11 @@ public override DllImportData GetDllImportData()
return null;
}
+ public override bool AreLocalsZeroed
+ {
+ get { return true; }
+ }
+
internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation
{
get { return null; }
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs
index deb83e727e26b..feb93dbe09815 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs
@@ -30,6 +30,14 @@ public sealed override bool IsImplicitlyDeclared
}
}
+ public override bool AreLocalsZeroed
+ {
+ get
+ {
+ return true;
+ }
+ }
+
internal override bool TryGetThisParameter(out ParameterSymbol thisParameter)
{
Debug.Assert(!IsStatic);
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs
index 544b2e958bbd4..696e6894eddfb 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs
@@ -163,6 +163,11 @@ public override DllImportData GetDllImportData()
return null;
}
+ public override bool AreLocalsZeroed
+ {
+ get { throw ExceptionUtilities.Unreachable; }
+ }
+
internal override IEnumerable GetSecurityInformation()
{
return SpecializedCollections.EmptyEnumerable();
diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs
index 6fd6822be3b90..c225277622c72 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedStaticConstructor.cs
@@ -334,6 +334,11 @@ public override DllImportData GetDllImportData()
return null;
}
+ public override bool AreLocalsZeroed
+ {
+ get { return true; }
+ }
+
internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation
{
get { return null; }
diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs
index 095f94414ea6e..0ef5df40e1017 100644
--- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs
+++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedMethodSymbol.cs
@@ -78,6 +78,14 @@ public override bool HidesBaseMethodsByName
}
}
+ public override bool AreLocalsZeroed
+ {
+ get
+ {
+ return UnderlyingMethod.AreLocalsZeroed;
+ }
+ }
+
public override ImmutableArray Locations
{
get
diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs
index 25cfab7699739..2296b94e18c2a 100644
--- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs
+++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_WellKnownAttributes.cs
@@ -8559,6 +8559,314 @@ public static int Main()
#endregion
+ #region SkipLocalsInitAttribute
+
+ [Fact]
+ public void SkipLocalsInitAttributeOnMethod()
+ {
+ var source = @"
+namespace System.Runtime.CompilerServices
+{
+ public class SkipLocalsInitAttribute : System.Attribute
+ {
+ }
+}
+
+public class C
+{
+ [System.Runtime.CompilerServices.SkipLocalsInitAttribute]
+ public void M_skip()
+ {
+ int x = 2;
+ x = x + x + x;
+ }
+
+ public void M_init()
+ {
+ int x = 2;
+ x = x + x + x;
+ }
+}
+";
+
+ var comp = CompileAndVerify(source, verify: Verification.Fails);
+
+ comp.VerifyIL("C.M_init", @"
+{
+ // Code size 9 (0x9)
+ .maxstack 2
+ .locals init (int V_0) //x
+ IL_0000: ldc.i4.2
+ IL_0001: stloc.0
+ IL_0002: ldloc.0
+ IL_0003: ldloc.0
+ IL_0004: add
+ IL_0005: ldloc.0
+ IL_0006: add
+ IL_0007: stloc.0
+ IL_0008: ret
+}", realIL: true);
+
+ comp.VerifyIL("C.M_skip", @"
+{
+ // Code size 9 (0x9)
+ .maxstack 2
+ .locals (int V_0) //x
+ IL_0000: ldc.i4.2
+ IL_0001: stloc.0
+ IL_0002: ldloc.0
+ IL_0003: ldloc.0
+ IL_0004: add
+ IL_0005: ldloc.0
+ IL_0006: add
+ IL_0007: stloc.0
+ IL_0008: ret
+}", realIL: true);
+
+ comp.VerifyIL("C.M_init", @"
+{
+ // Code size 9 (0x9)
+ .maxstack 2
+ .locals init (int V_0) //x
+ IL_0000: ldc.i4.2
+ IL_0001: stloc.0
+ IL_0002: ldloc.0
+ IL_0003: ldloc.0
+ IL_0004: add
+ IL_0005: ldloc.0
+ IL_0006: add
+ IL_0007: stloc.0
+ IL_0008: ret
+}", realIL: false);
+
+ comp.VerifyIL("C.M_skip", @"
+{
+ // Code size 9 (0x9)
+ .maxstack 2
+ .locals (int V_0) //x
+ IL_0000: ldc.i4.2
+ IL_0001: stloc.0
+ IL_0002: ldloc.0
+ IL_0003: ldloc.0
+ IL_0004: add
+ IL_0005: ldloc.0
+ IL_0006: add
+ IL_0007: stloc.0
+ IL_0008: ret
+}", realIL: false);
+ }
+
+ [Fact]
+ public void WhenMethodsDifferBySkipLocalsInitAttributeTheyMustHaveDifferentRVA()
+ {
+ var source = @"
+namespace System.Runtime.CompilerServices
+{
+ public class SkipLocalsInitAttribute : System.Attribute
+ {
+ }
+}
+
+public class C
+{
+ [System.Runtime.CompilerServices.SkipLocalsInitAttribute]
+ public unsafe void M_skip()
+ {
+ int *ptr = stackalloc int[10];
+ System.Console.WriteLine(ptr[0]);
+ }
+
+ [System.Runtime.CompilerServices.SkipLocalsInitAttribute]
+ public unsafe void M_skip_copy()
+ {
+ int *ptr = stackalloc int[10];
+ System.Console.WriteLine(ptr[0]);
+ }
+
+ [System.Runtime.CompilerServices.SkipLocalsInitAttribute]
+ public unsafe void M_skip_diff()
+ {
+ int *ptr = stackalloc int[11];
+ System.Console.WriteLine(ptr[0]);
+ }
+
+ public unsafe void M_init()
+ {
+ int *ptr = stackalloc int[10];
+ System.Console.WriteLine(ptr[0]);
+ }
+
+ public unsafe void M_init_copy()
+ {
+ int *ptr = stackalloc int[10];
+ System.Console.WriteLine(ptr[0]);
+ }
+
+ public unsafe void M_init_diff()
+ {
+ int *ptr = stackalloc int[11];
+ System.Console.WriteLine(ptr[0]);
+ }
+}
+";
+
+ var comp = CreateStandardCompilation(source, options: TestOptions.UnsafeReleaseDll);
+ var metadata = ModuleMetadata.CreateFromStream(comp.EmitToStream());
+ var peReader = metadata.Module.GetMetadataReader();
+
+ TypeDefinition typeC = default;
+
+ foreach (var typeHandle in peReader.TypeDefinitions)
+ {
+ var type = peReader.GetTypeDefinition(typeHandle);
+ var name = peReader.GetString(type.Name);
+
+ if (name == "C")
+ {
+ typeC = type;
+ break;
+ }
+ }
+
+ Assert.NotEqual(typeC, default);
+
+ MethodDefinition methodInit = default;
+ MethodDefinition methodSkip = default;
+ MethodDefinition methodInitCopy = default;
+ MethodDefinition methodSkipCopy = default;
+ MethodDefinition methodInitDiff = default;
+ MethodDefinition methodSkipDiff = default;
+
+ foreach (var methodHandle in typeC.GetMethods())
+ {
+ var method = peReader.GetMethodDefinition(methodHandle);
+ var name = peReader.GetString(method.Name);
+
+ if (name == "M_init")
+ {
+ methodInit = method;
+ }
+ else if (name == "M_skip")
+ {
+ methodSkip = method;
+ }
+ else if (name == "M_init_copy")
+ {
+ methodInitCopy = method;
+ }
+ else if (name == "M_skip_copy")
+ {
+ methodSkipCopy = method;
+ }
+ else if (name == "M_init_diff")
+ {
+ methodInitDiff = method;
+ }
+ else if (name == "M_skip_diff")
+ {
+ methodSkipDiff = method;
+ }
+ }
+
+ Assert.NotEqual(methodInit, default);
+ Assert.NotEqual(methodSkip, default);
+ Assert.NotEqual(methodInitCopy, default);
+ Assert.NotEqual(methodSkipCopy, default);
+ Assert.NotEqual(methodInitDiff, default);
+ Assert.NotEqual(methodSkipDiff, default);
+
+ Assert.NotEqual(methodInit.RelativeVirtualAddress, methodSkip.RelativeVirtualAddress);
+ Assert.Equal(methodInit.RelativeVirtualAddress, methodInitCopy.RelativeVirtualAddress);
+ Assert.Equal(methodSkip.RelativeVirtualAddress, methodSkipCopy.RelativeVirtualAddress);
+ Assert.NotEqual(methodInit.RelativeVirtualAddress, methodInitDiff.RelativeVirtualAddress);
+ Assert.NotEqual(methodSkip.RelativeVirtualAddress, methodSkipDiff.RelativeVirtualAddress);
+ }
+
+ [Fact]
+ public void SkipLocalsInitAttributeOnClassDoesNotPropagateToMethod()
+ {
+ var source = @"
+namespace System.Runtime.CompilerServices
+{
+ public class SkipLocalsInitAttribute : System.Attribute
+ {
+ }
+}
+
+[System.Runtime.CompilerServices.SkipLocalsInitAttribute]
+public class C
+{
+ public void M()
+ {
+ int x = 2;
+ x = x + x + x;
+ }
+}
+";
+
+ var comp = CompileAndVerify(source);
+
+ comp.VerifyIL("C.M", @"
+{
+ // Code size 9 (0x9)
+ .maxstack 2
+ .locals init (int V_0) //x
+ IL_0000: ldc.i4.2
+ IL_0001: stloc.0
+ IL_0002: ldloc.0
+ IL_0003: ldloc.0
+ IL_0004: add
+ IL_0005: ldloc.0
+ IL_0006: add
+ IL_0007: stloc.0
+ IL_0008: ret
+}");
+ }
+
+ [Fact]
+ public void SkipLocalsInitAttributeOnAssemblyDoesNotPropagateToMethod()
+ {
+ var source = @"
+[assembly: System.Runtime.CompilerServices.SkipLocalsInitAttribute]
+
+namespace System.Runtime.CompilerServices
+{
+ public class SkipLocalsInitAttribute : System.Attribute
+ {
+ }
+}
+
+public class C
+{
+ public void M()
+ {
+ int x = 2;
+ x = x + x + x;
+ }
+}
+";
+
+ var comp = CompileAndVerify(source);
+
+ comp.VerifyIL("C.M", @"
+{
+ // Code size 9 (0x9)
+ .maxstack 2
+ .locals init (int V_0) //x
+ IL_0000: ldc.i4.2
+ IL_0001: stloc.0
+ IL_0002: ldloc.0
+ IL_0003: ldloc.0
+ IL_0004: add
+ IL_0005: ldloc.0
+ IL_0006: add
+ IL_0007: stloc.0
+ IL_0008: ret
+}");
+ }
+
+ #endregion
+
[Fact, WorkItem(807, "https://github.com/dotnet/roslyn/issues/807")]
public void TestAttributePropagationForAsyncAndIterators_01()
{
diff --git a/src/Compilers/Core/Portable/CodeGen/ILBuilder.cs b/src/Compilers/Core/Portable/CodeGen/ILBuilder.cs
index da884aa92a19c..3bf48d8579019 100644
--- a/src/Compilers/Core/Portable/CodeGen/ILBuilder.cs
+++ b/src/Compilers/Core/Portable/CodeGen/ILBuilder.cs
@@ -32,6 +32,7 @@ internal sealed partial class ILBuilder
private SyntaxTree _lastSeqPointTree;
private readonly SmallDictionary