// 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 "src/code-stubs.h" #include "src/compiler/change-lowering.h" #include "src/compiler/js-graph.h" #include "src/compiler/linkage.h" #include "src/compiler/node-properties-inl.h" #include "src/compiler/simplified-operator.h" #include "test/unittests/compiler/compiler-test-utils.h" #include "test/unittests/compiler/graph-unittest.h" #include "test/unittests/compiler/node-test-utils.h" #include "testing/gmock-support.h" using testing::_; using testing::AllOf; using testing::BitEq; using testing::Capture; using testing::CaptureEq; namespace v8 { namespace internal { namespace compiler { class ChangeLoweringTest : public GraphTest { public: ChangeLoweringTest() : simplified_(zone()) {} ~ChangeLoweringTest() OVERRIDE {} virtual MachineType WordRepresentation() const = 0; protected: int HeapNumberValueOffset() const { STATIC_ASSERT(HeapNumber::kValueOffset % kApiPointerSize == 0); return (HeapNumber::kValueOffset / kApiPointerSize) * PointerSize() - kHeapObjectTag; } bool Is32() const { return WordRepresentation() == kRepWord32; } int PointerSize() const { switch (WordRepresentation()) { case kRepWord32: return 4; case kRepWord64: return 8; default: break; } UNREACHABLE(); return 0; } int SmiMaxValue() const { return -(SmiMinValue() + 1); } int SmiMinValue() const { return static_cast(0xffffffffu << (SmiValueSize() - 1)); } int SmiShiftAmount() const { return kSmiTagSize + SmiShiftSize(); } int SmiShiftSize() const { return Is32() ? SmiTagging<4>::SmiShiftSize() : SmiTagging<8>::SmiShiftSize(); } int SmiValueSize() const { return Is32() ? SmiTagging<4>::SmiValueSize() : SmiTagging<8>::SmiValueSize(); } Reduction Reduce(Node* node) { MachineOperatorBuilder machine(zone(), WordRepresentation()); JSOperatorBuilder javascript(zone()); JSGraph jsgraph(graph(), common(), &javascript, &machine); CompilationInfo info(isolate(), zone()); Linkage linkage(zone(), &info); ChangeLowering reducer(&jsgraph, &linkage); return reducer.Reduce(node); } SimplifiedOperatorBuilder* simplified() { return &simplified_; } Matcher IsAllocateHeapNumber(const Matcher& effect_matcher, const Matcher& control_matcher) { return IsCall(_, IsHeapConstant(Unique::CreateImmovable( AllocateHeapNumberStub(isolate()).GetCode())), IsNumberConstant(BitEq(0.0)), effect_matcher, control_matcher); } Matcher IsLoadHeapNumber(const Matcher& value_matcher, const Matcher& control_matcher) { return IsLoad(kMachFloat64, value_matcher, IsIntPtrConstant(HeapNumberValueOffset()), graph()->start(), control_matcher); } Matcher IsIntPtrConstant(int value) { return Is32() ? IsInt32Constant(value) : IsInt64Constant(value); } Matcher IsWordEqual(const Matcher& lhs_matcher, const Matcher& rhs_matcher) { return Is32() ? IsWord32Equal(lhs_matcher, rhs_matcher) : IsWord64Equal(lhs_matcher, rhs_matcher); } private: SimplifiedOperatorBuilder simplified_; }; // ----------------------------------------------------------------------------- // Common. class ChangeLoweringCommonTest : public ChangeLoweringTest, public ::testing::WithParamInterface { public: ~ChangeLoweringCommonTest() OVERRIDE {} MachineType WordRepresentation() const FINAL { return GetParam(); } }; TARGET_TEST_P(ChangeLoweringCommonTest, ChangeBitToBool) { Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeBitToBool(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); EXPECT_THAT(reduction.replacement(), IsSelect(static_cast(kTypeBool | kRepTagged), val, IsTrueConstant(), IsFalseConstant())); } TARGET_TEST_P(ChangeLoweringCommonTest, ChangeBoolToBit) { Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeBoolToBit(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); EXPECT_THAT(reduction.replacement(), IsWordEqual(val, IsTrueConstant())); } TARGET_TEST_P(ChangeLoweringCommonTest, ChangeFloat64ToTagged) { Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeFloat64ToTagged(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* finish = reduction.replacement(); Capture heap_number; EXPECT_THAT( finish, IsFinish( AllOf(CaptureEq(&heap_number), IsAllocateHeapNumber(IsValueEffect(val), graph()->start())), IsStore(StoreRepresentation(kMachFloat64, kNoWriteBarrier), CaptureEq(&heap_number), IsIntPtrConstant(HeapNumberValueOffset()), val, CaptureEq(&heap_number), graph()->start()))); } TARGET_TEST_P(ChangeLoweringCommonTest, StringAdd) { Node* node = graph()->NewNode(simplified()->StringAdd(), Parameter(0), Parameter(1)); Reduction reduction = Reduce(node); EXPECT_FALSE(reduction.Changed()); } INSTANTIATE_TEST_CASE_P(ChangeLoweringTest, ChangeLoweringCommonTest, ::testing::Values(kRepWord32, kRepWord64)); // ----------------------------------------------------------------------------- // 32-bit class ChangeLowering32Test : public ChangeLoweringTest { public: ~ChangeLowering32Test() OVERRIDE {} MachineType WordRepresentation() const FINAL { return kRepWord32; } }; TARGET_TEST_F(ChangeLowering32Test, ChangeInt32ToTagged) { Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeInt32ToTagged(), val); NodeProperties::SetBounds(val, Bounds(Type::None(), Type::Signed32())); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* phi = reduction.replacement(); Capture add, branch, heap_number, if_true; EXPECT_THAT( phi, IsPhi(kMachAnyTagged, IsFinish(AllOf(CaptureEq(&heap_number), IsAllocateHeapNumber(_, CaptureEq(&if_true))), IsStore(StoreRepresentation(kMachFloat64, kNoWriteBarrier), CaptureEq(&heap_number), IsIntPtrConstant(HeapNumberValueOffset()), IsChangeInt32ToFloat64(val), CaptureEq(&heap_number), CaptureEq(&if_true))), IsProjection( 0, AllOf(CaptureEq(&add), IsInt32AddWithOverflow(val, val))), IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), IsIfFalse(AllOf(CaptureEq(&branch), IsBranch(IsProjection(1, CaptureEq(&add)), graph()->start())))))); } TARGET_TEST_F(ChangeLowering32Test, ChangeInt32ToTaggedSmall) { Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeInt32ToTagged(), val); NodeProperties::SetBounds(val, Bounds(Type::None(), Type::SignedSmall())); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* change = reduction.replacement(); Capture add, branch, heap_number, if_true; EXPECT_THAT(change, IsWord32Shl(val, IsInt32Constant(SmiShiftAmount()))); } TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToFloat64) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeTaggedToFloat64(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* phi = reduction.replacement(); Capture branch, if_true; EXPECT_THAT( phi, IsPhi( kMachFloat64, IsLoadHeapNumber(val, CaptureEq(&if_true)), IsChangeInt32ToFloat64( IsWord32Sar(val, IsInt32Constant(SmiShiftAmount()))), IsMerge( AllOf(CaptureEq(&if_true), IsIfTrue(AllOf( CaptureEq(&branch), IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)), graph()->start())))), IsIfFalse(CaptureEq(&branch))))); } TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToInt32) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeTaggedToInt32(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* phi = reduction.replacement(); Capture branch, if_true; EXPECT_THAT( phi, IsPhi(kMachInt32, IsChangeFloat64ToInt32(IsLoadHeapNumber(val, CaptureEq(&if_true))), IsWord32Sar(val, IsInt32Constant(SmiShiftAmount())), IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), IsIfFalse(AllOf( CaptureEq(&branch), IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)), graph()->start())))))); } TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToUint32) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeTaggedToUint32(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* phi = reduction.replacement(); Capture branch, if_true; EXPECT_THAT( phi, IsPhi(kMachUint32, IsChangeFloat64ToUint32(IsLoadHeapNumber(val, CaptureEq(&if_true))), IsWord32Sar(val, IsInt32Constant(SmiShiftAmount())), IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), IsIfFalse(AllOf( CaptureEq(&branch), IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)), graph()->start())))))); } TARGET_TEST_F(ChangeLowering32Test, ChangeUint32ToTagged) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeUint32ToTagged(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* phi = reduction.replacement(); Capture branch, heap_number, if_false; EXPECT_THAT( phi, IsPhi( kMachAnyTagged, IsWord32Shl(val, IsInt32Constant(SmiShiftAmount())), IsFinish(AllOf(CaptureEq(&heap_number), IsAllocateHeapNumber(_, CaptureEq(&if_false))), IsStore(StoreRepresentation(kMachFloat64, kNoWriteBarrier), CaptureEq(&heap_number), IsInt32Constant(HeapNumberValueOffset()), IsChangeUint32ToFloat64(val), CaptureEq(&heap_number), CaptureEq(&if_false))), IsMerge( IsIfTrue(AllOf(CaptureEq(&branch), IsBranch(IsUint32LessThanOrEqual( val, IsInt32Constant(SmiMaxValue())), graph()->start()))), AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); } // ----------------------------------------------------------------------------- // 64-bit class ChangeLowering64Test : public ChangeLoweringTest { public: ~ChangeLowering64Test() OVERRIDE {} MachineType WordRepresentation() const FINAL { return kRepWord64; } }; TARGET_TEST_F(ChangeLowering64Test, ChangeInt32ToTagged) { Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeInt32ToTagged(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); EXPECT_THAT(reduction.replacement(), IsWord64Shl(IsChangeInt32ToInt64(val), IsInt64Constant(SmiShiftAmount()))); } TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToFloat64) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeTaggedToFloat64(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* phi = reduction.replacement(); Capture branch, if_true; EXPECT_THAT( phi, IsPhi( kMachFloat64, IsLoadHeapNumber(val, CaptureEq(&if_true)), IsChangeInt32ToFloat64(IsTruncateInt64ToInt32( IsWord64Sar(val, IsInt64Constant(SmiShiftAmount())))), IsMerge( AllOf(CaptureEq(&if_true), IsIfTrue(AllOf( CaptureEq(&branch), IsBranch(IsWord64And(val, IsInt64Constant(kSmiTagMask)), graph()->start())))), IsIfFalse(CaptureEq(&branch))))); } TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToInt32) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeTaggedToInt32(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* phi = reduction.replacement(); Capture branch, if_true; EXPECT_THAT( phi, IsPhi(kMachInt32, IsChangeFloat64ToInt32(IsLoadHeapNumber(val, CaptureEq(&if_true))), IsTruncateInt64ToInt32( IsWord64Sar(val, IsInt64Constant(SmiShiftAmount()))), IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), IsIfFalse(AllOf( CaptureEq(&branch), IsBranch(IsWord64And(val, IsInt64Constant(kSmiTagMask)), graph()->start())))))); } TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToUint32) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeTaggedToUint32(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* phi = reduction.replacement(); Capture branch, if_true; EXPECT_THAT( phi, IsPhi(kMachUint32, IsChangeFloat64ToUint32(IsLoadHeapNumber(val, CaptureEq(&if_true))), IsTruncateInt64ToInt32( IsWord64Sar(val, IsInt64Constant(SmiShiftAmount()))), IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), IsIfFalse(AllOf( CaptureEq(&branch), IsBranch(IsWord64And(val, IsInt64Constant(kSmiTagMask)), graph()->start())))))); } TARGET_TEST_F(ChangeLowering64Test, ChangeUint32ToTagged) { STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTagSize == 1); Node* val = Parameter(0); Node* node = graph()->NewNode(simplified()->ChangeUint32ToTagged(), val); Reduction reduction = Reduce(node); ASSERT_TRUE(reduction.Changed()); Node* phi = reduction.replacement(); Capture branch, heap_number, if_false; EXPECT_THAT( phi, IsPhi( kMachAnyTagged, IsWord64Shl(IsChangeUint32ToUint64(val), IsInt64Constant(SmiShiftAmount())), IsFinish(AllOf(CaptureEq(&heap_number), IsAllocateHeapNumber(_, CaptureEq(&if_false))), IsStore(StoreRepresentation(kMachFloat64, kNoWriteBarrier), CaptureEq(&heap_number), IsInt64Constant(HeapNumberValueOffset()), IsChangeUint32ToFloat64(val), CaptureEq(&heap_number), CaptureEq(&if_false))), IsMerge( IsIfTrue(AllOf(CaptureEq(&branch), IsBranch(IsUint32LessThanOrEqual( val, IsInt32Constant(SmiMaxValue())), graph()->start()))), AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); } } // namespace compiler } // namespace internal } // namespace v8