| // 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. |
| |
| #include "test/unittests/compiler/backend/instruction-selector-unittest.h" |
| |
| #include "src/compiler/node-matchers.h" |
| #include "src/objects/objects-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| // ----------------------------------------------------------------------------- |
| // Conversions. |
| |
| |
| TEST_F(InstructionSelectorTest, ChangeFloat32ToFloat64WithParameter) { |
| StreamBuilder m(this, MachineType::Float32(), MachineType::Float64()); |
| m.Return(m.ChangeFloat32ToFloat64(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kSSEFloat32ToFloat64, s[0]->arch_opcode()); |
| EXPECT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, ChangeInt32ToInt64WithParameter) { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int32()); |
| m.Return(m.ChangeInt32ToInt64(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movsxlq, s[0]->arch_opcode()); |
| } |
| |
| TEST_F(InstructionSelectorTest, ChangeUint32ToFloat64WithParameter) { |
| StreamBuilder m(this, MachineType::Float64(), MachineType::Uint32()); |
| m.Return(m.ChangeUint32ToFloat64(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kSSEUint32ToFloat64, s[0]->arch_opcode()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, ChangeUint32ToUint64WithParameter) { |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Uint32()); |
| m.Return(m.ChangeUint32ToUint64(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movl, s[0]->arch_opcode()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, TruncateFloat64ToFloat32WithParameter) { |
| StreamBuilder m(this, MachineType::Float64(), MachineType::Float32()); |
| m.Return(m.TruncateFloat64ToFloat32(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kSSEFloat64ToFloat32, s[0]->arch_opcode()); |
| EXPECT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, TruncateInt64ToInt32WithParameter) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int64()); |
| m.Return(m.TruncateInt64ToInt32(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movl, s[0]->arch_opcode()); |
| } |
| |
| namespace { |
| struct LoadWithToInt64Extension { |
| MachineType type; |
| ArchOpcode expected_opcode; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, |
| const LoadWithToInt64Extension& i32toi64) { |
| return os << i32toi64.type; |
| } |
| |
| static const LoadWithToInt64Extension kLoadWithToInt64Extensions[] = { |
| {MachineType::Int8(), kX64Movsxbq}, |
| {MachineType::Uint8(), kX64Movzxbq}, |
| {MachineType::Int16(), kX64Movsxwq}, |
| {MachineType::Uint16(), kX64Movzxwq}, |
| {MachineType::Int32(), kX64Movsxlq}}; |
| |
| } // namespace |
| |
| using InstructionSelectorChangeInt32ToInt64Test = |
| InstructionSelectorTestWithParam<LoadWithToInt64Extension>; |
| |
| TEST_P(InstructionSelectorChangeInt32ToInt64Test, ChangeInt32ToInt64WithLoad) { |
| const LoadWithToInt64Extension extension = GetParam(); |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Pointer()); |
| m.Return(m.ChangeInt32ToInt64(m.Load(extension.type, m.Parameter(0)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(extension.expected_opcode, s[0]->arch_opcode()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorChangeInt32ToInt64Test, |
| ::testing::ValuesIn(kLoadWithToInt64Extensions)); |
| |
| // ----------------------------------------------------------------------------- |
| // Loads and stores |
| |
| |
| namespace { |
| |
| struct MemoryAccess { |
| MachineType type; |
| ArchOpcode load_opcode; |
| ArchOpcode store_opcode; |
| }; |
| |
| |
| std::ostream& operator<<(std::ostream& os, const MemoryAccess& memacc) { |
| return os << memacc.type; |
| } |
| |
| |
| static const MemoryAccess kMemoryAccesses[] = { |
| {MachineType::Int8(), kX64Movsxbl, kX64Movb}, |
| {MachineType::Uint8(), kX64Movzxbl, kX64Movb}, |
| {MachineType::Int16(), kX64Movsxwl, kX64Movw}, |
| {MachineType::Uint16(), kX64Movzxwl, kX64Movw}, |
| {MachineType::Int32(), kX64Movl, kX64Movl}, |
| {MachineType::Uint32(), kX64Movl, kX64Movl}, |
| {MachineType::Int64(), kX64Movq, kX64Movq}, |
| {MachineType::Uint64(), kX64Movq, kX64Movq}, |
| {MachineType::Float32(), kX64Movss, kX64Movss}, |
| {MachineType::Float64(), kX64Movsd, kX64Movsd}}; |
| |
| } // namespace |
| |
| using InstructionSelectorMemoryAccessTest = |
| InstructionSelectorTestWithParam<MemoryAccess>; |
| |
| TEST_P(InstructionSelectorMemoryAccessTest, LoadWithParameters) { |
| const MemoryAccess memacc = GetParam(); |
| StreamBuilder m(this, memacc.type, MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.Load(memacc.type, m.Parameter(0), m.Parameter(1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(memacc.load_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| |
| TEST_P(InstructionSelectorMemoryAccessTest, StoreWithParameters) { |
| const MemoryAccess memacc = GetParam(); |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer(), |
| MachineType::Int32(), memacc.type); |
| m.Store(memacc.type.representation(), m.Parameter(0), m.Parameter(1), |
| m.Parameter(2), kNoWriteBarrier); |
| m.Return(m.Int32Constant(0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(memacc.store_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(0U, s[0]->OutputCount()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorMemoryAccessTest, |
| ::testing::ValuesIn(kMemoryAccesses)); |
| |
| // ----------------------------------------------------------------------------- |
| // ChangeUint32ToUint64. |
| |
| |
| namespace { |
| |
| using Constructor = Node* (RawMachineAssembler::*)(Node*, Node*); |
| |
| struct BinaryOperation { |
| Constructor constructor; |
| const char* constructor_name; |
| }; |
| |
| |
| std::ostream& operator<<(std::ostream& os, const BinaryOperation& bop) { |
| return os << bop.constructor_name; |
| } |
| |
| |
| const BinaryOperation kWord32BinaryOperations[] = { |
| {&RawMachineAssembler::Word32And, "Word32And"}, |
| {&RawMachineAssembler::Word32Or, "Word32Or"}, |
| {&RawMachineAssembler::Word32Xor, "Word32Xor"}, |
| {&RawMachineAssembler::Word32Shl, "Word32Shl"}, |
| {&RawMachineAssembler::Word32Shr, "Word32Shr"}, |
| {&RawMachineAssembler::Word32Sar, "Word32Sar"}, |
| {&RawMachineAssembler::Word32Ror, "Word32Ror"}, |
| {&RawMachineAssembler::Word32Equal, "Word32Equal"}, |
| {&RawMachineAssembler::Int32Add, "Int32Add"}, |
| {&RawMachineAssembler::Int32Sub, "Int32Sub"}, |
| {&RawMachineAssembler::Int32Mul, "Int32Mul"}, |
| {&RawMachineAssembler::Int32MulHigh, "Int32MulHigh"}, |
| {&RawMachineAssembler::Int32Div, "Int32Div"}, |
| {&RawMachineAssembler::Int32LessThan, "Int32LessThan"}, |
| {&RawMachineAssembler::Int32LessThanOrEqual, "Int32LessThanOrEqual"}, |
| {&RawMachineAssembler::Int32Mod, "Int32Mod"}, |
| {&RawMachineAssembler::Uint32Div, "Uint32Div"}, |
| {&RawMachineAssembler::Uint32LessThan, "Uint32LessThan"}, |
| {&RawMachineAssembler::Uint32LessThanOrEqual, "Uint32LessThanOrEqual"}, |
| {&RawMachineAssembler::Uint32Mod, "Uint32Mod"}}; |
| |
| } // namespace |
| |
| using InstructionSelectorChangeUint32ToUint64Test = |
| InstructionSelectorTestWithParam<BinaryOperation>; |
| |
| TEST_P(InstructionSelectorChangeUint32ToUint64Test, ChangeUint32ToUint64) { |
| const BinaryOperation& bop = GetParam(); |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return(m.ChangeUint32ToUint64((m.*bop.constructor)(p0, p1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorChangeUint32ToUint64Test, |
| ::testing::ValuesIn(kWord32BinaryOperations)); |
| |
| // ----------------------------------------------------------------------------- |
| // CanElideChangeUint32ToUint64 |
| |
| namespace { |
| |
| template <typename T> |
| struct MachInst { |
| T constructor; |
| const char* constructor_name; |
| ArchOpcode arch_opcode; |
| MachineType machine_type; |
| }; |
| |
| using MachInst2 = MachInst<Node* (RawMachineAssembler::*)(Node*, Node*)>; |
| |
| // X64 instructions that clear the top 32 bits of the destination. |
| const MachInst2 kCanElideChangeUint32ToUint64[] = { |
| {&RawMachineAssembler::Word32And, "Word32And", kX64And32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Word32Or, "Word32Or", kX64Or32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Word32Xor, "Word32Xor", kX64Xor32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Word32Shl, "Word32Shl", kX64Shl32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Word32Shr, "Word32Shr", kX64Shr32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Word32Sar, "Word32Sar", kX64Sar32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Word32Ror, "Word32Ror", kX64Ror32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Word32Equal, "Word32Equal", kX64Cmp32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Int32Add, "Int32Add", kX64Lea32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int32Sub, "Int32Sub", kX64Sub32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int32Mul, "Int32Mul", kX64Imul32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int32MulHigh, "Int32MulHigh", kX64ImulHigh32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int32Div, "Int32Div", kX64Idiv32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int32LessThan, "Int32LessThan", kX64Cmp32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int32LessThanOrEqual, "Int32LessThanOrEqual", |
| kX64Cmp32, MachineType::Int32()}, |
| {&RawMachineAssembler::Int32Mod, "Int32Mod", kX64Idiv32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Uint32Div, "Uint32Div", kX64Udiv32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Uint32LessThan, "Uint32LessThan", kX64Cmp32, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Uint32LessThanOrEqual, "Uint32LessThanOrEqual", |
| kX64Cmp32, MachineType::Uint32()}, |
| {&RawMachineAssembler::Uint32Mod, "Uint32Mod", kX64Udiv32, |
| MachineType::Uint32()}, |
| }; |
| |
| } // namespace |
| |
| using InstructionSelectorElidedChangeUint32ToUint64Test = |
| InstructionSelectorTestWithParam<MachInst2>; |
| |
| TEST_P(InstructionSelectorElidedChangeUint32ToUint64Test, Parameter) { |
| const MachInst2 binop = GetParam(); |
| StreamBuilder m(this, MachineType::Uint64(), binop.machine_type, |
| binop.machine_type); |
| m.Return(m.ChangeUint32ToUint64( |
| (m.*binop.constructor)(m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| // Make sure the `ChangeUint32ToUint64` node turned into a no-op. |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(binop.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorElidedChangeUint32ToUint64Test, |
| ::testing::ValuesIn(kCanElideChangeUint32ToUint64)); |
| |
| // ChangeUint32ToUint64AfterLoad |
| TEST_F(InstructionSelectorTest, ChangeUint32ToUint64AfterLoad) { |
| // For each case, make sure the `ChangeUint32ToUint64` node turned into a |
| // no-op. |
| |
| // movzxbl |
| { |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeUint32ToUint64( |
| m.Load(MachineType::Uint8(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movzxbl, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| // movsxbl |
| { |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeUint32ToUint64( |
| m.Load(MachineType::Int8(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movsxbl, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| // movzxwl |
| { |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeUint32ToUint64( |
| m.Load(MachineType::Uint16(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movzxwl, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| // movsxwl |
| { |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeUint32ToUint64( |
| m.Load(MachineType::Int16(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movsxwl, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // TruncateInt64ToInt32. |
| |
| |
| TEST_F(InstructionSelectorTest, TruncateInt64ToInt32WithWord64Sar) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int64()); |
| Node* const p = m.Parameter(0); |
| Node* const t = m.TruncateInt64ToInt32(m.Word64Sar(p, m.Int64Constant(32))); |
| m.Return(t); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Shr, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(32, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_TRUE(s.IsSameAsFirst(s[0]->OutputAt(0))); |
| EXPECT_EQ(s.ToVreg(t), s.ToVreg(s[0]->OutputAt(0))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, TruncateInt64ToInt32WithWord64Shr) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int64()); |
| Node* const p = m.Parameter(0); |
| Node* const t = m.TruncateInt64ToInt32(m.Word64Shr(p, m.Int64Constant(32))); |
| m.Return(t); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Shr, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(32, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_TRUE(s.IsSameAsFirst(s[0]->OutputAt(0))); |
| EXPECT_EQ(s.ToVreg(t), s.ToVreg(s[0]->OutputAt(0))); |
| } |
| |
| |
| // ----------------------------------------------------------------------------- |
| // Addition. |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddWithInt32ParametersLea) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const a0 = m.Int32Add(p0, p1); |
| // Additional uses of input to add chooses lea |
| Node* const a1 = m.Int32Div(p0, p1); |
| m.Return(m.Int32Div(a0, a1)); |
| Stream s = m.Build(); |
| ASSERT_EQ(3U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddConstantAsLeaSingle) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c0 = m.Int32Constant(15); |
| // If one of the add's operands is only used once, use an "leal", even though |
| // an "addl" could be used. The "leal" has proven faster--out best guess is |
| // that it gives the register allocation more freedom and it doesn't set |
| // flags, reducing pressure in the CPU's pipeline. If we're lucky with |
| // register allocation, then code generation will select an "addl" later for |
| // the cases that have been measured to be faster. |
| Node* const v0 = m.Int32Add(p0, c0); |
| m.Return(v0); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddConstantAsAdd) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c0 = m.Int32Constant(1); |
| // If there is only a single use of an add's input and the immediate constant |
| // for the add is 1, don't use an inc. It is much slower on modern Intel |
| // architectures. |
| m.Return(m.Int32Add(p0, c0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddConstantAsLeaDouble) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c0 = m.Int32Constant(15); |
| // A second use of an add's input uses lea |
| Node* const a0 = m.Int32Add(p0, c0); |
| m.Return(m.Int32Div(a0, p0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddCommutedConstantAsLeaSingle) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c0 = m.Int32Constant(15); |
| // If one of the add's operands is only used once, use an "leal", even though |
| // an "addl" could be used. The "leal" has proven faster--out best guess is |
| // that it gives the register allocation more freedom and it doesn't set |
| // flags, reducing pressure in the CPU's pipeline. If we're lucky with |
| // register allocation, then code generation will select an "addl" later for |
| // the cases that have been measured to be faster. |
| m.Return(m.Int32Add(c0, p0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddCommutedConstantAsLeaDouble) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c0 = m.Int32Constant(15); |
| // A second use of an add's input uses lea |
| Node* const a0 = m.Int32Add(c0, p0); |
| USE(a0); |
| m.Return(m.Int32Div(a0, p0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddSimpleAsAdd) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| // If one of the add's operands is only used once, use an "leal", even though |
| // an "addl" could be used. The "leal" has proven faster--out best guess is |
| // that it gives the register allocation more freedom and it doesn't set |
| // flags, reducing pressure in the CPU's pipeline. If we're lucky with |
| // register allocation, then code generation will select an "addl" later for |
| // the cases that have been measured to be faster. |
| m.Return(m.Int32Add(p0, p1)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddSimpleAsLea) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| // If all of of the add's operands are used multiple times, use an "leal". |
| Node* const v1 = m.Int32Add(p0, p1); |
| m.Return(m.Int32Add(m.Int32Add(v1, p1), p0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(3U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2Mul) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); |
| m.Return(m.Int32Add(p0, s0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddCommutedScaled2Mul) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); |
| m.Return(m.Int32Add(s0, p0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2Shl) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Word32Shl(p1, m.Int32Constant(1)); |
| m.Return(m.Int32Add(p0, s0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddCommutedScaled2Shl) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Word32Shl(p1, m.Int32Constant(1)); |
| m.Return(m.Int32Add(s0, p0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled4Mul) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(4)); |
| m.Return(m.Int32Add(p0, s0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR4, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled4Shl) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Word32Shl(p1, m.Int32Constant(2)); |
| m.Return(m.Int32Add(p0, s0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR4, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled8Mul) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(8)); |
| m.Return(m.Int32Add(p0, s0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR8, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled8Shl) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Word32Shl(p1, m.Int32Constant(3)); |
| m.Return(m.Int32Add(p0, s0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR8, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstant) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle1) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(p0, m.Int32Add(s0, c0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle2) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(s0, m.Int32Add(c0, p0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle3) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(m.Int32Add(s0, c0), p0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle4) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(m.Int32Add(c0, p0), s0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle5) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(m.Int32Add(p0, s0), c0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2ShlWithConstant) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Word32Shl(p1, m.Int32Constant(1)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled4MulWithConstant) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(4)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR4I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled4ShlWithConstant) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Word32Shl(p1, m.Int32Constant(2)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR4I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled8MulWithConstant) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(8)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR8I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled8ShlWithConstant) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const s0 = m.Word32Shl(p1, m.Int32Constant(3)); |
| Node* const c0 = m.Int32Constant(15); |
| m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR8I, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32SubConstantAsSub) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c0 = m.Int32Constant(-1); |
| // If there is only a single use of on of the sub's non-constant input, use a |
| // "subl" instruction. |
| m.Return(m.Int32Sub(p0, c0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32SubConstantAsLea) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c0 = m.Int32Constant(-1); |
| // If there are multiple uses of on of the sub's non-constant input, use a |
| // "leal" instruction. |
| Node* const v0 = m.Int32Sub(p0, c0); |
| m.Return(m.Int32Div(p0, v0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32AddScaled2Other) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const p2 = m.Parameter(2); |
| Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); |
| Node* const a0 = m.Int32Add(s0, p2); |
| Node* const a1 = m.Int32Add(p0, a0); |
| m.Return(a1); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_EQ(s.ToVreg(a0), s.ToVreg(s[0]->OutputAt(0))); |
| ASSERT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(kX64Lea32, s[1]->arch_opcode()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[1]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(a0), s.ToVreg(s[1]->InputAt(1))); |
| EXPECT_EQ(s.ToVreg(a1), s.ToVreg(s[1]->OutputAt(0))); |
| } |
| |
| |
| // ----------------------------------------------------------------------------- |
| // Multiplication. |
| |
| |
| TEST_F(InstructionSelectorTest, Int32MulWithInt32MulWithParameters) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const m0 = m.Int32Mul(p0, p1); |
| m.Return(m.Int32Mul(m0, p0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kX64Imul32, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(m0), s.ToVreg(s[0]->OutputAt(0))); |
| EXPECT_EQ(kX64Imul32, s[1]->arch_opcode()); |
| ASSERT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[1]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(m0), s.ToVreg(s[1]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32MulHigh) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const n = m.Int32MulHigh(p0, p1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64ImulHigh32, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_TRUE(s.IsFixed(s[0]->InputAt(0), rax)); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(!s.IsUsedAtStart(s[0]->InputAt(1))); |
| ASSERT_LE(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| EXPECT_TRUE(s.IsFixed(s[0]->OutputAt(0), rdx)); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Uint32MulHigh) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const n = m.Uint32MulHigh(p0, p1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64UmulHigh32, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_TRUE(s.IsFixed(s[0]->InputAt(0), rax)); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(!s.IsUsedAtStart(s[0]->InputAt(1))); |
| ASSERT_LE(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| EXPECT_TRUE(s.IsFixed(s[0]->OutputAt(0), rdx)); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32Mul2BecomesLea) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c1 = m.Int32Constant(2); |
| Node* const n = m.Int32Mul(p0, c1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32Mul3BecomesLea) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c1 = m.Int32Constant(3); |
| Node* const n = m.Int32Mul(p0, c1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32Mul4BecomesLea) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c1 = m.Int32Constant(4); |
| Node* const n = m.Int32Mul(p0, c1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_M4, s[0]->addressing_mode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32Mul5BecomesLea) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c1 = m.Int32Constant(5); |
| Node* const n = m.Int32Mul(p0, c1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR4, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32Mul8BecomesLea) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c1 = m.Int32Constant(8); |
| Node* const n = m.Int32Mul(p0, c1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_M8, s[0]->addressing_mode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32Mul9BecomesLea) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c1 = m.Int32Constant(9); |
| Node* const n = m.Int32Mul(p0, c1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR8, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| // ----------------------------------------------------------------------------- |
| // Word32Shl. |
| |
| |
| TEST_F(InstructionSelectorTest, Int32Shl1BecomesLea) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c1 = m.Int32Constant(1); |
| Node* const n = m.Word32Shl(p0, c1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32Shl2BecomesLea) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c1 = m.Int32Constant(2); |
| Node* const n = m.Word32Shl(p0, c1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_M4, s[0]->addressing_mode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Int32Shl4BecomesLea) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32(), |
| MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const c1 = m.Int32Constant(3); |
| Node* const n = m.Word32Shl(p0, c1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_M8, s[0]->addressing_mode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // Binops with a memory operand. |
| |
| TEST_F(InstructionSelectorTest, LoadCmp32) { |
| { |
| // Word32Equal(Load[Int8](p0, p1), Int32Constant(0)) -> cmpb [p0,p1], 0 |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Word32Equal(m.Load(MachineType::Int8(), p0, p1), m.Int32Constant(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Cmp8, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| { |
| // Word32Equal(Load[Uint8](p0, p1), Int32Constant(0)) -> cmpb [p0,p1], 0 |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return(m.Word32Equal(m.Load(MachineType::Uint8(), p0, p1), |
| m.Int32Constant(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Cmp8, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| { |
| // Word32Equal(Load[Int16](p0, p1), Int32Constant(0)) -> cmpw [p0,p1], 0 |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return(m.Word32Equal(m.Load(MachineType::Int16(), p0, p1), |
| m.Int32Constant(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Cmp16, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| { |
| // Word32Equal(Load[Uint16](p0, p1), Int32Constant(0)) -> cmpw [p0,p1], 0 |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return(m.Word32Equal(m.Load(MachineType::Uint16(), p0, p1), |
| m.Int32Constant(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Cmp16, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| { |
| // Word32Equal(Load[Int32](p0, p1), Int32Constant(0)) -> cmpl [p0,p1], 0 |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return(m.Word32Equal(m.Load(MachineType::Int32(), p0, p1), |
| m.Int32Constant(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Cmp32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| { |
| // Word32Equal(Load[Uint32](p0, p1), Int32Constant(0)) -> cmpl [p0,p1], 0 |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return(m.Word32Equal(m.Load(MachineType::Uint32(), p0, p1), |
| m.Int32Constant(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Cmp32, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MR1, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); |
| } |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadAnd32) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Word32And(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64And32, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadOr32) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Word32Or(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Or32, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadXor32) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Word32Xor(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Xor32, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadAdd32) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Int32Add(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| // Use lea instead of add, so memory operand is invalid. |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kX64Movl, s[0]->arch_opcode()); |
| EXPECT_EQ(kX64Lea32, s[1]->arch_opcode()); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadSub32) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(), |
| MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Int32Sub(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Sub32, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadAnd64) { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Word64And(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64And, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadOr64) { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Word64Or(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Or, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadXor64) { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Word64Xor(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Xor, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadAdd64) { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Int64Add(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| // Use lea instead of add, so memory operand is invalid. |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kX64Movq, s[0]->arch_opcode()); |
| EXPECT_EQ(kX64Lea, s[1]->arch_opcode()); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadSub64) { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(), |
| MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| m.Return( |
| m.Int64Sub(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Sub, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // Floating point operations. |
| |
| TEST_F(InstructionSelectorTest, Float32Abs) { |
| { |
| StreamBuilder m(this, MachineType::Float32(), MachineType::Float32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Float32Abs(p0); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kSSEFloat32Abs, s[0]->arch_opcode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_TRUE(s.IsSameAsFirst(s[0]->Output())); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| EXPECT_EQ(kFlags_none, s[0]->flags_mode()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Float32(), MachineType::Float32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Float32Abs(p0); |
| m.Return(n); |
| Stream s = m.Build(AVX); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kAVXFloat32Abs, s[0]->arch_opcode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| EXPECT_EQ(kFlags_none, s[0]->flags_mode()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Float64Abs) { |
| { |
| StreamBuilder m(this, MachineType::Float64(), MachineType::Float64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Float64Abs(p0); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kSSEFloat64Abs, s[0]->arch_opcode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_TRUE(s.IsSameAsFirst(s[0]->Output())); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| EXPECT_EQ(kFlags_none, s[0]->flags_mode()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Float64(), MachineType::Float64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Float64Abs(p0); |
| m.Return(n); |
| Stream s = m.Build(AVX); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kAVXFloat64Abs, s[0]->arch_opcode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| EXPECT_EQ(kFlags_none, s[0]->flags_mode()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Float64BinopArithmetic) { |
| { |
| StreamBuilder m(this, MachineType::Float64(), MachineType::Float64(), |
| MachineType::Float64()); |
| Node* add = m.Float64Add(m.Parameter(0), m.Parameter(1)); |
| Node* mul = m.Float64Mul(add, m.Parameter(1)); |
| Node* sub = m.Float64Sub(mul, add); |
| Node* ret = m.Float64Div(mul, sub); |
| m.Return(ret); |
| Stream s = m.Build(AVX); |
| ASSERT_EQ(4U, s.size()); |
| EXPECT_EQ(kAVXFloat64Add, s[0]->arch_opcode()); |
| EXPECT_EQ(kAVXFloat64Mul, s[1]->arch_opcode()); |
| EXPECT_EQ(kAVXFloat64Sub, s[2]->arch_opcode()); |
| EXPECT_EQ(kAVXFloat64Div, s[3]->arch_opcode()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Float64(), MachineType::Float64(), |
| MachineType::Float64()); |
| Node* add = m.Float64Add(m.Parameter(0), m.Parameter(1)); |
| Node* mul = m.Float64Mul(add, m.Parameter(1)); |
| Node* sub = m.Float64Sub(mul, add); |
| Node* ret = m.Float64Div(mul, sub); |
| m.Return(ret); |
| Stream s = m.Build(); |
| ASSERT_EQ(4U, s.size()); |
| EXPECT_EQ(kSSEFloat64Add, s[0]->arch_opcode()); |
| EXPECT_EQ(kSSEFloat64Mul, s[1]->arch_opcode()); |
| EXPECT_EQ(kSSEFloat64Sub, s[2]->arch_opcode()); |
| EXPECT_EQ(kSSEFloat64Div, s[3]->arch_opcode()); |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // Miscellaneous. |
| |
| |
| TEST_F(InstructionSelectorTest, Word64ShlWithChangeInt32ToInt64) { |
| TRACED_FORRANGE(int64_t, x, 32, 63) { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Word64Shl(m.ChangeInt32ToInt64(p0), m.Int64Constant(x)); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Shl, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(x, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_TRUE(s.IsSameAsFirst(s[0]->Output())); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word64ShlWithChangeUint32ToUint64) { |
| TRACED_FORRANGE(int64_t, x, 32, 63) { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Word64Shl(m.ChangeUint32ToUint64(p0), m.Int64Constant(x)); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Shl, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(x, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_TRUE(s.IsSameAsFirst(s[0]->Output())); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| } |
| } |
| |
| TEST_F(InstructionSelectorTest, Word32AndWith0xFF) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Word32And(p0, m.Int32Constant(0xFF)); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movzxbl, s[0]->arch_opcode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Word32And(m.Int32Constant(0xFF), p0); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movzxbl, s[0]->arch_opcode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| } |
| } |
| |
| TEST_F(InstructionSelectorTest, Word32AndWith0xFFFF) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Word32And(p0, m.Int32Constant(0xFFFF)); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movzxwl, s[0]->arch_opcode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Word32And(m.Int32Constant(0xFFFF), p0); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movzxwl, s[0]->arch_opcode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word32Clz) { |
| StreamBuilder m(this, MachineType::Uint32(), MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Word32Clz(p0); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Lzcnt32, s[0]->arch_opcode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadAndWord64ShiftRight32) { |
| { |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Uint32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const load = m.Load(MachineType::Uint64(), p0); |
| Node* const shift = m.Word64Shr(load, m.Int32Constant(32)); |
| m.Return(shift); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movl, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(4, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(shift), s.ToVreg(s[0]->Output())); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const load = m.Load(MachineType::Int64(), p0); |
| Node* const shift = m.Word64Sar(load, m.Int32Constant(32)); |
| m.Return(shift); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movsxlq, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(4, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(shift), s.ToVreg(s[0]->Output())); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const load = m.Load(MachineType::Int64(), p0); |
| Node* const shift = m.Word64Sar(load, m.Int32Constant(32)); |
| Node* const truncate = m.TruncateInt64ToInt32(shift); |
| m.Return(truncate); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Movl, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(4, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(shift), s.ToVreg(s[0]->Output())); |
| } |
| } |
| |
| TEST_F(InstructionSelectorTest, StackCheck0) { |
| ExternalReference js_stack_limit = |
| ExternalReference::Create(isolate()->stack_guard()->address_of_jslimit()); |
| StreamBuilder m(this, MachineType::Int32()); |
| Node* const sp = m.LoadStackPointer(); |
| Node* const stack_limit = |
| m.Load(MachineType::Pointer(), m.ExternalConstant(js_stack_limit)); |
| Node* const interrupt = m.UintPtrLessThan(sp, stack_limit); |
| |
| RawMachineLabel if_true, if_false; |
| m.Branch(interrupt, &if_true, &if_false); |
| |
| m.Bind(&if_true); |
| m.Return(m.Int32Constant(1)); |
| |
| m.Bind(&if_false); |
| m.Return(m.Int32Constant(0)); |
| |
| Stream s = m.Build(); |
| |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64Cmp, s[0]->arch_opcode()); |
| EXPECT_EQ(4U, s[0]->InputCount()); |
| EXPECT_EQ(0U, s[0]->OutputCount()); |
| } |
| |
| TEST_F(InstructionSelectorTest, StackCheck1) { |
| ExternalReference js_stack_limit = |
| ExternalReference::Create(isolate()->stack_guard()->address_of_jslimit()); |
| StreamBuilder m(this, MachineType::Int32()); |
| Node* const sp = m.LoadStackPointer(); |
| Node* const stack_limit = |
| m.Load(MachineType::Pointer(), m.ExternalConstant(js_stack_limit)); |
| Node* const sp_within_limit = m.UintPtrLessThan(stack_limit, sp); |
| |
| RawMachineLabel if_true, if_false; |
| m.Branch(sp_within_limit, &if_true, &if_false); |
| |
| m.Bind(&if_true); |
| m.Return(m.Int32Constant(1)); |
| |
| m.Bind(&if_false); |
| m.Return(m.Int32Constant(0)); |
| |
| Stream s = m.Build(); |
| |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kX64StackCheck, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(0U, s[0]->OutputCount()); |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |