Skip to content

Commit 486b4d1

Browse files
Use the signature's struct handle for making outgoing arg copies (#69971)
* Do not depend on class handle in "fgMorphArgs" Use the signature's handle when making an outgoing arg copy. * Add a test
1 parent e5746e9 commit 486b4d1

File tree

4 files changed

+79
-33
lines changed

4 files changed

+79
-33
lines changed

src/coreclr/jit/compiler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5616,7 +5616,7 @@ class Compiler
56165616
GenTreeFieldList* fgMorphLclArgToFieldlist(GenTreeLclVarCommon* lcl);
56175617
GenTreeCall* fgMorphArgs(GenTreeCall* call);
56185618

5619-
void fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg, CORINFO_CLASS_HANDLE copyBlkClass);
5619+
void fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg);
56205620

56215621
GenTree* fgMorphLocalVar(GenTree* tree, bool forceRemorph);
56225622

src/coreclr/jit/morph.cpp

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3112,8 +3112,6 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
31123112
}
31133113
}
31143114

3115-
CORINFO_CLASS_HANDLE copyBlkClass = NO_CLASS_HANDLE;
3116-
31173115
// TODO-ARGS: Review this, is it really necessary to treat them specially here?
31183116
if (call->gtArgs.IsNonStandard(this, call, &arg) && arg.AbiInfo.IsPassedInRegisters())
31193117
{
@@ -3138,41 +3136,40 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
31383136
// Struct arguments may be morphed into a node that is not a struct type.
31393137
// In such case the CallArgABIInformation keeps track of whether the original node (before morphing)
31403138
// was a struct and the struct classification.
3141-
bool isStructArg = arg.AbiInfo.IsStruct;
3139+
bool isStructArg = arg.AbiInfo.IsStruct;
3140+
GenTree* argObj = argx->gtEffectiveVal(true /*commaOnly*/);
3141+
bool makeOutArgCopy = false;
31423142

3143-
GenTree* argObj = argx->gtEffectiveVal(true /*commaOnly*/);
31443143
if (isStructArg && varTypeIsStruct(argObj) && !argObj->OperIs(GT_ASG, GT_MKREFANY, GT_FIELD_LIST))
31453144
{
3146-
CORINFO_CLASS_HANDLE objClass = gtGetStructHandle(argObj);
3147-
unsigned originalSize;
3145+
unsigned originalSize;
31483146
if (argObj->TypeGet() == TYP_STRUCT)
31493147
{
31503148
if (argObj->OperIs(GT_OBJ))
31513149
{
3152-
// Get the size off the OBJ node.
3153-
originalSize = argObj->AsObj()->GetLayout()->GetSize();
3154-
assert(originalSize == info.compCompHnd->getClassSize(objClass));
3150+
originalSize = argObj->AsObj()->Size();
31553151
}
31563152
else
31573153
{
3158-
// We have a BADCODE assert for this in AddFinalArgsAndDetermineABIInfo.
3159-
assert(argObj->OperIs(GT_LCL_VAR));
3160-
originalSize = lvaGetDesc(argObj->AsLclVarCommon())->lvExactSize;
3154+
// Must be LCL_VAR: we have a BADCODE assert for this in AddFinalArgsAndDetermineABIInfo.
3155+
originalSize = lvaGetDesc(argObj->AsLclVar())->lvExactSize;
31613156
}
31623157
}
31633158
else
31643159
{
31653160
originalSize = genTypeSize(argx);
3166-
assert(originalSize == info.compCompHnd->getClassSize(objClass));
31673161
}
3162+
3163+
assert(originalSize == info.compCompHnd->getClassSize(arg.GetSignatureClassHandle()));
3164+
31683165
unsigned roundupSize = (unsigned)roundUp(originalSize, TARGET_POINTER_SIZE);
31693166
var_types structBaseType = arg.AbiInfo.ArgType;
31703167

31713168
// First, handle the case where the argument is passed by reference.
31723169
if (arg.AbiInfo.PassedByRef)
31733170
{
31743171
assert(arg.AbiInfo.ByteSize == TARGET_POINTER_SIZE);
3175-
copyBlkClass = objClass;
3172+
makeOutArgCopy = true;
31763173
#ifdef UNIX_AMD64_ABI
31773174
assert(!"Structs are not passed by reference on x64/ux");
31783175
#endif // UNIX_AMD64_ABI
@@ -3219,7 +3216,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
32193216
// On Windows structs are always copied and passed by reference (handled above) unless they are
32203217
// passed by value in a single register.
32213218
assert(arg.AbiInfo.GetStackSlotsNumber() == 1);
3222-
copyBlkClass = objClass;
3219+
makeOutArgCopy = true;
32233220
#else // UNIX_AMD64_ABI
32243221
// On Unix, structs are always passed by value.
32253222
// We only need a copy if we have one of the following:
@@ -3233,7 +3230,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
32333230
{
32343231
if (passingSize != structSize)
32353232
{
3236-
copyBlkClass = objClass;
3233+
makeOutArgCopy = true;
32373234
}
32383235
}
32393236
else if (lclVar == nullptr)
@@ -3242,15 +3239,15 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
32423239
assert(argObj->TypeGet() != TYP_STRUCT);
32433240
if (arg.AbiInfo.NumRegs > 1)
32443241
{
3245-
copyBlkClass = objClass;
3242+
makeOutArgCopy = true;
32463243
}
32473244
}
32483245
}
32493246
#endif // UNIX_AMD64_ABI
32503247
#elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
32513248
if ((passingSize != structSize) && (lclVar == nullptr))
32523249
{
3253-
copyBlkClass = objClass;
3250+
makeOutArgCopy = true;
32543251
}
32553252
#endif
32563253

@@ -3260,12 +3257,12 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
32603257
(lvaGetPromotionType(lclVar->AsLclVarCommon()->GetLclNum()) == PROMOTION_TYPE_INDEPENDENT)) ||
32613258
((argObj->OperIs(GT_OBJ)) && (passingSize != structSize)))
32623259
{
3263-
copyBlkClass = objClass;
3260+
makeOutArgCopy = true;
32643261
}
32653262

32663263
if (structSize < TARGET_POINTER_SIZE)
32673264
{
3268-
copyBlkClass = objClass;
3265+
makeOutArgCopy = true;
32693266
}
32703267
#endif // TARGET_ARM
32713268
}
@@ -3328,7 +3325,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
33283325
argObj->gtType = structBaseType;
33293326
}
33303327
assert(varTypeIsEnregisterable(argObj->TypeGet()));
3331-
assert(copyBlkClass == NO_CLASS_HANDLE);
3328+
assert(!makeOutArgCopy);
33323329
}
33333330
else
33343331
{
@@ -3342,7 +3339,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
33423339
{
33433340
// The struct fits into a single register, but it has been promoted into its
33443341
// constituent fields, and so we have to re-assemble it
3345-
copyBlkClass = objClass;
3342+
makeOutArgCopy = true;
33463343
}
33473344
}
33483345
else if (genTypeSize(varDsc->TypeGet()) != genTypeSize(structBaseType))
@@ -3359,7 +3356,7 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
33593356
argObj->gtType = structBaseType;
33603357
}
33613358
assert(varTypeIsEnregisterable(argObj->TypeGet()) ||
3362-
((copyBlkClass != NO_CLASS_HANDLE) && varTypeIsEnregisterable(structBaseType)));
3359+
(makeOutArgCopy && varTypeIsEnregisterable(structBaseType)));
33633360
}
33643361

33653362
#if !defined(UNIX_AMD64_ABI) && !defined(TARGET_ARMARCH) && !defined(TARGET_LOONGARCH64)
@@ -3377,15 +3374,15 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
33773374
// the obj reading memory past the end of the valuetype
33783375
if (roundupSize > originalSize)
33793376
{
3380-
copyBlkClass = objClass;
3377+
makeOutArgCopy = true;
33813378

33823379
// There are a few special cases where we can omit using a CopyBlk
33833380
// where we normally would need to use one.
33843381

33853382
if (argObj->OperIs(GT_OBJ) &&
33863383
argObj->AsObj()->gtGetOp1()->IsLocalAddrExpr() != nullptr) // Is the source a LclVar?
33873384
{
3388-
copyBlkClass = NO_CLASS_HANDLE;
3385+
makeOutArgCopy = false;
33893386
}
33903387
}
33913388
}
@@ -3394,9 +3391,9 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call)
33943391
}
33953392
}
33963393

3397-
if (copyBlkClass != NO_CLASS_HANDLE)
3394+
if (makeOutArgCopy)
33983395
{
3399-
fgMakeOutgoingStructArgCopy(call, &arg, copyBlkClass);
3396+
fgMakeOutgoingStructArgCopy(call, &arg);
34003397
}
34013398

34023399
if (argx->gtOper == GT_MKREFANY)
@@ -4063,14 +4060,13 @@ GenTreeFieldList* Compiler::fgMorphLclArgToFieldlist(GenTreeLclVarCommon* lcl)
40634060
// Arguments:
40644061
// call - call being processed
40654062
// arg - arg for the call
4066-
// copyBlkClass - class handle for the struct
40674063
//
40684064
// The arg is updated if necessary with the copy.
40694065
//
4070-
void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg, CORINFO_CLASS_HANDLE copyBlkClass)
4066+
void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg)
40714067
{
40724068
GenTree* argx = arg->GetEarlyNode();
4073-
noway_assert(argx->gtOper != GT_MKREFANY);
4069+
noway_assert(!argx->OperIs(GT_MKREFANY));
40744070

40754071
// If we're optimizing, see if we can avoid making a copy.
40764072
//
@@ -4121,8 +4117,9 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg, CORI
41214117
fgOutgoingArgTemps = hashBv::Create(this);
41224118
}
41234119

4124-
unsigned tmp = 0;
4125-
bool found = false;
4120+
CORINFO_CLASS_HANDLE copyBlkClass = arg->GetSignatureClassHandle();
4121+
unsigned tmp = 0;
4122+
bool found = false;
41264123

41274124
// Attempt to find a local we have already used for an outgoing struct and reuse it.
41284125
// We do not reuse within a statement.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Numerics;
5+
using System.Runtime.Intrinsics;
6+
using System.Runtime.InteropServices;
7+
using System.Runtime.CompilerServices;
8+
9+
public unsafe class Runtime_69965
10+
{
11+
public static int Main()
12+
{
13+
const int Value = 10;
14+
var vtor = Vector128.Create(Value, Value, Value, Value);
15+
var vtors = new StructWithOverlappedVtor128[] { new StructWithOverlappedVtor128 { Vtor = vtor } };
16+
17+
return Problem(vtors) != Value ? 101 : 100;
18+
}
19+
20+
[MethodImpl(MethodImplOptions.NoInlining)]
21+
private static int Problem(StructWithOverlappedVtor128[] a)
22+
{
23+
static Vector128<int> Tunnel(StructWithOverlappedVtor128[] a) => a[0].Vtor;
24+
25+
return CallForVtor(Tunnel(a));
26+
}
27+
28+
[MethodImpl(MethodImplOptions.NoInlining)]
29+
private static int CallForVtor(Vector128<int> value) => value.GetElement(0);
30+
31+
[StructLayout(LayoutKind.Explicit)]
32+
struct StructWithOverlappedVtor128
33+
{
34+
[FieldOffset(16)]
35+
public Vector128<int> Vtor;
36+
[FieldOffset(16)]
37+
public Vector128<uint> AnotherVtor;
38+
}
39+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<Optimize>True</Optimize>
5+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<Compile Include="$(MSBuildProjectName).cs" />
9+
</ItemGroup>
10+
</Project>

0 commit comments

Comments
 (0)