diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc index aba7eb0189..56eb9e589b 100644 --- a/src/compiler/arm64/instruction-selector-arm64.cc +++ b/src/compiler/arm64/instruction-selector-arm64.cc @@ -1414,7 +1414,8 @@ InstructionSelector::SupportedMachineOperatorFlags() { return MachineOperatorBuilder::kFloat64Floor | MachineOperatorBuilder::kFloat64Ceil | MachineOperatorBuilder::kFloat64RoundTruncate | - MachineOperatorBuilder::kFloat64RoundTiesAway; + MachineOperatorBuilder::kFloat64RoundTiesAway | + MachineOperatorBuilder::kWord32ShiftIsSafe; } } // namespace compiler } // namespace internal diff --git a/src/compiler/ia32/instruction-selector-ia32.cc b/src/compiler/ia32/instruction-selector-ia32.cc index c242fb431c..dabaa56d17 100644 --- a/src/compiler/ia32/instruction-selector-ia32.cc +++ b/src/compiler/ia32/instruction-selector-ia32.cc @@ -524,13 +524,6 @@ static inline void VisitShift(InstructionSelector* selector, Node* node, selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), g.UseImmediate(right)); } else { - Int32BinopMatcher m(node); - if (m.right().IsWord32And()) { - Int32BinopMatcher mright(right); - if (mright.right().Is(0x1F)) { - right = mright.left().node(); - } - } selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), g.UseFixed(right, ecx)); } @@ -1096,7 +1089,8 @@ InstructionSelector::SupportedMachineOperatorFlags() { if (CpuFeatures::IsSupported(SSE4_1)) { return MachineOperatorBuilder::kFloat64Floor | MachineOperatorBuilder::kFloat64Ceil | - MachineOperatorBuilder::kFloat64RoundTruncate; + MachineOperatorBuilder::kFloat64RoundTruncate | + MachineOperatorBuilder::kWord32ShiftIsSafe; } return MachineOperatorBuilder::Flag::kNoFlags; } diff --git a/src/compiler/machine-operator-reducer.cc b/src/compiler/machine-operator-reducer.cc index f788cb54bc..6c4d5dcb95 100644 --- a/src/compiler/machine-operator-reducer.cc +++ b/src/compiler/machine-operator-reducer.cc @@ -151,29 +151,31 @@ Reduction MachineOperatorReducer::Reduce(Node* node) { Int32BinopMatcher mleft(m.left().node()); Int32BinopMatcher mright(m.right().node()); if (mleft.left().node() == mright.left().node()) { - // (x << y) | (x >> (32 - y)) => x ror y + // TODO(turbofan): here we are matching rotate left, shall we add + // support for rotate right? + // (x << y) | (x >>> (32 - y)) => x ror (32 - y) if (mright.right().IsInt32Sub()) { Int32BinopMatcher mrightright(mright.right().node()); if (mrightright.left().Is(32) && mrightright.right().node() == mleft.right().node()) { node->set_op(machine()->Word32Ror()); node->ReplaceInput(0, mleft.left().node()); - node->ReplaceInput(1, mleft.right().node()); + node->ReplaceInput(1, mright.right().node()); return Changed(node); } } - // (x << K) | (x >> (32 - K)) => x ror K + // (x << K) | (x >>> (32 - K)) => x ror (32 - K) if (mleft.right().IsInRange(0, 31) && mright.right().Is(32 - mleft.right().Value())) { node->set_op(machine()->Word32Ror()); node->ReplaceInput(0, mleft.left().node()); - node->ReplaceInput(1, mleft.right().node()); + node->ReplaceInput(1, mright.right().node()); return Changed(node); } } } if (m.left().IsWord32Shr() && m.right().IsWord32Shl()) { - // (x >> (32 - y)) | (x << y) => x ror y + // (x >>> (32 - y)) | (x << y) => x ror (32 -y) Int32BinopMatcher mleft(m.left().node()); Int32BinopMatcher mright(m.right().node()); if (mleft.left().node() == mright.left().node()) { @@ -183,16 +185,16 @@ Reduction MachineOperatorReducer::Reduce(Node* node) { mleftright.right().node() == mright.right().node()) { node->set_op(machine()->Word32Ror()); node->ReplaceInput(0, mright.left().node()); - node->ReplaceInput(1, mright.right().node()); + node->ReplaceInput(1, mleft.right().node()); return Changed(node); } } - // (x >> (32 - K)) | (x << K) => x ror K + // (x >>> (32 - K)) | (x << K) => x ror (32 - K) if (mright.right().IsInRange(0, 31) && mleft.right().Is(32 - mright.right().Value())) { node->set_op(machine()->Word32Ror()); node->ReplaceInput(0, mright.left().node()); - node->ReplaceInput(1, mright.right().node()); + node->ReplaceInput(1, mleft.right().node()); return Changed(node); } } @@ -234,7 +236,7 @@ Reduction MachineOperatorReducer::Reduce(Node* node) { } } } - break; + return ReduceWord32Shifts(node); } case IrOpcode::kWord32Shr: { Uint32BinopMatcher m(node); @@ -242,7 +244,7 @@ Reduction MachineOperatorReducer::Reduce(Node* node) { if (m.IsFoldable()) { // K >>> K => K return ReplaceInt32(m.left().Value() >> m.right().Value()); } - break; + return ReduceWord32Shifts(node); } case IrOpcode::kWord32Sar: { Int32BinopMatcher m(node); @@ -265,7 +267,7 @@ Reduction MachineOperatorReducer::Reduce(Node* node) { } } } - break; + return ReduceWord32Shifts(node); } case IrOpcode::kWord32Ror: { Int32BinopMatcher m(node); @@ -805,6 +807,27 @@ Reduction MachineOperatorReducer::ReduceProjection(size_t index, Node* node) { } +Reduction MachineOperatorReducer::ReduceWord32Shifts(Node* node) { + DCHECK((node->opcode() == IrOpcode::kWord32Shl) || + (node->opcode() == IrOpcode::kWord32Shr) || + (node->opcode() == IrOpcode::kWord32Sar)); + + if (machine()->Word32ShiftIsSafe()) { + // Remove the explicit 'and' with 0x1f if the shift provided by the machine + // instruction matches that required by JavaScript. + Int32BinopMatcher m(node); + if (m.right().IsWord32And()) { + Int32BinopMatcher mright(m.right().node()); + if (mright.right().Is(0x1f)) { + node->ReplaceInput(1, mright.left().node()); + return Changed(node); + } + } + } + return NoChange(); +} + + CommonOperatorBuilder* MachineOperatorReducer::common() const { return jsgraph()->common(); } diff --git a/src/compiler/machine-operator-reducer.h b/src/compiler/machine-operator-reducer.h index fefac7a239..2973ab5e03 100644 --- a/src/compiler/machine-operator-reducer.h +++ b/src/compiler/machine-operator-reducer.h @@ -68,6 +68,7 @@ class MachineOperatorReducer FINAL : public Reducer { Reduction ReduceTruncateFloat64ToInt32(Node* node); Reduction ReduceStore(Node* node); Reduction ReduceProjection(size_t index, Node* node); + Reduction ReduceWord32Shifts(Node* node); Graph* graph() const; JSGraph* jsgraph() const { return jsgraph_; } diff --git a/src/compiler/machine-operator.h b/src/compiler/machine-operator.h index 82d2e89a51..474c0d98e2 100644 --- a/src/compiler/machine-operator.h +++ b/src/compiler/machine-operator.h @@ -67,7 +67,8 @@ class MachineOperatorBuilder FINAL : public ZoneObject { kFloat64RoundTruncate = 1u << 2, kFloat64RoundTiesAway = 1u << 3, kInt32DivIsSafe = 1u << 4, - kUint32DivIsSafe = 1u << 5 + kUint32DivIsSafe = 1u << 5, + kWord32ShiftIsSafe = 1u << 6 }; typedef base::Flags Flags; @@ -82,6 +83,7 @@ class MachineOperatorBuilder FINAL : public ZoneObject { const Operator* Word32Sar(); const Operator* Word32Ror(); const Operator* Word32Equal(); + bool Word32ShiftIsSafe() const { return flags_ & kWord32ShiftIsSafe; } const Operator* Word64And(); const Operator* Word64Or(); diff --git a/src/compiler/x64/instruction-selector-x64.cc b/src/compiler/x64/instruction-selector-x64.cc index ce7ce56616..85d504390a 100644 --- a/src/compiler/x64/instruction-selector-x64.cc +++ b/src/compiler/x64/instruction-selector-x64.cc @@ -275,12 +275,6 @@ void VisitWord32Shift(InstructionSelector* selector, Node* node, selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), g.UseImmediate(right)); } else { - if (m.right().IsWord32And()) { - Int32BinopMatcher mright(right); - if (mright.right().Is(0x1F)) { - right = mright.left().node(); - } - } selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), g.UseFixed(right, rcx)); } @@ -1170,7 +1164,8 @@ InstructionSelector::SupportedMachineOperatorFlags() { if (CpuFeatures::IsSupported(SSE4_1)) { return MachineOperatorBuilder::kFloat64Floor | MachineOperatorBuilder::kFloat64Ceil | - MachineOperatorBuilder::kFloat64RoundTruncate; + MachineOperatorBuilder::kFloat64RoundTruncate | + MachineOperatorBuilder::kWord32ShiftIsSafe; } return MachineOperatorBuilder::kNoFlags; } diff --git a/test/cctest/compiler/test-machine-operator-reducer.cc b/test/cctest/compiler/test-machine-operator-reducer.cc index ec14cd2467..b538fb7ff5 100644 --- a/test/cctest/compiler/test-machine-operator-reducer.cc +++ b/test/cctest/compiler/test-machine-operator-reducer.cc @@ -52,11 +52,13 @@ double ValueOfOperator(const Operator* op) { class ReducerTester : public HandleAndZoneScope { public: - explicit ReducerTester(int num_parameters = 0) + explicit ReducerTester( + int num_parameters = 0, + MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::kNoFlags) : isolate(main_isolate()), binop(NULL), unop(NULL), - machine(main_zone()), + machine(main_zone(), kMachPtr, flags), common(main_zone()), graph(main_zone()), javascript(main_zone()), @@ -358,7 +360,36 @@ TEST(ReduceWord32Sar) { } -TEST(ReduceWord32Equal) { +static void CheckJsShift(ReducerTester* R) { + DCHECK(R->machine.Word32ShiftIsSafe()); + + Node* x = R->Parameter(0); + Node* y = R->Parameter(1); + Node* thirty_one = R->Constant(0x1f); + Node* y_and_thirty_one = + R->graph.NewNode(R->machine.Word32And(), y, thirty_one); + + // If the underlying machine shift instructions 'and' their right operand + // with 0x1f then: x << (y & 0x1f) => x << y + R->CheckFoldBinop(x, y, x, y_and_thirty_one); +} + + +TEST(ReduceJsShifts) { + ReducerTester R(0, MachineOperatorBuilder::kWord32ShiftIsSafe); + + R.binop = R.machine.Word32Shl(); + CheckJsShift(&R); + + R.binop = R.machine.Word32Shr(); + CheckJsShift(&R); + + R.binop = R.machine.Word32Sar(); + CheckJsShift(&R); +} + + +TEST(Word32Equal) { ReducerTester R; R.binop = R.machine.Word32Equal(); diff --git a/test/mjsunit/asm/word32ror.js b/test/mjsunit/asm/word32ror.js new file mode 100644 index 0000000000..d385eac52e --- /dev/null +++ b/test/mjsunit/asm/word32ror.js @@ -0,0 +1,22 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var stdlib = {}; +var foreign = {}; +var heap = new ArrayBuffer(64 * 1024); + +var rol = (function Module(stdlib, foreign, heap) { + "use asm"; + function rol(x, y) { + x = x | 0; + y = y | 0; + return (x << y) | (x >>> (32 - y)); + } + return { rol: rol }; +})(stdlib, foreign, heap).rol; + +assertEquals(10, rol(10, 0)); +assertEquals(2, rol(1, 1)); +assertEquals(0x40000000, rol(1, 30)); +assertEquals(-0x80000000, rol(1, 31)); diff --git a/test/unittests/compiler/machine-operator-reducer-unittest.cc b/test/unittests/compiler/machine-operator-reducer-unittest.cc index 1284c566a3..d8640163b3 100644 --- a/test/unittests/compiler/machine-operator-reducer-unittest.cc +++ b/test/unittests/compiler/machine-operator-reducer-unittest.cc @@ -573,23 +573,22 @@ TEST_F(MachineOperatorReducerTest, ReduceToWord32RorWithParameters) { Node* value = Parameter(0); Node* shift = Parameter(1); Node* shl = graph()->NewNode(machine()->Word32Shl(), value, shift); - Node* shr = graph()->NewNode( - machine()->Word32Shr(), value, - graph()->NewNode(machine()->Int32Sub(), Int32Constant(32), shift)); + Node* sub = graph()->NewNode(machine()->Int32Sub(), Int32Constant(32), shift); + Node* shr = graph()->NewNode(machine()->Word32Shr(), value, sub); - // (x << y) | (x >> (32 - y)) => x ror y + // (x << y) | (x >>> (32 - y)) => x ror (32 - y) Node* node1 = graph()->NewNode(machine()->Word32Or(), shl, shr); Reduction reduction1 = Reduce(node1); EXPECT_TRUE(reduction1.Changed()); EXPECT_EQ(reduction1.replacement(), node1); - EXPECT_THAT(reduction1.replacement(), IsWord32Ror(value, shift)); + EXPECT_THAT(reduction1.replacement(), IsWord32Ror(value, sub)); - // (x >> (32 - y)) | (x << y) => x ror y + // (x >>> (32 - y)) | (x << y) => x ror y Node* node2 = graph()->NewNode(machine()->Word32Or(), shr, shl); Reduction reduction2 = Reduce(node2); EXPECT_TRUE(reduction2.Changed()); EXPECT_EQ(reduction2.replacement(), node2); - EXPECT_THAT(reduction2.replacement(), IsWord32Ror(value, shift)); + EXPECT_THAT(reduction2.replacement(), IsWord32Ror(value, sub)); } @@ -601,21 +600,21 @@ TEST_F(MachineOperatorReducerTest, ReduceToWord32RorWithConstant) { Node* shr = graph()->NewNode(machine()->Word32Shr(), value, Int32Constant(32 - k)); - // (x << K) | (x >> ((32 - K) - y)) => x ror K + // (x << K) | (x >>> ((32 - K) - y)) => x ror (32 - K) Node* node1 = graph()->NewNode(machine()->Word32Or(), shl, shr); Reduction reduction1 = Reduce(node1); EXPECT_TRUE(reduction1.Changed()); EXPECT_EQ(reduction1.replacement(), node1); EXPECT_THAT(reduction1.replacement(), - IsWord32Ror(value, IsInt32Constant(k))); + IsWord32Ror(value, IsInt32Constant(32 - k))); - // (x >> (32 - K)) | (x << K) => x ror K + // (x >>> (32 - K)) | (x << K) => x ror K Node* node2 = graph()->NewNode(machine()->Word32Or(), shr, shl); Reduction reduction2 = Reduce(node2); EXPECT_TRUE(reduction2.Changed()); EXPECT_EQ(reduction2.replacement(), node2); EXPECT_THAT(reduction2.replacement(), - IsWord32Ror(value, IsInt32Constant(k))); + IsWord32Ror(value, IsInt32Constant(32 - k))); } }