Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,11 +641,12 @@ static void optAssertionProp_HWIntrinsic(Compiler* comp, GenTreeHWIntrinsic* tre
// Arguments:
// cast - the cast node for which the range will be computed
// compiler - Compiler object
// operandRange - if provided, the precomputed range of the cast operand
//
// Return Value:
// The range this cast produces - see description.
//
/* static */ IntegralRange IntegralRange::ForCastOutput(GenTreeCast* cast, Compiler* compiler)
static IntegralRange ForCastOutputCore(GenTreeCast* cast, Compiler* compiler, const IntegralRange* operandRange)
{
var_types fromType = genActualType(cast->CastOp());
var_types toType = cast->CastToType();
Expand Down Expand Up @@ -675,27 +676,33 @@ static void optAssertionProp_HWIntrinsic(Compiler* comp, GenTreeHWIntrinsic* tre

if (varTypeIsSmall(toType) || (genActualType(toType) == fromType))
{
return ForCastInput(cast);
return IntegralRange::ForCastInput(cast);
}

// if we're upcasting and the cast op is a known non-negative - consider
// this cast unsigned
if (!fromUnsigned && (genTypeSize(toType) >= genTypeSize(fromType)))
{
fromUnsigned = cast->CastOp()->IsNeverNegative(compiler);
fromUnsigned =
(operandRange != nullptr) ? operandRange->IsNonNegative() : cast->CastOp()->IsNeverNegative(compiler);
}

// CAST(uint/int <- ulong/long) - [INT_MIN..INT_MAX]
// CAST(ulong/long <- uint) - [0..UINT_MAX]
// CAST(ulong/long <- int) - [INT_MIN..INT_MAX]
if (!cast->gtOverflow())
{
if ((fromType == TYP_INT) && fromUnsigned)
IntegralRange result = ((fromType == TYP_INT) && fromUnsigned)
? IntegralRange{SymbolicIntegerValue::Zero, SymbolicIntegerValue::UIntMax}
: IntegralRange{SymbolicIntegerValue::IntMin, SymbolicIntegerValue::IntMax};

// Only preserve the source range when the cast is guaranteed
// to preserve every possible value in that range.
if ((operandRange != nullptr) && result.Contains(*operandRange))
{
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::UIntMax};
return *operandRange;
}

return {SymbolicIntegerValue::IntMin, SymbolicIntegerValue::IntMax};
return result;
}

SymbolicIntegerValue lowerBound;
Expand Down Expand Up @@ -737,6 +744,16 @@ static void optAssertionProp_HWIntrinsic(Compiler* comp, GenTreeHWIntrinsic* tre
return {lowerBound, upperBound};
}

/* static */ IntegralRange IntegralRange::ForCastOutput(GenTreeCast* cast, Compiler* compiler)
{
return ForCastOutputCore(cast, compiler, nullptr);
}

/* static */ IntegralRange IntegralRange::ForCastOutput(GenTreeCast* cast, const IntegralRange& operandRange)
{
return ForCastOutputCore(cast, nullptr, &operandRange);
}

/* static */ IntegralRange IntegralRange::Union(IntegralRange range1, IntegralRange range2)
{
return IntegralRange(min(range1.GetLowerBound(), range2.GetLowerBound()),
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,7 @@ class IntegralRange
static IntegralRange ForNode(GenTree* node, Compiler* compiler);
static IntegralRange ForCastInput(GenTreeCast* cast);
static IntegralRange ForCastOutput(GenTreeCast* cast, Compiler* compiler);
static IntegralRange ForCastOutput(GenTreeCast* cast, const IntegralRange& operandRange);
static IntegralRange Union(IntegralRange range1, IntegralRange range2);

#ifdef DEBUG
Expand Down
21 changes: 19 additions & 2 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8660,14 +8660,31 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast)
// Check for two consecutive casts, we may be able to discard the intermediate one.
if (opts.OptimizationEnabled() && src->OperIs(GT_CAST) && !src->gtOverflow())
{
GenTreeCast* srcCast = src->AsCast();
GenTree* srcCastOp = srcCast->CastOp();

// CAST(T <- CAST(U <- X)) can be replaced by X if both casts
// preserve X's range and the final type is the same as X's type.
if (cast->TypeIs(srcCastOp->TypeGet()))
{
IntegralRange operandRange = IntegralRange::ForNode(srcCastOp, this);
IntegralRange srcCastRange = IntegralRange::ForCastOutput(srcCast, operandRange);
IntegralRange dstRange = IntegralRange::ForCastOutput(cast, srcCastRange);

if (srcCastRange.Equals(operandRange) && dstRange.Equals(operandRange))
{
return srcCastOp;
}
}

var_types dstCastToType = castToType;
var_types srcCastToType = src->AsCast()->CastToType();
var_types srcCastToType = srcCast->CastToType();

// CAST(ubyte <- CAST(short <- X)): CAST(ubyte <- X).
// CAST(ushort <- CAST(short <- X)): CAST(ushort <- X).
if (varTypeIsSmall(srcCastToType) && (genTypeSize(dstCastToType) <= genTypeSize(srcCastToType)))
{
cast->CastOp() = src->AsCast()->CastOp();
cast->CastOp() = srcCastOp;
DEBUG_DESTROY_NODE(src);
}
}
Expand Down
63 changes: 62 additions & 1 deletion src/tests/JIT/opt/Casts/IntCast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using Xunit;

Expand All @@ -27,6 +28,42 @@ static long Cast_Short_To_Long_Add(short value1, short value2)
return (long)value1 + (long)value2;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static nint Cast_LeadingZeroCount_To_NInt(ulong value)
{
// X64-NOT: cdqe
// X64-NOT: movsxd
return BitOperations.LeadingZeroCount(value);
}

[MethodImpl(MethodImplOptions.NoInlining)]
static nint Cast_TrailingZeroCount_To_NInt(ulong value)
{
// X64-NOT: cdqe
// X64-NOT: movsxd
return BitOperations.TrailingZeroCount(value);
}

[MethodImpl(MethodImplOptions.NoInlining)]
static nint Cast_PopCount_To_NInt(ulong value)
{
// X64-NOT: cdqe
// X64-NOT: movsxd
return BitOperations.PopCount(value);
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long Cast_Long_To_Int_To_Long(long value)
{
return unchecked((long)(int)value);
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long Cast_Long_To_UInt_To_Long(long value)
{
return unchecked((long)(uint)value);
}

[Fact]
public static int TestEntryPoint()
{
Expand All @@ -36,7 +73,31 @@ public static int TestEntryPoint()
if (Cast_Short_To_Long_Add(Int16.MaxValue, Int16.MaxValue) != 65534)
return 0;

if (Cast_LeadingZeroCount_To_NInt(0) != 64)
return 0;

if (Cast_LeadingZeroCount_To_NInt(ulong.MaxValue) != 0)
return 0;

if (Cast_TrailingZeroCount_To_NInt(0) != 64)
return 0;

if (Cast_TrailingZeroCount_To_NInt(1) != 0)
return 0;

if (Cast_PopCount_To_NInt(0) != 0)
return 0;

if (Cast_PopCount_To_NInt(ulong.MaxValue) != 64)
return 0;

if (Cast_Long_To_Int_To_Long(0xFFFF_FFFFL) != -1)
return 0;

if (Cast_Long_To_UInt_To_Long(-1) != uint.MaxValue)
return 0;

return 100;
}
}
}
}
Loading