-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix passing too small args for PUTARG_STK on macOS arm64 ABI #68108
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -738,17 +738,6 @@ void CodeGen::genIntrinsic(GenTree* treeNode) | |||||||
void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) | ||||||||
{ | ||||||||
assert(treeNode->OperIs(GT_PUTARG_STK)); | ||||||||
GenTree* source = treeNode->gtOp1; | ||||||||
var_types targetType; | ||||||||
|
||||||||
if (!compMacOsArm64Abi()) | ||||||||
{ | ||||||||
targetType = genActualType(source->TypeGet()); | ||||||||
} | ||||||||
else | ||||||||
{ | ||||||||
targetType = source->TypeGet(); | ||||||||
} | ||||||||
emitter* emit = GetEmitter(); | ||||||||
|
||||||||
// This is the varNum for our store operations, | ||||||||
|
@@ -792,11 +781,13 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) | |||||||
argOffsetMax = compiler->lvaOutgoingArgSpaceSize; | ||||||||
} | ||||||||
|
||||||||
bool isStruct = (targetType == TYP_STRUCT) || (source->OperGet() == GT_FIELD_LIST); | ||||||||
GenTree* source = treeNode->gtGetOp1(); | ||||||||
|
||||||||
bool isStruct = source->TypeIs(TYP_STRUCT) || (source->OperGet() == GT_FIELD_LIST); | ||||||||
|
||||||||
if (!isStruct) // a normal non-Struct argument | ||||||||
{ | ||||||||
if (varTypeIsSIMD(targetType)) | ||||||||
if (varTypeIsSIMD(source->TypeGet())) | ||||||||
{ | ||||||||
assert(!source->isContained()); | ||||||||
|
||||||||
|
@@ -815,32 +806,35 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) | |||||||
else | ||||||||
#endif // TARGET_ARM64 | ||||||||
{ | ||||||||
emitAttr storeAttr = emitTypeSize(targetType); | ||||||||
emitAttr storeAttr = emitTypeSize(source->TypeGet()); | ||||||||
emit->emitIns_S_R(INS_str, storeAttr, srcReg, varNumOut, argOffsetOut); | ||||||||
argOffsetOut += EA_SIZE_IN_BYTES(storeAttr); | ||||||||
} | ||||||||
assert(argOffsetOut <= argOffsetMax); // We can't write beyond the outgoing arg area | ||||||||
return; | ||||||||
} | ||||||||
|
||||||||
var_types slotType = genActualType(source); | ||||||||
if (compMacOsArm64Abi()) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jakobbotsch, just for my own education; is it specific to mac ABI? i.e. if we apply the same classification to linux and windows arm64, it will break putarg on those targets? This TODO is probably related: runtime/src/coreclr/jit/morph.cpp Lines 736 to 738 in d682d68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. macOS arm64 ABI is different in that multiple small arguments can be packed into the same stack slot. It means we have different behavior in some places in the JIT, and that different behavior was wrong here. |
||||||||
{ | ||||||||
// Small typed args do not get their own full stack slots, so make | ||||||||
// sure we do not overwrite adjacent arguments. | ||||||||
switch (treeNode->GetStackByteSize()) | ||||||||
{ | ||||||||
case 1: | ||||||||
targetType = TYP_BYTE; | ||||||||
slotType = TYP_BYTE; | ||||||||
break; | ||||||||
case 2: | ||||||||
targetType = TYP_SHORT; | ||||||||
slotType = TYP_SHORT; | ||||||||
break; | ||||||||
default: | ||||||||
assert(treeNode->GetStackByteSize() >= 4); | ||||||||
break; | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
instruction storeIns = ins_Store(targetType); | ||||||||
emitAttr storeAttr = emitTypeSize(targetType); | ||||||||
instruction storeIns = ins_Store(slotType); | ||||||||
emitAttr storeAttr = emitTypeSize(slotType); | ||||||||
|
||||||||
// If it is contained then source must be the integer constant zero | ||||||||
if (source->isContained()) | ||||||||
|
@@ -860,7 +854,7 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode) | |||||||
genConsumeReg(source); | ||||||||
emit->emitIns_S_R(storeIns, storeAttr, source->GetRegNum(), varNumOut, argOffsetOut); | ||||||||
#ifdef TARGET_ARM | ||||||||
if (targetType == TYP_LONG) | ||||||||
if (source->TypeIs(TYP_LONG)) | ||||||||
{ | ||||||||
// This case currently only occurs for double types that are passed as TYP_LONG; | ||||||||
// actual long types would have been decomposed by now. | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Runtime.CompilerServices; | ||
|
||
public class Runtime_66720 | ||
{ | ||
public static int Main() | ||
{ | ||
return Test(0); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static int Test(in short zero) | ||
{ | ||
// Fill arg stack slot with all ones | ||
LastArg(0, 0, 0, 0, 0, 0, 0, 0, -1); | ||
// Bug was that the last arg passed here would write only 16 bits | ||
// instead of 32 bits | ||
int last = LastArg(0, 0, 0, 0, 0, 0, 0, 0, zero); | ||
return last == 0 ? 100 : -1; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.NoInlining)] | ||
private static int LastArg(int a, int b, int c, int d, int e, int f, int g, int h, int i) | ||
{ | ||
return i; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<Optimize>True</Optimize> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Compile Include="$(MSBuildProjectName).cs" /> | ||
</ItemGroup> | ||
</Project> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do I understand this change correctly that now we have such slot types:
1byte - TYP_BYTE
2bytes - TYP_SHORT
4bytes - TYP_LONG (was TYP_INT before the change).
8bytes - TYP_LONG
?
so if we pass two
TYP_INT
on the stack, in one 8byte stack slot, don't we override anything?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not totally sure I understand.
genActualType(TYP_INT)
returnsTYP_INT
, notTYP_LONG
.The difference before was that we used the real type of the operand to decide on how much to store on the stack. That's not right, a small-typed operand still produces a value of type
genActualType(operand)
and it is thePUTARG_STK
node that decides how much should be put on the stack. So we had a problem for e.g. aTYP_SHORT
indirection feeding into aTYP_INT
parameter.A
TYP_INT
indirection feeding into a 4 byte parameter will still only store 4 bytes on the stack.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks!