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
33 changes: 29 additions & 4 deletions src/coreclr/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,13 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un
unsigned cSlotsToEnregister = cSlots;

#if defined(TARGET_ARM64)
if (!compMacOsArm64Abi())
{
if (!info.compIsVarArgs && (cSlots == 2) && varTypeIsStruct(argType) && varDscInfo->canEnreg(argType, cSlots) && hfaType == TYP_UNDEF && info.compCompHnd->getClassAlignmentRequirement(varDsc->GetStructHnd()) == 16)
{
compArgSize += varDscInfo->alignReg(argType, 2) * REGSIZE_BYTES;
}
}

if (compFeatureArgSplit())
{
Expand Down Expand Up @@ -1228,11 +1235,21 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un
unsigned argAlignment = cAlign * TARGET_POINTER_SIZE;
#else
unsigned argAlignment = eeGetArgSizeAlignment(origArgType, (hfaType == TYP_FLOAT));

#ifdef TARGET_ARM64
if (!info.compIsVarArgs && varDscInfo->stackArgSize == 16 && !varTypeUsesFloatReg(argType) && varTypeIsStruct(argType) && info.compCompHnd->getClassAlignmentRequirement(varDsc->GetStructHnd()) == 16)
{
// TODO-Cleanup: use "eeGetArgSizeAlignment" here. See also: https://github.com/dotnet/runtime/issues/46026.
argAlignment = 16;
}
#endif // TARGET_ARM64

// We expect the following rounding operation to be a noop on all
// ABIs except ARM (where we have 8-byte aligned args) and macOS
// ARM64 (that allows to pack multiple smaller parameters in a
// single stack slot).
assert(compMacOsArm64Abi() || ((varDscInfo->stackArgSize % argAlignment) == 0));
// single stack slot), and ARM64 128 bit layout structures passed by value
// that would naturally be passed in integer registers if there was space
assert(compMacOsArm64Abi() || (argAlignment == 16) || ((varDscInfo->stackArgSize % argAlignment) == 0));
#endif
varDscInfo->stackArgSize = roundUp(varDscInfo->stackArgSize, argAlignment);

Expand Down Expand Up @@ -6317,11 +6334,19 @@ int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum,
}
#endif // TARGET_ARM
const bool isFloatHfa = (varDsc->lvIsHfa() && (varDsc->GetHfaType() == TYP_FLOAT));
const unsigned argAlignment = eeGetArgSizeAlignment(varDsc->lvType, isFloatHfa);
if (compMacOsArm64Abi())
unsigned argAlignment = eeGetArgSizeAlignment(varDsc->lvType, isFloatHfa);

#if TARGET_ARM64
if (!info.compIsVarArgs && varDsc->lvType == TYP_STRUCT && varDsc->lvSize() == 16 && !isFloatHfa && info.compCompHnd->getClassAlignmentRequirement(varDsc->GetStructHnd()) == 16)
{
// TODO-Cleanup: use "eeGetArgSizeAlignment" here. See also: https://github.com/dotnet/runtime/issues/46026.
argAlignment = 16;
}
if (compMacOsArm64Abi() || (argAlignment == 16))
{
argOffs = roundUp(argOffs, argAlignment);
}
#endif

assert((argSize % argAlignment) == 0);
assert((argOffs % argAlignment) == 0);
Expand Down
22 changes: 22 additions & 0 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2510,6 +2510,28 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call
}
else
{
if (!callIsVararg && size == 2)
{
assert(varTypeIsStruct(argSigType));
if (comp->info.compCompHnd->getClassAlignmentRequirement(argSigClass) == 16)
{
// Implement rule C.11 from the ARM64 Procedure calling standard.
argAlignBytes = 16;

// Implement rule C.10 from the ARM64 Procedure Calling standard.
// If the argument has an alignment of 16 then the NGRN is rounded up to the next even number.
//
// This rule is not used for Apple platforms. See https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
if (!compMacOsArm64Abi())
{
if ((intArgRegNum & 1) && size == 2)
{
intArgRegNum++;
}
}
}
}

// Check if the last register needed is still in the int argument register range.
isRegArg = (intArgRegNum + (size - 1)) < maxRegArgs;

Expand Down
6 changes: 4 additions & 2 deletions src/coreclr/jit/register_arg_convention.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ bool InitVarDscInfo::enoughAvailRegs(var_types type, unsigned numRegs /* = 1 */)
return regArgNum(type) + numRegs - backFillCount <= maxRegArgNum(type);
}

#ifdef TARGET_ARM
#if defined(TARGET_ARM) || defined(TARGET_ARM64)
unsigned InitVarDscInfo::alignReg(var_types type, unsigned requiredRegAlignment)
{
assert(requiredRegAlignment > 0);
Expand All @@ -91,18 +91,20 @@ unsigned InitVarDscInfo::alignReg(var_types type, unsigned requiredRegAlignment)
unsigned cAlignSkipped = requiredRegAlignment - alignMask;
assert(cAlignSkipped == 1); // Alignment is currently only 1 or 2, so misalignment can only be 1.

#ifdef TARGET_ARM
if (varTypeIsFloating(type))
{
fltArgSkippedRegMask |= genMapFloatRegArgNumToRegMask(floatRegArgNum);
}
#endif

assert(regArgNum(type) + cAlignSkipped <= maxRegArgNum(type)); // if equal, then we aligned the last slot, and the
// arg can't be enregistered
regArgNum(type) += cAlignSkipped;

return cAlignSkipped;
}
#endif // TARGET_ARM
#endif // TARGET_ARM || TARGET_ARM64

bool InitVarDscInfo::canEnreg(var_types type, unsigned numRegs /* = 1 */)
{
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/register_arg_convention.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ struct InitVarDscInfo
// Returns the first argument register of the allocated set.
unsigned allocRegArg(var_types type, unsigned numRegs = 1);

#ifdef TARGET_ARM
#if defined(TARGET_ARM) || defined(TARGET_ARM64)
// We are aligning the register to an ABI-required boundary, such as putting
// double-precision floats in even-numbered registers, by skipping one register.
// "requiredRegAlignment" is the amount to align to: 1 for no alignment (everything
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defTyp

ComputedInstanceFieldLayout layoutFromMetadata = _fallbackAlgorithm.ComputeInstanceLayout(defType, layoutKind);

if (defType.Context.Target.IsWindows || (defType.Context.Target.PointerSize == 4))
if (defType.Context.Target.Architecture != TargetArchitecture.ARM64 && defType.Context.Target.IsWindows || (defType.Context.Target.PointerSize == 4))
{
return layoutFromMetadata;
}
Expand Down Expand Up @@ -72,7 +72,7 @@ public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristi
public static bool IsIntegerType(DefType type)
{
return type.IsIntrinsic
&& type.Namespace == "System."
&& type.Namespace == "System"
&& ((type.Name == "Int128") || (type.Name == "UInt128"));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type,

if (IsByValueClass(fieldType))
{
// Valuetypes which are not primitives or enums
instanceValueClassFieldCount++;
}
else if (fieldType.IsGCPointer)
Expand Down Expand Up @@ -520,25 +521,47 @@ protected ComputedInstanceFieldLayout ComputeAutoFieldLayout(MetadataType type,
if (!fieldLayoutAbiStable)
layoutAbiStable = false;

largestAlignmentRequired = LayoutInt.Max(fieldSizeAndAlignment.Alignment, largestAlignmentRequired);

if (IsByValueClass(fieldType))
{
// This block handles valuetypes which are not primitives or enums, it only has a meaningful effect, if the
// type has an alignment greater than pointer size.
largestAlignmentRequired = LayoutInt.Max(fieldSizeAndAlignment.Alignment, largestAlignmentRequired);
instanceValueClassFieldsArr[instanceValueClassFieldCount++] = field;
}
else if (fieldType.IsGCPointer)
{
instanceGCPointerFieldsArr[instanceGCPointerFieldsCount++] = field;
}
else
{
int log2size = CalculateLog2(fieldSizeAndAlignment.Size.AsInt);
instanceNonGCPointerFieldsArr[log2size][instanceNonGCPointerFieldsCount[log2size]++] = field;
// non-value-type (and primitive type) fields will add an alignment requirement of pointer size
// This alignment requirement will not be significant in the final alignment calculation unlesss the
// type is greater than the size of a single pointer.
//
// This does not account for types that are marked IsAlign8Candidate due to 8-byte fields
// but that is explicitly handled when we calculate the final alignment for the type.

// This behavior is extremely strange for primitive types, as it makes a struct with a single byte in it
// have 8 byte alignment, but that is the current implementation.

largestAlignmentRequired = LayoutInt.Max(new LayoutInt(context.Target.PointerSize), largestAlignmentRequired);

if (fieldType.IsGCPointer)
{
instanceGCPointerFieldsArr[instanceGCPointerFieldsCount++] = field;
}
else
{
Debug.Assert(fieldType.IsPrimitive || fieldType.IsPointer || fieldType.IsFunctionPointer || fieldType.IsEnum || fieldType.IsByRef);
int log2size = CalculateLog2(fieldSizeAndAlignment.Size.AsInt);
instanceNonGCPointerFieldsArr[log2size][instanceNonGCPointerFieldsCount[log2size]++] = field;

if (fieldType.IsPrimitive || fieldType.IsEnum)
{
// Handle alignment of long/ulong/double on ARM32
largestAlignmentRequired = LayoutInt.Max(context.Target.GetObjectAlignment(fieldSizeAndAlignment.Size), largestAlignmentRequired);
}
}
}
}

largestAlignmentRequired = context.Target.GetObjectAlignment(largestAlignmentRequired);
bool requiresAlign8 = !largestAlignmentRequired.IsIndeterminate && largestAlignmentRequired.AsInt > 4;
bool requiresAlign8 = !largestAlignmentRequired.IsIndeterminate && context.Target.PointerSize == 4 && context.Target.GetObjectAlignment(largestAlignmentRequired).AsInt > 4 && context.Target.PointerSize == 4;

// For types inheriting from another type, field offsets continue on from where they left off
// Base alignment is not always required, it's only applied when there's a version bubble boundary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,16 @@ public int GetNextOffset()
Debug.Assert(_transitionBlock.IsAppleArm64ABI || (cbArg % _transitionBlock.PointerSize) == 0);

int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize;

if (!IsVarArg && !_transitionBlock.IsAppleArm64ABI && ((_arm64IdxGenReg & 1) == 1) && regSlots == 2 && ((DefType)_argTypeHandle.GetRuntimeTypeHandle()).InstanceFieldAlignment == new LayoutInt(16))
{
// Implement rule C.10 from the ARM64 Procedure Calling standard.
// If the argument has an alignment of 16 then the NGRN is rounded up to the next even number.
//
// This rule is not used for Apple platforms. See https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms
_arm64IdxGenReg++;
}

// Only x0-x7 are valid argument registers (x8 is always the return buffer)
if (_arm64IdxGenReg + regSlots <= 8)
{
Expand Down Expand Up @@ -1288,9 +1298,9 @@ public int GetNextOffset()
}
}

int alignment = 1;
if (_transitionBlock.IsAppleArm64ABI)
{
int alignment;
if (!isValueType)
{
Debug.Assert((cbArg & (cbArg - 1)) == 0);
Expand All @@ -1304,9 +1314,16 @@ public int GetNextOffset()
{
alignment = 8;
}
_arm64OfsStack = ALIGN_UP(_arm64OfsStack, alignment);
}

if (!IsVarArg && cbArg == 16 && ((DefType)_argTypeHandle.GetRuntimeTypeHandle()).InstanceFieldAlignment == new LayoutInt(16))
{
// Implement rule C.11 from the ARM64 Procedure Calling standard.
alignment = 16;
}

_arm64OfsStack = ALIGN_UP(_arm64OfsStack, alignment);

argOfs = _transitionBlock.OffsetOfArgs + _arm64OfsStack;
_arm64OfsStack += cbArg;
return argOfs;
Expand Down
Loading