diff --git a/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs b/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs index b1329685c0f..921812658ac 100644 --- a/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs +++ b/src/Common/src/TypeSystem/Common/MetadataFieldLayoutAlgorithm.cs @@ -297,7 +297,7 @@ protected virtual void FinalizeRuntimeSpecificStaticFieldLayout(TypeSystemContex { } - private static ComputedInstanceFieldLayout ComputeExplicitFieldLayout(MetadataType type, int numInstanceFields) + protected static ComputedInstanceFieldLayout ComputeExplicitFieldLayout(MetadataType type, int numInstanceFields) { // Instance slice size is the total size of instance not including the base type. // It is calculated as the field whose offset and size add to the greatest value. diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs index eeed8216518..6e54bc08597 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs @@ -9,7 +9,14 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun { public class ImportSectionNode : EmbeddedObjectNode { - private readonly ArrayOfEmbeddedDataNode _imports; + private class ImportTable : ArrayOfEmbeddedDataNode + { + public ImportTable(string startSymbol, string endSymbol) : base(startSymbol, endSymbol, nodeSorter: null) {} + + public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => false; + } + + private readonly ImportTable _imports; // TODO: annoying - today there's no way to put signature RVA's into R/O data section private readonly ArrayOfEmbeddedPointersNode _signatures; // TODO: annoying - cannot enumerate the ArrayOfEmbeddedPointersNode so we must keep a copy. @@ -34,7 +41,7 @@ public ImportSectionNode(string name, CorCompileImportType importType, CorCompil _emitPrecode = emitPrecode; _emitGCRefMap = emitGCRefMap; - _imports = new ArrayOfEmbeddedDataNode(_name + "_ImportBegin", _name + "_ImportEnd", null); + _imports = new ImportTable(_name + "_ImportBegin", _name + "_ImportEnd"); _signatures = new ArrayOfEmbeddedPointersNode(_name + "_SigBegin", _name + "_SigEnd", null); _signatureList = new List(); _gcRefMap = (_emitGCRefMap ? new GCRefMapNode(this) : null); @@ -78,20 +85,17 @@ public void AddImport(NodeFactory factory, Import import) public override int ClassCode => -62839441; - public bool ShouldSkipEmittingTable(NodeFactory factory) - { - return _imports.ShouldSkipEmittingObjectNode(factory); - } - public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory factory, bool relocsOnly) { - if (!relocsOnly && _imports.ShouldSkipEmittingObjectNode(factory)) + if (!_imports.ShouldSkipEmittingObjectNode(factory)) + { + dataBuilder.EmitReloc(_imports.StartSymbol, RelocType.IMAGE_REL_BASED_ADDR32NB, 0); + } + else { - // Don't emit import section node at all if there are no entries in it - return; + dataBuilder.EmitUInt(0); } - dataBuilder.EmitReloc(_imports.StartSymbol, RelocType.IMAGE_REL_BASED_ADDR32NB, 0); if (!relocsOnly) { dataBuilder.EmitInt(_imports.GetData(factory, false).Data.Length); @@ -100,6 +104,7 @@ public override void EncodeData(ref ObjectDataBuilder dataBuilder, NodeFactory f dataBuilder.EmitByte((byte)_type); dataBuilder.EmitByte(_entrySize); } + if (!_signatures.ShouldSkipEmittingObjectNode(factory)) { dataBuilder.EmitReloc(_signatures.StartSymbol, RelocType.IMAGE_REL_BASED_ADDR32NB, 0); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionsTableNode.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionsTableNode.cs index 98a37ca62e1..beaafb48c5f 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionsTableNode.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionsTableNode.cs @@ -37,7 +37,7 @@ protected override void GetElementDataForNodes(ref ObjectDataBuilder builder, No int index = 0; foreach (ImportSectionNode node in NodesList) { - if (!relocsOnly && !node.ShouldSkipEmittingTable(factory)) + if (!relocsOnly) { node.InitializeOffsetFromBeginningOfArray(builder.CountBytes); node.InitializeIndexFromBeginningOfArray(index++); diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs index e970d0a675d..ce623fc3855 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs @@ -341,9 +341,8 @@ private void EmitInstantiatedTypeSignature(InstantiatedType type, SignatureConte { EcmaModule targetModule = context.GetTargetModule(type); EmitModuleOverride(targetModule, context); - context = context.InnerContext(targetModule); EmitElementType(CorElementType.ELEMENT_TYPE_GENERICINST); - EmitTypeSignature(type.GetTypeDefinition(), context); + EmitTypeSignature(type.GetTypeDefinition(), context.InnerContext(targetModule)); EmitUInt((uint)type.Instantiation.Length); for (int paramIndex = 0; paramIndex < type.Instantiation.Length; paramIndex++) { diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index 44dcfa0658e..ec1cb73c885 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -253,7 +253,7 @@ private ISymbolNode CreateCctorTrigger(TypeDesc type, SignatureContext signature { return new DelayLoadHelperImport( _codegenNodeFactory, - _codegenNodeFactory.DispatchImports, + _codegenNodeFactory.HelperImports, ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper, _codegenNodeFactory.TypeSignature(ReadyToRunFixupKind.READYTORUN_FIXUP_CctorTrigger, type, signatureContext)); } diff --git a/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs b/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs index ffaa0cfd675..47d7a51ece7 100644 --- a/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs +++ b/src/ILCompiler.ReadyToRun/src/Compiler/ReadyToRunMetadataFieldLayoutAlgorithm.cs @@ -5,7 +5,6 @@ using System; using System.Diagnostics; using System.Collections.Generic; -using System.Collections.Immutable; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; @@ -688,6 +687,11 @@ public void AddDynamicLayout(DefType instantiatedType, FieldAndOffset[] fieldMap protected override ComputedInstanceFieldLayout ComputeInstanceFieldLayout(MetadataType type, int numInstanceFields) { + if (type.IsExplicitLayout) + { + return ComputeExplicitFieldLayout(type, numInstanceFields); + } + else if (type.IsValueType && (MarshalUtils.IsBlittableType(type) || MarshalUtils.IsManagedSequentialType(type))) { return ComputeSequentialFieldLayout(type, numInstanceFields); diff --git a/src/JitInterface/src/CorInfoImpl.cs b/src/JitInterface/src/CorInfoImpl.cs index ec583e98255..2cf28c2f534 100644 --- a/src/JitInterface/src/CorInfoImpl.cs +++ b/src/JitInterface/src/CorInfoImpl.cs @@ -2177,7 +2177,7 @@ private void getFieldInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_MET } #if READYTORUN - if (!_compilation.NodeFactory.CompilationModuleGroup.ContainsType(field.OwningType) && + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(field.OwningType) && fieldAccessor == CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER) { PreventRecursiveFieldInlinesOutsideVersionBubble(field, callerMethod); diff --git a/tests/src/Simple/ReadyToRunUnit/Program.cs b/tests/src/Simple/ReadyToRunUnit/Program.cs index 79859835101..ea62198f079 100644 --- a/tests/src/Simple/ReadyToRunUnit/Program.cs +++ b/tests/src/Simple/ReadyToRunUnit/Program.cs @@ -8,6 +8,7 @@ using System.Linq.Expressions; using System.Numerics; using System.Reflection; +using System.Runtime.InteropServices; using System.Text; internal class ClassWithStatic @@ -136,7 +137,104 @@ private static bool BoxUnbox() } return success; } + + [StructLayout(LayoutKind.Explicit)] + private struct ExplicitFieldOffsetStruct + { + [FieldOffset(0)] + public int Field00; + [FieldOffset(0x0f)] + public int Field15; + } + + private static ExplicitFieldOffsetStruct HelperCreateExplicitLayoutStruct() + { + ExplicitFieldOffsetStruct epl = new ExplicitFieldOffsetStruct(); + epl.Field00 = 40; + epl.Field15 = 15; + return epl; + } + + private static bool HelperCompare(ExplicitFieldOffsetStruct val, ExplicitFieldOffsetStruct val1) + { + bool match = true; + if (val.Field00 != val1.Field00) + { + match = false; + Console.WriteLine("ExplicitLayout: val.Field00 = {0}, val1.Field00 = {1}", val.Field00, val1.Field00); + } + if (val.Field15 != val1.Field15) + { + match = false; + Console.WriteLine("ExplicitLayout: val.Field15 = {0}, val1.Field15 = {1}", val.Field15, val1.Field15); + } + return match; + } + + private static bool HelperCompare(ExplicitFieldOffsetStruct? val, ExplicitFieldOffsetStruct val1) + { + return val == null ? false : HelperCompare(val.Value, val1); + } + + private static bool BoxUnboxToQ2(ExplicitFieldOffsetStruct? val) + { + return HelperCompare(val, HelperCreateExplicitLayoutStruct()); + } + + private static bool BoxUnboxToQ1(ValueType vt) + { + return BoxUnboxToQ2((ExplicitFieldOffsetStruct?)vt); + } + + private static bool BoxUnboxToQ(object o) + { + return BoxUnboxToQ1((ValueType)o); + } + + private static bool NullableWithExplicitLayoutTest() + { + ExplicitFieldOffsetStruct? s = HelperCreateExplicitLayoutStruct(); + return BoxUnboxToQ(s); + } + + private static char HelperCreateChar() + { + return 'c'; + } + + private static bool HelperCompare(char val, char val1) + { + if (val == val1) + { + return true; + } + Console.Error.WriteLine("val = {0} = 0x{1:x2}, val1 = {2} = 0x{3:x2}", val, (int)val, val1, (int)val1); + return false; + } + + private static bool BoxUnboxToNQ2(char c) + { + return HelperCompare(c, HelperCreateChar()); + } + + private static bool BoxUnboxToNQ1(ValueType vt) + { + Console.WriteLine("BoxUnboxToNQ1: {0}", vt); + return BoxUnboxToNQ2((char)vt); + } + + private static bool BoxUnboxToNQ(object o) + { + Console.WriteLine("BoxUnboxToNQ: {0}", o); + return BoxUnboxToNQ1((ValueType)o); + } + private static bool CastClassWithCharTest() + { + char? s = HelperCreateChar(); + return BoxUnboxToNQ(s); + } + private static bool TypeHandle() { Console.WriteLine(TextFileName.GetType().ToString()); @@ -1063,6 +1161,10 @@ public static int Main(string[] args) RunTest("ChkCast", ChkCast()); RunTest("ChkCastValueType", ChkCastValueType()); RunTest("BoxUnbox", BoxUnbox()); + // TODO: enabling this test requires fixes to IsManagedSequential I'm going to send out + // in a subsequent PR together with removal of this temporary clause [trylek] + // RunTest("NullableWithExplicitLayoutTest", NullableWithExplicitLayoutTest()); + RunTest("CastClassWithCharTest", CastClassWithCharTest()); RunTest("TypeHandle", TypeHandle()); RunTest("RuntimeTypeHandle", RuntimeTypeHandle()); RunTest("ReadAllText", ReadAllText()); diff --git a/tests/src/tools/ReadyToRun.SuperIlc/CompilerRunner.cs b/tests/src/tools/ReadyToRun.SuperIlc/CompilerRunner.cs index 1d69503901f..5adcf4231d2 100644 --- a/tests/src/tools/ReadyToRun.SuperIlc/CompilerRunner.cs +++ b/tests/src/tools/ReadyToRun.SuperIlc/CompilerRunner.cs @@ -94,6 +94,12 @@ public ProcessParameters CompilationR2RDumpProcess(string compiledExecutable, bo { commonBuilder.Append($@" --rp ""{referencePath}"""); } + + if (_options.CoreRootDirectory != null) + { + commonBuilder.Append($@" --rp ""{_options.CoreRootDirectory.FullName}"""); + } + commonBuilder.Append($@" --in ""{compiledExecutable}"""); StringBuilder builder = new StringBuilder(commonBuilder.ToString());