summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPat Gavlin <pgavlin@gmail.com>2016-11-17 22:37:17 -0800
committerPat Gavlin <pgavlin@gmail.com>2016-11-18 08:21:47 -0800
commit7e00ee6be9590670d060117fd3932808bd7a86e6 (patch)
tree95fb41c410e39224134b614a53cbf463efaf8378
parentf261bb509b615a80215f59d26277ed50b877c8fc (diff)
downloadcoreclr-7e00ee6be9590670d060117fd3932808bd7a86e6.tar.gz
coreclr-7e00ee6be9590670d060117fd3932808bd7a86e6.tar.bz2
coreclr-7e00ee6be9590670d060117fd3932808bd7a86e6.zip
Fix codegen for `(umod (gt_long) (const int))`.
When targeting x86, we opt not to use a helper call to implement the 64-bit unsigned modulus operator if we have a tree of either of the following forms: - `(umod ulong (x ulong) (cast ulong (const int const_val)))` - `(umod ulong (x ulong) (const ulong const_val))` where `const_val` falls in the range `[2, 0x3fffffff]`. We eventually decompose this into `(umod uint (gt_long lo hi) (const int const_val))`. This was then emitted as: mov eax, lo mov edx, hi mov reg, const_val div edx:eax reg Unfortunately, this generated code does not take into account the fact that the result of the modulus may not fit into 32 bits. This can cause an overflow exception at runtime despite the fact that the modulus originally produced a 64-bit result. This change fixes the code generation to check for overflow before performing the modulus and perform a 64-bit division if the result would overflow. This results in generated code like the following: mov eax, lo mov edx, hi mov reg, const_val cmp edx, reg jb noOverflow mov temp, eax mov edx, eax xor edx, edx div edx:eax, const_val mov eax, temp noOverflow: div edx:eax, const_val Which is identical to the code produced by JIT32 and the legacy backend.
-rw-r--r--src/jit/codegenlinear.h4
-rw-r--r--src/jit/codegenxarch.cpp167
-rw-r--r--src/jit/lowerxarch.cpp4
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_8170/GitHub_8170.cs23
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_8170/GitHub_8170.csproj46
5 files changed, 188 insertions, 56 deletions
diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h
index c0260ce405..6ad72cf781 100644
--- a/src/jit/codegenlinear.h
+++ b/src/jit/codegenlinear.h
@@ -16,6 +16,10 @@ void genCodeForTreeNode(GenTreePtr treeNode);
void genCodeForBinary(GenTreePtr treeNode);
+#if defined(_TARGET_X86_)
+void genCodeForLongUMod(GenTreeOp* node);
+#endif // _TARGET_X86_
+
void genCodeForDivMod(GenTreeOp* treeNode);
void genCodeForMulHi(GenTreeOp* treeNode);
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index 6553d8c6ee..d7f1ba52db 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -546,6 +546,90 @@ void CodeGen::genCodeForMulHi(GenTreeOp* treeNode)
}
}
+#ifdef _TARGET_X86_
+//------------------------------------------------------------------------
+// genCodeForLongUMod: Generate code for a tree of the form
+// `(umod (gt_long x y) (const int))`
+//
+// Arguments:
+// node - the node for which to generate code
+//
+void CodeGen::genCodeForLongUMod(GenTreeOp* node)
+{
+ assert(node != nullptr);
+ assert(node->OperGet() == GT_UMOD);
+ assert(node->TypeGet() == TYP_INT);
+
+ GenTreeOp* const dividend = node->gtOp1->AsOp();
+ assert(dividend->OperGet() == GT_LONG);
+ assert(varTypeIsLong(dividend));
+
+ GenTree* const dividendLo = dividend->gtOp1;
+ GenTree* const dividendHi = dividend->gtOp2;
+ assert(!dividendLo->isContained());
+ assert(!dividendHi->isContained());
+
+ GenTreeIntCon* const divisor = node->gtOp2->AsIntCon();
+ assert(!divisor->isContained());
+ assert(divisor->gtIconVal >= 2);
+ assert(divisor->gtIconVal <= 0x3fffffff);
+
+ genConsumeOperands(node);
+
+ // dividendLo must be in RAX; dividendHi must be in RDX
+ genCopyRegIfNeeded(dividendLo, REG_EAX);
+ genCopyRegIfNeeded(dividendHi, REG_EDX);
+
+ // At this point, EAX:EDX contains the 64bit dividend and op2->gtRegNum
+ // contains the 32bit divisor. We want to generate the following code:
+ //
+ // cmp edx, divisor->gtRegNum
+ // jb noOverflow
+ //
+ // mov temp, eax
+ // mov eax, edx
+ // xor edx, edx
+ // div divisor->gtRegNum
+ // mov eax, temp
+ //
+ // noOverflow:
+ // div divisor->gtRegNum
+ //
+ // This works because (a * 2^32 + b) % c = ((a % c) * 2^32 + b) % c.
+
+ BasicBlock* const noOverflow = genCreateTempLabel();
+
+ // cmp edx, divisor->gtRegNum
+ // jb noOverflow
+ inst_RV_RV(INS_cmp, REG_EDX, divisor->gtRegNum);
+ inst_JMP(EJ_jb, noOverflow);
+
+ // mov temp, eax
+ // mov eax, edx
+ // xor edx, edx
+ // div divisor->gtRegNum
+ // mov eax, temp
+ const regNumber tempReg = genRegNumFromMask(node->gtRsvdRegs);
+ inst_RV_RV(INS_mov, tempReg, REG_EAX, TYP_INT);
+ inst_RV_RV(INS_mov, REG_EAX, REG_EDX, TYP_INT);
+ instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_EDX);
+ inst_RV(INS_div, divisor->gtRegNum, TYP_INT);
+ inst_RV_RV(INS_mov, REG_EAX, tempReg, TYP_INT);
+
+ // noOverflow:
+ // div divisor->gtRegNum
+ genDefineTempLabel(noOverflow);
+ inst_RV(INS_div, divisor->gtRegNum, TYP_INT);
+
+ const regNumber targetReg = node->gtRegNum;
+ if (targetReg != REG_EDX)
+ {
+ inst_RV_RV(INS_mov, targetReg, REG_RDX, TYP_INT);
+ }
+ genProduceReg(node);
+}
+#endif // _TARGET_X86_
+
//------------------------------------------------------------------------
// genCodeForDivMod: Generate code for a DIV or MOD operation.
//
@@ -554,7 +638,15 @@ void CodeGen::genCodeForMulHi(GenTreeOp* treeNode)
//
void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
{
- GenTree* dividend = treeNode->gtOp1;
+ GenTree* dividend = treeNode->gtOp1;
+#ifdef _TARGET_X86_
+ if (varTypeIsLong(dividend->TypeGet()))
+ {
+ genCodeForLongUMod(treeNode);
+ return;
+ }
+#endif // _TARGET_X86_
+
GenTree* divisor = treeNode->gtOp2;
genTreeOps oper = treeNode->OperGet();
emitAttr size = emitTypeSize(treeNode);
@@ -562,27 +654,8 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
var_types targetType = treeNode->TypeGet();
emitter* emit = getEmitter();
-#ifdef _TARGET_X86_
- bool dividendIsLong = varTypeIsLong(dividend->TypeGet());
- GenTree* dividendLo = nullptr;
- GenTree* dividendHi = nullptr;
-
- if (dividendIsLong)
- {
- // If dividend is a GT_LONG, the we need to make sure its lo and hi parts are not contained.
- dividendLo = dividend->gtGetOp1();
- dividendHi = dividend->gtGetOp2();
-
- assert(!dividendLo->isContained());
- assert(!dividendHi->isContained());
- assert(divisor->IsCnsIntOrI());
- }
- else
-#endif
- {
- // dividend is not contained.
- assert(!dividend->isContained());
- }
+ // dividend is not contained.
+ assert(!dividend->isContained());
genConsumeOperands(treeNode->AsOp());
if (varTypeIsFloating(targetType))
@@ -617,37 +690,19 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
}
else
{
-#ifdef _TARGET_X86_
- if (dividendIsLong)
- {
- assert(dividendLo != nullptr && dividendHi != nullptr);
+ // dividend must be in RAX
+ genCopyRegIfNeeded(dividend, REG_RAX);
- // dividendLo must be in RAX; dividendHi must be in RDX
- genCopyRegIfNeeded(dividendLo, REG_EAX);
- genCopyRegIfNeeded(dividendHi, REG_EDX);
- }
- else
-#endif
+ // zero or sign extend rax to rdx
+ if (oper == GT_UMOD || oper == GT_UDIV)
{
- // dividend must be in RAX
- genCopyRegIfNeeded(dividend, REG_RAX);
+ instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_EDX);
}
-
-#ifdef _TARGET_X86_
- if (!dividendIsLong)
-#endif
+ else
{
- // zero or sign extend rax to rdx
- if (oper == GT_UMOD || oper == GT_UDIV)
- {
- instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_EDX);
- }
- else
- {
- emit->emitIns(INS_cdq, size);
- // the cdq instruction writes RDX, So clear the gcInfo for RDX
- gcInfo.gcMarkRegSetNpt(RBM_RDX);
- }
+ emit->emitIns(INS_cdq, size);
+ // the cdq instruction writes RDX, So clear the gcInfo for RDX
+ gcInfo.gcMarkRegSetNpt(RBM_RDX);
}
// Perform the 'targetType' (64-bit or 32-bit) divide instruction
@@ -7016,27 +7071,27 @@ void CodeGen::genCkfinite(GenTreePtr treeNode)
//
// For TYP_DOUBLE, we'll generate (for targetReg != op1->gtRegNum):
// movaps targetReg, op1->gtRegNum
- // shufps targetReg, targetReg, 0xB1 // WZYX => ZWXY
- // mov_xmm2i tmpReg, targetReg // tmpReg <= Y
+ // shufps targetReg, targetReg, 0xB1 // WZYX => ZWXY
+ // mov_xmm2i tmpReg, targetReg // tmpReg <= Y
// and tmpReg, <mask>
// cmp tmpReg, <mask>
// je <throw block>
// movaps targetReg, op1->gtRegNum // copy the value again, instead of un-shuffling it
//
// For TYP_DOUBLE with (targetReg == op1->gtRegNum):
- // shufps targetReg, targetReg, 0xB1 // WZYX => ZWXY
- // mov_xmm2i tmpReg, targetReg // tmpReg <= Y
+ // shufps targetReg, targetReg, 0xB1 // WZYX => ZWXY
+ // mov_xmm2i tmpReg, targetReg // tmpReg <= Y
// and tmpReg, <mask>
// cmp tmpReg, <mask>
// je <throw block>
- // shufps targetReg, targetReg, 0xB1 // ZWXY => WZYX
+ // shufps targetReg, targetReg, 0xB1 // ZWXY => WZYX
//
// For TYP_FLOAT, it's the same as _TARGET_64BIT_:
- // mov_xmm2i tmpReg, targetReg // tmpReg <= low 32 bits
+ // mov_xmm2i tmpReg, targetReg // tmpReg <= low 32 bits
// and tmpReg, <mask>
// cmp tmpReg, <mask>
// je <throw block>
- // movaps targetReg, op1->gtRegNum // only if targetReg != op1->gtRegNum
+ // movaps targetReg, op1->gtRegNum // only if targetReg != op1->gtRegNum
regNumber copyToTmpSrcReg; // The register we'll copy to the integer temp.
diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp
index da51f3a8fc..a1711efb95 100644
--- a/src/jit/lowerxarch.cpp
+++ b/src/jit/lowerxarch.cpp
@@ -2493,6 +2493,10 @@ void Lowering::TreeNodeInfoInitModDiv(GenTree* tree)
assert(op2->IsCnsIntOrI());
info->srcCount++;
+ // This situation also requires an internal register.
+ info->internalIntCount = 1;
+ info->setInternalCandidates(l, l->allRegs(TYP_INT));
+
loVal->gtLsraInfo.setSrcCandidates(l, RBM_EAX);
hiVal->gtLsraInfo.setSrcCandidates(l, RBM_EDX);
}
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_8170/GitHub_8170.cs b/tests/src/JIT/Regression/JitBlue/GitHub_8170/GitHub_8170.cs
new file mode 100644
index 0000000000..d3fe8d9cf3
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_8170/GitHub_8170.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+
+// This test checks for proper behavior w.r.t. overflow for expressions of the form `val % constant` where `val` is an
+// unsigned long and `constant` is a 32- or 64-bit integer constant in the range [2,0x3fffffff]. These expressions
+// should never produce an overflow exception.
+
+static class C
+{
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ static uint M(ulong l)
+ {
+ return (uint)(l % 10000000);
+ }
+
+ static int Main()
+ {
+ return M(ulong.MaxValue) == 9551615 ? 100 : 101;
+ }
+}
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_8170/GitHub_8170.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_8170/GitHub_8170.csproj
new file mode 100644
index 0000000000..81aa7299d0
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_8170/GitHub_8170.csproj
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <FileAlignment>512</FileAlignment>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+
+ <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <PropertyGroup>
+ <DebugType>None</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="GitHub_8170.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <PropertyGroup>
+ <ProjectJson>$(JitPackagesConfigFileDirectory)minimal\project.json</ProjectJson>
+ <ProjectLockJson>$(JitPackagesConfigFileDirectory)minimal\project.lock.json</ProjectLockJson>
+ </PropertyGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+ </PropertyGroup>
+</Project>