From d4245d132056f17ff0e7a090e2b0f68a892201f2 Mon Sep 17 00:00:00 2001 From: jashook Date: Mon, 10 Apr 2017 16:14:04 -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 | 41 ++++ src/vm/callingconvention.h | 29 ++- src/vm/object.cpp | 7 + src/vm/reflectioninvocation.cpp | 10 + tests/arm64/Tests.lst | 2 +- .../coreclr/GitHub_7685/test7685.cs | 191 +++++++++++++++++- 6 files changed, 263 insertions(+), 17 deletions(-) diff --git a/src/vm/argdestination.h b/src/vm/argdestination.h index 8a3fb4fdf2d2..f8d7653a2365 100644 --- a/src/vm/argdestination.h +++ b/src/vm/argdestination.h @@ -30,6 +30,9 @@ class ArgDestination 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,44 @@ class ArgDestination return dac_cast(dac_cast(m_base) + m_offset); } +#if defined(_TARGET_ARM64_) + + // 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 // 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 cde2ba465a1e..9c56de3eb1d3 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 @@ -556,7 +564,10 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE 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 { @@ -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; diff --git a/src/vm/object.cpp b/src/vm/object.cpp index 1725ef7db43b..9d1ff969eabe 100644 --- a/src/vm/object.cpp +++ b/src/vm/object.cpp @@ -1592,6 +1592,13 @@ void STDCALL CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, Meth return; } +#elif defined(_TARGET_ARM64_) + + if (argDest->IsHFA()) + { + argDest->CopyHFAStructToRegister(src, pMT->GetAlignedNumInstanceFieldBytes()); + } + #endif // UNIX_AMD64_ABI && FEATURE_UNIX_AMD64_STRUCT_PASSING // destOffset is only valid for Nullable passed in registers _ASSERTE(destOffset == 0); diff --git a/src/vm/reflectioninvocation.cpp b/src/vm/reflectioninvocation.cpp index 626e872255c3..a2b8c4aefc3b 100644 --- a/src/vm/reflectioninvocation.cpp +++ b/src/vm/reflectioninvocation.cpp @@ -1383,8 +1383,18 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, } #endif +#ifdef defined(_TARGET_ARM64_) + ArgLocDesc argLocDescForStructInRegs; + argit.GetArgLoc(ofs, &argLocDescForStructInRegs); + + ArgDestination argDest(pTransitionBlock, ofs, &argLocDescForStructInRegs); + +#else // !_TARGET_ARM64_ + ArgDestination argDest(pTransitionBlock, ofs, argit.GetArgLocDescForStructInRegs()); +#endif // defined(_TARGET_ARM64_) + if(needsStackCopy) { MethodTable * pMT = th.GetMethodTable(); diff --git a/tests/arm64/Tests.lst b/tests/arm64/Tests.lst index ee641ef23f5c..53801cb072d4 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 a1d35bc11c9f..77b25dc59cb5 100644 --- a/tests/src/Regressions/coreclr/GitHub_7685/test7685.cs +++ b/tests/src/Regressions/coreclr/GitHub_7685/test7685.cs @@ -6,28 +6,100 @@ 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 static bool Equals(ref RectangleF r1, ref RectangleF r2) 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