Skip to content

Commit

Permalink
Make BSWAP16 nodes normalize upper 16 bits (#67903)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jakobbotsch authored Apr 20, 2022
1 parent 68e4794 commit 6a728e2
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genCodeForIndir(GenTreeIndir* tree);
void genCodeForNegNot(GenTree* tree);
void genCodeForBswap(GenTree* tree);
bool genCanOmitNormalizationForBswap16(GenTree* tree);
void genCodeForLclVar(GenTreeLclVar* tree);
void genCodeForLclFld(GenTreeLclFld* tree);
void genCodeForStoreLclFld(GenTreeLclFld* tree);
Expand Down
11 changes: 8 additions & 3 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3083,13 +3083,18 @@ void CodeGen::genCodeForBswap(GenTree* tree)
assert(operand->isUsedFromReg());
regNumber operandReg = genConsumeReg(operand);

if (tree->OperIs(GT_BSWAP16))
if (tree->OperIs(GT_BSWAP))
{
inst_RV_RV(INS_rev16, targetReg, operandReg, targetType);
inst_RV_RV(INS_rev, targetReg, operandReg, targetType);
}
else
{
inst_RV_RV(INS_rev, targetReg, operandReg, targetType);
inst_RV_RV(INS_rev16, targetReg, operandReg, targetType);

if (!genCanOmitNormalizationForBswap16(tree))
{
GetEmitter()->emitIns_Mov(INS_uxth, EA_4BYTE, targetReg, targetReg, /* canSkip */ false);
}
}

genProduceReg(tree);
Expand Down
34 changes: 34 additions & 0 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9553,3 +9553,37 @@ void CodeGen::genCodeForBitCast(GenTreeOp* treeNode)
}
genProduceReg(treeNode);
}

//----------------------------------------------------------------------
// genCanOmitNormalizationForBswap16:
// Small peephole to check if a bswap16 node can omit normalization.
//
// Arguments:
// tree - The BSWAP16 node
//
// Remarks:
// BSWAP16 nodes are required to zero extend the upper 16 bits, but since the
// importer always inserts a normalizing cast (either sign or zero extending)
// we almost never need to actually do this.
//
bool CodeGen::genCanOmitNormalizationForBswap16(GenTree* tree)
{
if (compiler->opts.OptimizationDisabled())
{
return false;
}

assert(tree->OperIs(GT_BSWAP16));
if ((tree->gtNext == nullptr) || !tree->gtNext->OperIs(GT_CAST))
{
return false;
}

GenTreeCast* cast = tree->gtNext->AsCast();
if (cast->gtOverflow() || (cast->CastOp() != tree))
{
return false;
}

return (cast->gtCastType == TYP_USHORT) || (cast->gtCastType == TYP_SHORT);
}
5 changes: 5 additions & 0 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,11 @@ void CodeGen::genCodeForBswap(GenTree* tree)
{
// 16-bit byte swaps use "ror reg.16, 8"
inst_RV_IV(INS_ror_N, targetReg, 8 /* val */, emitAttr::EA_2BYTE);

if (!genCanOmitNormalizationForBswap16(tree))
{
GetEmitter()->emitIns_Mov(INS_movzx, EA_2BYTE, targetReg, targetReg, /* canSkip */ false);
}
}

genProduceReg(tree);
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/gtlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR)
GTNODE(ARR_ADDR , GenTreeArrAddr ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Wraps an array address expression

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

//-----------------------------------------------------------------------------
// Binary operators (2 operands):
Expand Down
14 changes: 14 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_67723/Runtime_67723.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers.Binary;

public class Runtime_67223
{
public static int Main()
{
short[] foo = { short.MinValue };
int test = BinaryPrimitives.ReverseEndianness(foo[0]);
return test == 0x80 ? 100 : -1;
}
}
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>

0 comments on commit 6a728e2

Please sign in to comment.