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