Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/vm/argdestination.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -42,6 +45,44 @@ class ArgDestination
return dac_cast<PTR_VOID>(dac_cast<TADDR>(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.
Expand Down
29 changes: 21 additions & 8 deletions src/vm/callingconvention.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -1103,7 +1114,9 @@ int ArgIteratorTemplate<ARGITERATOR_BASE>::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;
Expand Down
7 changes: 7 additions & 0 deletions src/vm/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> passed in registers
_ASSERTE(destOffset == 0);
Expand Down
10 changes: 10 additions & 0 deletions src/vm/reflectioninvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion tests/arm64/Tests.lst
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
191 changes: 183 additions & 8 deletions tests/src/Regressions/coreclr/GitHub_7685/test7685.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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}]";
}