From 352733dca2a1d58bd23d860a5640b577055779a2 Mon Sep 17 00:00:00 2001 From: "m.m.capewell@googlemail.com" Date: Wed, 24 Sep 2014 14:55:50 +0000 Subject: [PATCH] [turbofan] ARM64 support for inverted logical ops Select ARM64 inverted rhs instructions (bic, orn, eon) for cases where the rhs input is inverted (using e/xor), and add some tests for this. Also, rename xor to eor in the ARM64 backend. BUG= R=bmeurer@chromium.org Review URL: https://codereview.chromium.org/591383003 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24188 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/compiler/arm64/code-generator-arm64.cc | 22 ++- src/compiler/arm64/instruction-codes-arm64.h | 10 +- .../instruction-selector-arm64-unittest.cc | 166 +++++++++++++++++- .../arm64/instruction-selector-arm64.cc | 106 +++++++++-- 4 files changed, 281 insertions(+), 23 deletions(-) diff --git a/src/compiler/arm64/code-generator-arm64.cc b/src/compiler/arm64/code-generator-arm64.cc index 798e5c199c..28be056d6c 100644 --- a/src/compiler/arm64/code-generator-arm64.cc +++ b/src/compiler/arm64/code-generator-arm64.cc @@ -189,6 +189,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArm64And32: __ And(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); break; + case kArm64Bic: + __ Bic(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kArm64Bic32: + __ Bic(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); + break; case kArm64Mul: __ Mul(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1)); break; @@ -256,12 +262,24 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { case kArm64Or32: __ Orr(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); break; - case kArm64Xor: + case kArm64Orn: + __ Orn(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kArm64Orn32: + __ Orn(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); + break; + case kArm64Eor: __ Eor(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); break; - case kArm64Xor32: + case kArm64Eor32: __ Eor(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); break; + case kArm64Eon: + __ Eon(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); + break; + case kArm64Eon32: + __ Eon(i.OutputRegister32(), i.InputRegister32(0), i.InputOperand32(1)); + break; case kArm64Sub: __ Sub(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1)); break; diff --git a/src/compiler/arm64/instruction-codes-arm64.h b/src/compiler/arm64/instruction-codes-arm64.h index 1a9fa7b122..91bca81306 100644 --- a/src/compiler/arm64/instruction-codes-arm64.h +++ b/src/compiler/arm64/instruction-codes-arm64.h @@ -16,6 +16,8 @@ namespace compiler { V(Arm64Add32) \ V(Arm64And) \ V(Arm64And32) \ + V(Arm64Bic) \ + V(Arm64Bic32) \ V(Arm64Cmp) \ V(Arm64Cmp32) \ V(Arm64Cmn) \ @@ -24,8 +26,12 @@ namespace compiler { V(Arm64Tst32) \ V(Arm64Or) \ V(Arm64Or32) \ - V(Arm64Xor) \ - V(Arm64Xor32) \ + V(Arm64Orn) \ + V(Arm64Orn32) \ + V(Arm64Eor) \ + V(Arm64Eor32) \ + V(Arm64Eon) \ + V(Arm64Eon32) \ V(Arm64Sub) \ V(Arm64Sub32) \ V(Arm64Mul) \ diff --git a/src/compiler/arm64/instruction-selector-arm64-unittest.cc b/src/compiler/arm64/instruction-selector-arm64-unittest.cc index 6ad960324c..72a9a19ff4 100644 --- a/src/compiler/arm64/instruction-selector-arm64-unittest.cc +++ b/src/compiler/arm64/instruction-selector-arm64-unittest.cc @@ -58,8 +58,8 @@ static const MachInst2 kLogicalInstructions[] = { {&RawMachineAssembler::Word64And, "Word64And", kArm64And, kMachInt64}, {&RawMachineAssembler::Word32Or, "Word32Or", kArm64Or32, kMachInt32}, {&RawMachineAssembler::Word64Or, "Word64Or", kArm64Or, kMachInt64}, - {&RawMachineAssembler::Word32Xor, "Word32Xor", kArm64Xor32, kMachInt32}, - {&RawMachineAssembler::Word64Xor, "Word64Xor", kArm64Xor, kMachInt64}}; + {&RawMachineAssembler::Word32Xor, "Word32Xor", kArm64Eor32, kMachInt32}, + {&RawMachineAssembler::Word64Xor, "Word64Xor", kArm64Eor, kMachInt64}}; // ARM64 logical immediates: contiguous set bits, rotated about a power of two @@ -1049,7 +1049,7 @@ TEST_P(InstructionSelectorComparisonTest, WithImmediate) { // Compare with 0 are turned into tst instruction. if (imm == 0) continue; StreamBuilder m(this, type, type); - m.Return((m.*cmp.constructor)(m.Parameter(0), BuildConstant(m, type, imm))); + m.Return((m.*cmp.constructor)(BuildConstant(m, type, imm), m.Parameter(0))); Stream s = m.Build(); ASSERT_EQ(1U, s.size()); EXPECT_EQ(cmp.arch_opcode, s[0]->arch_opcode()); @@ -1122,6 +1122,166 @@ TEST_F(InstructionSelectorTest, Word64EqualWithZero) { } } + +// ----------------------------------------------------------------------------- +// Miscellaneous + + +static const MachInst2 kLogicalWithNotRHSs[] = { + {&RawMachineAssembler::Word32And, "Word32And", kArm64Bic32, kMachInt32}, + {&RawMachineAssembler::Word64And, "Word64And", kArm64Bic, kMachInt64}, + {&RawMachineAssembler::Word32Or, "Word32Or", kArm64Orn32, kMachInt32}, + {&RawMachineAssembler::Word64Or, "Word64Or", kArm64Orn, kMachInt64}, + {&RawMachineAssembler::Word32Xor, "Word32Xor", kArm64Eon32, kMachInt32}, + {&RawMachineAssembler::Word64Xor, "Word64Xor", kArm64Eon, kMachInt64}}; + + +typedef InstructionSelectorTestWithParam + InstructionSelectorLogicalWithNotRHSTest; + + +TEST_P(InstructionSelectorLogicalWithNotRHSTest, Parameter) { + const MachInst2 inst = GetParam(); + const MachineType type = inst.machine_type; + // Test cases where RHS is Xor(x, -1). + { + StreamBuilder m(this, type, type, type); + if (type == kMachInt32) { + m.Return((m.*inst.constructor)( + m.Parameter(0), m.Word32Xor(m.Parameter(1), m.Int32Constant(-1)))); + } else { + ASSERT_EQ(kMachInt64, type); + m.Return((m.*inst.constructor)( + m.Parameter(0), m.Word64Xor(m.Parameter(1), m.Int64Constant(-1)))); + } + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(inst.arch_opcode, s[0]->arch_opcode()); + EXPECT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + } + { + StreamBuilder m(this, type, type, type); + if (type == kMachInt32) { + m.Return((m.*inst.constructor)( + m.Word32Xor(m.Parameter(0), m.Int32Constant(-1)), m.Parameter(1))); + } else { + ASSERT_EQ(kMachInt64, type); + m.Return((m.*inst.constructor)( + m.Word64Xor(m.Parameter(0), m.Int64Constant(-1)), m.Parameter(1))); + } + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(inst.arch_opcode, s[0]->arch_opcode()); + EXPECT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + } + // Test cases where RHS is Not(x). + { + StreamBuilder m(this, type, type, type); + if (type == kMachInt32) { + m.Return( + (m.*inst.constructor)(m.Parameter(0), m.Word32Not(m.Parameter(1)))); + } else { + ASSERT_EQ(kMachInt64, type); + m.Return( + (m.*inst.constructor)(m.Parameter(0), m.Word64Not(m.Parameter(1)))); + } + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(inst.arch_opcode, s[0]->arch_opcode()); + EXPECT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + } + { + StreamBuilder m(this, type, type, type); + if (type == kMachInt32) { + m.Return( + (m.*inst.constructor)(m.Word32Not(m.Parameter(0)), m.Parameter(1))); + } else { + ASSERT_EQ(kMachInt64, type); + m.Return( + (m.*inst.constructor)(m.Word64Not(m.Parameter(0)), m.Parameter(1))); + } + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(inst.arch_opcode, s[0]->arch_opcode()); + EXPECT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + } +} + + +INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, + InstructionSelectorLogicalWithNotRHSTest, + ::testing::ValuesIn(kLogicalWithNotRHSs)); + + +TEST_F(InstructionSelectorTest, Word32NotWithParameter) { + StreamBuilder m(this, kMachInt32, kMachInt32); + m.Return(m.Word32Not(m.Parameter(0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Not32, s[0]->arch_opcode()); + EXPECT_EQ(1U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); +} + + +TEST_F(InstructionSelectorTest, Word64NotWithParameter) { + StreamBuilder m(this, kMachInt64, kMachInt64); + m.Return(m.Word64Not(m.Parameter(0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Not, s[0]->arch_opcode()); + EXPECT_EQ(1U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); +} + + +TEST_F(InstructionSelectorTest, Word32XorMinusOneWithParameter) { + { + StreamBuilder m(this, kMachInt32, kMachInt32); + m.Return(m.Word32Xor(m.Parameter(0), m.Int32Constant(-1))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Not32, s[0]->arch_opcode()); + EXPECT_EQ(1U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + } + { + StreamBuilder m(this, kMachInt32, kMachInt32); + m.Return(m.Word32Xor(m.Int32Constant(-1), m.Parameter(0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Not32, s[0]->arch_opcode()); + EXPECT_EQ(1U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + } +} + + +TEST_F(InstructionSelectorTest, Word64XorMinusOneWithParameter) { + { + StreamBuilder m(this, kMachInt64, kMachInt64); + m.Return(m.Word64Xor(m.Parameter(0), m.Int64Constant(-1))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Not, s[0]->arch_opcode()); + EXPECT_EQ(1U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + } + { + StreamBuilder m(this, kMachInt64, kMachInt64); + m.Return(m.Word64Xor(m.Int64Constant(-1), m.Parameter(0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Not, s[0]->arch_opcode()); + EXPECT_EQ(1U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + } +} + } // namespace compiler } // namespace internal } // namespace v8 diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc index 528d18db96..730b1193d9 100644 --- a/src/compiler/arm64/instruction-selector-arm64.cc +++ b/src/compiler/arm64/instruction-selector-arm64.cc @@ -267,45 +267,119 @@ void InstructionSelector::VisitStore(Node* node) { } +template +static void VisitLogical(InstructionSelector* selector, Node* node, Matcher* m, + ArchOpcode opcode, bool left_can_cover, + bool right_can_cover, ImmediateMode imm_mode) { + Arm64OperandGenerator g(selector); + + // Map instruction to equivalent operation with inverted right input. + ArchOpcode inv_opcode = opcode; + switch (opcode) { + case kArm64And32: + inv_opcode = kArm64Bic32; + break; + case kArm64And: + inv_opcode = kArm64Bic; + break; + case kArm64Or32: + inv_opcode = kArm64Orn32; + break; + case kArm64Or: + inv_opcode = kArm64Orn; + break; + case kArm64Eor32: + inv_opcode = kArm64Eon32; + break; + case kArm64Eor: + inv_opcode = kArm64Eon; + break; + default: + UNREACHABLE(); + } + + // Select Logical(y, ~x) for Logical(Xor(x, -1), y). + if ((m->left().IsWord32Xor() || m->left().IsWord64Xor()) && left_can_cover) { + Matcher mleft(m->left().node()); + if (mleft.right().Is(-1)) { + // TODO(all): support shifted operand on right. + selector->Emit(inv_opcode, g.DefineAsRegister(node), + g.UseRegister(m->right().node()), + g.UseRegister(mleft.left().node())); + return; + } + } + + // Select Logical(x, ~y) for Logical(x, Xor(y, -1)). + if ((m->right().IsWord32Xor() || m->right().IsWord64Xor()) && + right_can_cover) { + Matcher mright(m->right().node()); + if (mright.right().Is(-1)) { + // TODO(all): support shifted operand on right. + selector->Emit(inv_opcode, g.DefineAsRegister(node), + g.UseRegister(m->left().node()), + g.UseRegister(mright.left().node())); + return; + } + } + + if (m->IsWord32Xor() && m->right().Is(-1)) { + selector->Emit(kArm64Not32, g.DefineAsRegister(node), + g.UseRegister(m->left().node())); + } else if (m->IsWord64Xor() && m->right().Is(-1)) { + selector->Emit(kArm64Not, g.DefineAsRegister(node), + g.UseRegister(m->left().node())); + } else { + VisitBinop(selector, node, opcode, imm_mode); + } +} + + void InstructionSelector::VisitWord32And(Node* node) { - VisitBinop(this, node, kArm64And32, kLogical32Imm); + Int32BinopMatcher m(node); + VisitLogical( + this, node, &m, kArm64And32, CanCover(node, m.left().node()), + CanCover(node, m.right().node()), kLogical32Imm); } void InstructionSelector::VisitWord64And(Node* node) { - VisitBinop(this, node, kArm64And, kLogical64Imm); + Int64BinopMatcher m(node); + VisitLogical( + this, node, &m, kArm64And, CanCover(node, m.left().node()), + CanCover(node, m.right().node()), kLogical64Imm); } void InstructionSelector::VisitWord32Or(Node* node) { - VisitBinop(this, node, kArm64Or32, kLogical32Imm); + Int32BinopMatcher m(node); + VisitLogical( + this, node, &m, kArm64Or32, CanCover(node, m.left().node()), + CanCover(node, m.right().node()), kLogical32Imm); } void InstructionSelector::VisitWord64Or(Node* node) { - VisitBinop(this, node, kArm64Or, kLogical64Imm); + Int64BinopMatcher m(node); + VisitLogical( + this, node, &m, kArm64Or, CanCover(node, m.left().node()), + CanCover(node, m.right().node()), kLogical64Imm); } void InstructionSelector::VisitWord32Xor(Node* node) { - Arm64OperandGenerator g(this); Int32BinopMatcher m(node); - if (m.right().Is(-1)) { - Emit(kArm64Not32, g.DefineAsRegister(node), g.UseRegister(m.left().node())); - } else { - VisitBinop(this, node, kArm64Xor32, kLogical32Imm); - } + VisitLogical( + this, node, &m, kArm64Eor32, CanCover(node, m.left().node()), + CanCover(node, m.right().node()), kLogical32Imm); } void InstructionSelector::VisitWord64Xor(Node* node) { - Arm64OperandGenerator g(this); Int64BinopMatcher m(node); - if (m.right().Is(-1)) { - Emit(kArm64Not, g.DefineAsRegister(node), g.UseRegister(m.left().node())); - } else { - VisitBinop(this, node, kArm64Xor, kLogical32Imm); - } + VisitLogical( + this, node, &m, kArm64Eor, CanCover(node, m.left().node()), + CanCover(node, m.right().node()), kLogical64Imm); }