|
// 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.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;
using ILCompiler.ObjectWriter;
using ILLink.Shared;
using Internal.IL;
using Internal.IL.Stubs;
using Internal.TypeSystem;
using Internal.JitInterface;
namespace ILCompiler
{
public sealed class RyuJitCompilation : Compilation
{
private readonly ConditionalWeakTable<Thread, CorInfoImpl> _corinfos = new ConditionalWeakTable<Thread, CorInfoImpl>();
internal readonly RyuJitCompilationOptions _compilationOptions;
private readonly ProfileDataManager _profileDataManager;
private readonly FileLayoutOptimizer _fileLayoutOptimizer;
private readonly MethodImportationErrorProvider _methodImportationErrorProvider;
private readonly ReadOnlyFieldPolicy _readOnlyFieldPolicy;
private readonly int _parallelism;
public InstructionSetSupport InstructionSetSupport { get; }
internal RyuJitCompilation(
DependencyAnalyzerBase<NodeFactory> dependencyGraph,
NodeFactory nodeFactory,
IEnumerable<ICompilationRootProvider> roots,
ILProvider ilProvider,
DebugInformationProvider debugInformationProvider,
Logger logger,
IInliningPolicy inliningPolicy,
InstructionSetSupport instructionSetSupport,
ProfileDataManager profileDataManager,
MethodImportationErrorProvider errorProvider,
ReadOnlyFieldPolicy readOnlyFieldPolicy,
RyuJitCompilationOptions options,
MethodLayoutAlgorithm methodLayoutAlgorithm,
FileLayoutAlgorithm fileLayoutAlgorithm,
int parallelism,
string orderFile)
: base(dependencyGraph, nodeFactory, roots, ilProvider, debugInformationProvider, inliningPolicy, logger)
{
_compilationOptions = options;
InstructionSetSupport = instructionSetSupport;
_profileDataManager = profileDataManager;
_methodImportationErrorProvider = errorProvider;
_readOnlyFieldPolicy = readOnlyFieldPolicy;
_parallelism = parallelism;
_fileLayoutOptimizer = new FileLayoutOptimizer(logger, methodLayoutAlgorithm, fileLayoutAlgorithm, profileDataManager, nodeFactory, orderFile);
}
public ProfileDataManager ProfileData => _profileDataManager;
public bool IsInitOnly(FieldDesc field) => _readOnlyFieldPolicy.IsReadOnly(field);
public override IEETypeNode NecessaryTypeSymbolIfPossible(TypeDesc type)
{
// RyuJIT makes assumptions around the value of these symbols - in particular, it assumes
// that type handles and type symbols have a 1:1 relationship. We therefore need to
// make sure RyuJIT never sees a constructed and unconstructed type symbol for the
// same type. If the type is constructable and we don't have whole program view
// information proving that it isn't, give RyuJIT the constructed symbol even
// though we just need the unconstructed one.
// https://github.com/dotnet/runtimelab/issues/1128
return GetLdTokenHelperForType(type) switch
{
ReadyToRunHelperId.MetadataTypeHandle => _nodeFactory.MetadataTypeSymbol(type),
ReadyToRunHelperId.TypeHandle => _nodeFactory.MaximallyConstructableType(type),
ReadyToRunHelperId.NecessaryTypeHandle => _nodeFactory.NecessaryTypeSymbol(type),
_ => throw new UnreachableException()
};
}
public FrozenRuntimeTypeNode NecessaryRuntimeTypeIfPossible(TypeDesc type)
{
return GetLdTokenHelperForType(type) switch
{
ReadyToRunHelperId.TypeHandle or ReadyToRunHelperId.MetadataTypeHandle => _nodeFactory.SerializedMetadataRuntimeTypeObject(type),
ReadyToRunHelperId.NecessaryTypeHandle => _nodeFactory.SerializedNecessaryRuntimeTypeObject(type),
_ => throw new UnreachableException()
};
}
protected override void CompileInternal(string outputFile, ObjectDumper dumper)
{
_dependencyGraph.ComputeMarkedNodes();
var nodes = _dependencyGraph.MarkedNodeList;
nodes = _fileLayoutOptimizer.ApplyProfilerGuidedMethodSort(nodes);
NodeFactory.SetMarkingComplete();
ObjectWritingOptions options = ObjectWritingOptions.GenerateUnwindInfo;
if ((_compilationOptions & RyuJitCompilationOptions.UseDwarf5) != 0)
options |= ObjectWritingOptions.UseDwarf5;
if (_debugInformationProvider is not NullDebugInformationProvider)
options |= ObjectWritingOptions.GenerateDebugInfo;
if ((_compilationOptions & RyuJitCompilationOptions.ControlFlowGuardAnnotations) != 0)
options |= ObjectWritingOptions.ControlFlowGuard;
ObjectWriter.ObjectWriter.EmitObject(outputFile, nodes, NodeFactory, options, dumper, _logger);
}
protected override void ComputeDependencyNodeDependencies(List<DependencyNodeCore<NodeFactory>> obj)
{
// Determine the list of method we actually need to compile
var methodsToCompile = new List<MethodCodeNode>();
var canonicalMethodsToCompile = new HashSet<MethodDesc>();
foreach (DependencyNodeCore<NodeFactory> dependency in obj)
{
var methodCodeNodeNeedingCode = dependency as MethodCodeNode;
if (methodCodeNodeNeedingCode == null)
{
// To compute dependencies of the shadow method that tracks dictionary
// dependencies we need to ensure there is code for the canonical method body.
var dependencyMethod = (ShadowMethodNode)dependency;
methodCodeNodeNeedingCode = (MethodCodeNode)dependencyMethod.CanonicalMethodNode;
}
// We might have already queued this method for compilation
MethodDesc method = methodCodeNodeNeedingCode.Method;
if (method.IsCanonicalMethod(CanonicalFormKind.Any)
&& !canonicalMethodsToCompile.Add(method))
{
continue;
}
methodsToCompile.Add(methodCodeNodeNeedingCode);
}
if (_parallelism == 1)
{
CompileSingleThreaded(methodsToCompile);
}
else
{
CompileMultiThreaded(methodsToCompile);
}
}
private void CompileMultiThreaded(List<MethodCodeNode> methodsToCompile)
{
if (Logger.IsVerbose)
{
Logger.LogMessage($"Compiling {methodsToCompile.Count} methods...");
}
Parallel.ForEach(
methodsToCompile,
new ParallelOptions { MaxDegreeOfParallelism = _parallelism },
CompileSingleMethod);
}
private void CompileSingleThreaded(List<MethodCodeNode> methodsToCompile)
{
CorInfoImpl corInfo = _corinfos.GetValue(Thread.CurrentThread, thread => new CorInfoImpl(this));
foreach (MethodCodeNode methodCodeNodeNeedingCode in methodsToCompile)
{
if (Logger.IsVerbose)
{
Logger.LogMessage($"Compiling {methodCodeNodeNeedingCode.Method}...");
}
CompileSingleMethod(corInfo, methodCodeNodeNeedingCode);
}
}
private void CompileSingleMethod(MethodCodeNode methodCodeNodeNeedingCode)
{
CorInfoImpl corInfo = _corinfos.GetValue(Thread.CurrentThread, thread => new CorInfoImpl(this));
CompileSingleMethod(corInfo, methodCodeNodeNeedingCode);
}
private void CompileSingleMethod(CorInfoImpl corInfo, MethodCodeNode methodCodeNodeNeedingCode)
{
MethodDesc method = methodCodeNodeNeedingCode.Method;
TypeSystemException exception = _methodImportationErrorProvider.GetCompilationError(method);
// If we previously failed to import the method, do not try to import it again and go
// directly to the error path.
if (exception == null)
{
try
{
corInfo.CompileMethod(methodCodeNodeNeedingCode);
}
catch (TypeSystemException ex)
{
exception = ex;
}
}
if (exception != null)
{
if (exception is TypeSystemException.InvalidProgramException
&& method.OwningType is MetadataType mdOwningType
&& mdOwningType.HasCustomAttribute("System.Runtime.InteropServices", "ClassInterfaceAttribute"))
{
Logger.LogWarning(method, DiagnosticId.COMInteropNotSupportedInFullAOT);
}
if ((_compilationOptions & RyuJitCompilationOptions.UseResilience) != 0)
Logger.LogMessage($"Method '{method}' will always throw because: {exception.Message}");
else
Logger.LogError($"Method will always throw because: {exception.Message}", 1005, method, MessageSubCategory.AotAnalysis);
// Try to compile the method again, but with a throwing method body this time.
MethodIL throwingIL = TypeSystemThrowingILEmitter.EmitIL(method, exception);
corInfo.CompileMethod(methodCodeNodeNeedingCode, throwingIL);
}
}
}
[Flags]
public enum RyuJitCompilationOptions
{
ControlFlowGuardAnnotations = 0x1,
UseDwarf5 = 0x2,
UseResilience = 0x4,
}
}
|