Add switch to crossgen2 to ignore compile failures
authorDavid Wrighton <davidwr@microsoft.com>
Thu, 12 Sep 2019 23:39:11 +0000 (23:39 +0000)
committerDavid Wrighton <davidwr@microsoft.com>
Fri, 20 Sep 2019 00:45:37 +0000 (17:45 -0700)
- the default failure behavior is good for most cases, but it is a problem when not actively working on codegen issues
- Method name printing is a risky process as it may throw. This changeset implements a ToString algorithm which attempts to print something even in the presence of errors

Commit migrated from https://github.com/dotnet/coreclr/commit/a7d0d0964cfea8d77194f49280dfdadea7390153

src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs
src/coreclr/src/tools/crossgen2/crossgen2/Program.cs

index 2a9662f..169b032 100644 (file)
@@ -6,6 +6,8 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Reflection.PortableExecutable;
+using System.Reflection.Metadata.Ecma335;
+using System.Text;
 
 using Internal.IL;
 using Internal.JitInterface;
@@ -181,6 +183,8 @@ namespace ILCompiler
         /// </summary>
         private readonly CorInfoImpl _corInfo;
 
+        private bool _doNotFailOnCodegenCompilationExceptions;
+
         public new ReadyToRunCodegenNodeFactory NodeFactory { get; }
 
         public ReadyToRunSymbolNodeFactory SymbolNodeFactory { get; }
@@ -194,9 +198,11 @@ namespace ILCompiler
             DevirtualizationManager devirtualizationManager,
             JitConfigProvider configProvider,
             string inputFilePath,
-            IEnumerable<ModuleDesc> modulesBeingInstrumented)
+            IEnumerable<ModuleDesc> modulesBeingInstrumented,
+            bool doNotFailOnCodegenCompilationExceptions)
             : base(dependencyGraph, nodeFactory, roots, ilProvider, devirtualizationManager, modulesBeingInstrumented, logger)
         {
+            _doNotFailOnCodegenCompilationExceptions = doNotFailOnCodegenCompilationExceptions;
             NodeFactory = nodeFactory;
             SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory);
             _jitConfigProvider = configProvider;
@@ -258,7 +264,7 @@ namespace ILCompiler
 
                 if (Logger.IsVerbose)
                 {
-                    string methodName = method.ToString();
+                    string methodName = ResilientNameComputation(method);
                     Logger.Writer.WriteLine("Compiling " + methodName);
                 }
 
@@ -270,11 +276,15 @@ namespace ILCompiler
                 catch (TypeSystemException ex)
                 {
                     // If compilation fails, don't emit code for this method. It will be Jitted at runtime
-                    Logger.Writer.WriteLine($"Warning: Method `{method}` was not compiled because: {ex.Message}");
+                    Logger.Writer.WriteLine($"Warning: Method `{ResilientNameComputation(method)}` was not compiled because: {ex.Message}");
                 }
                 catch (RequiresRuntimeJitException ex)
                 {
-                    Logger.Writer.WriteLine($"Info: Method `{method}` was not compiled because `{ex.Message}` requires runtime JIT");
+                    Logger.Writer.WriteLine($"Info: Method `{ResilientNameComputation(method)}` was not compiled because `{ex.Message}` requires runtime JIT");
+                }
+                catch (CodeGenerationFailedException ex) when (_doNotFailOnCodegenCompilationExceptions)
+                {
+                    Logger.Writer.WriteLine($"Warning: Method `{ResilientNameComputation(method)}` was not compiled because `{ex.Message}` requires runtime JIT");
                 }
                 finally
                 {
@@ -283,6 +293,107 @@ namespace ILCompiler
             }
         }
 
+        private string ResilientNameComputation(MethodDesc method)
+        {
+            try
+            {
+                return method.ToString();
+            }
+            catch (TypeSystemException)
+            {
+                string typeName = ResilientNameComputation((MetadataType)method.OwningType);
+                if (typeName == null)
+                {
+                    if (method is EcmaMethod)
+                    {
+                        typeName = "";
+                    }
+                    else
+                    {
+                        return "Unknown";
+                    }
+                }
+
+                return $"{typeName}.{ResilientNameComputationMethodNameWorker(method)}";
+            }
+        }
+
+        private string ResilientNameComputationMethodNameWorker(MethodDesc method)
+        {
+            if (method is EcmaMethod ecmaMethod)
+            {
+                return $"MethodDef({ecmaMethod.MetadataReader.GetToken(ecmaMethod.Handle):x8})";
+            }
+            else if (method is InstantiatedMethod instMethod)
+            {
+                StringBuilder nameBuilder = new StringBuilder();
+                nameBuilder.Append(ResilientNameComputationMethodNameWorker(instMethod.GetMethodDefinition()));
+                AppendInstantiation(nameBuilder, instMethod.Instantiation);
+                return nameBuilder.ToString();
+            }
+            return "Unknown";
+        }
+
+        private void AppendInstantiation(StringBuilder nameBuilder, Instantiation instantiation)
+        {
+            nameBuilder.Append('<');
+            bool printedFirst = false;
+            foreach (TypeDesc type in instantiation)
+            {
+                if (printedFirst)
+                    nameBuilder.Append(',');
+                else
+                    printedFirst = true;
+                nameBuilder.Append(ResilientNameComputation(type));
+            }
+            nameBuilder.Append('>');
+        }
+
+        private string ResilientNameComputation(TypeDesc type)
+        {
+            string typeName = null;
+            try
+            {
+                try
+                {
+                    typeName = type.ToString();
+                }
+                catch (TypeSystemException)
+                {
+                    if (type is InstantiatedType instType)
+                    {
+                        StringBuilder nameBuilder = new StringBuilder();
+                        nameBuilder.Append(ResilientNameComputation(instType.UnderlyingType));
+                        AppendInstantiation(nameBuilder, instType.Instantiation);
+                        typeName = nameBuilder.ToString();
+                    }
+                    else if (type is EcmaType ecmaType)
+                    {
+                        typeName = $"[{ecmaType.EcmaModule.Assembly.GetName().Name.ToString()}]TypeDef({ecmaType.MetadataReader.GetToken(ecmaType.Handle):x8})";
+                    }
+                    else if (type is ArrayType arrayType)
+                    {
+                        StringBuilder nameBuilder = new StringBuilder();
+                        nameBuilder.Append(ResilientNameComputation(arrayType));
+                        nameBuilder.Append('[');
+                        for (int i = 1; i < arrayType.Rank; i++)
+                        {
+                            nameBuilder.Append(i);
+                        }
+                        if ((arrayType.Rank == 1) && arrayType.IsMdArray)
+                            nameBuilder.Append('*');
+
+                        nameBuilder.Append(']');
+                    }
+                }
+            }
+            catch (TypeSystemException)
+            {
+            }
+
+            return typeName;
+        }
+
         public ISymbolNode GetFieldRvaData(FieldDesc field) => NodeFactory.CopiedFieldRva(field);
     }
 }
index 89c86d1..85f16d7 100644 (file)
@@ -21,16 +21,18 @@ namespace ILCompiler
         private readonly string _inputFilePath;
         private readonly EcmaModule _inputModule;
         private readonly bool _ibcTuning;
+        private readonly bool _doNotFailOnCodegenCompilationExceptions;
 
         // These need to provide reasonable defaults so that the user can optionally skip
         // calling the Use/Configure methods and still get something reasonable back.
         private KeyValuePair<string, string>[] _ryujitOptions = Array.Empty<KeyValuePair<string, string>>();
         private ILProvider _ilProvider = new ReadyToRunILProvider();
 
-        public ReadyToRunCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group, string inputFilePath, bool ibcTuning)
+        public ReadyToRunCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group, string inputFilePath, bool ibcTuning, bool doNotFailOnCodegenCompilationExceptions)
             : base(context, group, new CoreRTNameMangler())
         {
             _ibcTuning = ibcTuning;
+            _doNotFailOnCodegenCompilationExceptions = doNotFailOnCodegenCompilationExceptions;
             _inputFilePath = inputFilePath;
 
             _inputModule = context.GetModuleFromPath(_inputFilePath);
@@ -146,7 +148,8 @@ namespace ILCompiler
                 new DependencyAnalysis.ReadyToRun.DevirtualizationManager(_compilationGroup),
                 jitConfig,
                 _inputFilePath,
-                new ModuleDesc[] { _inputModule });
+                new ModuleDesc[] { _inputModule },
+                _doNotFailOnCodegenCompilationExceptions);
         }
     }
 }
index 16b4a39..66b03a0 100644 (file)
@@ -39,6 +39,7 @@ namespace ILCompiler
         private string _systemModuleName = DefaultSystemModule;
         private bool _tuning;
         private bool _partial;
+        private bool _doNotFailOnCodegenCompilationExceptions;
 
         private string _singleMethodTypeName;
         private string _singleMethodName;
@@ -135,6 +136,7 @@ namespace ILCompiler
                 syntax.DefineOption("systemmodule", ref _systemModuleName, "System module name (default: System.Private.CoreLib)");
                 syntax.DefineOption("waitfordebugger", ref waitForDebugger, "Pause to give opportunity to attach debugger");
                 syntax.DefineOptionList("codegenopt", ref _codegenOptions, "Define a codegen option");
+                syntax.DefineOption("doNotFailOnCodegenCompilationExceptions", ref _doNotFailOnCodegenCompilationExceptions, "Disable behavior where unexpected compilation failures cause overall compilation failure");
 
                 syntax.DefineOption("targetarch", ref _targetArchitectureStr, "Target architecture for cross compilation");
                 syntax.DefineOption("targetos", ref _targetOSStr, "Target OS for cross compilation");
@@ -371,7 +373,9 @@ namespace ILCompiler
                 inputFilePath = input.Value;
                 break;
             }
-            CompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder(typeSystemContext, compilationGroup, inputFilePath, _tuning);
+            CompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder(typeSystemContext, compilationGroup, inputFilePath,
+                ibcTuning: _tuning,
+                doNotFailOnCodegenCompilationExceptions: _doNotFailOnCodegenCompilationExceptions);
 
             string compilationUnitPrefix = "";
             builder.UseCompilationUnitPrefix(compilationUnitPrefix);