Skip to content

Commit 7069930

Browse files
authored
Add cast/proper load size when replacing promoted struct with field in lowering (#58589)
We may need to sign/zero-extend when we do this replacement very late. Normally this cast would be inserted in morph.
1 parent 409bc4d commit 7069930

File tree

5 files changed

+142
-7
lines changed

5 files changed

+142
-7
lines changed

src/coreclr/jit/lower.cpp

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2984,9 +2984,8 @@ void Lowering::LowerRet(GenTreeUnOp* ret)
29842984
// There are two kinds of retyping:
29852985
// - A simple bitcast can be inserted when:
29862986
// - We're returning a floating type as an integral type or vice-versa, or
2987-
// - We're returning a struct as a primitive type and using the old form of retyping.
2988-
// - If we're returning a struct as a primitive type and *not* using old retying, we change the type of
2989-
// 'retval' in 'LowerRetStructLclVar()'
2987+
// - If we're returning a struct as a primitive type, we change the type of
2988+
// 'retval' in 'LowerRetStructLclVar()'
29902989
bool needBitcast =
29912990
(ret->TypeGet() != TYP_VOID) && (varTypeUsesFloatReg(ret) != varTypeUsesFloatReg(ret->gtGetOp1()));
29922991
bool doPrimitiveBitcast = false;
@@ -3408,6 +3407,7 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret)
34083407
unsigned lclNum = lclVar->GetLclNum();
34093408
LclVarDsc* varDsc = comp->lvaGetDesc(lclNum);
34103409

3410+
bool replacedInLowering = false;
34113411
if (varDsc->CanBeReplacedWithItsField(comp))
34123412
{
34133413
// We can replace the struct with its only field and keep the field on a register.
@@ -3420,8 +3420,9 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret)
34203420
"[%06u]\n",
34213421
lclNum, fieldLclNum, comp->dspTreeID(ret));
34223422
lclVar->ChangeType(fieldDsc->lvType);
3423-
lclNum = fieldLclNum;
3424-
varDsc = comp->lvaGetDesc(lclNum);
3423+
lclNum = fieldLclNum;
3424+
varDsc = comp->lvaGetDesc(lclNum);
3425+
replacedInLowering = true;
34253426
}
34263427
else if (varDsc->lvPromoted)
34273428
{
@@ -3432,20 +3433,52 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret)
34323433

34333434
if (varDsc->lvDoNotEnregister)
34343435
{
3436+
assert(!replacedInLowering);
34353437
lclVar->ChangeOper(GT_LCL_FLD);
34363438
lclVar->AsLclFld()->SetLclOffs(0);
3437-
lclVar->ChangeType(ret->TypeGet());
3439+
3440+
// We are returning as a primitive type and the lcl is of struct type.
3441+
assert(comp->info.compRetNativeType != TYP_STRUCT);
3442+
assert((genTypeSize(comp->info.compRetNativeType) == genTypeSize(ret)) ||
3443+
(varTypeIsIntegral(ret) && varTypeIsIntegral(comp->info.compRetNativeType) &&
3444+
(genTypeSize(comp->info.compRetNativeType) <= genTypeSize(ret))));
3445+
// If the actual return type requires normalization, then make sure we
3446+
// do so by using the correct small type for the GT_LCL_FLD. It would
3447+
// be conservative to check just compRetNativeType for this since small
3448+
// structs are normalized to primitive types when they are returned in
3449+
// registers, so we would normalize for them as well.
3450+
if (varTypeIsSmall(comp->info.compRetType))
3451+
{
3452+
assert(genTypeSize(comp->info.compRetNativeType) == genTypeSize(comp->info.compRetType));
3453+
lclVar->ChangeType(comp->info.compRetType);
3454+
}
3455+
else
3456+
{
3457+
// Otherwise we don't mind that we leave the upper bits undefined.
3458+
lclVar->ChangeType(ret->TypeGet());
3459+
}
34383460
}
34393461
else
34403462
{
34413463
const var_types lclVarType = varDsc->GetRegisterType(lclVar);
34423464
assert(lclVarType != TYP_UNDEF);
3465+
3466+
if (varDsc->lvNormalizeOnLoad() && replacedInLowering)
3467+
{
3468+
// For a normalize-on-load var that we replaced late we need to insert a cast
3469+
// as morph would typically be responsible for this.
3470+
GenTreeCast* cast = comp->gtNewCastNode(TYP_INT, lclVar, false, lclVarType);
3471+
ret->gtOp1 = cast;
3472+
BlockRange().InsertBefore(ret, cast);
3473+
ContainCheckCast(cast);
3474+
}
3475+
34433476
const var_types actualType = genActualType(lclVarType);
34443477
lclVar->ChangeType(actualType);
34453478

34463479
if (varTypeUsesFloatReg(ret) != varTypeUsesFloatReg(lclVarType))
34473480
{
3448-
GenTree* bitcast = comp->gtNewBitCastNode(ret->TypeGet(), lclVar);
3481+
GenTree* bitcast = comp->gtNewBitCastNode(ret->TypeGet(), ret->gtOp1);
34493482
ret->gtOp1 = bitcast;
34503483
BlockRange().InsertBefore(ret, bitcast);
34513484
ContainCheckBitCast(bitcast);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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;
5+
using System.Runtime.CompilerServices;
6+
7+
public unsafe class Runtime_58373
8+
{
9+
public static int Main()
10+
{
11+
short halfValue = HalfToInt16Bits(MakeHalf());
12+
int x = halfValue;
13+
short val2 = HalfToInt16Bits(*(Half*)&x);
14+
15+
return halfValue == val2 ? 100 : -1;
16+
}
17+
18+
[MethodImpl(MethodImplOptions.NoInlining)]
19+
static Half MakeHalf()
20+
{
21+
return (Half)(-1.0f);
22+
}
23+
24+
[MethodImpl(MethodImplOptions.NoInlining)]
25+
static short HalfToInt16Bits(Half h)
26+
{
27+
return *(short*)&h;
28+
}
29+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
</PropertyGroup>
5+
<PropertyGroup>
6+
<DebugType>None</DebugType>
7+
<Optimize>True</Optimize>
8+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
9+
</PropertyGroup>
10+
<ItemGroup>
11+
<Compile Include="$(MSBuildProjectName).cs" />
12+
</ItemGroup>
13+
</Project>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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;
5+
using System.Runtime.CompilerServices;
6+
7+
public unsafe class Runtime_58373
8+
{
9+
public static int Main()
10+
{
11+
// Use up a lot of registers
12+
int a = GetVal();
13+
int b = GetVal();
14+
int c = GetVal();
15+
int d = GetVal();
16+
int e = GetVal();
17+
int f = GetVal();
18+
int g = GetVal();
19+
int h = GetVal();
20+
int i = GetVal();
21+
22+
short val1 = HalfToInt16Bits(MakeHalf());
23+
Half half = MakeHalf();
24+
MakeHalf(); // This will spill lower 16 bits of 'half' to memory
25+
short val2 = HalfToInt16Bits(half); // This will pass 32 bits as arg with upper 16 bits undefined
26+
27+
return val1 == val2 ? 100 + a + b + c + d + e + f + g + h + i : -1;
28+
}
29+
30+
[MethodImpl(MethodImplOptions.NoInlining)]
31+
static int GetVal()
32+
{
33+
return 0;
34+
}
35+
36+
[MethodImpl(MethodImplOptions.NoInlining)]
37+
static Half MakeHalf()
38+
{
39+
return default;
40+
}
41+
42+
[MethodImpl(MethodImplOptions.NoInlining)]
43+
static short HalfToInt16Bits(Half h)
44+
{
45+
return *(short*)&h;
46+
}
47+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
</PropertyGroup>
5+
<PropertyGroup>
6+
<DebugType>None</DebugType>
7+
<Optimize>True</Optimize>
8+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
9+
</PropertyGroup>
10+
<ItemGroup>
11+
<Compile Include="$(MSBuildProjectName).cs" />
12+
</ItemGroup>
13+
</Project>

0 commit comments

Comments
 (0)