diff --git a/src/coreclr/jit/lclmorph.cpp b/src/coreclr/jit/lclmorph.cpp index 38431916e065fd..78994398456b0a 100644 --- a/src/coreclr/jit/lclmorph.cpp +++ b/src/coreclr/jit/lclmorph.cpp @@ -464,6 +464,7 @@ class LocalAddressVisitor final : public GenTreeVisitor None, Nop, BitCast, + NarrowCast, #ifdef FEATURE_HW_INTRINSICS GetElement, WithElement, @@ -1138,6 +1139,7 @@ class LocalAddressVisitor final : public GenTreeVisitor unsigned lclNum = val.LclNum(); LclVarDsc* varDsc = m_compiler->lvaGetDesc(lclNum); GenTreeLclVarCommon* lclNode = nullptr; + bool isDef = (user != nullptr) && user->OperIs(GT_ASG) && (user->AsOp()->gtGetOp1() == indir); switch (transform) { @@ -1244,6 +1246,16 @@ class LocalAddressVisitor final : public GenTreeVisitor lclNode = indir->AsLclVarCommon(); break; + case IndirTransform::NarrowCast: + assert(varTypeIsIntegral(indir)); + assert(varTypeIsIntegral(varDsc)); + assert(genTypeSize(varDsc) >= genTypeSize(indir)); + assert(!isDef); + + lclNode = BashToLclVar(indir->gtGetOp1(), lclNum); + *val.Use() = m_compiler->gtNewCastNode(genActualType(indir), lclNode, false, indir->TypeGet()); + break; + case IndirTransform::LclFld: indir->ChangeOper(GT_LCL_FLD); indir->AsLclFld()->SetLclNum(lclNum); @@ -1266,7 +1278,7 @@ class LocalAddressVisitor final : public GenTreeVisitor GenTreeFlags lclNodeFlags = GTF_EMPTY; - if (user->OperIs(GT_ASG) && (user->AsOp()->gtGetOp1() == indir)) + if (isDef) { lclNodeFlags |= (GTF_VAR_DEF | GTF_DONT_CSE); @@ -1365,6 +1377,12 @@ class LocalAddressVisitor final : public GenTreeVisitor } #endif // FEATURE_HW_INTRINSICS + // Turn this into a narrow-cast if we can. + if (!isDef && varTypeIsIntegral(indir) && varTypeIsIntegral(varDsc)) + { + return IndirTransform::NarrowCast; + } + // Turn this into a bitcast if we can. if ((genTypeSize(indir) == genTypeSize(varDsc)) && (varTypeIsFloating(indir) || varTypeIsFloating(varDsc))) { diff --git a/src/tests/JIT/opt/Unsafe/Unsafe.cs b/src/tests/JIT/opt/Unsafe/Unsafe.cs new file mode 100644 index 00000000000000..dd9134b5125cc0 --- /dev/null +++ b/src/tests/JIT/opt/Unsafe/Unsafe.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; + +namespace CodeGenTests +{ + class UnsafeTests + { + [MethodImpl(MethodImplOptions.NoInlining)] + static byte UnsafeAsNarrowCast_Short(short value) + { + // X64-NOT: dword ptr + return Unsafe.As(ref value); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte UnsafeAsNarrowCast_Int(int value) + { + // X64-NOT: dword ptr + return Unsafe.As(ref value); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte UnsafeAsNarrowCast_Long(long value) + { + // X64-NOT: qword ptr + return Unsafe.As(ref value); + } + + static int Main() + { + if (UnsafeAsNarrowCast_Short(255) != 255) + return 0; + + if (UnsafeAsNarrowCast_Int(255) != 255) + return 0; + + if (UnsafeAsNarrowCast_Long(255) != 255) + return 0; + + return 100; + } + } +} diff --git a/src/tests/JIT/opt/Unsafe/Unsafe.csproj b/src/tests/JIT/opt/Unsafe/Unsafe.csproj new file mode 100644 index 00000000000000..42a89c8384d74e --- /dev/null +++ b/src/tests/JIT/opt/Unsafe/Unsafe.csproj @@ -0,0 +1,17 @@ + + + Exe + + + None + True + + + + true + + + + + +