Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/Compilation.cs18
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs7
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/DevirtualizationManager.cs90
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/ILScanner.cs96
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs3
-rw-r--r--src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs2
-rw-r--r--src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj1
-rw-r--r--src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs2
-rw-r--r--src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs2
-rw-r--r--src/ILCompiler/src/Program.cs6
-rw-r--r--src/JitInterface/src/CorInfoImpl.cs42
-rw-r--r--src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs17
-rw-r--r--src/System.Private.Jit/src/System.Private.Jit.csproj1
13 files changed, 252 insertions, 35 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs
index 443716a15..dd3760c39 100644
--- a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs
@@ -25,6 +25,7 @@ namespace ILCompiler
protected readonly NodeFactory _nodeFactory;
protected readonly Logger _logger;
private readonly DebugInformationProvider _debugInformationProvider;
+ private readonly DevirtualizationManager _devirtualizationManager;
public NameMangler NameMangler => _nodeFactory.NameMangler;
public NodeFactory NodeFactory => _nodeFactory;
@@ -41,12 +42,14 @@ namespace ILCompiler
NodeFactory nodeFactory,
IEnumerable<ICompilationRootProvider> compilationRoots,
DebugInformationProvider debugInformationProvider,
+ DevirtualizationManager devirtualizationManager,
Logger logger)
{
_dependencyGraph = dependencyGraph;
_nodeFactory = nodeFactory;
_logger = logger;
_debugInformationProvider = debugInformationProvider;
+ _devirtualizationManager = devirtualizationManager;
_dependencyGraph.ComputeDependencyRoutine += ComputeDependencyNodeDependencies;
NodeFactory.AttachToDependencyGraph(_dependencyGraph);
@@ -178,6 +181,21 @@ namespace ILCompiler
return NodeFactory.VTable(type).HasFixedSlots;
}
+ public bool IsEffectivelySealed(TypeDesc type)
+ {
+ return _devirtualizationManager.IsEffectivelySealed(type);
+ }
+
+ public bool IsEffectivelySealed(MethodDesc method)
+ {
+ return _devirtualizationManager.IsEffectivelySealed(method);
+ }
+
+ public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType)
+ {
+ return _devirtualizationManager.ResolveVirtualMethod(declMethod, implType);
+ }
+
public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLookup)
{
switch (lookupKind)
diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs
index b93fafd42..7bdaf6abe 100644
--- a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs
@@ -26,6 +26,7 @@ namespace ILCompiler
protected VTableSliceProvider _vtableSliceProvider = new LazyVTableSliceProvider();
protected DictionaryLayoutProvider _dictionaryLayoutProvider = new LazyDictionaryLayoutProvider();
protected DebugInformationProvider _debugInformationProvider = new DebugInformationProvider();
+ protected DevirtualizationManager _devirtualizationManager = new DevirtualizationManager();
public CompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler nameMangler)
{
@@ -77,6 +78,12 @@ namespace ILCompiler
return this;
}
+ public CompilationBuilder UseDevirtualizationManager(DevirtualizationManager manager)
+ {
+ _devirtualizationManager = manager;
+ return this;
+ }
+
public CompilationBuilder UseDebugInfoProvider(DebugInformationProvider provider)
{
_debugInformationProvider = provider;
diff --git a/src/ILCompiler.Compiler/src/Compiler/DevirtualizationManager.cs b/src/ILCompiler.Compiler/src/Compiler/DevirtualizationManager.cs
new file mode 100644
index 000000000..8a6227ee4
--- /dev/null
+++ b/src/ILCompiler.Compiler/src/Compiler/DevirtualizationManager.cs
@@ -0,0 +1,90 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Internal.TypeSystem;
+
+using Debug = System.Diagnostics.Debug;
+
+namespace ILCompiler
+{
+ /// <summary>
+ /// Manages devirtualization behaviors. Devirtualization is the process of converting
+ /// virtual calls to direct calls in cases where we can compute the result of a virtual
+ /// lookup at compile time.
+ /// </summary>
+ public class DevirtualizationManager
+ {
+ /// <summary>
+ /// Returns true if <paramref name="type"/> cannot be the base class of any other
+ /// type.
+ /// </summary>
+ public virtual bool IsEffectivelySealed(TypeDesc type)
+ {
+ switch (type.Category)
+ {
+ case TypeFlags.Array:
+ case TypeFlags.SzArray:
+ case TypeFlags.ByRef:
+ case TypeFlags.Pointer:
+ case TypeFlags.FunctionPointer:
+ return true;
+
+ default:
+ Debug.Assert(type.IsDefType);
+ var metadataType = (MetadataType)type;
+ return metadataType.IsSealed || metadataType.IsModuleType;
+ }
+ }
+
+ /// <summary>
+ /// Returns true if <paramref name="method"/> cannot be overriden by any other method.
+ /// </summary>
+ public virtual bool IsEffectivelySealed(MethodDesc method)
+ {
+ return method.IsFinal || IsEffectivelySealed(method.OwningType);
+ }
+
+ /// <summary>
+ /// Attempts to resolve the <paramref name="declMethod"/> virtual method into
+ /// a method on <paramref name="implType"/> that implements the declaring method.
+ /// Returns null if this is not possible.
+ /// </summary>
+ /// <remarks>
+ /// Note that if <paramref name="implType"/> is a value type, the result of the resolution
+ /// might have to be treated as an unboxing thunk by the caller.
+ /// </remarks>
+ public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType)
+ {
+ Debug.Assert(declMethod.IsVirtual);
+
+ // Quick check: if decl matches impl, we're done.
+ if (declMethod.OwningType == implType)
+ return declMethod;
+
+ // We're operating on virtual methods. This means that if implType is an array, we need
+ // to get the type that has all the virtual methods provided by the class library.
+ return ResolveVirtualMethod(declMethod, implType.GetClosestDefType());
+ }
+
+ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType implType)
+ {
+ MethodDesc impl;
+
+ if (declMethod.OwningType.IsInterface)
+ {
+ impl = implType.ResolveInterfaceMethodTarget(declMethod);
+ if (impl != null)
+ {
+ impl = implType.FindVirtualFunctionTargetMethodOnObjectType(impl);
+ }
+ }
+ else
+ {
+ impl = implType.FindVirtualFunctionTargetMethodOnObjectType(declMethod);
+ }
+
+ return impl;
+ }
+ }
+}
diff --git a/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs b/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs
index 9b50db7db..aea6f4229 100644
--- a/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs
@@ -30,7 +30,7 @@ namespace ILCompiler
IEnumerable<ICompilationRootProvider> roots,
DebugInformationProvider debugInformationProvider,
Logger logger)
- : base(dependencyGraph, nodeFactory, roots, debugInformationProvider, logger)
+ : base(dependencyGraph, nodeFactory, roots, debugInformationProvider, null, logger)
{
}
@@ -106,6 +106,11 @@ namespace ILCompiler
return new ScannedDictionaryLayoutProvider(MarkedNodes);
}
+ public DevirtualizationManager GetDevirtualizationManager()
+ {
+ return new ScannedDevirtualizationManager(MarkedNodes);
+ }
+
private class ScannedVTableProvider : VTableSliceProvider
{
private Dictionary<TypeDesc, IReadOnlyList<MethodDesc>> _vtableSlices = new Dictionary<TypeDesc, IReadOnlyList<MethodDesc>>();
@@ -175,5 +180,94 @@ namespace ILCompiler
}
}
}
+
+ private class ScannedDevirtualizationManager : DevirtualizationManager
+ {
+ private HashSet<TypeDesc> _constructedTypes = new HashSet<TypeDesc>();
+ private HashSet<TypeDesc> _unsealedTypes = new HashSet<TypeDesc>();
+
+ public ScannedDevirtualizationManager(ImmutableArray<DependencyNodeCore<NodeFactory>> markedNodes)
+ {
+ foreach (var node in markedNodes)
+ {
+ if (node is ConstructedEETypeNode eetypeNode)
+ {
+ TypeDesc type = eetypeNode.Type;
+
+ if (!type.IsInterface)
+ {
+ //
+ // We collect this information:
+ //
+ // 1. What types got allocated
+ // This is needed for optimizing codegens that might attempt to devirtualize
+ // calls to sealed types. The devirtualization is not allowed to succeed
+ // for types that never got allocated because the scanner likely didn't scan
+ // the target of the virtual call.
+ // 2. What types are the base types of other types
+ // This is needed for optimizations. We use this information to effectively
+ // seal types that are not base types for any other type.
+ //
+
+ TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific);
+
+ _constructedTypes.Add(canonType);
+
+ // Since this is used for the purposes of devirtualization, it's really convenient
+ // to also have Array<T> for each T[].
+ if (canonType.IsArray)
+ _constructedTypes.Add(canonType.GetClosestDefType());
+
+ TypeDesc baseType = canonType.BaseType;
+ bool added = true;
+ while (baseType != null && added)
+ {
+ baseType = baseType.ConvertToCanonForm(CanonicalFormKind.Specific);
+ added = _unsealedTypes.Add(baseType);
+ baseType = baseType.BaseType;
+ }
+ }
+
+ }
+ }
+ }
+
+ public override bool IsEffectivelySealed(TypeDesc type)
+ {
+ // If we know we scanned a type that derives from this one, this for sure can't be reported as sealed.
+ TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific);
+ if (_unsealedTypes.Contains(canonType))
+ return false;
+
+ // We don't want to report types that never got allocated as sealed because that would allow
+ // the codegen to do direct calls to the type's methods. That can potentially lead to codegen
+ // generating calls to methods we never scanned (consider a sealed type that never got allocated
+ // with a virtual method that can be devirtualized because the type is sealed).
+ // Codegen looking at code we didn't scan is never okay.
+ if (!_constructedTypes.Contains(canonType))
+ return false;
+
+ if (type is MetadataType metadataType)
+ {
+ // Due to how the compiler is structured, we might see "constructed" EETypes for things
+ // that never got allocated (doing a typeof() on a class that is otherwise never used is
+ // a good example of when that happens). This can put us into a position where we could
+ // report `sealed` on an `abstract` class, but that doesn't lead to anything good.
+ return !metadataType.IsAbstract;
+ }
+
+ // Everything else can be considered sealed.
+ return true;
+ }
+
+ public override bool IsEffectivelySealed(MethodDesc method)
+ {
+ // For the same reason as above, don't report methods on unallocated types as sealed.
+ if (!_constructedTypes.Contains(method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific)))
+ return false;
+
+ return base.IsEffectivelySealed(method);
+ }
+ }
}
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs
index 1f993c832..97b6ce723 100644
--- a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs
@@ -25,8 +25,9 @@ namespace ILCompiler
IEnumerable<ICompilationRootProvider> roots,
DebugInformationProvider debugInformationProvider,
Logger logger,
+ DevirtualizationManager devirtualizationManager,
JitConfigProvider configProvider)
- : base(dependencyGraph, nodeFactory, roots, debugInformationProvider, logger)
+ : base(dependencyGraph, nodeFactory, roots, debugInformationProvider, devirtualizationManager, logger)
{
_jitConfigProvider = configProvider;
}
diff --git a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs
index fbd06eabc..773560798 100644
--- a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs
+++ b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs
@@ -91,7 +91,7 @@ namespace ILCompiler
var jitConfig = new JitConfigProvider(jitFlagBuilder.ToArray(), _ryujitOptions);
DependencyAnalyzerBase<NodeFactory> graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(new CompilerComparer()));
- return new RyuJitCompilation(graph, factory, _compilationRoots, _debugInformationProvider, _logger, jitConfig);
+ return new RyuJitCompilation(graph, factory, _compilationRoots, _debugInformationProvider, _logger, _devirtualizationManager, jitConfig);
}
}
}
diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
index 9215bb331..50d13989d 100644
--- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
+++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj
@@ -132,6 +132,7 @@
<Compile Include="Compiler\DependencyAnalysis\IMethodBodyNodeWithFuncletSymbols.cs" />
<Compile Include="Compiler\DependencyAnalysis\ISymbolNodeWithFuncletId.cs" />
<Compile Include="Compiler\DependencyAnalysis\LoopHijackFlagNode.cs" />
+ <Compile Include="Compiler\DevirtualizationManager.cs" />
<Compile Include="Compiler\DictionaryLayoutProvider.cs" />
<Compile Include="Compiler\EmptyInteropStubManager.cs" />
<Compile Include="Compiler\DependencyAnalysis\SortableDependencyNode.cs" />
diff --git a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs
index 2a3e474a9..92a26eb42 100644
--- a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs
+++ b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs
@@ -25,7 +25,7 @@ namespace ILCompiler
DebugInformationProvider debugInformationProvider,
Logger logger,
CppCodegenConfigProvider options)
- : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), debugInformationProvider, logger)
+ : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), debugInformationProvider, null, logger)
{
Options = options;
}
diff --git a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs
index 2853751ec..94b35a3f4 100644
--- a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs
+++ b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs
@@ -23,7 +23,7 @@ namespace ILCompiler
IEnumerable<ICompilationRootProvider> roots,
Logger logger,
WebAssemblyCodegenConfigProvider options)
- : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), null, logger)
+ : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), null, null, logger)
{
NodeFactory = nodeFactory;
LLVM.LoadLibrary_libLLVM("./libLLVM-x64.dll");
diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs
index d0991609c..96e63fd2f 100644
--- a/src/ILCompiler/src/Program.cs
+++ b/src/ILCompiler/src/Program.cs
@@ -437,6 +437,12 @@ namespace ILCompiler
// If we have a scanner, feed the generic dictionary results to the compilation.
// This could be a command line switch if we really wanted to.
builder.UseGenericDictionaryLayoutProvider(scanResults.GetDictionaryLayoutInfo());
+
+ // If we feed any outputs of the scanner into the compilation, it's essential
+ // we use scanner's devirtualization manager. It prevents optimizing codegens
+ // from accidentally devirtualizing cases that can never happen at runtime
+ // (e.g. devirtualizing a method on a type that never gets allocated).
+ builder.UseDevirtualizationManager(scanResults.GetDevirtualizationManager());
}
ICompilation compilation = builder.ToCompilation();
diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs
index d71b9a8f1..04bb65cf9 100644
--- a/src/JitInterface/src/CorInfoImpl.cs
+++ b/src/JitInterface/src/CorInfoImpl.cs
@@ -723,11 +723,8 @@ namespace Internal.JitInterface
// method body.
//
- var owningType = method.OwningType;
- var owningMetadataType = owningType as MetadataType;
-
// method or class might have the final bit
- if (method.IsFinal || (owningMetadataType != null && owningMetadataType.IsSealed))
+ if (_compilation.IsEffectivelySealed(method))
result |= CorInfoFlag.CORINFO_FLG_FINAL;
if (method.IsSharedByGenericInstantiations)
@@ -754,7 +751,7 @@ namespace Internal.JitInterface
result |= CorInfoFlag.CORINFO_FLG_FORCEINLINE;
}
- if (owningType.IsDelegate)
+ if (method.OwningType.IsDelegate)
{
if (method.Name == "Invoke")
// This is now used to emit efficient invoke code for any delegate invoke,
@@ -867,35 +864,20 @@ namespace Internal.JitInterface
return null;
}
- implType = implType.GetClosestDefType();
-
MethodDesc decl = HandleToObject(baseMethod);
- Debug.Assert(decl.IsVirtual);
Debug.Assert(!decl.HasInstantiation);
- MethodDesc impl;
-
- TypeDesc declOwningType = decl.OwningType;
- if (declOwningType.IsInterface)
+ if (ownerType != null)
{
- // Interface call devirtualization.
-
- if (implType.IsCanonicalSubtype(CanonicalFormKind.Any))
- {
- // TODO: attempt to devirtualize methods on canonical interfaces
- return null;
- }
-
- impl = implType.ResolveInterfaceMethodTarget(decl);
- if (impl != null)
+ TypeDesc ownerTypeDesc = typeFromContext(ownerType);
+ if (decl.OwningType != ownerTypeDesc)
{
- impl = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(impl);
+ Debug.Assert(ownerTypeDesc is InstantiatedType);
+ decl = _compilation.TypeSystemContext.GetMethodForInstantiatedType(decl.GetTypicalMethodDefinition(), (InstantiatedType)ownerTypeDesc);
}
}
- else
- {
- impl = implType.GetClosestDefType().FindVirtualFunctionTargetMethodOnObjectType(decl);
- }
+
+ MethodDesc impl = _compilation.ResolveVirtualMethod(decl, implType);
return impl != null ? ObjectToHandle(impl) : null;
}
@@ -1320,6 +1302,9 @@ namespace Internal.JitInterface
if (type.IsDelegate)
result |= CorInfoFlag.CORINFO_FLG_DELEGATE;
+ if (_compilation.IsEffectivelySealed(type))
+ result |= CorInfoFlag.CORINFO_FLG_FINAL;
+
if (metadataType != null)
{
if (metadataType.ContainsGCPointers)
@@ -1328,9 +1313,6 @@ namespace Internal.JitInterface
if (metadataType.IsBeforeFieldInit)
result |= CorInfoFlag.CORINFO_FLG_BEFOREFIELDINIT;
- if (metadataType.IsSealed)
- result |= CorInfoFlag.CORINFO_FLG_FINAL;
-
// Assume overlapping fields for explicit layout.
if (metadataType.IsExplicitLayout)
result |= CorInfoFlag.CORINFO_FLG_OVERLAPPING_FIELDS;
diff --git a/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs b/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs
index 85eb8a03c..e628978b9 100644
--- a/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs
+++ b/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs
@@ -23,6 +23,7 @@ namespace ILCompiler
_typeGetTypeMethodThunks = new TypeGetTypeMethodThunkCache(context.GetWellKnownType(WellKnownType.Object));
_methodILCache = new ILProvider(new PInvokeILProvider(new PInvokeILEmitterConfiguration(forceLazyResolution: true), null));
_nodeFactory = new NodeFactory(context);
+ _devirtualizationManager = new DevirtualizationManager();
}
private readonly NodeFactory _nodeFactory;
@@ -30,6 +31,7 @@ namespace ILCompiler
protected readonly Logger _logger = Logger.Null;
private readonly TypeGetTypeMethodThunkCache _typeGetTypeMethodThunks;
private ILProvider _methodILCache;
+ private readonly DevirtualizationManager _devirtualizationManager;
internal Logger Logger => _logger;
@@ -102,6 +104,21 @@ namespace ILCompiler
return true;
}
+ public bool IsEffectivelySealed(TypeDesc type)
+ {
+ return _devirtualizationManager.IsEffectivelySealed(type);
+ }
+
+ public bool IsEffectivelySealed(MethodDesc method)
+ {
+ return _devirtualizationManager.IsEffectivelySealed(method);
+ }
+
+ public MethodDesc ResolveVirtualMethod(MethodDesc declMethod, TypeDesc implType)
+ {
+ return _devirtualizationManager.ResolveVirtualMethod(declMethod, implType);
+ }
+
public bool NeedsRuntimeLookup(ReadyToRunHelperId lookupKind, object targetOfLookup)
{
// The current plan seem to be to copy paste from ILCompiler.Compilation, but that's not a sustainable plan
diff --git a/src/System.Private.Jit/src/System.Private.Jit.csproj b/src/System.Private.Jit/src/System.Private.Jit.csproj
index 5df579bb4..270c33469 100644
--- a/src/System.Private.Jit/src/System.Private.Jit.csproj
+++ b/src/System.Private.Jit/src/System.Private.Jit.csproj
@@ -134,6 +134,7 @@
<Compile Include="$(ILCompilerBasePath)\Compiler\TypeExtensions.cs" />
<Compile Include="$(ILCompilerBasePath)\Compiler\NameMangler.cs" />
<Compile Include="$(ILCompilerBasePath)\Compiler\NodeMangler.cs" />
+ <Compile Include="$(ILCompilerBasePath)\Compiler\DevirtualizationManager.cs" />
<Compile Include="$(ILCompilerBasePath)\IL\Stubs\PInvokeILProvider.cs" />
<Compile Include="$(DependencyAnalysisFrameworkBasePath)\IDependencyNode.cs" />
<Compile Include="$(DependencyAnalysisFrameworkBasePath)\DependencyNode.cs" />