Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5800,6 +5800,7 @@ class Compiler
GenTree* fgOptimizeBitwiseXor(GenTreeOp* xorOp);
GenTree* fgPropagateCommaThrow(GenTree* parent, GenTreeOp* commaThrow, GenTreeFlags precedingSideEffects);
GenTree* fgMorphRetInd(GenTreeUnOp* tree);
GenTree* fgMorphModToZero(GenTreeOp* tree);
GenTree* fgMorphModToSubMulDiv(GenTreeOp* tree);
GenTree* fgMorphUModToAndSub(GenTreeOp* tree);
GenTree* fgMorphSmpOpOptional(GenTreeOp* tree, bool* optAssertionPropDone);
Expand Down
94 changes: 82 additions & 12 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9495,20 +9495,29 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA

ASSIGN_HELPER_FOR_MOD:

// For "val % 1", return 0 if op1 doesn't have any side effects
// and we are not in the CSE phase, we cannot discard 'tree'
// because it may contain CSE expressions that we haven't yet examined.
//
if (((op1->gtFlags & GTF_SIDE_EFFECT) == 0) && !optValnumCSE_phase)
if (!optValnumCSE_phase)
{
if (op2->IsIntegralConst(1))
if (tree->OperIs(GT_MOD, GT_UMOD) && (op2->IsIntegralConst(1)))
{
GenTree* zeroNode = gtNewZeroConNode(typ);
#ifdef DEBUG
zeroNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED;
#endif
DEBUG_DESTROY_NODE(tree);
return zeroNode;
// Transformation: a % 1 = 0
GenTree* optimizedTree = fgMorphModToZero(tree->AsOp());
if (optimizedTree != nullptr)
{
tree = optimizedTree;

if (tree->OperIs(GT_COMMA))
{
op1 = tree->gtGetOp1();
op2 = tree->gtGetOp2();
}
else
{
assert(tree->IsIntegralConst());
op1 = nullptr;
op2 = nullptr;
}
break;
}
}
}

Expand Down Expand Up @@ -12774,6 +12783,67 @@ GenTree* Compiler::fgMorphMultiOp(GenTreeMultiOp* multiOp)
}
#endif // defined(FEATURE_SIMD) || defined(FEATURE_HW_INTRINSICS)

//------------------------------------------------------------------------
// fgMorphModToZero: Transform 'a % 1' into the equivalent '0'.
//
// Arguments:
// tree - The GT_MOD/GT_UMOD tree to morph
//
// Returns:
// The morphed tree, will be a GT_COMMA or a zero constant node.
// Can return null if the transformation did not happen.
//
GenTree* Compiler::fgMorphModToZero(GenTreeOp* tree)
{
assert(tree->OperIs(GT_MOD, GT_UMOD));
assert(tree->gtOp2->IsIntegralConst(1));

if (opts.OptimizationDisabled())
return nullptr;

// Do not transform this if there are side effects and we are not in global morph.
// If we want to allow this, we need to update value numbers for the GT_COMMA.
if (!fgGlobalMorph && ((tree->gtGetOp1()->gtFlags & GTF_SIDE_EFFECT) != 0))
return nullptr;

JITDUMP("\nMorphing MOD/UMOD [%06u] to Zero\n", dspTreeID(tree));

GenTree* op1 = tree->gtGetOp1();
GenTree* op2 = tree->gtGetOp2();

op2->AsIntConCommon()->SetIntegralValue(0);
fgUpdateConstTreeValueNumber(op2);

GenTree* const zero = op2;

GenTree* op1SideEffects = nullptr;
gtExtractSideEffList(op1, &op1SideEffects, GTF_ALL_EFFECT);
if (op1SideEffects != nullptr)
{
GenTree* comma = gtNewOperNode(GT_COMMA, zero->TypeGet(), op1SideEffects, zero);

#ifdef DEBUG
// op1 may already have been morphed, so unset this bit.
op1SideEffects->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED;
Comment thread
TIHan marked this conversation as resolved.
#endif // DEBUG

INDEBUG(comma->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);

DEBUG_DESTROY_NODE(tree);

return comma;
}
else
{
INDEBUG(zero->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);

DEBUG_DESTROY_NODE(tree->gtOp1);
DEBUG_DESTROY_NODE(tree);

return zero;
}
}

//------------------------------------------------------------------------
// fgMorphModToSubMulDiv: Transform a % b into the equivalent a - (a / b) * b
// (see ECMA III 3.55 and III.3.56).
Expand Down
46 changes: 46 additions & 0 deletions src/tests/JIT/opt/Remainder/IntRemainder.cs
Original file line number Diff line number Diff line change
@@ -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
{
static class IntRemainder
{
static int _fieldValue = 123;

[MethodImpl(MethodImplOptions.NoInlining)]
static int Int32_RemainderByOne()
{
// X64-FULL-LINE: call CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE
// X64-FULL-LINE-NEXT: xor [[REG0:[a-z]+]], [[REG0]]

// ARM64-FULL-LINE: bl CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE
// ARM64-FULL-LINE-NEXT: mov [[REG0:[a-z0-9]+]], wzr

return _fieldValue % 1;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int Int32_RemainderByOneWithValue(int value)
{
// X64-FULL-LINE: xor [[REG0:[a-z]+]], [[REG0]]

// ARM64-FULL-LINE: mov [[REG0:[a-z0-9]+]], wzr

return value % 1;
}

static int Main()
{
if (Int32_RemainderByOne() != 0)
return 0;

if (Int32_RemainderByOneWithValue(-123) != 0)
return 0;

return 100;
}
}
}
17 changes: 17 additions & 0 deletions src/tests/JIT/opt/Remainder/IntRemainder.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs">
<HasDisasmCheck>true</HasDisasmCheck>
</Compile>

<CLRTestEnvironmentVariable Include="DOTNET_TieredCompilation" Value="0" />
<CLRTestEnvironmentVariable Include="DOTNET_JITMinOpts" Value="0" />
Comment on lines +14 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I don't remember:

If we have <HasDisasmCheck>true<...>, why do we need to clear TieredCompilation/JITMinOpts? Shouldn't the "HasDisasmCheck" logic do that for us?

Copy link
Copy Markdown
Contributor Author

@TIHan TIHan Dec 8, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<HasDisasmCheck>true<...> does not force any environment variables that affect codegen - so it is up to the test itself to decide them.

</ItemGroup>
</Project>
19 changes: 19 additions & 0 deletions src/tests/JIT/opt/Remainder/Regressions/Regression1/Regression1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

public class Program
{
public static ulong[,] s_1;
public static int Main()
{
// This should not assert.
try
{
ushort vr10 = default(ushort);
bool vr11 = 0 < ((s_1[0, 0] * (uint)(0 / vr10)) % 1);
}
catch {}

return 100;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>
5 changes: 4 additions & 1 deletion src/tests/issues.targets
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,9 @@

<!-- Known failures for mono runtime on *all* architectures/operating systems in *all* runtime modes -->
<ItemGroup Condition="'$(RuntimeFlavor)' == 'mono'" >
<ExcludeList Include="$(XunitTestBinBase)/JIT/opt/Remainder/Regressions/Regression1/**">
<Issue>https://github.com/dotnet/runtime/issues/79022</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Interop/PInvoke/Int128/Int128TestFieldLayout/*">
<Issue>https://github.com/dotnet/runtime/issues/74223</Issue>
</ExcludeList>
Expand Down Expand Up @@ -4049,4 +4052,4 @@
Condition="'%(ExcludeList.Extension)' == '.OutOfProcessTest'" />
</ItemGroup>
</Target>
</Project>
</Project>