Control Crossgen2 optimizations with DebuggableAttribute (#44514)
authorSimon Nattress <nattress@gmail.com>
Fri, 8 Jan 2021 18:15:30 +0000 (10:15 -0800)
committerGitHub <noreply@github.com>
Fri, 8 Jan 2021 18:15:30 +0000 (10:15 -0800)
* Assemblies compiled in debug configuration use the `System.Diagnostics.DebuggableAttribute` to indicate optimizations are disabled.
* Match Crossgen 1's behavior and respect this setting if no explicit optimization arguments are passed in.
* Add `-Od` switch to disable optimizations since now the default behavior of Crossgen2 is to optimize release-built assemblies.
* Fix runtime assert when checking the GC ref map for multi-dimensional array's intrinsic `Address` method on x86. `Address` has a custom calling convention which always requires the extra param type in the signature. The extra shared generic check is too restrictive and doesn't match what we tell the JIT the signature is in https://github.com/dotnet/runtime/blob/4b8d10154c39b1f56424d4ba2068a3150d90d475/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs#L500.

src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/AssemblyExtensions.cs [new file with mode: 0644]
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs
src/coreclr/tools/aot/crossgen2/Program.cs
src/coreclr/tools/aot/crossgen2/Properties/Resources.resx
src/tests/Common/CLRTest.CrossGen.targets

diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/AssemblyExtensions.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/AssemblyExtensions.cs
new file mode 100644 (file)
index 0000000..c24657f
--- /dev/null
@@ -0,0 +1,54 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.Reflection.Metadata;
+
+using Internal.TypeSystem;
+using Internal.TypeSystem.Ecma;
+
+using Debug = System.Diagnostics.Debug;
+
+namespace ILCompiler
+{
+    public static class AssemblyExtensions
+    {
+        /// <summary>
+        /// Determines whether the assembly was compiled without optimizations using the DebuggableAttribute
+        /// </summary>
+        public static bool HasOptimizationsDisabled(this EcmaAssembly assembly)
+        {
+            bool result = false;
+            MetadataReader reader = assembly.MetadataReader;
+            var attributeHandles = assembly.AssemblyDefinition.GetCustomAttributes();
+            CustomAttributeHandle attributeHandle = reader.GetCustomAttributeHandle(attributeHandles, "System.Diagnostics", "DebuggableAttribute");
+            if (!attributeHandle.IsNil)
+            {
+                CustomAttribute attribute = reader.GetCustomAttribute(attributeHandle);
+                CustomAttributeValue<TypeDesc> decoded = attribute.DecodeValue(new CustomAttributeTypeProvider(assembly));
+
+                if (decoded.FixedArguments.Length == 1)
+                {
+                    // DebuggableAttribute( DebuggableAttribute.DebuggingModes modes )
+                    if (!(decoded.FixedArguments[0].Value is int))
+                    {
+                        ThrowHelper.ThrowBadImageFormatException();
+                    }
+                    DebuggableAttribute.DebuggingModes modes = (DebuggableAttribute.DebuggingModes)decoded.FixedArguments[0].Value;
+                    result = modes.HasFlag(DebuggableAttribute.DebuggingModes.DisableOptimizations) && modes.HasFlag(DebuggableAttribute.DebuggingModes.Default);
+                }
+                else if (decoded.FixedArguments.Length == 2)
+                {
+                    // DebuggableAttribute( bool isJITTrackingEnabled, bool isJITOptimizerDisabled )
+                    if (!(decoded.FixedArguments[0].Value is bool) || !(decoded.FixedArguments[1].Value is bool))
+                    {
+                        ThrowHelper.ThrowBadImageFormatException();
+                    }
+                    result = ((bool)decoded.FixedArguments[1].Value);
+                }
+            }
+            return result;
+        }
+    }
+}
index fb6b4f9..6fcb4e2 100644 (file)
@@ -82,8 +82,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
 
             // On X86 the Array address method doesn't use IL stubs, and instead has a custom calling convention
             if ((method.Context.Target.Architecture == TargetArchitecture.X86) &&
-                (method.IsArrayAddressMethod()) &&
-                method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific).IsCanonicalSubtype(CanonicalFormKind.Any))
+                method.IsArrayAddressMethod())
             {
                 hasParamType = true;
             }
index aa2bf84..0d8a93f 100644 (file)
@@ -94,6 +94,7 @@
     <Compile Include="..\..\Common\JitInterface\CorInfoTypes.VarInfo.cs" Link="JitInterface\CorInfoTypes.VarInfo.cs" />
     <Compile Include="..\..\Common\JitInterface\SystemVStructClassificator.cs" Link="JitInterface\SystemVStructClassificator.cs" />
     <Compile Include="..\..\Common\TypeSystem\Interop\InteropTypes.cs" Link="Interop\InteropTypes.cs" />
+    <Compile Include="Compiler\AssemblyExtensions.cs" />
     <Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DelayLoadMethodCallThunkNodeRange.cs" />
     <Compile Include="ObjectWriter\MapFileBuilder.cs" />
     <Compile Include="CodeGen\ReadyToRunObjectWriter.cs" />
index 7fc55e3..ab4afd4 100644 (file)
@@ -24,6 +24,7 @@ namespace ILCompiler
 
         public string CompositeRootPath;
         public bool Optimize;
+        public bool OptimizeDisabled;
         public bool OptimizeSpace;
         public bool OptimizeTime;
         public bool InputBubble;
@@ -86,6 +87,7 @@ namespace ILCompiler
                 syntax.DefineOption("o|out|outputfilepath", ref OutputFilePath, SR.OutputFilePath);
                 syntax.DefineOption("crp|compositerootpath", ref CompositeRootPath, SR.CompositeRootPath);
                 syntax.DefineOption("O|optimize", ref Optimize, SR.EnableOptimizationsOption);
+                syntax.DefineOption("Od|optimize-disabled", ref OptimizeDisabled, SR.DisableOptimizationsOption);
                 syntax.DefineOption("Os|optimize-space", ref OptimizeSpace, SR.OptimizeSpaceOption);
                 syntax.DefineOption("Ot|optimize-time", ref OptimizeTime, SR.OptimizeSpeedOption);
                 syntax.DefineOption("inputbubble", ref InputBubble, SR.InputBubbleOption);
index 751e10b..e20b25b 100644 (file)
@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Reflection.Metadata;
 using System.Reflection.PortableExecutable;
 using System.Runtime.InteropServices;
 
@@ -92,7 +93,12 @@ namespace ILCompiler
             }
 
             _optimizationMode = OptimizationMode.None;
-            if (_commandLineOptions.OptimizeSpace)
+            if (_commandLineOptions.OptimizeDisabled)
+            {
+                if (_commandLineOptions.Optimize || _commandLineOptions.OptimizeSpace || _commandLineOptions.OptimizeTime)
+                    Console.WriteLine(SR.WarningOverridingOptimize);
+            }
+            else if (_commandLineOptions.OptimizeSpace)
             {
                 if (_commandLineOptions.OptimizeTime)
                     Console.WriteLine(SR.WarningOverridingOptimizeSpace);
@@ -549,6 +555,13 @@ namespace ILCompiler
                             }
                         }
                     }
+                    // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize
+                    // or produce debuggable code if an explicit optimization level was not specified on the command line 
+                    if (_optimizationMode == OptimizationMode.None && !_commandLineOptions.OptimizeDisabled && !_commandLineOptions.Composite)
+                    {
+                        System.Diagnostics.Debug.Assert(inputModules.Count == 1);
+                        _optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended;
+                    }
 
                     //
                     // Compile
index 6020c7a..d677fe8 100644 (file)
   <data name="MapCsvFileOption" xml:space="preserve">
     <value>Generate a CSV formatted map file</value>
   </data>
+  <data name="DisableOptimizationsOption" xml:space="preserve">
+    <value>Disable optimizations to simplify debugging</value>
+  </data>
+  <data name="WarningOverridingOptimize" xml:space="preserve">
+    <value>Warning: -Od overrides other optimization options</value>
+  </data>
 </root>
\ No newline at end of file
index 7c31fb4..34cbfe1 100644 (file)
@@ -128,7 +128,6 @@ if [ ! -z ${RunCrossGen2+x} ]%3B then
         echo -r:$CORE_ROOT/mscorlib.dll>>$__ResponseFile
         echo --verify-type-and-field-layout>>$__ResponseFile
         echo --targetarch:$(TargetArchitecture)>>$__ResponseFile
-        echo -O>>$__ResponseFile
   
         echo "Response file: $__ResponseFile"
         cat $__ResponseFile
@@ -279,7 +278,6 @@ if defined RunCrossGen2 (
     echo -o:!__OutputFile!>>!__ResponseFile!
     echo --targetarch:$(TargetArchitecture)>>!__ResponseFile!
     echo --verify-type-and-field-layout>>!__ResponseFile!
-    echo -O>>!__ResponseFile!
     echo -r:!CORE_ROOT!\System.*.dll>>!__ResponseFile!
     echo -r:!CORE_ROOT!\Microsoft.*.dll>>!__ResponseFile!
     echo -r:!CORE_ROOT!\mscorlib.dll>>!__ResponseFile!