Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 0880a64

Browse files
committed
Fix inconsistent handling of zero extending casts
For casts that are supposed to zero extend the GTF_UNSIGNED must always be set (and obeyed). Some code failed to set the flag (e.g. when importing add.ovf.un instructions having native int and int32 operands) and some other code failed to check the flag (e.g. x64 codegen, gtFoldExprConst) and instead decided to zero extend based on the cast destination type. This resulted in discrepancies between ARM64 and x64 codegen and between constant folding performed by gtFoldExprConst and VN's EvalCastForConstantArgs.
1 parent 1e9f9ab commit 0880a64

File tree

16 files changed

+252
-137
lines changed

16 files changed

+252
-137
lines changed

src/jit/codegenxarch.cpp

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6491,7 +6491,7 @@ void CodeGen::genIntToIntCast(GenTree* treeNode)
64916491
bool isUnsignedSrc = varTypeIsUnsigned(srcType);
64926492

64936493
// if necessary, force the srcType to unsigned when the GT_UNSIGNED flag is set
6494-
if (!isUnsignedSrc && (treeNode->gtFlags & GTF_UNSIGNED) != 0)
6494+
if (!isUnsignedSrc && treeNode->IsUnsigned())
64956495
{
64966496
srcType = genUnsignedType(srcType);
64976497
isUnsignedSrc = true;
@@ -6508,37 +6508,29 @@ void CodeGen::genIntToIntCast(GenTree* treeNode)
65086508

65096509
if (srcSize < dstSize)
65106510
{
6511-
// Widening cast
6512-
// Is this an Overflow checking cast?
6513-
// We only need to handle one case, as the other casts can never overflow.
6514-
// cast from TYP_INT to TYP_ULONG
6515-
//
6516-
if (treeNode->gtOverflow() && (srcType == TYP_INT) && (dstType == TYP_ULONG))
6511+
#ifdef _TARGET_X86_
6512+
// dstType cannot be a long type on x86, such casts should have been decomposed.
6513+
// srcType cannot be a small type since it's the "actual type" of the cast operand.
6514+
// This means that widening casts do not actually occur on x86.
6515+
unreached();
6516+
#else
6517+
// This is a widening cast from TYP_(U)INT to TYP_(U)LONG.
6518+
assert(dstSize == EA_8BYTE);
6519+
assert(srcSize == EA_4BYTE);
6520+
6521+
// When widening, overflows can only happen if the source type is signed and the
6522+
// destination type is unsigned. Since the overflow check ensures that the value
6523+
// is positive a cheaper mov instruction can be used instead of movsxd.
6524+
if (treeNode->gtOverflow() && !isUnsignedSrc && isUnsignedDst)
65176525
{
65186526
requiresOverflowCheck = true;
65196527
ins = INS_mov;
65206528
}
65216529
else
65226530
{
6523-
noway_assert(srcSize < EA_PTRSIZE);
6524-
6525-
ins = ins_Move_Extend(srcType, false);
6526-
6527-
/*
6528-
Special case: ins_Move_Extend assumes the destination type is no bigger
6529-
than TYP_INT. movsx and movzx can already extend all the way to
6530-
64-bit, and a regular 32-bit mov clears the high 32 bits (like the non-existant movzxd),
6531-
but for a sign extension from TYP_INT to TYP_LONG, we need to use movsxd opcode.
6532-
*/
6533-
if (!isUnsignedSrc && !isUnsignedDst)
6534-
{
6535-
#ifdef _TARGET_X86_
6536-
NYI_X86("Cast to 64 bit for x86/RyuJIT");
6537-
#else // !_TARGET_X86_
6538-
ins = INS_movsxd;
6539-
#endif // !_TARGET_X86_
6540-
}
6531+
ins = isUnsignedSrc ? INS_mov : INS_movsxd;
65416532
}
6533+
#endif
65426534
}
65436535
else
65446536
{

src/jit/compiler.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,9 +2128,9 @@ class Compiler
21282128

21292129
GenTree* gtUnusedValNode(GenTree* expr);
21302130

2131-
GenTree* gtNewCastNode(var_types typ, GenTree* op1, var_types castType);
2131+
GenTreeCast* gtNewCastNode(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType);
21322132

2133-
GenTree* gtNewCastNodeL(var_types typ, GenTree* op1, var_types castType);
2133+
GenTreeCast* gtNewCastNodeL(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType);
21342134

21352135
GenTree* gtNewAllocObjNode(unsigned int helper, CORINFO_CLASS_HANDLE clsHnd, var_types type, GenTree* op1);
21362136

src/jit/compiler.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,13 +1476,13 @@ inline void GenTree::SetOper(genTreeOps oper, ValueNumberUpdate vnUpdate)
14761476
}
14771477
}
14781478

1479-
inline GenTree* Compiler::gtNewCastNode(var_types typ, GenTree* op1, var_types castType)
1479+
inline GenTreeCast* Compiler::gtNewCastNode(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType)
14801480
{
1481-
GenTree* res = new (this, GT_CAST) GenTreeCast(typ, op1, castType);
1481+
GenTreeCast* res = new (this, GT_CAST) GenTreeCast(typ, op1, fromUnsigned, castType);
14821482
return res;
14831483
}
14841484

1485-
inline GenTree* Compiler::gtNewCastNodeL(var_types typ, GenTree* op1, var_types castType)
1485+
inline GenTreeCast* Compiler::gtNewCastNodeL(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType)
14861486
{
14871487
/* Some casts get transformed into 'GT_CALL' or 'GT_IND' nodes */
14881488

@@ -1491,7 +1491,8 @@ inline GenTree* Compiler::gtNewCastNodeL(var_types typ, GenTree* op1, var_types
14911491

14921492
/* Make a big node first and then change it to be GT_CAST */
14931493

1494-
GenTree* res = new (this, LargeOpOpcode()) GenTreeCast(typ, op1, castType DEBUGARG(/*largeNode*/ true));
1494+
GenTreeCast* res =
1495+
new (this, LargeOpOpcode()) GenTreeCast(typ, op1, fromUnsigned, castType DEBUGARG(/*largeNode*/ true));
14951496
return res;
14961497
}
14971498

src/jit/decomposelongs.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,12 +1924,7 @@ GenTree* DecomposeLongs::EnsureIntSized(GenTree* node, bool signExtend)
19241924
return node;
19251925
}
19261926

1927-
GenTree* const cast = m_compiler->gtNewCastNode(TYP_INT, node, node->TypeGet());
1928-
if (!signExtend)
1929-
{
1930-
cast->gtFlags |= GTF_UNSIGNED;
1931-
}
1932-
1927+
GenTree* const cast = m_compiler->gtNewCastNode(TYP_INT, node, !signExtend, node->TypeGet());
19331928
Range().InsertAfter(node, cast);
19341929
return cast;
19351930
}

src/jit/flowgraph.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7456,7 +7456,7 @@ GenTree* Compiler::fgDoNormalizeOnStore(GenTree* tree)
74567456

74577457
if (fgCastNeeded(op2, varDsc->TypeGet()))
74587458
{
7459-
op2 = gtNewCastNode(TYP_INT, op2, varDsc->TypeGet());
7459+
op2 = gtNewCastNode(TYP_INT, op2, false, varDsc->TypeGet());
74607460
tree->gtOp.gtOp2 = op2;
74617461

74627462
// Propagate GTF_COLON_COND
@@ -20673,12 +20673,12 @@ Compiler::fgWalkResult Compiler::fgStress64RsltMulCB(GenTree** pTree, fgWalkData
2067320673
#endif // DEBUG
2067420674

2067520675
// To ensure optNarrowTree() doesn't fold back to the original tree.
20676-
tree->gtOp.gtOp1 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp1, TYP_LONG);
20676+
tree->gtOp.gtOp1 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp1, false, TYP_LONG);
2067720677
tree->gtOp.gtOp1 = pComp->gtNewOperNode(GT_NOP, TYP_LONG, tree->gtOp.gtOp1);
20678-
tree->gtOp.gtOp1 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp1, TYP_LONG);
20679-
tree->gtOp.gtOp2 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp2, TYP_LONG);
20678+
tree->gtOp.gtOp1 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp1, false, TYP_LONG);
20679+
tree->gtOp.gtOp2 = pComp->gtNewCastNode(TYP_LONG, tree->gtOp.gtOp2, false, TYP_LONG);
2068020680
tree->gtType = TYP_LONG;
20681-
*pTree = pComp->gtNewCastNode(TYP_INT, tree, TYP_INT);
20681+
*pTree = pComp->gtNewCastNode(TYP_INT, tree, false, TYP_INT);
2068220682

2068320683
#ifdef DEBUG
2068420684
if (pComp->verbose)

src/jit/gentree.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7793,8 +7793,9 @@ GenTree* Compiler::gtCloneExpr(
77937793
break;
77947794

77957795
case GT_CAST:
7796-
copy = new (this, LargeOpOpcode()) GenTreeCast(tree->TypeGet(), tree->gtCast.CastOp(),
7797-
tree->gtCast.gtCastType DEBUGARG(/*largeNode*/ TRUE));
7796+
copy =
7797+
new (this, LargeOpOpcode()) GenTreeCast(tree->TypeGet(), tree->gtCast.CastOp(), tree->IsUnsigned(),
7798+
tree->gtCast.gtCastType DEBUGARG(/*largeNode*/ TRUE));
77987799
break;
77997800

78007801
// The nodes below this are not bashed, so they can be allocated at their individual sizes.
@@ -7966,10 +7967,6 @@ GenTree* Compiler::gtCloneExpr(
79667967
{
79677968
copy->gtFlags |= GTF_OVERFLOW;
79687969
}
7969-
if (copy->OperGet() == GT_CAST)
7970-
{
7971-
copy->gtFlags |= (tree->gtFlags & GTF_UNSIGNED);
7972-
}
79737970

79747971
if (tree->gtOp.gtOp1)
79757972
{
@@ -13953,15 +13950,22 @@ GenTree* Compiler::gtFoldExprConst(GenTree* tree)
1395313950
goto CNS_INT;
1395413951

1395513952
case TYP_ULONG:
13956-
if (!(tree->gtFlags & GTF_UNSIGNED) && tree->gtOverflow() && i1 < 0)
13953+
if (tree->IsUnsigned())
1395713954
{
13958-
goto LNG_OVF;
13955+
lval1 = UINT64(UINT32(i1));
13956+
}
13957+
else
13958+
{
13959+
if (tree->gtOverflow() && (i1 < 0))
13960+
{
13961+
goto LNG_OVF;
13962+
}
13963+
lval1 = UINT64(INT32(i1));
1395913964
}
13960-
lval1 = UINT64(UINT32(i1));
1396113965
goto CNS_LONG;
1396213966

1396313967
case TYP_LONG:
13964-
if (tree->gtFlags & GTF_UNSIGNED)
13968+
if (tree->IsUnsigned())
1396513969
{
1396613970
lval1 = INT64(UINT32(i1));
1396713971
}
@@ -15384,11 +15388,11 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr,
1538415388
}
1538515389
else if (lclTyp == TYP_DOUBLE && assg->TypeGet() == TYP_FLOAT)
1538615390
{
15387-
assg = gtNewCastNode(TYP_DOUBLE, assg, TYP_DOUBLE);
15391+
assg = gtNewCastNode(TYP_DOUBLE, assg, false, TYP_DOUBLE);
1538815392
}
1538915393
else if (lclTyp == TYP_FLOAT && assg->TypeGet() == TYP_DOUBLE)
1539015394
{
15391-
assg = gtNewCastNode(TYP_FLOAT, assg, TYP_FLOAT);
15395+
assg = gtNewCastNode(TYP_FLOAT, assg, false, TYP_FLOAT);
1539215396
}
1539315397

1539415398
args = gtNewArgList(assg);
@@ -15448,7 +15452,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr,
1544815452
else if (varTypeIsIntegral(lclTyp) && genTypeSize(lclTyp) < genTypeSize(TYP_INT))
1544915453
{
1545015454
// The helper does not extend the small return types.
15451-
tree = gtNewCastNode(genActualType(lclTyp), tree, lclTyp);
15455+
tree = gtNewCastNode(genActualType(lclTyp), tree, false, lclTyp);
1545215456
}
1545315457
}
1545415458
}

src/jit/gentree.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2993,9 +2993,10 @@ struct GenTreeCast : public GenTreeOp
29932993
}
29942994
var_types gtCastType;
29952995

2996-
GenTreeCast(var_types type, GenTree* op, var_types castType DEBUGARG(bool largeNode = false))
2996+
GenTreeCast(var_types type, GenTree* op, bool fromUnsigned, var_types castType DEBUGARG(bool largeNode = false))
29972997
: GenTreeOp(GT_CAST, type, op, nullptr DEBUGARG(largeNode)), gtCastType(castType)
29982998
{
2999+
gtFlags |= fromUnsigned ? GTF_UNSIGNED : 0;
29993000
}
30003001
#if DEBUGGABLE_GENTREE
30013002
GenTreeCast() : GenTreeOp()

0 commit comments

Comments
 (0)