0d0d623b57
BUG= Review-Url: https://codereview.chromium.org/2398993003 Cr-Commit-Position: refs/heads/master@{#42358}
1994 lines
74 KiB
C++
1994 lines
74 KiB
C++
// 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/instruction-selector-unittest.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;
|
|
}
|
|
|
|
typedef MachInst<Node* (RawMachineAssembler::*)(Node*)> MachInst1;
|
|
typedef MachInst<Node* (RawMachineAssembler::*)(Node*, Node*)> MachInst2;
|
|
|
|
|
|
// 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
|
|
|
|
|
|
typedef InstructionSelectorTestWithParam<FPCmp> InstructionSelectorFPCmpTest;
|
|
|
|
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_CASE_P(InstructionSelectorTest, InstructionSelectorFPCmpTest,
|
|
::testing::ValuesIn(kFPCmpInstructions));
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Arithmetic compare instructions integers
|
|
// ----------------------------------------------------------------------------
|
|
typedef InstructionSelectorTestWithParam<IntCmp> InstructionSelectorCmpTest;
|
|
|
|
|
|
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_CASE_P(InstructionSelectorTest, InstructionSelectorCmpTest,
|
|
::testing::ValuesIn(kCmpInstructions));
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Shift instructions.
|
|
// ----------------------------------------------------------------------------
|
|
typedef InstructionSelectorTestWithParam<MachInst2>
|
|
InstructionSelectorShiftTest;
|
|
|
|
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_CASE_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 =
|
|
((V8_UINT64_C(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 =
|
|
((V8_UINT64_C(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.
|
|
// ----------------------------------------------------------------------------
|
|
typedef InstructionSelectorTestWithParam<MachInst2>
|
|
InstructionSelectorLogicalTest;
|
|
|
|
|
|
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_CASE_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 = (V8_UINT64_C(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 = (V8_UINT64_C(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.
|
|
// ----------------------------------------------------------------------------
|
|
typedef InstructionSelectorTestWithParam<MachInst2>
|
|
InstructionSelectorMulDivTest;
|
|
|
|
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_CASE_P(InstructionSelectorTest, InstructionSelectorMulDivTest,
|
|
::testing::ValuesIn(kMulDivInstructions));
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MOD instructions.
|
|
// ----------------------------------------------------------------------------
|
|
typedef InstructionSelectorTestWithParam<MachInst2> InstructionSelectorModTest;
|
|
|
|
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_CASE_P(InstructionSelectorTest, InstructionSelectorModTest,
|
|
::testing::ValuesIn(kModInstructions));
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Floating point instructions.
|
|
// ----------------------------------------------------------------------------
|
|
typedef InstructionSelectorTestWithParam<MachInst2>
|
|
InstructionSelectorFPArithTest;
|
|
|
|
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_CASE_P(InstructionSelectorTest, InstructionSelectorFPArithTest,
|
|
::testing::ValuesIn(kFPArithInstructions));
|
|
// ----------------------------------------------------------------------------
|
|
// Integer arithmetic
|
|
// ----------------------------------------------------------------------------
|
|
typedef InstructionSelectorTestWithParam<MachInst2>
|
|
InstructionSelectorIntArithTwoTest;
|
|
|
|
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_CASE_P(InstructionSelectorTest,
|
|
InstructionSelectorIntArithTwoTest,
|
|
::testing::ValuesIn(kAddSubInstructions));
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// One node.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
typedef InstructionSelectorTestWithParam<MachInst1>
|
|
InstructionSelectorIntArithOneTest;
|
|
|
|
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_CASE_P(InstructionSelectorTest,
|
|
InstructionSelectorIntArithOneTest,
|
|
::testing::ValuesIn(kAddSubOneInstructions));
|
|
// ----------------------------------------------------------------------------
|
|
// Conversions.
|
|
// ----------------------------------------------------------------------------
|
|
typedef InstructionSelectorTestWithParam<Conversion>
|
|
InstructionSelectorConversionTest;
|
|
|
|
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_CASE_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());
|
|
}
|
|
}
|
|
|
|
|
|
typedef InstructionSelectorTestWithParam<Conversion>
|
|
CombineChangeFloat64ToInt32WithRoundFloat64;
|
|
|
|
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_CASE_P(InstructionSelectorTest,
|
|
CombineChangeFloat64ToInt32WithRoundFloat64,
|
|
::testing::ValuesIn(kFloat64RoundInstructions));
|
|
|
|
typedef InstructionSelectorTestWithParam<Conversion>
|
|
CombineChangeFloat32ToInt32WithRoundFloat32;
|
|
|
|
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_CASE_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());
|
|
}
|
|
}
|
|
|
|
typedef InstructionSelectorTestWithParam<MachInst2>
|
|
InstructionSelectorElidedChangeUint32ToUint64Test;
|
|
|
|
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_CASE_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
|
|
|
|
|
|
typedef InstructionSelectorTestWithParam<MemoryAccess>
|
|
InstructionSelectorMemoryAccessTest;
|
|
|
|
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_CASE_P(InstructionSelectorTest,
|
|
InstructionSelectorMemoryAccessTest,
|
|
::testing::ValuesIn(kMemoryAccesses));
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Load immediate.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
typedef InstructionSelectorTestWithParam<MemoryAccessImm>
|
|
InstructionSelectorMemoryAccessImmTest;
|
|
|
|
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_CASE_P(InstructionSelectorTest,
|
|
InstructionSelectorMemoryAccessImmTest,
|
|
::testing::ValuesIn(kMemoryAccessesImm));
|
|
|
|
typedef InstructionSelectorTestWithParam<MemoryAccessImm2>
|
|
InstructionSelectorMemoryAccessUnalignedImmTest;
|
|
|
|
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(
|
|
MachineType::TypeForRepresentation(memacc.type.representation()), 1);
|
|
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_CASE_P(InstructionSelectorTest,
|
|
InstructionSelectorMemoryAccessUnalignedImmTest,
|
|
::testing::ValuesIn(kMemoryAccessesImmUnaligned));
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Load/store offsets more than 16 bits.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
typedef InstructionSelectorTestWithParam<MemoryAccessImm1>
|
|
InstructionSelectorMemoryAccessImmMoreThan16bitTest;
|
|
|
|
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_CASE_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, Float32AddWithFloat32Mul) {
|
|
if (kArchVariant != kMips64r2) {
|
|
return;
|
|
}
|
|
{
|
|
StreamBuilder m(this, MachineType::Float32(), MachineType::Float32(),
|
|
MachineType::Float32(), MachineType::Float32());
|
|
Node* const p0 = m.Parameter(0);
|
|
Node* const p1 = m.Parameter(1);
|
|
Node* const p2 = m.Parameter(2);
|
|
Node* const n = m.Float32Add(m.Float32Mul(p0, p1), p2);
|
|
m.Return(n);
|
|
Stream s = m.Build();
|
|
ASSERT_EQ(1U, s.size());
|
|
EXPECT_EQ(kMips64MaddS, s[0]->arch_opcode());
|
|
ASSERT_EQ(3U, s[0]->InputCount());
|
|
EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(0)));
|
|
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1)));
|
|
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(2)));
|
|
ASSERT_EQ(1U, s[0]->OutputCount());
|
|
EXPECT_FALSE(
|
|
UnallocatedOperand::cast(s[0]->Output())->HasSameAsInputPolicy());
|
|
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(),
|
|
MachineType::Float32(), MachineType::Float32());
|
|
Node* const p0 = m.Parameter(0);
|
|
Node* const p1 = m.Parameter(1);
|
|
Node* const p2 = m.Parameter(2);
|
|
Node* const n = m.Float32Add(p0, m.Float32Mul(p1, p2));
|
|
m.Return(n);
|
|
Stream s = m.Build();
|
|
ASSERT_EQ(1U, s.size());
|
|
EXPECT_EQ(kMips64MaddS, 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)));
|
|
EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(2)));
|
|
ASSERT_EQ(1U, s[0]->OutputCount());
|
|
EXPECT_FALSE(
|
|
UnallocatedOperand::cast(s[0]->Output())->HasSameAsInputPolicy());
|
|
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
|
|
EXPECT_EQ(kFlags_none, s[0]->flags_mode());
|
|
}
|
|
}
|
|
|
|
TEST_F(InstructionSelectorTest, Float64AddWithFloat64Mul) {
|
|
if (kArchVariant != kMips64r2) {
|
|
return;
|
|
}
|
|
{
|
|
StreamBuilder m(this, MachineType::Float64(), MachineType::Float64(),
|
|
MachineType::Float64(), MachineType::Float64());
|
|
Node* const p0 = m.Parameter(0);
|
|
Node* const p1 = m.Parameter(1);
|
|
Node* const p2 = m.Parameter(2);
|
|
Node* const n = m.Float64Add(m.Float64Mul(p0, p1), p2);
|
|
m.Return(n);
|
|
Stream s = m.Build();
|
|
ASSERT_EQ(1U, s.size());
|
|
EXPECT_EQ(kMips64MaddD, s[0]->arch_opcode());
|
|
ASSERT_EQ(3U, s[0]->InputCount());
|
|
EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(0)));
|
|
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1)));
|
|
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(2)));
|
|
ASSERT_EQ(1U, s[0]->OutputCount());
|
|
EXPECT_FALSE(
|
|
UnallocatedOperand::cast(s[0]->Output())->HasSameAsInputPolicy());
|
|
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(),
|
|
MachineType::Float64(), MachineType::Float64());
|
|
Node* const p0 = m.Parameter(0);
|
|
Node* const p1 = m.Parameter(1);
|
|
Node* const p2 = m.Parameter(2);
|
|
Node* const n = m.Float64Add(p0, m.Float64Mul(p1, p2));
|
|
m.Return(n);
|
|
Stream s = m.Build();
|
|
ASSERT_EQ(1U, s.size());
|
|
EXPECT_EQ(kMips64MaddD, 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)));
|
|
EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(2)));
|
|
ASSERT_EQ(1U, s[0]->OutputCount());
|
|
EXPECT_FALSE(
|
|
UnallocatedOperand::cast(s[0]->Output())->HasSameAsInputPolicy());
|
|
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
|
|
EXPECT_EQ(kFlags_none, s[0]->flags_mode());
|
|
}
|
|
}
|
|
|
|
TEST_F(InstructionSelectorTest, Float32SubWithFloat32Mul) {
|
|
StreamBuilder m(this, MachineType::Float32(), MachineType::Float32(),
|
|
MachineType::Float32(), MachineType::Float32());
|
|
if (kArchVariant != kMips64r2) {
|
|
return;
|
|
}
|
|
{
|
|
Node* const p0 = m.Parameter(0);
|
|
Node* const p1 = m.Parameter(1);
|
|
Node* const p2 = m.Parameter(2);
|
|
Node* n;
|
|
n = m.Float32Sub(m.Float32Mul(p1, p2), p0);
|
|
m.Return(n);
|
|
Stream s = m.Build();
|
|
ASSERT_EQ(1U, s.size());
|
|
EXPECT_EQ(kMips64MsubS, 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)));
|
|
EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(2)));
|
|
ASSERT_EQ(1U, s[0]->OutputCount());
|
|
EXPECT_FALSE(
|
|
UnallocatedOperand::cast(s[0]->Output())->HasSameAsInputPolicy());
|
|
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
|
|
EXPECT_EQ(kFlags_none, s[0]->flags_mode());
|
|
}
|
|
}
|
|
|
|
TEST_F(InstructionSelectorTest, Float64SubWithFloat64Mul) {
|
|
StreamBuilder m(this, MachineType::Float64(), MachineType::Float64(),
|
|
MachineType::Float64(), MachineType::Float64());
|
|
if (kArchVariant != kMips64r2) {
|
|
return;
|
|
}
|
|
{
|
|
Node* const p0 = m.Parameter(0);
|
|
Node* const p1 = m.Parameter(1);
|
|
Node* const p2 = m.Parameter(2);
|
|
Node* n;
|
|
n = m.Float64Sub(m.Float64Mul(p1, p2), p0);
|
|
m.Return(n);
|
|
Stream s = m.Build();
|
|
ASSERT_EQ(1U, s.size());
|
|
EXPECT_EQ(kMips64MsubD, 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)));
|
|
EXPECT_EQ(s.ToVreg(p2), s.ToVreg(s[0]->InputAt(2)));
|
|
ASSERT_EQ(1U, s[0]->OutputCount());
|
|
EXPECT_FALSE(
|
|
UnallocatedOperand::cast(s[0]->Output())->HasSameAsInputPolicy());
|
|
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
|
|
EXPECT_EQ(kFlags_none, s[0]->flags_mode());
|
|
}
|
|
}
|
|
|
|
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
|