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

Commit

Permalink
Lower TEST(x, LSH(1, y)) to BT(x, y)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikedn committed Sep 7, 2017
1 parent 9a3f37c commit e079bab
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 0 deletions.
49 changes: 49 additions & 0 deletions src/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2135,6 +2135,7 @@ GenTree* Lowering::LowerTailCallViaHelper(GenTreeCall* call, GenTree* callTarget
// - Narrow operands to enable memory operand containment (XARCH specific).
// - Transform cmp(and(x, y), 0) into test(x, y) (XARCH specific but could
// be used for ARM as well if support for GT_TEST_EQ/GT_TEST_NE is added).
// - Transform TEST(x, LSH(1, y)) into BT(x, y) (XARCH specific)

void Lowering::LowerCompare(GenTree* cmp)
{
Expand Down Expand Up @@ -2525,6 +2526,54 @@ void Lowering::LowerCompare(GenTree* cmp)
}
}

if (cmp->OperIs(GT_TEST_EQ, GT_TEST_NE))
{
//
// Transform TEST_EQ|NE(x, LSH(1, y)) into BT(x, y) when possible. Using BT
// results in smaller and faster code. It also doesn't have special register
// requirements, unlike LSH that requires the shift count to be in ECX.
// Note that BT has the same behavior as LSH when the bit index exceeds the
// operand bit size - it uses (bit_index MOD bit_size).
//

GenTree* lsh = cmp->gtGetOp2();
LIR::Use cmpUse;

if (lsh->OperIs(GT_LSH) && varTypeIsIntOrI(lsh->TypeGet()) && lsh->gtGetOp1()->IsIntegralConst(1) &&
BlockRange().TryGetUse(cmp, &cmpUse))
{
genTreeOps condition = cmp->OperIs(GT_TEST_NE) ? GT_LT : GT_GE;

cmp->SetOper(GT_BT);
cmp->gtType = TYP_VOID;
cmp->gtFlags |= GTF_SET_FLAGS;
cmp->gtOp.gtOp2 = lsh->gtGetOp2();
cmp->gtGetOp2()->ClearContained();

BlockRange().Remove(lsh->gtGetOp1());
BlockRange().Remove(lsh);

GenTreeCC* cc;

if (cmpUse.User()->OperIs(GT_JTRUE))
{
cmpUse.User()->ChangeOper(GT_JCC);
cc = cmpUse.User()->AsCC();
cc->gtCondition = condition;
}
else
{
cc = new (comp, GT_SETCC) GenTreeCC(GT_SETCC, condition, TYP_INT);
BlockRange().InsertAfter(cmp, cc);
cmpUse.ReplaceWith(comp, cc);
}

cc->gtFlags |= GTF_USE_FLAGS | GTF_UNSIGNED;

return;
}
}

if (cmp->gtGetOp1()->TypeGet() == cmp->gtGetOp2()->TypeGet())
{
if (varTypeIsSmall(cmp->gtGetOp1()->TypeGet()) && varTypeIsUnsigned(cmp->gtGetOp1()->TypeGet()))
Expand Down
120 changes: 120 additions & 0 deletions tests/src/JIT/Directed/BitTest/BitTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 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;
using System.Runtime.CompilerServices;

class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
static bool I1_BT_reg_reg(sbyte x, int y) => (x & (1 << y)) != 0;

[MethodImpl(MethodImplOptions.NoInlining)]
static bool I1_BT_mem_reg(ref sbyte x, int y) => (x & (1 << y)) != 0;

[MethodImpl(MethodImplOptions.NoInlining)]
static bool I2_BT_reg_reg(short x, int y) => (x & (1 << y)) != 0;

[MethodImpl(MethodImplOptions.NoInlining)]
static bool I2_BT_mem_reg(ref short x, int y) => (x & (1 << y)) != 0;

[MethodImpl(MethodImplOptions.NoInlining)]
static bool I4_BT_reg_reg(int x, int y) => (x & (1 << y)) != 0;

[MethodImpl(MethodImplOptions.NoInlining)]
static bool I4_BT_reg_reg_EQ(int x, int y) => (x & (1 << y)) == 0;

[MethodImpl(MethodImplOptions.NoInlining)]
static int I4_BT_reg_reg_JCC(int x, int y) => (x & (1 << y)) == 0 ? (x + 1) : (x - 1);

[MethodImpl(MethodImplOptions.NoInlining)]
static bool I4_BT_mem_reg(ref int x, int y) => (x & (1 << y)) != 0;

[MethodImpl(MethodImplOptions.NoInlining)]
static bool I8_BT_reg_reg(long x, int y) => (x & (1L << y)) != 0;

[MethodImpl(MethodImplOptions.NoInlining)]
static bool I8_BT_mem_reg(ref long x, int y) => (x & (1L << y)) != 0;

static int Main()
{
sbyte i1min = sbyte.MinValue;
sbyte i1one = 1;
sbyte i1two = 2;
short i2min = short.MinValue;
short i2one = 1;
short i2two = 2;
int i4one = 1;
int i4two = 2;
long i8one = 1;
long i8two = 2;
bool pass = true;

pass &= I1_BT_reg_reg(i1min, 7);
pass &= I1_BT_reg_reg(i1min, 8);
pass &= I1_BT_reg_reg(i1one, 0);
pass &= !I1_BT_reg_reg(i1one, 8);
pass &= I1_BT_reg_reg(i1one, 32);
pass &= !I1_BT_reg_reg(i1two, 0);

pass &= I1_BT_mem_reg(ref i1min, 7);
pass &= I1_BT_mem_reg(ref i1min, 8);
pass &= I1_BT_mem_reg(ref i1one, 0);
pass &= !I1_BT_mem_reg(ref i1one, 8);
pass &= I1_BT_mem_reg(ref i1one, 32);
pass &= !I1_BT_mem_reg(ref i1two, 0);

pass &= I2_BT_reg_reg(i2min, 15);
pass &= I2_BT_reg_reg(i2min, 16);
pass &= I2_BT_reg_reg(i2one, 0);
pass &= !I2_BT_reg_reg(i2one, 16);
pass &= I2_BT_reg_reg(i2one, 32);
pass &= !I2_BT_reg_reg(i2two, 0);

pass &= I2_BT_mem_reg(ref i2min, 15);
pass &= I2_BT_mem_reg(ref i2min, 16);
pass &= I2_BT_mem_reg(ref i2one, 0);
pass &= !I2_BT_mem_reg(ref i2one, 16);
pass &= I2_BT_mem_reg(ref i2one, 32);
pass &= !I2_BT_mem_reg(ref i2two, 0);

pass &= I4_BT_reg_reg(i4one, 0);
pass &= I4_BT_reg_reg(i4one, 32);
pass &= !I4_BT_reg_reg(i4two, 0);

pass &= !I4_BT_reg_reg_EQ(i4one, 0);
pass &= !I4_BT_reg_reg_EQ(i4one, 32);
pass &= I4_BT_reg_reg_EQ(i4two, 0);

pass &= I4_BT_reg_reg_JCC(i4one, 0) == 0;
pass &= I4_BT_reg_reg_JCC(i4one, 32) == 0;
pass &= I4_BT_reg_reg_JCC(i4two, 0) == 3;

pass &= I4_BT_mem_reg(ref i4one, 0);
pass &= I4_BT_mem_reg(ref i4one, 32);
pass &= !I4_BT_mem_reg(ref i4two, 0);

pass &= I8_BT_reg_reg(i8one, 0);
pass &= !I8_BT_reg_reg(i8one, 32);
pass &= I8_BT_reg_reg(i8one, 64);
pass &= !I8_BT_reg_reg(i8two, 0);

pass &= I8_BT_mem_reg(ref i8one, 0);
pass &= !I8_BT_mem_reg(ref i8one, 32);
pass &= I8_BT_mem_reg(ref i8one, 64);
pass &= !I8_BT_mem_reg(ref i8two, 0);

if (pass)
{
Console.WriteLine("PASSED");
return 100;
}
else
{
Console.WriteLine("FAILED");
return 1;
}
}
}
24 changes: 24 additions & 0 deletions tests/src/JIT/Directed/BitTest/BitTest.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?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>
<ProjectGuid>{A62D095E-4206-4D11-8762-11DDD63E931E}</ProjectGuid>
<OutputType>Exe</OutputType>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<CLRTestPriority>1</CLRTestPriority>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="BitTest.cs" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
<PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
</Project>

0 comments on commit e079bab

Please sign in to comment.