Fix crossgen2 ArgIterator for SystemV Amd64 Unix ABI (#499)
authorJan Vorlicek <janvorli@microsoft.com>
Thu, 5 Dec 2019 10:40:22 +0000 (11:40 +0100)
committerGitHub <noreply@github.com>
Thu, 5 Dec 2019 10:40:22 +0000 (11:40 +0100)
* Fix crossgen2 ArgIterator for amd64 Unix ABI

The ArgIterator was missing proper handling of the SystemV struct classification.
There were also several minor issues.

* Remove the SystemV struct classification cache

Running without the cache showed no difference in the total build time of
all the coreclr pri1 tests and all the runtime assemblies, so I am removing
the cache.

src/coreclr/src/tools/Common/JitInterface/CorInfoImpl.cs
src/coreclr/src/tools/Common/JitInterface/SystemVStructClassificator.cs
src/coreclr/src/tools/ReadyToRun.SuperIlc/BuildFolderSet.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GCRefMapBuilder.cs
src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs

index bad1708..e2f7a53 100644 (file)
@@ -2524,13 +2524,12 @@ namespace Internal.JitInterface
         private byte* findNameOfToken(CORINFO_MODULE_STRUCT_* moduleHandle, mdToken token, byte* szFQName, UIntPtr FQNameCapacity)
         { throw new NotImplementedException("findNameOfToken"); }
 
-        SystemVStructClassificator _systemVStructClassificator = new SystemVStructClassificator();
-
         private bool getSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_STRUCT_* structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
         {
             TypeDesc typeDesc = HandleToObject(structHnd);
 
-            return _systemVStructClassificator.getSystemVAmd64PassStructInRegisterDescriptor(typeDesc, structPassInRegDescPtr);
+            SystemVStructClassificator.GetSystemVAmd64PassStructInRegisterDescriptor(typeDesc, out *structPassInRegDescPtr);
+            return true;
         }
 
         private uint getThreadTLSIndex(ref void* ppIndirection)
index fe7f13d..3f437c5 100644 (file)
@@ -12,10 +12,8 @@ namespace Internal.JitInterface
     using static SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR;
     using static SystemVClassificationType;
 
-    internal class SystemVStructClassificator
+    internal static class SystemVStructClassificator
     {
-        private Dictionary<TypeDesc, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR> _classificationCache = new Dictionary<TypeDesc, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR>();
-
         private struct SystemVStructRegisterPassingHelper
         {
             internal SystemVStructRegisterPassingHelper(int totalStructSize)
@@ -93,44 +91,37 @@ namespace Internal.JitInterface
             }
         }
 
-        public unsafe bool getSystemVAmd64PassStructInRegisterDescriptor(TypeDesc typeDesc, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
+        public static void GetSystemVAmd64PassStructInRegisterDescriptor(TypeDesc typeDesc, out SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structPassInRegDescPtr)
         {
-            structPassInRegDescPtr->passedInRegisters = false;
+            structPassInRegDescPtr = default;
+            structPassInRegDescPtr.passedInRegisters = false;
             
             int typeSize = typeDesc.GetElementSize().AsInt;
             if (typeDesc.IsValueType && (typeSize <= CLR_SYSTEMV_MAX_STRUCT_BYTES_TO_PASS_IN_REGISTERS))
             {
-                Debug.Assert((TypeDef2SystemVClassification(typeDesc) == SystemVClassificationTypeStruct) ||
-                             (TypeDef2SystemVClassification(typeDesc) == SystemVClassificationTypeTypedReference));
-
-                if (_classificationCache.TryGetValue(typeDesc, out SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR descriptor))
+                if ((TypeDef2SystemVClassification(typeDesc) != SystemVClassificationTypeStruct) &&
+                    (TypeDef2SystemVClassification(typeDesc) != SystemVClassificationTypeTypedReference))
                 {
-                    *structPassInRegDescPtr = descriptor;
+                    return;
                 }
-                else
+
+                SystemVStructRegisterPassingHelper helper = new SystemVStructRegisterPassingHelper(typeSize);
+                bool canPassInRegisters = ClassifyEightBytes(typeDesc, ref helper, 0);
+                if (canPassInRegisters)
                 {
-                    SystemVStructRegisterPassingHelper helper = new SystemVStructRegisterPassingHelper(typeSize);
-                    bool canPassInRegisters = ClassifyEightBytes(typeDesc, ref helper, 0);
-                    if (canPassInRegisters)
-                    {
-                        structPassInRegDescPtr->passedInRegisters = canPassInRegisters;
-                        structPassInRegDescPtr->eightByteCount = (byte)helper.EightByteCount;
-                        Debug.Assert(structPassInRegDescPtr->eightByteCount <= CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS);
+                    structPassInRegDescPtr.passedInRegisters = canPassInRegisters;
+                    structPassInRegDescPtr.eightByteCount = (byte)helper.EightByteCount;
+                    Debug.Assert(structPassInRegDescPtr.eightByteCount <= CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS);
 
-                        structPassInRegDescPtr->eightByteClassifications0 = helper.EightByteClassifications[0];
-                        structPassInRegDescPtr->eightByteSizes0 = (byte)helper.EightByteSizes[0];
-                        structPassInRegDescPtr->eightByteOffsets0 = (byte)helper.EightByteOffsets[0];
+                    structPassInRegDescPtr.eightByteClassifications0 = helper.EightByteClassifications[0];
+                    structPassInRegDescPtr.eightByteSizes0 = (byte)helper.EightByteSizes[0];
+                    structPassInRegDescPtr.eightByteOffsets0 = (byte)helper.EightByteOffsets[0];
                         
-                        structPassInRegDescPtr->eightByteClassifications1 = helper.EightByteClassifications[1];
-                        structPassInRegDescPtr->eightByteSizes1 = (byte)helper.EightByteSizes[1];
-                        structPassInRegDescPtr->eightByteOffsets1 = (byte)helper.EightByteOffsets[1];
-                    }
-
-                    _classificationCache.Add(typeDesc, *structPassInRegDescPtr);
+                    structPassInRegDescPtr.eightByteClassifications1 = helper.EightByteClassifications[1];
+                    structPassInRegDescPtr.eightByteSizes1 = (byte)helper.EightByteSizes[1];
+                    structPassInRegDescPtr.eightByteOffsets1 = (byte)helper.EightByteOffsets[1];
                 }
             }
-
-            return true;
         }
 
         private static SystemVClassificationType TypeDef2SystemVClassification(TypeDesc typeDesc)
index 51c5da6..2189465 100644 (file)
@@ -41,6 +41,8 @@ namespace ReadyToRun.SuperIlc
             new FrameworkExclusion("Microsoft.CodeAnalysis.CSharp", "Ibc TypeToken 6200019a has type token which resolves to a nil token", crossgen2Only: true),
             new FrameworkExclusion("Microsoft.CodeAnalysis", "Ibc TypeToken 620001af unable to find external typedef", crossgen2Only: true),
             new FrameworkExclusion("Microsoft.CodeAnalysis.VisualBasic", "Ibc TypeToken 620002ce unable to find external typedef", crossgen2Only: true),
+            
+            new FrameworkExclusion("System.Runtime.Serialization.Formatters", "Assert in JIT on Linux", crossgen2Only: true)
         };
 
         private readonly IEnumerable<BuildFolder> _buildFolders;
index d03ab7a..619df70 100644 (file)
@@ -312,92 +312,41 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
             return _transitionBlock.OffsetOfArgumentRegisters + _argLocDescForStructInRegs.Value.m_idxGenReg * 8;
         }
 
-        private SystemVClassificationType GetTypeClassification(TypeDesc type)
-        {
-            switch (type.Category)
-            {
-                case Internal.TypeSystem.TypeFlags.Void:
-                    return SystemVClassificationType.SystemVClassificationTypeUnknown;
-
-                case Internal.TypeSystem.TypeFlags.Boolean:
-                case Internal.TypeSystem.TypeFlags.Char:
-                case Internal.TypeSystem.TypeFlags.SByte:
-                case Internal.TypeSystem.TypeFlags.Byte:
-                case Internal.TypeSystem.TypeFlags.Int16:
-                case Internal.TypeSystem.TypeFlags.UInt16:
-                case Internal.TypeSystem.TypeFlags.Int32:
-                case Internal.TypeSystem.TypeFlags.UInt32:
-                case Internal.TypeSystem.TypeFlags.Int64:
-                case Internal.TypeSystem.TypeFlags.UInt64:
-                case Internal.TypeSystem.TypeFlags.IntPtr:
-                case Internal.TypeSystem.TypeFlags.UIntPtr:
-                case Internal.TypeSystem.TypeFlags.Enum:
-                case Internal.TypeSystem.TypeFlags.Pointer:
-                case Internal.TypeSystem.TypeFlags.FunctionPointer:
-                    return SystemVClassificationType.SystemVClassificationTypeInteger;
-
-                case Internal.TypeSystem.TypeFlags.Single:
-                case Internal.TypeSystem.TypeFlags.Double:
-                    return SystemVClassificationType.SystemVClassificationTypeSSE;
-
-                case Internal.TypeSystem.TypeFlags.ByRef:
-                    return SystemVClassificationType.SystemVClassificationTypeIntegerByRef;
-
-                case Internal.TypeSystem.TypeFlags.ValueType:
-                    return SystemVClassificationType.SystemVClassificationTypeStruct;
-
-                case Internal.TypeSystem.TypeFlags.Class:
-                case Internal.TypeSystem.TypeFlags.GenericParameter:
-                case Internal.TypeSystem.TypeFlags.Array:
-                case Internal.TypeSystem.TypeFlags.SzArray:
-                case Internal.TypeSystem.TypeFlags.Interface:
-                    return SystemVClassificationType.SystemVClassificationTypeIntegerReference;
-
-                default:
-                    return SystemVClassificationType.SystemVClassificationTypeUnknown;
-            }
-        }
-
         // Report managed object pointers in the struct in registers
         // Arguments:
         //  fn - promotion function to apply to each managed object pointer
         //  sc - scan context to pass to the promotion function
         //  fieldBytes - size of the structure
-        void ReportPointersFromStructInRegisters(TypeDesc type, int delta, CORCOMPILE_GCREFMAP_TOKENS[] frame)
+        internal void ReportPointersFromStructInRegisters(TypeDesc type, int delta, CORCOMPILE_GCREFMAP_TOKENS[] frame)
         {
             // SPAN-TODO: GC reporting - https://github.com/dotnet/coreclr/issues/8517
 
             Debug.Assert(IsStructPassedInRegs());
 
             int genRegDest = GetStructGenRegDestinationAddress();
-            foreach (FieldDesc field in type.GetFields())
+
+            SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR descriptor;
+            SystemVStructClassificator.GetSystemVAmd64PassStructInRegisterDescriptor(type, out descriptor);
+
+            for (int i = 0; i < descriptor.eightByteCount; i++)
             {
-                if (field.IsStatic)
-                {
-                    continue;
-                }
-                SystemVClassificationType eightByteClassification = GetTypeClassification(field.FieldType);
+                int eightByteSize = (i == 0) ? descriptor.eightByteSizes0 : descriptor.eightByteSizes1;
+                SystemVClassificationType eightByteClassification = (i == 0) ? descriptor.eightByteClassifications0 : descriptor.eightByteClassifications1;
 
                 if (eightByteClassification != SystemVClassificationType.SystemVClassificationTypeSSE)
                 {
                     if ((eightByteClassification == SystemVClassificationType.SystemVClassificationTypeIntegerReference) ||
                         (eightByteClassification == SystemVClassificationType.SystemVClassificationTypeIntegerByRef))
                     {
-                        int eightByteSize = field.FieldType.GetElementSize().AsInt;
+                        Debug.Assert(eightByteSize == 8);
                         Debug.Assert((genRegDest & 7) == 0);
 
-                        CORCOMPILE_GCREFMAP_TOKENS token;
-                        if (eightByteClassification == SystemVClassificationType.SystemVClassificationTypeIntegerByRef)
-                        {
-                            token = CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_INTERIOR;
-                        }
-                        else
-                        {
-                            token = CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_REF;
-                        }
-                        int eightByteIndex = (genRegDest + field.Offset.AsInt) >> 3;
+                        CORCOMPILE_GCREFMAP_TOKENS token = (eightByteClassification == SystemVClassificationType.SystemVClassificationTypeIntegerByRef) ? CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_INTERIOR : CORCOMPILE_GCREFMAP_TOKENS.GCREFMAP_REF;
+                        int eightByteIndex = (genRegDest >> 3) + i;
                         frame[delta + eightByteIndex] = token;
                     }
+
+                    genRegDest += eightByteSize;
                 }
             }
         }
@@ -506,6 +455,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         private bool _skipFirstArg;
         private bool _extraObjectFirstArg;
         private CallingConventions _interpreterCallingConvention;
+        private bool _hasArgLocDescForStructInRegs;
+        private ArgLocDesc _argLocDescForStructInRegs;
 
         public bool HasThis => _hasThis;
         public bool IsVarArg => _argData.IsVarArg();
@@ -949,7 +900,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                 case TargetArchitecture.X64:
                     if (_transitionBlock.IsX64UnixABI)
                     {
+                        _hasArgLocDescForStructInRegs = false;
+                        _fX64UnixArgInRegisters = true;
                         int cFPRegs = 0;
+                        int cGenRegs = 0;
 
                         switch (argType)
                         {
@@ -965,12 +919,55 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                                 break;
 
                             case CorElementType.ELEMENT_TYPE_VALUETYPE:
+                            {
+                                SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR descriptor;
+                                SystemVStructClassificator.GetSystemVAmd64PassStructInRegisterDescriptor(_argTypeHandle.GetRuntimeTypeHandle(), out descriptor);
+
+                                if (descriptor.passedInRegisters)
                                 {
-                                    // UNIXTODO: FEATURE_UNIX_AMD64_STRUCT_PASSING: Passing of structs, HFAs. For now, use the Windows convention.
-                                    argSize = _transitionBlock.PointerSize;
-                                    break;
+                                    cGenRegs = 0;
+                                    for (int i = 0; i < descriptor.eightByteCount; i++)
+                                    {
+                                        switch ((i == 0) ? descriptor.eightByteClassifications0 : descriptor.eightByteClassifications1)
+                                        {
+                                            case SystemVClassificationType.SystemVClassificationTypeInteger:
+                                            case SystemVClassificationType.SystemVClassificationTypeIntegerReference:
+                                            case SystemVClassificationType.SystemVClassificationTypeIntegerByRef:
+                                                cGenRegs++;
+                                                break;
+                                            case SystemVClassificationType.SystemVClassificationTypeSSE:
+                                                cFPRegs++;
+                                                break;
+                                            default:
+                                                Debug.Assert(false);
+                                                break;
+                                        }
+                                    }
+
+                                    // Check if we have enough registers available for the struct passing
+                                    if ((cFPRegs + _x64UnixIdxFPReg <= TransitionBlock.X64UnixTransitionBlock.NUM_FLOAT_ARGUMENT_REGISTERS) && (cGenRegs + _x64UnixIdxGenReg) <= _transitionBlock.NumArgumentRegisters)
+                                    {
+                                        _argLocDescForStructInRegs = new ArgLocDesc();
+                                        _argLocDescForStructInRegs.m_cGenReg = (short)cGenRegs;
+                                        _argLocDescForStructInRegs.m_cFloatReg = cFPRegs;
+                                        _argLocDescForStructInRegs.m_idxGenReg = _x64UnixIdxGenReg;
+                                        _argLocDescForStructInRegs.m_idxFloatReg = _x64UnixIdxFPReg;
+
+                                        _hasArgLocDescForStructInRegs = true;
+
+                                        _x64UnixIdxGenReg += cGenRegs;
+                                        _x64UnixIdxFPReg += cFPRegs;
+
+                                        return TransitionBlock.StructInRegsOffset;
+                                    }
                                 }
 
+                                // Set the register counts to indicate that this argument will not be passed in registers
+                                cFPRegs = 0;
+                                cGenRegs = 0;
+                                break;
+                            }
+
                             default:
                                 break;
                         }
@@ -980,7 +977,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
 
                         if (cFPRegs > 0)
                         {
-                            if (cFPRegs + _x64UnixIdxFPReg <= 8)
+                            if (cFPRegs + _x64UnixIdxFPReg <= TransitionBlock.X64UnixTransitionBlock.NUM_FLOAT_ARGUMENT_REGISTERS)
                             {
                                 int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _x64UnixIdxFPReg * 8;
                                 _x64UnixIdxFPReg += cFPRegs;
@@ -989,7 +986,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         }
                         else
                         {
-                            if (_x64UnixIdxGenReg + cArgSlots <= 6)
+                            if (_x64UnixIdxGenReg + cArgSlots <= _transitionBlock.NumArgumentRegisters)
                             {
                                 int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _x64UnixIdxGenReg * 8;
                                 _x64UnixIdxGenReg += cArgSlots;
@@ -997,6 +994,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                             }
                         }
 
+                        _fX64UnixArgInRegisters = false;
+
                         argOfs = _transitionBlock.OffsetOfArgs + _x64UnixIdxStack * 8;
                         _x64UnixIdxStack += cArgSlots;
                         return argOfs;
@@ -1408,9 +1407,21 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
 
                     if (_transitionBlock.IsX64)
                     {
-                        // All stack arguments take just one stack slot on AMD64 because of arguments bigger 
-                        // than a stack slot are passed by reference. 
-                        stackElemSize = _transitionBlock.StackElemSize();
+                        if (_transitionBlock.IsX64UnixABI)
+                        {
+                            if (_fX64UnixArgInRegisters)
+                            {
+                                continue;
+                            }
+
+                            stackElemSize = _transitionBlock.StackElemSize(GetArgSize());
+                        }
+                        else
+                        {
+                            // All stack arguments take just one stack slot on AMD64 because of arguments bigger 
+                            // than a stack slot are passed by reference. 
+                            stackElemSize = _transitionBlock.StackElemSize();
+                        }
                     }
                     else
                     {
@@ -1546,7 +1557,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                 case TargetArchitecture.X64:
                     if (_transitionBlock.IsX64UnixABI)
                     {
-                        //        LIMITED_METHOD_CONTRACT;
+                        if (_hasArgLocDescForStructInRegs)
+                        {
+                            return _argLocDescForStructInRegs;
+                        }
 
                         if (argOffset == TransitionBlock.StructInRegsOffset)
                         {
@@ -1562,24 +1576,23 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
                         {
                             // Dividing by 8 as size of each register in FloatArgumentRegisters is 8 bytes.
                             pLoc.m_idxFloatReg = (argOffset - _transitionBlock.OffsetOfFloatArgumentRegisters) / 8;
-
-                            // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention.
                             pLoc.m_cFloatReg = 1;
-                            return pLoc;
                         }
-
-                        // UNIXTODO: Passing of structs, HFAs. For now, use the Windows convention.
-                        int cSlots = 1;
-
-                        if (!_transitionBlock.IsStackArgumentOffset(argOffset))
+                        else if (!_transitionBlock.IsStackArgumentOffset(argOffset))
                         {
                             pLoc.m_idxGenReg = _transitionBlock.GetArgumentIndexFromOffset(argOffset);
-                            pLoc.m_cGenReg = (short)cSlots;
+                            pLoc.m_cGenReg = 1;
                         }
                         else
                         {
                             pLoc.m_idxStack = (argOffset - _transitionBlock.OffsetOfArgs) / 8;
-                            pLoc.m_cStack = cSlots;
+                            int argOnStackSize;
+                            int stackElemSize = _transitionBlock.StackElemSize();
+                            if (IsArgPassedByRef())
+                                argOnStackSize = stackElemSize;
+                            else
+                                argOnStackSize = GetArgSize();
+                            pLoc.m_cStack = (argOnStackSize + stackElemSize - 1) / stackElemSize;
                         }
                         return pLoc;
                     }
@@ -1613,6 +1626,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         private int _x64UnixIdxGenReg;
         private int _x64UnixIdxStack;
         private int _x64UnixIdxFPReg;
+        private bool _fX64UnixArgInRegisters;
         private int _x64WindowsCurOfs;           // Current position of the stack iterator
 
         private int _armIdxGenReg;        // Next general register to be assigned a value
index da49be8..9c7ef19 100644 (file)
@@ -269,8 +269,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
             // ReportPointersFromValueTypeArg
             if (argDest.IsStructPassedInRegs())
             {
-                // ReportPointersFromStructPassedInRegs
-                throw new NotImplementedException();
+                argDest.ReportPointersFromStructInRegisters(type, delta, frame);
+                return;
             }
             // ReportPointersFromValueType
             if (type.IsByRefLike)
index ab5a633..dac2a93 100644 (file)
@@ -361,7 +361,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
         /// <summary>
         /// X64 properties common to Windows and Unix ABI.
         /// </summary>
-        private abstract class X64TransitionBlock : TransitionBlock
+        internal abstract class X64TransitionBlock : TransitionBlock
         {
             public override TargetArchitecture Architecture => TargetArchitecture.X64;
             public override int PointerSize => 8;
@@ -398,10 +398,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
             public override int EnregisteredReturnTypeIntegerMaxSize => 8;
         }
 
-        private sealed class X64UnixTransitionBlock : X64TransitionBlock
+        internal sealed class X64UnixTransitionBlock : X64TransitionBlock
         {
             public static readonly TransitionBlock Instance = new X64UnixTransitionBlock();
 
+            public override bool IsX64UnixABI => true;
+
             public const int NUM_FLOAT_ARGUMENT_REGISTERS = 8;
 
             // RDI, RSI, RDX, RCX, R8, R9
@@ -414,6 +416,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
             public override int OffsetOfFloatArgumentRegisters => SizeOfM128A * NUM_FLOAT_ARGUMENT_REGISTERS;
             public override int EnregisteredParamTypeMaxSize => 16;
             public override int EnregisteredReturnTypeIntegerMaxSize => 16;
+            public override bool IsArgPassedByRef(TypeHandle th) => false;
         }
 
         private sealed class Arm32TransitionBlock : TransitionBlock