}
#endif // LEA_AVAILABLE
}
- else if(oper == GT_OR)
+ else if (fgOperIsBitwiseRotationRoot(oper))
{
- tree = fgMorphRotation(tree);
+ tree = fgRecognizeAndMorphBitwiseRotation(tree);
- // fgMorphRotation may return a new tree
+ // fgRecognizeAndMorphBitwiseRotation may return a new tree
oper = tree->OperGet();
typ = tree->TypeGet();
op1 = tree->gtOp.gtOp1;
}
//------------------------------------------------------------------------------
-// fgMorphRotation : Check if the tree represents a left or right rotation. If so, return
-// an equivalent GT_ROL or GT_ROR tree; otherwise, return the original tree.
+// fgOperIsBitwiseRotationRoot : Check if the operation can be a root of a bitwise rotation tree.
+//
+//
+// Arguments:
+// oper - Operation to check
+//
+// Return Value:
+// True if the operation can be a root of a bitwise rotation tree; false otherwise.
+
+bool Compiler::fgOperIsBitwiseRotationRoot(genTreeOps oper)
+{
+ return (oper == GT_OR) || (oper == GT_XOR);
+}
+
+//------------------------------------------------------------------------------
+// fgRecognizeAndMorphBitwiseRotation : Check if the tree represents a left or right rotation. If so, return
+// an equivalent GT_ROL or GT_ROR tree; otherwise, return the original tree.
//
// Arguments:
// tree - tree to check for a rotation pattern
// An equivalent GT_ROL or GT_ROR tree if a pattern is found; original tree otherwise.
//
// Assumption:
-// The input is a GT_OR tree.
+// The input is a GT_OR or a GT_XOR tree.
-GenTreePtr Compiler::fgMorphRotation(GenTreePtr tree)
+GenTreePtr Compiler::fgRecognizeAndMorphBitwiseRotation(GenTreePtr tree)
{
#ifndef LEGACY_BACKEND
//
//
// OR ROL
// / \ / \
- // LSH RSZ -> x y
+ // LSH RSZ -> x y
// / \ / \
- // x AND x AND
+ // x AND x AND
// / \ / \
- // y 31 ADD 31
+ // y 31 ADD 31
// / \
- // NEG 32
+ // NEG 32
// |
// y
+ // The patterns recognized:
+ // (x << (y & M)) op (x >>> ((-y + N) & M))
+ // (x >>> ((-y + N) & M)) op (x << (y & M))
+ //
+ // (x << y) op (x >>> (-y + N))
+ // (x >> > (-y + N)) op (x << y)
+ //
+ // (x >>> (y & M)) op (x << ((-y + N) & M))
+ // (x << ((-y + N) & M)) op (x >>> (y & M))
+ //
+ // (x >>> y) op (x << (-y + N))
+ // (x << (-y + N)) op (x >>> y)
+ //
+ // (x << c1) op (x >>> c2)
+ // (x >>> c1) op (x << c2)
+ //
+ // where
+ // c1 and c2 are const
+ // c1 + c2 == bitsize(x)
+ // N == bitsize(x)
+ // M is const
+ // M & (N - 1) == N - 1
+ // op is either | or ^
+
+ if (((tree->gtFlags & GTF_PERSISTENT_SIDE_EFFECTS) != 0) ||
+ ((tree->gtFlags & GTF_ORDER_SIDEEFF) != 0))
+ {
+ // We can't do anything if the tree has assignments, calls, or volatile
+ // reads. Note that we allow GTF_EXCEPT side effect since any exceptions
+ // thrown by the original tree will be thrown by the transformed tree as well.
+ return tree;
+ }
genTreeOps oper = tree->OperGet();
- noway_assert(oper == GT_OR);
-
- if ((tree->gtFlags & GTF_ALL_EFFECT) != 0)
- {
- return tree; // Can't do anything due to side effects.
- }
+ assert(fgOperIsBitwiseRotationRoot(oper));
// Check if we have an LSH on one side of the OR and an RSZ on the other side.
GenTreePtr op1 = tree->gtGetOp1();
{
noway_assert((rotateOp == GT_ROL) || (rotateOp == GT_ROR));
+ unsigned inputTreeEffects = tree->gtFlags & GTF_ALL_EFFECT;
+
// We can use the same tree only during global morph; reusing the tree in a later morph
// may invalidate value numbers.
if (fgGlobalMorph)
tree->gtOp.gtOp1 = rotatedValue;
tree->gtOp.gtOp2 = rotateIndex;
tree->ChangeOper(rotateOp);
+ noway_assert(inputTreeEffects == ((rotatedValue->gtFlags | rotateIndex->gtFlags) & GTF_ALL_EFFECT));
}
else
{
tree = gtNewOperNode(rotateOp, genActualType(rotatedValue->gtType), rotatedValue, rotateIndex);
+ noway_assert(inputTreeEffects == (tree->gtFlags & GTF_ALL_EFFECT));
}
+
return tree;
}
}
public class Test
{
+ static ulong s_field;
+
+ ulong field;
+
+ volatile uint volatile_field;
+
[MethodImpl(MethodImplOptions.NoInlining)]
static uint rol32(uint value, int amount)
{
}
[MethodImpl(MethodImplOptions.NoInlining)]
+ static uint rol32xor(uint value, int amount)
+ {
+ return (value << amount) ^ (value >> (32 - amount));
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
static uint ror32(uint value, int amount)
{
return (value << ((32 - amount))) | (value >> amount);
}
[MethodImpl(MethodImplOptions.NoInlining)]
+ uint ror32vfield(int amount)
+ {
+ return (volatile_field << ((32 - amount))) | (volatile_field >> amount);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
static ulong rol64(ulong value, int amount)
{
return (value << amount) | (value >> (64 - amount));
}
[MethodImpl(MethodImplOptions.NoInlining)]
+ ulong rol64field(int amount)
+ {
+ return (field << amount) | (field >> (64 - amount));
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
static ulong ror64(ulong value, int amount)
{
return (value << (64 - amount)) | (value >> amount);
}
[MethodImpl(MethodImplOptions.NoInlining)]
+ static ulong ror64sfield(int amount)
+ {
+ return (s_field << (64 - amount)) | (s_field >> amount);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
static uint rol32_call(uint value, int amount)
{
return (foo(value) << amount) | (foo(value) >> (32 - amount));
return (value >> 10) | (value << 5);
}
+ Test(ulong i, uint j)
+ {
+ field = i;
+ volatile_field = j;
+ }
+
public static int Main()
{
const int Pass = 100;
const int Fail = -1;
+ s_field = 0x123456789abcdef;
+
if (rol32(0x12345678, 16) != 0x56781234)
{
return Fail;
return Fail;
}
+ if (rol32xor(0x12345678, 16) != 0x56781234)
+ {
+ return Fail;
+ }
+
+ if (ror64sfield(7) != 0xde02468acf13579b)
+ {
+ return Fail;
+ }
+
+ Test test = new Test(0x123456789abcdef, 0x12345678);
+
+ if (test.rol64field(11) != 0x1a2b3c4d5e6f7809)
+ {
+ return Fail;
+ }
+
+ if (test.ror32vfield(3) != 0x2468acf)
+ {
+ return Fail;
+ }
+
return Pass;
}
}