| // 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/objects/objects-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| namespace { |
| template <typename T> |
| struct MachInst { |
| T constructor; |
| const char* constructor_name; |
| ArchOpcode arch_opcode; |
| MachineType machine_type; |
| }; |
| |
| template <typename T> |
| std::ostream& operator<<(std::ostream& os, const MachInst<T>& mi) { |
| return os << mi.constructor_name; |
| } |
| |
| using MachInst1 = MachInst<Node* (RawMachineAssembler::*)(Node*)>; |
| using MachInst2 = MachInst<Node* (RawMachineAssembler::*)(Node*, Node*)>; |
| |
| // To avoid duplicated code IntCmp helper structure |
| // is created. It contains MachInst2 with two nodes and expected_size |
| // because different cmp instructions have different size. |
| struct IntCmp { |
| MachInst2 mi; |
| uint32_t expected_size; |
| }; |
| |
| struct FPCmp { |
| MachInst2 mi; |
| FlagsCondition cond; |
| }; |
| |
| const FPCmp kFPCmpInstructions[] = { |
| {{&RawMachineAssembler::Float64Equal, "Float64Equal", kMips64CmpD, |
| MachineType::Float64()}, |
| kEqual}, |
| {{&RawMachineAssembler::Float64LessThan, "Float64LessThan", kMips64CmpD, |
| MachineType::Float64()}, |
| kUnsignedLessThan}, |
| {{&RawMachineAssembler::Float64LessThanOrEqual, "Float64LessThanOrEqual", |
| kMips64CmpD, MachineType::Float64()}, |
| kUnsignedLessThanOrEqual}, |
| {{&RawMachineAssembler::Float64GreaterThan, "Float64GreaterThan", |
| kMips64CmpD, MachineType::Float64()}, |
| kUnsignedLessThan}, |
| {{&RawMachineAssembler::Float64GreaterThanOrEqual, |
| "Float64GreaterThanOrEqual", kMips64CmpD, MachineType::Float64()}, |
| kUnsignedLessThanOrEqual}}; |
| |
| struct Conversion { |
| // The machine_type field in MachInst1 represents the destination type. |
| MachInst1 mi; |
| MachineType src_machine_type; |
| }; |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Logical instructions. |
| // ---------------------------------------------------------------------------- |
| |
| const MachInst2 kLogicalInstructions[] = { |
| {&RawMachineAssembler::Word32And, "Word32And", kMips64And32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Word64And, "Word64And", kMips64And, |
| MachineType::Int64()}, |
| {&RawMachineAssembler::Word32Or, "Word32Or", kMips64Or32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Word64Or, "Word64Or", kMips64Or, |
| MachineType::Int64()}, |
| {&RawMachineAssembler::Word32Xor, "Word32Xor", kMips64Xor32, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Word64Xor, "Word64Xor", kMips64Xor, |
| MachineType::Int64()}}; |
| |
| // ---------------------------------------------------------------------------- |
| // Shift instructions. |
| // ---------------------------------------------------------------------------- |
| |
| |
| const MachInst2 kShiftInstructions[] = { |
| {&RawMachineAssembler::Word32Shl, "Word32Shl", kMips64Shl, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Word64Shl, "Word64Shl", kMips64Dshl, |
| MachineType::Int64()}, |
| {&RawMachineAssembler::Word32Shr, "Word32Shr", kMips64Shr, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Word64Shr, "Word64Shr", kMips64Dshr, |
| MachineType::Int64()}, |
| {&RawMachineAssembler::Word32Sar, "Word32Sar", kMips64Sar, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Word64Sar, "Word64Sar", kMips64Dsar, |
| MachineType::Int64()}, |
| {&RawMachineAssembler::Word32Ror, "Word32Ror", kMips64Ror, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Word64Ror, "Word64Ror", kMips64Dror, |
| MachineType::Int64()}}; |
| |
| |
| // ---------------------------------------------------------------------------- |
| // MUL/DIV instructions. |
| // ---------------------------------------------------------------------------- |
| |
| |
| const MachInst2 kMulDivInstructions[] = { |
| {&RawMachineAssembler::Int32Mul, "Int32Mul", kMips64Mul, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int32Div, "Int32Div", kMips64Div, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Uint32Div, "Uint32Div", kMips64DivU, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Int64Mul, "Int64Mul", kMips64Dmul, |
| MachineType::Int64()}, |
| {&RawMachineAssembler::Int64Div, "Int64Div", kMips64Ddiv, |
| MachineType::Int64()}, |
| {&RawMachineAssembler::Uint64Div, "Uint64Div", kMips64DdivU, |
| MachineType::Uint64()}, |
| {&RawMachineAssembler::Float64Mul, "Float64Mul", kMips64MulD, |
| MachineType::Float64()}, |
| {&RawMachineAssembler::Float64Div, "Float64Div", kMips64DivD, |
| MachineType::Float64()}}; |
| |
| |
| // ---------------------------------------------------------------------------- |
| // MOD instructions. |
| // ---------------------------------------------------------------------------- |
| |
| |
| const MachInst2 kModInstructions[] = { |
| {&RawMachineAssembler::Int32Mod, "Int32Mod", kMips64Mod, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Uint32Mod, "Uint32Mod", kMips64ModU, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Float64Mod, "Float64Mod", kMips64ModD, |
| MachineType::Float64()}}; |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Arithmetic FPU instructions. |
| // ---------------------------------------------------------------------------- |
| |
| |
| const MachInst2 kFPArithInstructions[] = { |
| {&RawMachineAssembler::Float64Add, "Float64Add", kMips64AddD, |
| MachineType::Float64()}, |
| {&RawMachineAssembler::Float64Sub, "Float64Sub", kMips64SubD, |
| MachineType::Float64()}}; |
| |
| |
| // ---------------------------------------------------------------------------- |
| // IntArithTest instructions, two nodes. |
| // ---------------------------------------------------------------------------- |
| |
| |
| const MachInst2 kAddSubInstructions[] = { |
| {&RawMachineAssembler::Int32Add, "Int32Add", kMips64Add, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int64Add, "Int64Add", kMips64Dadd, |
| MachineType::Int64()}, |
| {&RawMachineAssembler::Int32Sub, "Int32Sub", kMips64Sub, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int64Sub, "Int64Sub", kMips64Dsub, |
| MachineType::Int64()}}; |
| |
| |
| // ---------------------------------------------------------------------------- |
| // IntArithTest instructions, one node. |
| // ---------------------------------------------------------------------------- |
| |
| |
| const MachInst1 kAddSubOneInstructions[] = { |
| {&RawMachineAssembler::Int32Neg, "Int32Neg", kMips64Sub, |
| MachineType::Int32()}, |
| {&RawMachineAssembler::Int64Neg, "Int64Neg", kMips64Dsub, |
| MachineType::Int64()}}; |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Arithmetic compare instructions. |
| // ---------------------------------------------------------------------------- |
| |
| |
| const IntCmp kCmpInstructions[] = { |
| {{&RawMachineAssembler::WordEqual, "WordEqual", kMips64Cmp, |
| MachineType::Int64()}, |
| 1U}, |
| {{&RawMachineAssembler::WordNotEqual, "WordNotEqual", kMips64Cmp, |
| MachineType::Int64()}, |
| 1U}, |
| {{&RawMachineAssembler::Word32Equal, "Word32Equal", kMips64Cmp, |
| MachineType::Int32()}, |
| 1U}, |
| {{&RawMachineAssembler::Word32NotEqual, "Word32NotEqual", kMips64Cmp, |
| MachineType::Int32()}, |
| 1U}, |
| {{&RawMachineAssembler::Int32LessThan, "Int32LessThan", kMips64Cmp, |
| MachineType::Int32()}, |
| 1U}, |
| {{&RawMachineAssembler::Int32LessThanOrEqual, "Int32LessThanOrEqual", |
| kMips64Cmp, MachineType::Int32()}, |
| 1U}, |
| {{&RawMachineAssembler::Int32GreaterThan, "Int32GreaterThan", kMips64Cmp, |
| MachineType::Int32()}, |
| 1U}, |
| {{&RawMachineAssembler::Int32GreaterThanOrEqual, "Int32GreaterThanOrEqual", |
| kMips64Cmp, MachineType::Int32()}, |
| 1U}, |
| {{&RawMachineAssembler::Uint32LessThan, "Uint32LessThan", kMips64Cmp, |
| MachineType::Uint32()}, |
| 1U}, |
| {{&RawMachineAssembler::Uint32LessThanOrEqual, "Uint32LessThanOrEqual", |
| kMips64Cmp, MachineType::Uint32()}, |
| 1U}}; |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Conversion instructions. |
| // ---------------------------------------------------------------------------- |
| |
| const Conversion kConversionInstructions[] = { |
| // Conversion instructions are related to machine_operator.h: |
| // FPU conversions: |
| // Convert representation of integers between float64 and int32/uint32. |
| // The precise rounding mode and handling of out of range inputs are *not* |
| // defined for these operators, since they are intended only for use with |
| // integers. |
| // mips instructions: |
| // mtc1, cvt.d.w |
| {{&RawMachineAssembler::ChangeInt32ToFloat64, "ChangeInt32ToFloat64", |
| kMips64CvtDW, MachineType::Float64()}, |
| MachineType::Int32()}, |
| |
| // mips instructions: |
| // cvt.d.uw |
| {{&RawMachineAssembler::ChangeUint32ToFloat64, "ChangeUint32ToFloat64", |
| kMips64CvtDUw, MachineType::Float64()}, |
| MachineType::Int32()}, |
| |
| // mips instructions: |
| // mfc1, trunc double to word, for more details look at mips macro |
| // asm and mips asm file |
| {{&RawMachineAssembler::ChangeFloat64ToInt32, "ChangeFloat64ToInt32", |
| kMips64TruncWD, MachineType::Float64()}, |
| MachineType::Int32()}, |
| |
| // mips instructions: |
| // trunc double to unsigned word, for more details look at mips macro |
| // asm and mips asm file |
| {{&RawMachineAssembler::ChangeFloat64ToUint32, "ChangeFloat64ToUint32", |
| kMips64TruncUwD, MachineType::Float64()}, |
| MachineType::Int32()}}; |
| |
| const Conversion kFloat64RoundInstructions[] = { |
| {{&RawMachineAssembler::Float64RoundUp, "Float64RoundUp", kMips64CeilWD, |
| MachineType::Int32()}, |
| MachineType::Float64()}, |
| {{&RawMachineAssembler::Float64RoundDown, "Float64RoundDown", |
| kMips64FloorWD, MachineType::Int32()}, |
| MachineType::Float64()}, |
| {{&RawMachineAssembler::Float64RoundTiesEven, "Float64RoundTiesEven", |
| kMips64RoundWD, MachineType::Int32()}, |
| MachineType::Float64()}, |
| {{&RawMachineAssembler::Float64RoundTruncate, "Float64RoundTruncate", |
| kMips64TruncWD, MachineType::Int32()}, |
| MachineType::Float64()}}; |
| |
| const Conversion kFloat32RoundInstructions[] = { |
| {{&RawMachineAssembler::Float32RoundUp, "Float32RoundUp", kMips64CeilWS, |
| MachineType::Int32()}, |
| MachineType::Float32()}, |
| {{&RawMachineAssembler::Float32RoundDown, "Float32RoundDown", |
| kMips64FloorWS, MachineType::Int32()}, |
| MachineType::Float32()}, |
| {{&RawMachineAssembler::Float32RoundTiesEven, "Float32RoundTiesEven", |
| kMips64RoundWS, MachineType::Int32()}, |
| MachineType::Float32()}, |
| {{&RawMachineAssembler::Float32RoundTruncate, "Float32RoundTruncate", |
| kMips64TruncWS, MachineType::Int32()}, |
| MachineType::Float32()}}; |
| |
| // MIPS64 instructions that clear the top 32 bits of the destination. |
| const MachInst2 kCanElideChangeUint32ToUint64[] = { |
| {&RawMachineAssembler::Uint32Div, "Uint32Div", kMips64DivU, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Uint32Mod, "Uint32Mod", kMips64ModU, |
| MachineType::Uint32()}, |
| {&RawMachineAssembler::Uint32MulHigh, "Uint32MulHigh", kMips64MulHighU, |
| MachineType::Uint32()}}; |
| |
| } // namespace |
| |
| using InstructionSelectorFPCmpTest = InstructionSelectorTestWithParam<FPCmp>; |
| |
| TEST_P(InstructionSelectorFPCmpTest, Parameter) { |
| const FPCmp cmp = GetParam(); |
| StreamBuilder m(this, MachineType::Int32(), cmp.mi.machine_type, |
| cmp.mi.machine_type); |
| m.Return((m.*cmp.mi.constructor)(m.Parameter(0), m.Parameter(1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(cmp.mi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(kFlags_set, s[0]->flags_mode()); |
| EXPECT_EQ(cmp.cond, s[0]->flags_condition()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorFPCmpTest, |
| ::testing::ValuesIn(kFPCmpInstructions)); |
| |
| // ---------------------------------------------------------------------------- |
| // Arithmetic compare instructions integers |
| // ---------------------------------------------------------------------------- |
| using InstructionSelectorCmpTest = InstructionSelectorTestWithParam<IntCmp>; |
| |
| TEST_P(InstructionSelectorCmpTest, Parameter) { |
| const IntCmp cmp = GetParam(); |
| const MachineType type = cmp.mi.machine_type; |
| StreamBuilder m(this, type, type, type); |
| m.Return((m.*cmp.mi.constructor)(m.Parameter(0), m.Parameter(1))); |
| Stream s = m.Build(); |
| |
| if (FLAG_debug_code && |
| type.representation() == MachineRepresentation::kWord32) { |
| ASSERT_EQ(6U, s.size()); |
| |
| EXPECT_EQ(cmp.mi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| |
| EXPECT_EQ(kMips64Dshl, s[1]->arch_opcode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| |
| EXPECT_EQ(kMips64Dshl, s[2]->arch_opcode()); |
| EXPECT_EQ(2U, s[2]->InputCount()); |
| EXPECT_EQ(1U, s[2]->OutputCount()); |
| |
| EXPECT_EQ(cmp.mi.arch_opcode, s[3]->arch_opcode()); |
| EXPECT_EQ(2U, s[3]->InputCount()); |
| EXPECT_EQ(1U, s[3]->OutputCount()); |
| |
| EXPECT_EQ(kMips64AssertEqual, s[4]->arch_opcode()); |
| EXPECT_EQ(3U, s[4]->InputCount()); |
| EXPECT_EQ(0U, s[4]->OutputCount()); |
| |
| EXPECT_EQ(cmp.mi.arch_opcode, s[5]->arch_opcode()); |
| EXPECT_EQ(2U, s[5]->InputCount()); |
| EXPECT_EQ(1U, s[5]->OutputCount()); |
| } else { |
| ASSERT_EQ(cmp.expected_size, s.size()); |
| EXPECT_EQ(cmp.mi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorCmpTest, |
| ::testing::ValuesIn(kCmpInstructions)); |
| |
| // ---------------------------------------------------------------------------- |
| // Shift instructions. |
| // ---------------------------------------------------------------------------- |
| using InstructionSelectorShiftTest = |
| InstructionSelectorTestWithParam<MachInst2>; |
| |
| TEST_P(InstructionSelectorShiftTest, Immediate) { |
| const MachInst2 dpi = GetParam(); |
| const MachineType type = dpi.machine_type; |
| TRACED_FORRANGE(int32_t, imm, 0, |
| ((1 << ElementSizeLog2Of(type.representation())) * 8) - 1) { |
| StreamBuilder m(this, type, type); |
| m.Return((m.*dpi.constructor)(m.Parameter(0), m.Int32Constant(imm))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); |
| EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(1))); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorShiftTest, |
| ::testing::ValuesIn(kShiftInstructions)); |
| |
| TEST_F(InstructionSelectorTest, Word32ShrWithWord32AndWithImmediate) { |
| // The available shift operand range is `0 <= imm < 32`, but we also test |
| // that immediates outside this range are handled properly (modulo-32). |
| TRACED_FORRANGE(int32_t, shift, -32, 63) { |
| int32_t lsb = shift & 0x1F; |
| TRACED_FORRANGE(int32_t, width, 1, 32 - lsb) { |
| uint32_t jnk = rng()->NextInt(); |
| jnk = (lsb > 0) ? (jnk >> (32 - lsb)) : 0; |
| uint32_t msk = ((0xFFFFFFFFu >> (32 - width)) << lsb) | jnk; |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32Shr(m.Word32And(m.Parameter(0), m.Int32Constant(msk)), |
| m.Int32Constant(shift))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Ext, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(lsb, s.ToInt32(s[0]->InputAt(1))); |
| EXPECT_EQ(width, s.ToInt32(s[0]->InputAt(2))); |
| } |
| } |
| TRACED_FORRANGE(int32_t, shift, -32, 63) { |
| int32_t lsb = shift & 0x1F; |
| TRACED_FORRANGE(int32_t, width, 1, 32 - lsb) { |
| uint32_t jnk = rng()->NextInt(); |
| jnk = (lsb > 0) ? (jnk >> (32 - lsb)) : 0; |
| uint32_t msk = ((0xFFFFFFFFu >> (32 - width)) << lsb) | jnk; |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32Shr(m.Word32And(m.Int32Constant(msk), m.Parameter(0)), |
| m.Int32Constant(shift))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Ext, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(lsb, s.ToInt32(s[0]->InputAt(1))); |
| EXPECT_EQ(width, s.ToInt32(s[0]->InputAt(2))); |
| } |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word64ShrWithWord64AndWithImmediate) { |
| // The available shift operand range is `0 <= imm < 64`, but we also test |
| // that immediates outside this range are handled properly (modulo-64). |
| TRACED_FORRANGE(int32_t, shift, -64, 127) { |
| int32_t lsb = shift & 0x3F; |
| TRACED_FORRANGE(int32_t, width, 1, 64 - lsb) { |
| uint64_t jnk = rng()->NextInt64(); |
| jnk = (lsb > 0) ? (jnk >> (64 - lsb)) : 0; |
| uint64_t msk = |
| ((uint64_t{0xFFFFFFFFFFFFFFFF} >> (64 - width)) << lsb) | jnk; |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64Shr(m.Word64And(m.Parameter(0), m.Int64Constant(msk)), |
| m.Int64Constant(shift))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dext, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(lsb, s.ToInt64(s[0]->InputAt(1))); |
| EXPECT_EQ(width, s.ToInt64(s[0]->InputAt(2))); |
| } |
| } |
| TRACED_FORRANGE(int32_t, shift, -64, 127) { |
| int32_t lsb = shift & 0x3F; |
| TRACED_FORRANGE(int32_t, width, 1, 64 - lsb) { |
| uint64_t jnk = rng()->NextInt64(); |
| jnk = (lsb > 0) ? (jnk >> (64 - lsb)) : 0; |
| uint64_t msk = |
| ((uint64_t{0xFFFFFFFFFFFFFFFF} >> (64 - width)) << lsb) | jnk; |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64Shr(m.Word64And(m.Int64Constant(msk), m.Parameter(0)), |
| m.Int64Constant(shift))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dext, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(lsb, s.ToInt64(s[0]->InputAt(1))); |
| EXPECT_EQ(width, s.ToInt64(s[0]->InputAt(2))); |
| } |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word32AndToClearBits) { |
| TRACED_FORRANGE(int32_t, shift, 1, 31) { |
| int32_t mask = ~((1 << shift) - 1); |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32And(m.Parameter(0), m.Int32Constant(mask))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Ins, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(1))); |
| EXPECT_EQ(shift, s.ToInt32(s[0]->InputAt(2))); |
| } |
| TRACED_FORRANGE(int32_t, shift, 1, 31) { |
| int32_t mask = ~((1 << shift) - 1); |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32And(m.Int32Constant(mask), m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Ins, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(1))); |
| EXPECT_EQ(shift, s.ToInt32(s[0]->InputAt(2))); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word64AndToClearBits) { |
| TRACED_FORRANGE(int32_t, shift, 1, 31) { |
| int64_t mask = ~((1 << shift) - 1); |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64And(m.Parameter(0), m.Int64Constant(mask))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dins, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(1))); |
| EXPECT_EQ(shift, s.ToInt32(s[0]->InputAt(2))); |
| } |
| TRACED_FORRANGE(int32_t, shift, 1, 31) { |
| int64_t mask = ~((1 << shift) - 1); |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64And(m.Int64Constant(mask), m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dins, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(1))); |
| EXPECT_EQ(shift, s.ToInt32(s[0]->InputAt(2))); |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Logical instructions. |
| // ---------------------------------------------------------------------------- |
| using InstructionSelectorLogicalTest = |
| InstructionSelectorTestWithParam<MachInst2>; |
| |
| TEST_P(InstructionSelectorLogicalTest, Parameter) { |
| const MachInst2 dpi = GetParam(); |
| const MachineType type = dpi.machine_type; |
| StreamBuilder m(this, type, type, type); |
| m.Return((m.*dpi.constructor)(m.Parameter(0), m.Parameter(1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorLogicalTest, |
| ::testing::ValuesIn(kLogicalInstructions)); |
| |
| TEST_F(InstructionSelectorTest, Word64XorMinusOneWithParameter) { |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64Xor(m.Parameter(0), m.Int64Constant(-1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Nor, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64Xor(m.Int64Constant(-1), m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Nor, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word32XorMinusOneWithParameter) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32Xor(m.Parameter(0), m.Int32Constant(-1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Nor32, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32Xor(m.Int32Constant(-1), m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Nor32, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word64XorMinusOneWithWord64Or) { |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64Xor(m.Word64Or(m.Parameter(0), m.Parameter(0)), |
| m.Int64Constant(-1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Nor, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64Xor(m.Int64Constant(-1), |
| m.Word64Or(m.Parameter(0), m.Parameter(0)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Nor, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word32XorMinusOneWithWord32Or) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32Xor(m.Word32Or(m.Parameter(0), m.Parameter(0)), |
| m.Int32Constant(-1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Nor32, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32Xor(m.Int32Constant(-1), |
| m.Word32Or(m.Parameter(0), m.Parameter(0)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Nor32, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word32AndWithImmediateWithWord32Shr) { |
| // The available shift operand range is `0 <= imm < 32`, but we also test |
| // that immediates outside this range are handled properly (modulo-32). |
| TRACED_FORRANGE(int32_t, shift, -32, 63) { |
| int32_t lsb = shift & 0x1F; |
| TRACED_FORRANGE(int32_t, width, 1, 31) { |
| uint32_t msk = (1 << width) - 1; |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32And(m.Word32Shr(m.Parameter(0), m.Int32Constant(shift)), |
| m.Int32Constant(msk))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Ext, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(lsb, s.ToInt32(s[0]->InputAt(1))); |
| int32_t actual_width = (lsb + width > 32) ? (32 - lsb) : width; |
| EXPECT_EQ(actual_width, s.ToInt32(s[0]->InputAt(2))); |
| } |
| } |
| TRACED_FORRANGE(int32_t, shift, -32, 63) { |
| int32_t lsb = shift & 0x1F; |
| TRACED_FORRANGE(int32_t, width, 1, 31) { |
| uint32_t msk = (1 << width) - 1; |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return( |
| m.Word32And(m.Int32Constant(msk), |
| m.Word32Shr(m.Parameter(0), m.Int32Constant(shift)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Ext, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(lsb, s.ToInt32(s[0]->InputAt(1))); |
| int32_t actual_width = (lsb + width > 32) ? (32 - lsb) : width; |
| EXPECT_EQ(actual_width, s.ToInt32(s[0]->InputAt(2))); |
| } |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word64AndWithImmediateWithWord64Shr) { |
| // The available shift operand range is `0 <= imm < 64`, but we also test |
| // that immediates outside this range are handled properly (modulo-64). |
| TRACED_FORRANGE(int64_t, shift, -64, 127) { |
| int64_t lsb = shift & 0x3F; |
| TRACED_FORRANGE(int64_t, width, 1, 63) { |
| uint64_t msk = (uint64_t{1} << width) - 1; |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64And(m.Word64Shr(m.Parameter(0), m.Int64Constant(shift)), |
| m.Int64Constant(msk))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dext, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(lsb, s.ToInt64(s[0]->InputAt(1))); |
| int64_t actual_width = (lsb + width > 64) ? (64 - lsb) : width; |
| EXPECT_EQ(actual_width, s.ToInt64(s[0]->InputAt(2))); |
| } |
| } |
| TRACED_FORRANGE(int64_t, shift, -64, 127) { |
| int64_t lsb = shift & 0x3F; |
| TRACED_FORRANGE(int64_t, width, 1, 63) { |
| uint64_t msk = (uint64_t{1} << width) - 1; |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return( |
| m.Word64And(m.Int64Constant(msk), |
| m.Word64Shr(m.Parameter(0), m.Int64Constant(shift)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dext, s[0]->arch_opcode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(lsb, s.ToInt64(s[0]->InputAt(1))); |
| int64_t actual_width = (lsb + width > 64) ? (64 - lsb) : width; |
| EXPECT_EQ(actual_width, s.ToInt64(s[0]->InputAt(2))); |
| } |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word32ShlWithWord32And) { |
| TRACED_FORRANGE(int32_t, shift, 0, 30) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const r = |
| m.Word32Shl(m.Word32And(p0, m.Int32Constant((1 << (31 - shift)) - 1)), |
| m.Int32Constant(shift + 1)); |
| m.Return(r); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Shl, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word64ShlWithWord64And) { |
| TRACED_FORRANGE(int32_t, shift, 0, 62) { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const r = |
| m.Word64Shl(m.Word64And(p0, m.Int64Constant((1L << (63 - shift)) - 1)), |
| m.Int64Constant(shift + 1)); |
| m.Return(r); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dshl, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); |
| } |
| } |
| |
| TEST_F(InstructionSelectorTest, Word32SarWithWord32Shl) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const r = |
| m.Word32Sar(m.Word32Shl(p0, m.Int32Constant(24)), m.Int32Constant(24)); |
| m.Return(r); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Seb, 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(r), s.ToVreg(s[0]->Output())); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const r = |
| m.Word32Sar(m.Word32Shl(p0, m.Int32Constant(16)), m.Int32Constant(16)); |
| m.Return(r); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Seh, 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(r), s.ToVreg(s[0]->Output())); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| Node* const p0 = m.Parameter(0); |
| Node* const r = |
| m.Word32Sar(m.Word32Shl(p0, m.Int32Constant(32)), m.Int32Constant(32)); |
| m.Return(r); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Shl, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); |
| EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MUL/DIV instructions. |
| // ---------------------------------------------------------------------------- |
| using InstructionSelectorMulDivTest = |
| InstructionSelectorTestWithParam<MachInst2>; |
| |
| TEST_P(InstructionSelectorMulDivTest, Parameter) { |
| const MachInst2 dpi = GetParam(); |
| const MachineType type = dpi.machine_type; |
| StreamBuilder m(this, type, type, type); |
| m.Return((m.*dpi.constructor)(m.Parameter(0), m.Parameter(1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorMulDivTest, |
| ::testing::ValuesIn(kMulDivInstructions)); |
| |
| // ---------------------------------------------------------------------------- |
| // MOD instructions. |
| // ---------------------------------------------------------------------------- |
| using InstructionSelectorModTest = InstructionSelectorTestWithParam<MachInst2>; |
| |
| TEST_P(InstructionSelectorModTest, Parameter) { |
| const MachInst2 dpi = GetParam(); |
| const MachineType type = dpi.machine_type; |
| StreamBuilder m(this, type, type, type); |
| m.Return((m.*dpi.constructor)(m.Parameter(0), m.Parameter(1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(dpi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorModTest, |
| ::testing::ValuesIn(kModInstructions)); |
| |
| // ---------------------------------------------------------------------------- |
| // Floating point instructions. |
| // ---------------------------------------------------------------------------- |
| using InstructionSelectorFPArithTest = |
| InstructionSelectorTestWithParam<MachInst2>; |
| |
| TEST_P(InstructionSelectorFPArithTest, Parameter) { |
| const MachInst2 fpa = GetParam(); |
| StreamBuilder m(this, fpa.machine_type, fpa.machine_type, fpa.machine_type); |
| m.Return((m.*fpa.constructor)(m.Parameter(0), m.Parameter(1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(fpa.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorFPArithTest, |
| ::testing::ValuesIn(kFPArithInstructions)); |
| // ---------------------------------------------------------------------------- |
| // Integer arithmetic |
| // ---------------------------------------------------------------------------- |
| using InstructionSelectorIntArithTwoTest = |
| InstructionSelectorTestWithParam<MachInst2>; |
| |
| TEST_P(InstructionSelectorIntArithTwoTest, Parameter) { |
| const MachInst2 intpa = GetParam(); |
| StreamBuilder m(this, intpa.machine_type, intpa.machine_type, |
| intpa.machine_type); |
| m.Return((m.*intpa.constructor)(m.Parameter(0), m.Parameter(1))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(intpa.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorIntArithTwoTest, |
| ::testing::ValuesIn(kAddSubInstructions)); |
| |
| // ---------------------------------------------------------------------------- |
| // One node. |
| // ---------------------------------------------------------------------------- |
| |
| using InstructionSelectorIntArithOneTest = |
| InstructionSelectorTestWithParam<MachInst1>; |
| |
| TEST_P(InstructionSelectorIntArithOneTest, Parameter) { |
| const MachInst1 intpa = GetParam(); |
| StreamBuilder m(this, intpa.machine_type, intpa.machine_type, |
| intpa.machine_type); |
| m.Return((m.*intpa.constructor)(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(intpa.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorIntArithOneTest, |
| ::testing::ValuesIn(kAddSubOneInstructions)); |
| // ---------------------------------------------------------------------------- |
| // Conversions. |
| // ---------------------------------------------------------------------------- |
| using InstructionSelectorConversionTest = |
| InstructionSelectorTestWithParam<Conversion>; |
| |
| TEST_P(InstructionSelectorConversionTest, Parameter) { |
| const Conversion conv = GetParam(); |
| StreamBuilder m(this, conv.mi.machine_type, conv.src_machine_type); |
| m.Return((m.*conv.mi.constructor)(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(conv.mi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorConversionTest, |
| ::testing::ValuesIn(kConversionInstructions)); |
| |
| TEST_F(InstructionSelectorTest, ChangesFromToSmi) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.TruncateInt64ToInt32( |
| m.Word64Sar(m.Parameter(0), m.Int32Constant(32)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dsar, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return( |
| m.Word64Shl(m.ChangeInt32ToInt64(m.Parameter(0)), m.Int32Constant(32))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dshl, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| using CombineChangeFloat64ToInt32WithRoundFloat64 = |
| InstructionSelectorTestWithParam<Conversion>; |
| |
| TEST_P(CombineChangeFloat64ToInt32WithRoundFloat64, Parameter) { |
| { |
| const Conversion conv = GetParam(); |
| StreamBuilder m(this, conv.mi.machine_type, conv.src_machine_type); |
| m.Return(m.ChangeFloat64ToInt32((m.*conv.mi.constructor)(m.Parameter(0)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(conv.mi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| CombineChangeFloat64ToInt32WithRoundFloat64, |
| ::testing::ValuesIn(kFloat64RoundInstructions)); |
| |
| using CombineChangeFloat32ToInt32WithRoundFloat32 = |
| InstructionSelectorTestWithParam<Conversion>; |
| |
| TEST_P(CombineChangeFloat32ToInt32WithRoundFloat32, Parameter) { |
| { |
| const Conversion conv = GetParam(); |
| StreamBuilder m(this, conv.mi.machine_type, conv.src_machine_type); |
| m.Return(m.ChangeFloat64ToInt32( |
| m.ChangeFloat32ToFloat64((m.*conv.mi.constructor)(m.Parameter(0))))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(conv.mi.arch_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| CombineChangeFloat32ToInt32WithRoundFloat32, |
| ::testing::ValuesIn(kFloat32RoundInstructions)); |
| |
| TEST_F(InstructionSelectorTest, ChangeFloat64ToInt32OfChangeFloat32ToFloat64) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Float32()); |
| m.Return(m.ChangeFloat64ToInt32(m.ChangeFloat32ToFloat64(m.Parameter(0)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64TruncWS, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, |
| TruncateFloat64ToFloat32OfChangeInt32ToFloat64) { |
| { |
| StreamBuilder m(this, MachineType::Float32(), MachineType::Int32()); |
| m.Return( |
| m.TruncateFloat64ToFloat32(m.ChangeInt32ToFloat64(m.Parameter(0)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64CvtSW, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, CombineShiftsWithMul) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Int32Mul(m.Word64Sar(m.Parameter(0), m.Int32Constant(32)), |
| m.Word64Sar(m.Parameter(0), m.Int32Constant(32)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64DMulHigh, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, CombineShiftsWithDivMod) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Int32Div(m.Word64Sar(m.Parameter(0), m.Int32Constant(32)), |
| m.Word64Sar(m.Parameter(0), m.Int32Constant(32)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Ddiv, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Int32Mod(m.Word64Sar(m.Parameter(0), m.Int32Constant(32)), |
| m.Word64Sar(m.Parameter(0), m.Int32Constant(32)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dmod, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| TEST_F(InstructionSelectorTest, ChangeInt32ToInt64AfterLoad) { |
| // For each case, test that the conversion is merged into the load |
| // operation. |
| // ChangeInt32ToInt64(Load_Uint8) -> Lbu |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeInt32ToInt64( |
| m.Load(MachineType::Uint8(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kMips64Lbu, s[1]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[1]->addressing_mode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| } |
| // ChangeInt32ToInt64(Load_Int8) -> Lb |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeInt32ToInt64( |
| m.Load(MachineType::Int8(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kMips64Lb, s[1]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[1]->addressing_mode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| } |
| // ChangeInt32ToInt64(Load_Uint16) -> Lhu |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeInt32ToInt64( |
| m.Load(MachineType::Uint16(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kMips64Lhu, s[1]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[1]->addressing_mode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| } |
| // ChangeInt32ToInt64(Load_Int16) -> Lh |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeInt32ToInt64( |
| m.Load(MachineType::Int16(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kMips64Lh, s[1]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[1]->addressing_mode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| } |
| // ChangeInt32ToInt64(Load_Uint32) -> Lw |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeInt32ToInt64( |
| m.Load(MachineType::Uint32(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kMips64Lw, s[1]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[1]->addressing_mode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| } |
| // ChangeInt32ToInt64(Load_Int32) -> Lw |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeInt32ToInt64( |
| m.Load(MachineType::Int32(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kMips64Lw, s[1]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[1]->addressing_mode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| } |
| } |
| |
| 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)); |
| |
| TEST_F(InstructionSelectorTest, ChangeUint32ToUint64AfterLoad) { |
| // For each case, make sure the `ChangeUint32ToUint64` node turned into a |
| // no-op. |
| |
| // Lbu |
| { |
| 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(2U, s.size()); |
| EXPECT_EQ(kMips64Dadd, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(kMips64Lbu, s[1]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[1]->addressing_mode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| } |
| // Lhu |
| { |
| 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(2U, s.size()); |
| EXPECT_EQ(kMips64Dadd, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(kMips64Lhu, s[1]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[1]->addressing_mode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| } |
| // Lwu |
| { |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Pointer(), |
| MachineType::Int32()); |
| m.Return(m.ChangeUint32ToUint64( |
| m.Load(MachineType::Uint32(), m.Parameter(0), m.Parameter(1)))); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kMips64Dadd, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(kMips64Lwu, s[1]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[1]->addressing_mode()); |
| EXPECT_EQ(2U, s[1]->InputCount()); |
| EXPECT_EQ(1U, s[1]->OutputCount()); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Loads and stores. |
| // ---------------------------------------------------------------------------- |
| |
| |
| namespace { |
| |
| struct MemoryAccess { |
| MachineType type; |
| ArchOpcode load_opcode; |
| ArchOpcode store_opcode; |
| }; |
| |
| static const MemoryAccess kMemoryAccesses[] = { |
| {MachineType::Int8(), kMips64Lb, kMips64Sb}, |
| {MachineType::Uint8(), kMips64Lbu, kMips64Sb}, |
| {MachineType::Int16(), kMips64Lh, kMips64Sh}, |
| {MachineType::Uint16(), kMips64Lhu, kMips64Sh}, |
| {MachineType::Int32(), kMips64Lw, kMips64Sw}, |
| {MachineType::Float32(), kMips64Lwc1, kMips64Swc1}, |
| {MachineType::Float64(), kMips64Ldc1, kMips64Sdc1}, |
| {MachineType::Int64(), kMips64Ld, kMips64Sd}}; |
| |
| |
| struct MemoryAccessImm { |
| MachineType type; |
| ArchOpcode load_opcode; |
| ArchOpcode store_opcode; |
| bool (InstructionSelectorTest::Stream::*val_predicate)( |
| const InstructionOperand*) const; |
| const int32_t immediates[40]; |
| }; |
| |
| |
| std::ostream& operator<<(std::ostream& os, const MemoryAccessImm& acc) { |
| return os << acc.type; |
| } |
| |
| |
| struct MemoryAccessImm1 { |
| MachineType type; |
| ArchOpcode load_opcode; |
| ArchOpcode store_opcode; |
| bool (InstructionSelectorTest::Stream::*val_predicate)( |
| const InstructionOperand*) const; |
| const int32_t immediates[5]; |
| }; |
| |
| |
| std::ostream& operator<<(std::ostream& os, const MemoryAccessImm1& acc) { |
| return os << acc.type; |
| } |
| |
| struct MemoryAccessImm2 { |
| MachineType type; |
| ArchOpcode store_opcode; |
| ArchOpcode store_opcode_unaligned; |
| bool (InstructionSelectorTest::Stream::*val_predicate)( |
| const InstructionOperand*) const; |
| const int32_t immediates[40]; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, const MemoryAccessImm2& acc) { |
| return os << acc.type; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // Loads and stores immediate values |
| // ---------------------------------------------------------------------------- |
| |
| |
| const MemoryAccessImm kMemoryAccessesImm[] = { |
| {MachineType::Int8(), |
| kMips64Lb, |
| kMips64Sb, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, -89, |
| -87, -86, -82, -44, -23, -3, 0, 7, 10, 39, 52, 69, 71, 91, 92, 107, 109, |
| 115, 124, 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Uint8(), |
| kMips64Lbu, |
| kMips64Sb, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, -89, |
| -87, -86, -82, -44, -23, -3, 0, 7, 10, 39, 52, 69, 71, 91, 92, 107, 109, |
| 115, 124, 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Int16(), |
| kMips64Lh, |
| kMips64Sh, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, -89, |
| -87, -86, -82, -44, -23, -3, 0, 7, 10, 39, 52, 69, 71, 91, 92, 107, 109, |
| 115, 124, 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Uint16(), |
| kMips64Lhu, |
| kMips64Sh, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, -89, |
| -87, -86, -82, -44, -23, -3, 0, 7, 10, 39, 52, 69, 71, 91, 92, 107, 109, |
| 115, 124, 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Int32(), |
| kMips64Lw, |
| kMips64Sw, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, -89, |
| -87, -86, -82, -44, -23, -3, 0, 7, 10, 39, 52, 69, 71, 91, 92, 107, 109, |
| 115, 124, 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Float32(), |
| kMips64Lwc1, |
| kMips64Swc1, |
| &InstructionSelectorTest::Stream::IsDouble, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, -89, |
| -87, -86, -82, -44, -23, -3, 0, 7, 10, 39, 52, 69, 71, 91, 92, 107, 109, |
| 115, 124, 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Float64(), |
| kMips64Ldc1, |
| kMips64Sdc1, |
| &InstructionSelectorTest::Stream::IsDouble, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, -89, |
| -87, -86, -82, -44, -23, -3, 0, 7, 10, 39, 52, 69, 71, 91, 92, 107, 109, |
| 115, 124, 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Int64(), |
| kMips64Ld, |
| kMips64Sd, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, -89, |
| -87, -86, -82, -44, -23, -3, 0, 7, 10, 39, 52, 69, 71, 91, 92, 107, 109, |
| 115, 124, 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}}; |
| |
| const MemoryAccessImm1 kMemoryAccessImmMoreThan16bit[] = { |
| {MachineType::Int8(), |
| kMips64Lb, |
| kMips64Sb, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-65000, -55000, 32777, 55000, 65000}}, |
| {MachineType::Uint8(), |
| kMips64Lbu, |
| kMips64Sb, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-65000, -55000, 32777, 55000, 65000}}, |
| {MachineType::Int16(), |
| kMips64Lh, |
| kMips64Sh, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-65000, -55000, 32777, 55000, 65000}}, |
| {MachineType::Uint16(), |
| kMips64Lhu, |
| kMips64Sh, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-65000, -55000, 32777, 55000, 65000}}, |
| {MachineType::Int32(), |
| kMips64Lw, |
| kMips64Sw, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-65000, -55000, 32777, 55000, 65000}}, |
| {MachineType::Float32(), |
| kMips64Lwc1, |
| kMips64Swc1, |
| &InstructionSelectorTest::Stream::IsDouble, |
| {-65000, -55000, 32777, 55000, 65000}}, |
| {MachineType::Float64(), |
| kMips64Ldc1, |
| kMips64Sdc1, |
| &InstructionSelectorTest::Stream::IsDouble, |
| {-65000, -55000, 32777, 55000, 65000}}, |
| {MachineType::Int64(), |
| kMips64Ld, |
| kMips64Sd, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-65000, -55000, 32777, 55000, 65000}}}; |
| |
| const MemoryAccessImm2 kMemoryAccessesImmUnaligned[] = { |
| {MachineType::Int16(), |
| kMips64Ush, |
| kMips64Sh, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, |
| -89, -87, -86, -82, -44, -23, -3, 0, 7, 10, |
| 39, 52, 69, 71, 91, 92, 107, 109, 115, 124, |
| 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Int32(), |
| kMips64Usw, |
| kMips64Sw, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, |
| -89, -87, -86, -82, -44, -23, -3, 0, 7, 10, |
| 39, 52, 69, 71, 91, 92, 107, 109, 115, 124, |
| 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Int64(), |
| kMips64Usd, |
| kMips64Sd, |
| &InstructionSelectorTest::Stream::IsInteger, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, |
| -89, -87, -86, -82, -44, -23, -3, 0, 7, 10, |
| 39, 52, 69, 71, 91, 92, 107, 109, 115, 124, |
| 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Float32(), |
| kMips64Uswc1, |
| kMips64Swc1, |
| &InstructionSelectorTest::Stream::IsDouble, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, |
| -89, -87, -86, -82, -44, -23, -3, 0, 7, 10, |
| 39, 52, 69, 71, 91, 92, 107, 109, 115, 124, |
| 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}, |
| {MachineType::Float64(), |
| kMips64Usdc1, |
| kMips64Sdc1, |
| &InstructionSelectorTest::Stream::IsDouble, |
| {-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91, |
| -89, -87, -86, -82, -44, -23, -3, 0, 7, 10, |
| 39, 52, 69, 71, 91, 92, 107, 109, 115, 124, |
| 286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}}; |
| |
| } // 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))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(memacc.load_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| } |
| |
| |
| 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), |
| 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(kMode_MRI, s[0]->addressing_mode()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorMemoryAccessTest, |
| ::testing::ValuesIn(kMemoryAccesses)); |
| |
| // ---------------------------------------------------------------------------- |
| // Load immediate. |
| // ---------------------------------------------------------------------------- |
| |
| using InstructionSelectorMemoryAccessImmTest = |
| InstructionSelectorTestWithParam<MemoryAccessImm>; |
| |
| TEST_P(InstructionSelectorMemoryAccessImmTest, LoadWithImmediateIndex) { |
| const MemoryAccessImm memacc = GetParam(); |
| TRACED_FOREACH(int32_t, index, memacc.immediates) { |
| StreamBuilder m(this, memacc.type, MachineType::Pointer()); |
| m.Return(m.Load(memacc.type, m.Parameter(0), m.Int32Constant(index))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(memacc.load_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind()); |
| EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_TRUE((s.*memacc.val_predicate)(s[0]->Output())); |
| } |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| // Store immediate. |
| // ---------------------------------------------------------------------------- |
| |
| |
| TEST_P(InstructionSelectorMemoryAccessImmTest, StoreWithImmediateIndex) { |
| const MemoryAccessImm memacc = GetParam(); |
| TRACED_FOREACH(int32_t, index, memacc.immediates) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer(), |
| memacc.type); |
| m.Store(memacc.type.representation(), m.Parameter(0), |
| m.Int32Constant(index), m.Parameter(1), 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(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind()); |
| EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1))); |
| EXPECT_EQ(0U, s[0]->OutputCount()); |
| } |
| } |
| |
| TEST_P(InstructionSelectorMemoryAccessImmTest, StoreZero) { |
| const MemoryAccessImm memacc = GetParam(); |
| TRACED_FOREACH(int32_t, index, memacc.immediates) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer()); |
| m.Store(memacc.type.representation(), m.Parameter(0), |
| m.Int32Constant(index), m.Int32Constant(0), 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(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind()); |
| EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(2)->kind()); |
| EXPECT_EQ(0, s.ToInt64(s[0]->InputAt(2))); |
| EXPECT_EQ(0U, s[0]->OutputCount()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorMemoryAccessImmTest, |
| ::testing::ValuesIn(kMemoryAccessesImm)); |
| |
| using InstructionSelectorMemoryAccessUnalignedImmTest = |
| InstructionSelectorTestWithParam<MemoryAccessImm2>; |
| |
| TEST_P(InstructionSelectorMemoryAccessUnalignedImmTest, StoreZero) { |
| const MemoryAccessImm2 memacc = GetParam(); |
| TRACED_FOREACH(int32_t, index, memacc.immediates) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer()); |
| bool unaligned_store_supported = |
| m.machine()->UnalignedStoreSupported(memacc.type.representation()); |
| m.UnalignedStore(memacc.type.representation(), m.Parameter(0), |
| m.Int32Constant(index), m.Int32Constant(0)); |
| m.Return(m.Int32Constant(0)); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(unaligned_store_supported ? memacc.store_opcode_unaligned |
| : memacc.store_opcode, |
| s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| ASSERT_EQ(3U, s[0]->InputCount()); |
| ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind()); |
| EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1))); |
| ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(2)->kind()); |
| EXPECT_EQ(0, s.ToInt64(s[0]->InputAt(2))); |
| EXPECT_EQ(0U, s[0]->OutputCount()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorMemoryAccessUnalignedImmTest, |
| ::testing::ValuesIn(kMemoryAccessesImmUnaligned)); |
| |
| // ---------------------------------------------------------------------------- |
| // Load/store offsets more than 16 bits. |
| // ---------------------------------------------------------------------------- |
| |
| using InstructionSelectorMemoryAccessImmMoreThan16bitTest = |
| InstructionSelectorTestWithParam<MemoryAccessImm1>; |
| |
| TEST_P(InstructionSelectorMemoryAccessImmMoreThan16bitTest, |
| LoadWithImmediateIndex) { |
| const MemoryAccessImm1 memacc = GetParam(); |
| TRACED_FOREACH(int32_t, index, memacc.immediates) { |
| StreamBuilder m(this, memacc.type, MachineType::Pointer()); |
| m.Return(m.Load(memacc.type, m.Parameter(0), m.Int32Constant(index))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(memacc.load_opcode, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| TEST_P(InstructionSelectorMemoryAccessImmMoreThan16bitTest, |
| StoreWithImmediateIndex) { |
| const MemoryAccessImm1 memacc = GetParam(); |
| TRACED_FOREACH(int32_t, index, memacc.immediates) { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer(), |
| memacc.type); |
| m.Store(memacc.type.representation(), m.Parameter(0), |
| m.Int32Constant(index), m.Parameter(1), 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(kMode_MRI, s[0]->addressing_mode()); |
| EXPECT_EQ(3U, s[0]->InputCount()); |
| EXPECT_EQ(0U, s[0]->OutputCount()); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, |
| InstructionSelectorMemoryAccessImmMoreThan16bitTest, |
| ::testing::ValuesIn(kMemoryAccessImmMoreThan16bit)); |
| |
| // ---------------------------------------------------------------------------- |
| // kMips64Cmp with zero testing. |
| // ---------------------------------------------------------------------------- |
| |
| |
| TEST_F(InstructionSelectorTest, Word32EqualWithZero) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32Equal(m.Parameter(0), m.Int32Constant(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Cmp, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(kFlags_set, s[0]->flags_mode()); |
| EXPECT_EQ(kEqual, s[0]->flags_condition()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32Equal(m.Int32Constant(0), m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Cmp, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(kFlags_set, s[0]->flags_mode()); |
| EXPECT_EQ(kEqual, s[0]->flags_condition()); |
| } |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Word64EqualWithZero) { |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64Equal(m.Parameter(0), m.Int64Constant(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Cmp, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(kFlags_set, s[0]->flags_mode()); |
| EXPECT_EQ(kEqual, s[0]->flags_condition()); |
| } |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64Equal(m.Int32Constant(0), m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Cmp, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_None, s[0]->addressing_mode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(kFlags_set, s[0]->flags_mode()); |
| EXPECT_EQ(kEqual, s[0]->flags_condition()); |
| } |
| } |
| |
| |
| 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(kMips64Clz, 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, Word64Clz) { |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Uint64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const n = m.Word64Clz(p0); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Dclz, 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, 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(kMips64AbsS, 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, 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(kMips64AbsD, 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, Float64Max) { |
| StreamBuilder m(this, MachineType::Float64(), MachineType::Float64(), |
| MachineType::Float64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const n = m.Float64Max(p0, p1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Float64Max, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| } |
| |
| |
| TEST_F(InstructionSelectorTest, Float64Min) { |
| StreamBuilder m(this, MachineType::Float64(), MachineType::Float64(), |
| MachineType::Float64()); |
| Node* const p0 = m.Parameter(0); |
| Node* const p1 = m.Parameter(1); |
| Node* const n = m.Float64Min(p0, p1); |
| m.Return(n); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64Float64Min, s[0]->arch_opcode()); |
| ASSERT_EQ(2U, s[0]->InputCount()); |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); |
| } |
| |
| TEST_F(InstructionSelectorTest, LoadAndShiftRight) { |
| { |
| int32_t immediates[] = {-256, -255, -3, -2, -1, 0, 1, |
| 2, 3, 255, 256, 260, 4096, 4100, |
| 8192, 8196, 3276, 3280, 16376, 16380}; |
| TRACED_FOREACH(int32_t, index, immediates) { |
| StreamBuilder m(this, MachineType::Uint64(), MachineType::Pointer()); |
| Node* const load = |
| m.Load(MachineType::Uint64(), m.Parameter(0), m.Int32Constant(index)); |
| Node* const sar = m.Word64Sar(load, m.Int32Constant(32)); |
| // Make sure we don't fold the shift into the following add: |
| m.Return(m.Int64Add(sar, m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(2U, s.size()); |
| EXPECT_EQ(kMips64Lw, s[0]->arch_opcode()); |
| EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); |
| EXPECT_EQ(2U, s[0]->InputCount()); |
| EXPECT_EQ(s.ToVreg(m.Parameter(0)), s.ToVreg(s[0]->InputAt(0))); |
| ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind()); |
| #if defined(V8_TARGET_LITTLE_ENDIAN) |
| EXPECT_EQ(index + 4, s.ToInt32(s[0]->InputAt(1))); |
| #elif defined(V8_TARGET_BIG_ENDIAN) |
| EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1))); |
| #endif |
| |
| ASSERT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| } |
| |
| TEST_F(InstructionSelectorTest, Word32ReverseBytes) { |
| { |
| StreamBuilder m(this, MachineType::Int32(), MachineType::Int32()); |
| m.Return(m.Word32ReverseBytes(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64ByteSwap32, s[0]->arch_opcode()); |
| EXPECT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| TEST_F(InstructionSelectorTest, Word64ReverseBytes) { |
| { |
| StreamBuilder m(this, MachineType::Int64(), MachineType::Int64()); |
| m.Return(m.Word64ReverseBytes(m.Parameter(0))); |
| Stream s = m.Build(); |
| ASSERT_EQ(1U, s.size()); |
| EXPECT_EQ(kMips64ByteSwap64, s[0]->arch_opcode()); |
| EXPECT_EQ(1U, s[0]->InputCount()); |
| EXPECT_EQ(1U, s[0]->OutputCount()); |
| } |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |