From aa0eee4ae0ea70620783e215c68a7cdeb9439cf4 Mon Sep 17 00:00:00 2001 From: jashook Date: Wed, 19 Apr 2017 10:03:13 -0700 Subject: [PATCH] For Reflection invoke, use ArgLocDesc For arm64 if the struct needs to be passed in registers, then pass that information along when we do the reflection invoke. Note this adds more test cases for passing different struct types via reflection. I am guessing similar work would need to be done for Arm32. --- src/vm/argdestination.h | 43 +++++ src/vm/callingconvention.h | 43 +++-- src/vm/object.cpp | 8 + tests/arm64/Tests.lst | 2 +- .../Regressions/coreclr/GitHub_7685/test7685.cs | 191 ++++++++++++++++++++- 5 files changed, 268 insertions(+), 19 deletions(-) diff --git a/src/vm/argdestination.h b/src/vm/argdestination.h index 8a3fb4f..8ab0a56 100644 --- a/src/vm/argdestination.h +++ b/src/vm/argdestination.h @@ -30,6 +30,9 @@ public: LIMITED_METHOD_CONTRACT; #if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) _ASSERTE((argLocDescForStructInRegs != NULL) || (offset != TransitionBlock::StructInRegsOffset)); +#elif defined(_TARGET_ARM64_) + // This assert is not interesting on arm64. argLocDescForStructInRegs could be + // initialized if the args are being enregistered. #else _ASSERTE(argLocDescForStructInRegs == NULL); #endif @@ -42,6 +45,46 @@ public: return dac_cast(dac_cast(m_base) + m_offset); } +#if defined(_TARGET_ARM64_) +#ifndef DACCESS_COMPILE + + // Returns true if the ArgDestination represents an HFA struct + bool IsHFA() + { + return m_argLocDescForStructInRegs != NULL; + } + + // Copy struct argument into registers described by the current ArgDestination. + // Arguments: + // src = source data of the structure + // fieldBytes - size of the structure + void CopyHFAStructToRegister(void *src, int fieldBytes) + { + // We are either copying either a float or double HFA and need to + // enregister each field. + + int floatRegCount = m_argLocDescForStructInRegs->m_cFloatReg; + bool typeFloat = m_argLocDescForStructInRegs->m_isSinglePrecision; + void* dest = this->GetDestinationAddress(); + + if (typeFloat) + { + for (int i = 0; i < floatRegCount; ++i) + { + // Copy 4 bytes on 8 bytes alignment + *((UINT64*)dest + i) = *((UINT32*)src + i); + } + } + else + { + // We can just do a memcpy. + memcpyNoGCRefs(dest, src, fieldBytes); + } + } + +#endif // !DACCESS_COMPILE +#endif // defined(_TARGET_ARM64_) + #if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) // Returns true if the ArgDestination represents a struct passed in registers. diff --git a/src/vm/callingconvention.h b/src/vm/callingconvention.h index cde2ba4..b707b4b 100644 --- a/src/vm/callingconvention.h +++ b/src/vm/callingconvention.h @@ -34,21 +34,26 @@ BOOL IsRetBuffPassedAsFirstArg(); // and possibly on to the stack as well. struct ArgLocDesc { - int m_idxFloatReg; // First floating point register used (or -1) - int m_cFloatReg; // Count of floating point registers used (or 0) + int m_idxFloatReg; // First floating point register used (or -1) + int m_cFloatReg; // Count of floating point registers used (or 0) - int m_idxGenReg; // First general register used (or -1) - int m_cGenReg; // Count of general registers used (or 0) + int m_idxGenReg; // First general register used (or -1) + int m_cGenReg; // Count of general registers used (or 0) - int m_idxStack; // First stack slot used (or -1) - int m_cStack; // Count of stack slots used (or 0) + int m_idxStack; // First stack slot used (or -1) + int m_cStack; // Count of stack slots used (or 0) #if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) - EEClass* m_eeClass; // For structs passed in register, it points to the EEClass of the struct + EEClass* m_eeClass; // For structs passed in register, it points to the EEClass of the struct #endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING +#if defined(_TARGET_ARM64_) + bool m_isSinglePrecision; // For determining if HFA is single or double + // precision +#endif // defined(_TARGET_ARM64_) + #if defined(_TARGET_ARM_) BOOL m_fRequires64BitAlignment; // True if the argument should always be aligned (in registers or on the stack #endif @@ -70,6 +75,9 @@ struct ArgLocDesc #if defined(_TARGET_ARM_) m_fRequires64BitAlignment = FALSE; #endif +#if defined(_TARGET_ARM64_) + m_isSinglePrecision = FALSE; +#endif // defined(_TARGET_ARM64_) #if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) m_eeClass = NULL; #endif @@ -490,7 +498,7 @@ public: ArgLocDesc* GetArgLocDescForStructInRegs() { -#if defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) +#if (defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)) || defined (_TARGET_ARM64_) return m_hasArgLocDescForStructInRegs ? &m_argLocDescForStructInRegs : NULL; #else return NULL; @@ -556,7 +564,10 @@ public: if (!m_argTypeHandle.IsNull() && m_argTypeHandle.IsHFA()) { CorElementType type = m_argTypeHandle.GetHFAType(); - pLoc->m_cFloatReg = (type == ELEMENT_TYPE_R4)? GetArgSize()/sizeof(float): GetArgSize()/sizeof(double); + bool isFloatType = (type == ELEMENT_TYPE_R4); + + pLoc->m_cFloatReg = isFloatType ? GetArgSize()/sizeof(float): GetArgSize()/sizeof(double); + pLoc->m_isSinglePrecision = isFloatType; } else { @@ -639,7 +650,7 @@ protected: CorElementType m_argType; int m_argSize; TypeHandle m_argTypeHandle; -#if defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) +#if (defined(_TARGET_AMD64_) && defined(UNIX_AMD64_ABI) && defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)) || defined(_TARGET_ARM64_) ArgLocDesc m_argLocDescForStructInRegs; bool m_hasArgLocDescForStructInRegs; #endif // _TARGET_AMD64_ && UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING @@ -1103,7 +1114,9 @@ int ArgIteratorTemplate::GetNextOffset() // Handle HFAs: packed structures of 1-4 floats or doubles that are passed in FP argument // registers if possible. if (thValueType.IsHFA()) + { fFloatingPoint = true; + } #endif break; @@ -1255,7 +1268,17 @@ int ArgIteratorTemplate::GetNextOffset() if (thValueType.IsHFA()) { CorElementType type = thValueType.GetHFAType(); + bool isFloatType = (type == ELEMENT_TYPE_R4); + cFPRegs = (type == ELEMENT_TYPE_R4)? (argSize/sizeof(float)): (argSize/sizeof(double)); + + m_argLocDescForStructInRegs.Init(); + m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs; + m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; + + m_argLocDescForStructInRegs.m_isSinglePrecision = isFloatType; + + m_hasArgLocDescForStructInRegs = true; } else { diff --git a/src/vm/object.cpp b/src/vm/object.cpp index 1725ef7..3e3f6d1 100644 --- a/src/vm/object.cpp +++ b/src/vm/object.cpp @@ -1592,6 +1592,14 @@ void STDCALL CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, Meth return; } +#elif defined(_TARGET_ARM64_) + + if (argDest->IsHFA()) + { + argDest->CopyHFAStructToRegister(src, pMT->GetAlignedNumInstanceFieldBytes()); + return; + } + #endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING // destOffset is only valid for Nullable passed in registers _ASSERTE(destOffset == 0); diff --git a/tests/arm64/Tests.lst b/tests/arm64/Tests.lst index ee641ef..53801cb 100644 --- a/tests/arm64/Tests.lst +++ b/tests/arm64/Tests.lst @@ -76801,7 +76801,7 @@ RelativePath=Regressions\coreclr\GitHub_7685\Test7685\Test7685.cmd WorkingDir=Regressions\coreclr\GitHub_7685\Test7685 Expected=0 MaxAllowedDurationSeconds=600 -Categories=EXPECTED_FAIL;10107;NEW +Categories=EXPECTED_PASS;NEW HostStyle=0 [Generated921.cmd_9903] diff --git a/tests/src/Regressions/coreclr/GitHub_7685/test7685.cs b/tests/src/Regressions/coreclr/GitHub_7685/test7685.cs index a1d35bc..77b25dc 100644 --- a/tests/src/Regressions/coreclr/GitHub_7685/test7685.cs +++ b/tests/src/Regressions/coreclr/GitHub_7685/test7685.cs @@ -6,28 +6,100 @@ using System.Reflection; public class Test7685 { - static RectangleF argumentInDStuff; - + static RectangleF passedFloatStruct; + static RectangleD passedDoubleStruct; + static RectangleI passedIntStruct; + static RectangleLLarge passedLongLargeStruct; + static RectangleLSmall passedLongSmallStruct; + static RectangleNestedF passedNestedSmallFStruct; + public static int Main() { int iRetVal = 100; - var r = new RectangleF(1.2f, 3.4f, 5.6f, 7.8f); - typeof(Test7685).GetTypeInfo().GetDeclaredMethod("DoStuff").Invoke(null, new object[] { r }); + var rF = new RectangleF(1.2f, 3.4f, 5.6f, 7.8f); + var rD = new RectangleD(1.7E+3d, 4.5d, 500.1d, 60.0d); + var rI = new RectangleI(100, -2, 3, 64); + var rLSmall = new RectangleLSmall(11231L); + var rLLarge = new RectangleLLarge(1L, 20041L, 22L, 88L); + var rNestedFSmall = new RectangleNestedF(1.2f, 3.4f, 5.6f, 7.8f); + + typeof(Test7685).GetTypeInfo().GetDeclaredMethod("DoStuffF").Invoke(null, new object[] { rF }); + typeof(Test7685).GetTypeInfo().GetDeclaredMethod("DoStuffD").Invoke(null, new object[] { rD }); + typeof(Test7685).GetTypeInfo().GetDeclaredMethod("DoStuffI").Invoke(null, new object[] { rI }); + typeof(Test7685).GetTypeInfo().GetDeclaredMethod("DoStuffLSmall").Invoke(null, new object[] { rLSmall }); + typeof(Test7685).GetTypeInfo().GetDeclaredMethod("DoStuffLLarge").Invoke(null, new object[] { rLLarge }); + typeof(Test7685).GetTypeInfo().GetDeclaredMethod("DoStuffNestedF").Invoke(null, new object[] { rNestedFSmall }); - if (!RectangleF.Equals(ref argumentInDStuff, ref r)) + if (!RectangleF.Equals(ref passedFloatStruct, ref rF)) { - TestLibrary.Logging.WriteLine($"Error: passing struct with floats via reflection. Callee received {argumentInDStuff} instead of {r}"); + TestLibrary.Logging.WriteLine($"Error: passing struct with floats via reflection. Callee received {passedFloatStruct} instead of {rF}"); iRetVal = 0; } + + if (!RectangleD.Equals(ref passedDoubleStruct, ref rD)) + { + TestLibrary.Logging.WriteLine($"Error: passing struct with doubles via reflection. Callee received {passedDoubleStruct} instead of {rD}"); + iRetVal = 1; + } + + if (!RectangleI.Equals(ref passedIntStruct, ref rI)) + { + TestLibrary.Logging.WriteLine($"Error: passing struct with ints via reflection. Callee received {passedIntStruct} instead of {rI}"); + iRetVal = 2; + } + + if (!RectangleLSmall.Equals(ref passedLongSmallStruct, ref rLSmall)) + { + TestLibrary.Logging.WriteLine($"Error: passing struct with a long via reflection. Callee received {passedLongSmallStruct} instead of {rLSmall}"); + iRetVal = 3; + } + + if (!RectangleLLarge.Equals(ref passedLongLargeStruct, ref rLLarge)) + { + TestLibrary.Logging.WriteLine($"Error: passing struct with longs via reflection. Callee received {passedLongLargeStruct} instead of {rLLarge}"); + iRetVal = 4; + } + + if (!RectangleNestedF.Equals(ref passedNestedSmallFStruct, ref rNestedFSmall)) + { + TestLibrary.Logging.WriteLine($"Error: passing struct with longs via reflection. Callee received {passedNestedSmallFStruct} instead of {rNestedFSmall}"); + iRetVal = 5; + } return iRetVal; } - public static void DoStuff(RectangleF r) + public static void DoStuffF(RectangleF r) + { + passedFloatStruct = r; + } + + public static void DoStuffD(RectangleD r) + { + passedDoubleStruct = r; + } + + public static void DoStuffI(RectangleI r) + { + passedIntStruct = r; + } + + public static void DoStuffLSmall(RectangleLSmall r) { - argumentInDStuff = r; + passedLongSmallStruct = r; } + + public static void DoStuffLLarge(RectangleLLarge r) + { + passedLongLargeStruct = r; + } + + public static void DoStuffNestedF(RectangleNestedF r) + { + passedNestedSmallFStruct = r; + } + } public struct RectangleF @@ -46,3 +118,106 @@ public struct RectangleF public override string ToString() => $"[{_x}, {_y}, {_width}, {_height}]"; } + +public struct RectangleFSmall +{ + public float _x, _y; + + public RectangleFSmall(float x, float y) + { + _x = x; _y = y; + } + + public static bool Equals(ref RectangleFSmall r1, ref RectangleFSmall r2) + { + return (r2._x == r1._x) && (r2._y == r1._y); + } + + public override string ToString() => $"[{_x}, {_y}]"; +} + +public struct RectangleD +{ + private double _x, _y, _width, _height; + + public RectangleD(double x, double y, double width, double height) + { + _x = x; _y = y; _width = width; _height = height; + } + + public static bool Equals(ref RectangleD r1, ref RectangleD r2) + { + return (r2._x == r1._x) && (r2._y == r1._y) && (r2._width == r1._width) && (r2._height == r1._height); + } + + public override string ToString() => $"[{_x}, {_y}, {_width}, {_height}]"; +} + +public struct RectangleI +{ + private int _x, _y, _width, _height; + + public RectangleI(int x, int y, int width, int height) + { + _x = x; _y = y; _width = width; _height = height; + } + + public static bool Equals(ref RectangleI r1, ref RectangleI r2) + { + return (r2._x == r1._x) && (r2._y == r1._y) && (r2._width == r1._width) && (r2._height == r1._height); + } + + public override string ToString() => $"[{_x}, {_y}, {_width}, {_height}]"; +} + +public struct RectangleLSmall +{ + private long _x; + + public RectangleLSmall(long x) + { + _x = x; + } + + public static bool Equals(ref RectangleLSmall r1, ref RectangleLSmall r2) + { + return (r2._x == r1._x); + } + + public override string ToString() => $"[{_x}]"; +} + +public struct RectangleLLarge +{ + private long _x, _y, _width, _height; + + public RectangleLLarge(long x, long y, long width, long height) + { + _x = x; _y = y; _width = width; _height = height; + } + + public static bool Equals(ref RectangleLLarge r1, ref RectangleLLarge r2) + { + return (r2._x == r1._x) && (r2._y == r1._y) && (r2._width == r1._width) && (r2._height == r1._height); + } + + public override string ToString() => $"[{_x}, {_y}, {_width}, {_height}]"; +} + +public struct RectangleNestedF +{ + private RectangleFSmall first, second; + + public RectangleNestedF(float x, float y, float width, float height) + { + first = new RectangleFSmall(x, y); + second = new RectangleFSmall(width, height); + } + + public static bool Equals(ref RectangleNestedF r1, ref RectangleNestedF r2) + { + return (r1.first._x == r2.first._x) && (r1.first._y == r2.first._y) && (r1.second._x == r2.second._x) && (r1.second._y == r2.second._y); + } + + public override string ToString() => $"[{first._x}, {first._y}, {second._x}, {second._y}]"; +} \ No newline at end of file -- 2.7.4