Skip to content

Commit 6a728e2

Browse files
authored
Make BSWAP16 nodes normalize upper 16 bits (#67903)
Currently the JIT's constant folding (gtFoldExprConst and VNs EvalOpSpecialized) assumes that BSWAP16 zero extends into the upper 16 bits. This was not the case, and in fact the behavior of BSWAP16 depended on platform. Normally this would not be a problem since we always insert normalizing casts when creating BSWAP16 nodes, however VN was smart enough to remove this cast in some cases (see the test). Change the semantics of BSWAP16 nodes to zero extend into the upper 16 bits to match constant folding, and add a small peephole to avoid inserting this normalization in the common case where it is not necessary. Fixes #67723 Subsumes #67726
1 parent 68e4794 commit 6a728e2

File tree

7 files changed

+72
-4
lines changed

7 files changed

+72
-4
lines changed

src/coreclr/jit/codegen.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12651265
void genCodeForIndir(GenTreeIndir* tree);
12661266
void genCodeForNegNot(GenTree* tree);
12671267
void genCodeForBswap(GenTree* tree);
1268+
bool genCanOmitNormalizationForBswap16(GenTree* tree);
12681269
void genCodeForLclVar(GenTreeLclVar* tree);
12691270
void genCodeForLclFld(GenTreeLclFld* tree);
12701271
void genCodeForStoreLclFld(GenTreeLclFld* tree);

src/coreclr/jit/codegenarm64.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3083,13 +3083,18 @@ void CodeGen::genCodeForBswap(GenTree* tree)
30833083
assert(operand->isUsedFromReg());
30843084
regNumber operandReg = genConsumeReg(operand);
30853085

3086-
if (tree->OperIs(GT_BSWAP16))
3086+
if (tree->OperIs(GT_BSWAP))
30873087
{
3088-
inst_RV_RV(INS_rev16, targetReg, operandReg, targetType);
3088+
inst_RV_RV(INS_rev, targetReg, operandReg, targetType);
30893089
}
30903090
else
30913091
{
3092-
inst_RV_RV(INS_rev, targetReg, operandReg, targetType);
3092+
inst_RV_RV(INS_rev16, targetReg, operandReg, targetType);
3093+
3094+
if (!genCanOmitNormalizationForBswap16(tree))
3095+
{
3096+
GetEmitter()->emitIns_Mov(INS_uxth, EA_4BYTE, targetReg, targetReg, /* canSkip */ false);
3097+
}
30933098
}
30943099

30953100
genProduceReg(tree);

src/coreclr/jit/codegencommon.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9553,3 +9553,37 @@ void CodeGen::genCodeForBitCast(GenTreeOp* treeNode)
95539553
}
95549554
genProduceReg(treeNode);
95559555
}
9556+
9557+
//----------------------------------------------------------------------
9558+
// genCanOmitNormalizationForBswap16:
9559+
// Small peephole to check if a bswap16 node can omit normalization.
9560+
//
9561+
// Arguments:
9562+
// tree - The BSWAP16 node
9563+
//
9564+
// Remarks:
9565+
// BSWAP16 nodes are required to zero extend the upper 16 bits, but since the
9566+
// importer always inserts a normalizing cast (either sign or zero extending)
9567+
// we almost never need to actually do this.
9568+
//
9569+
bool CodeGen::genCanOmitNormalizationForBswap16(GenTree* tree)
9570+
{
9571+
if (compiler->opts.OptimizationDisabled())
9572+
{
9573+
return false;
9574+
}
9575+
9576+
assert(tree->OperIs(GT_BSWAP16));
9577+
if ((tree->gtNext == nullptr) || !tree->gtNext->OperIs(GT_CAST))
9578+
{
9579+
return false;
9580+
}
9581+
9582+
GenTreeCast* cast = tree->gtNext->AsCast();
9583+
if (cast->gtOverflow() || (cast->CastOp() != tree))
9584+
{
9585+
return false;
9586+
}
9587+
9588+
return (cast->gtCastType == TYP_USHORT) || (cast->gtCastType == TYP_SHORT);
9589+
}

src/coreclr/jit/codegenxarch.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,11 @@ void CodeGen::genCodeForBswap(GenTree* tree)
577577
{
578578
// 16-bit byte swaps use "ror reg.16, 8"
579579
inst_RV_IV(INS_ror_N, targetReg, 8 /* val */, emitAttr::EA_2BYTE);
580+
581+
if (!genCanOmitNormalizationForBswap16(tree))
582+
{
583+
GetEmitter()->emitIns_Mov(INS_movzx, EA_2BYTE, targetReg, targetReg, /* canSkip */ false);
584+
}
580585
}
581586

582587
genProduceReg(tree);

src/coreclr/jit/gtlist.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR)
104104
GTNODE(ARR_ADDR , GenTreeArrAddr ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Wraps an array address expression
105105

106106
GTNODE(BSWAP , GenTreeOp ,0,GTK_UNOP) // Byte swap (32-bit or 64-bit)
107-
GTNODE(BSWAP16 , GenTreeOp ,0,GTK_UNOP) // Byte swap (16-bit)
107+
GTNODE(BSWAP16 , GenTreeOp ,0,GTK_UNOP) // Byte swap lower 16-bits and zero upper 16 bits
108108

109109
//-----------------------------------------------------------------------------
110110
// Binary operators (2 operands):
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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.Buffers.Binary;
5+
6+
public class Runtime_67223
7+
{
8+
public static int Main()
9+
{
10+
short[] foo = { short.MinValue };
11+
int test = BinaryPrimitives.ReverseEndianness(foo[0]);
12+
return test == 0x80 ? 100 : -1;
13+
}
14+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<Optimize>True</Optimize>
5+
</PropertyGroup>
6+
<ItemGroup>
7+
<Compile Include="$(MSBuildProjectName).cs" />
8+
</ItemGroup>
9+
</Project>

0 commit comments

Comments
 (0)