v8/test/unittests/compiler/change-lowering-unittest.cc
mstarzinger b7990793cf [turbofan] Move SimplifiedOperatorBuilder into JSGraph.
This fixes the lifetime of nodes created by JSGlobalSpecialization that
contain a simplified operator. In the case where this reducer runs as
part of the inliner, the SimplifiedOperatorBuilder was instantiated with
the wrong zone. This led to use-after-free of simplified operators.

To avoid such situations in the future, we decided to move this operator
builder into the JSGraph and make the situation uniform with all other
operator builders.

R=bmeurer@chromium.org
BUG=chromium:543528
LOG=n

Review URL: https://codereview.chromium.org/1409993002

Cr-Commit-Position: refs/heads/master@{#31334}
2015-10-16 12:38:52 +00:00

470 lines
17 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 "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.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 TypedGraphTest {
public:
ChangeLoweringTest() : simplified_(zone()) {}
virtual MachineType WordRepresentation() const = 0;
protected:
bool Is32() const { return WordRepresentation() == kRepWord32; }
bool Is64() const { return WordRepresentation() == kRepWord64; }
Reduction Reduce(Node* node) {
MachineOperatorBuilder machine(zone(), WordRepresentation());
JSOperatorBuilder javascript(zone());
JSGraph jsgraph(isolate(), graph(), common(), &javascript, nullptr,
&machine);
ChangeLowering reducer(&jsgraph);
return reducer.Reduce(node);
}
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
Matcher<Node*> IsAllocateHeapNumber(const Matcher<Node*>& effect_matcher,
const Matcher<Node*>& control_matcher) {
return IsCall(
_, IsHeapConstant(AllocateHeapNumberStub(isolate()).GetCode()),
IsNumberConstant(BitEq(0.0)), effect_matcher, control_matcher);
}
Matcher<Node*> IsChangeInt32ToSmi(const Matcher<Node*>& value_matcher) {
return Is64() ? IsWord64Shl(IsChangeInt32ToInt64(value_matcher),
IsSmiShiftBitsConstant())
: IsWord32Shl(value_matcher, IsSmiShiftBitsConstant());
}
Matcher<Node*> IsChangeSmiToInt32(const Matcher<Node*>& value_matcher) {
return Is64() ? IsTruncateInt64ToInt32(
IsWord64Sar(value_matcher, IsSmiShiftBitsConstant()))
: IsWord32Sar(value_matcher, IsSmiShiftBitsConstant());
}
Matcher<Node*> IsChangeUint32ToSmi(const Matcher<Node*>& value_matcher) {
return Is64() ? IsWord64Shl(IsChangeUint32ToUint64(value_matcher),
IsSmiShiftBitsConstant())
: IsWord32Shl(value_matcher, IsSmiShiftBitsConstant());
}
Matcher<Node*> IsLoadHeapNumber(const Matcher<Node*>& value_matcher,
const Matcher<Node*>& control_matcher) {
return IsLoad(kMachFloat64, value_matcher,
IsIntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag),
graph()->start(), control_matcher);
}
Matcher<Node*> IsIntPtrConstant(int value) {
return Is32() ? IsInt32Constant(value) : IsInt64Constant(value);
}
Matcher<Node*> IsSmiShiftBitsConstant() {
return IsIntPtrConstant(kSmiShiftSize + kSmiTagSize);
}
Matcher<Node*> IsWordEqual(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& 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<MachineType> {
public:
~ChangeLoweringCommonTest() override {}
MachineType WordRepresentation() const final { return GetParam(); }
};
TARGET_TEST_P(ChangeLoweringCommonTest, ChangeBitToBool) {
Node* value = Parameter(Type::Boolean());
Reduction r =
Reduce(graph()->NewNode(simplified()->ChangeBitToBool(), value));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsSelect(kMachAnyTagged, value, IsTrueConstant(),
IsFalseConstant()));
}
TARGET_TEST_P(ChangeLoweringCommonTest, ChangeBoolToBit) {
Node* value = Parameter(Type::Number());
Reduction r =
Reduce(graph()->NewNode(simplified()->ChangeBoolToBit(), value));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsWordEqual(value, IsTrueConstant()));
}
TARGET_TEST_P(ChangeLoweringCommonTest, ChangeFloat64ToTagged) {
Node* value = Parameter(Type::Number());
Reduction r =
Reduce(graph()->NewNode(simplified()->ChangeFloat64ToTagged(), value));
ASSERT_TRUE(r.Changed());
Capture<Node*> heap_number;
EXPECT_THAT(
r.replacement(),
IsFinishRegion(
AllOf(CaptureEq(&heap_number),
IsAllocateHeapNumber(IsBeginRegion(graph()->start()),
graph()->start())),
IsStore(StoreRepresentation(kMachFloat64, kNoWriteBarrier),
CaptureEq(&heap_number),
IsIntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag),
value, CaptureEq(&heap_number), graph()->start())));
}
TARGET_TEST_P(ChangeLoweringCommonTest, ChangeInt32ToTaggedWithSignedSmall) {
Node* value = Parameter(Type::SignedSmall());
Reduction r =
Reduce(graph()->NewNode(simplified()->ChangeInt32ToTagged(), value));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsChangeInt32ToSmi(value));
}
TARGET_TEST_P(ChangeLoweringCommonTest, ChangeUint32ToTaggedWithUnsignedSmall) {
Node* value = Parameter(Type::UnsignedSmall());
Reduction r =
Reduce(graph()->NewNode(simplified()->ChangeUint32ToTagged(), value));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsChangeUint32ToSmi(value));
}
TARGET_TEST_P(ChangeLoweringCommonTest, ChangeTaggedToInt32WithTaggedSigned) {
Node* value = Parameter(Type::TaggedSigned());
Reduction r =
Reduce(graph()->NewNode(simplified()->ChangeTaggedToInt32(), value));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsChangeSmiToInt32(value));
}
TARGET_TEST_P(ChangeLoweringCommonTest, ChangeTaggedToInt32WithTaggedPointer) {
Node* value = Parameter(Type::TaggedPointer());
Reduction r =
Reduce(graph()->NewNode(simplified()->ChangeTaggedToInt32(), value));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsChangeFloat64ToInt32(
IsLoadHeapNumber(value, graph()->start())));
}
TARGET_TEST_P(ChangeLoweringCommonTest, ChangeTaggedToUint32WithTaggedSigned) {
Node* value = Parameter(Type::TaggedSigned());
Reduction r =
Reduce(graph()->NewNode(simplified()->ChangeTaggedToUint32(), value));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsChangeSmiToInt32(value));
}
TARGET_TEST_P(ChangeLoweringCommonTest, ChangeTaggedToUint32WithTaggedPointer) {
Node* value = Parameter(Type::TaggedPointer());
Reduction r =
Reduce(graph()->NewNode(simplified()->ChangeTaggedToUint32(), value));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsChangeFloat64ToUint32(
IsLoadHeapNumber(value, graph()->start())));
}
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* value = Parameter(Type::Integral32());
Node* node = graph()->NewNode(simplified()->ChangeInt32ToTagged(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
Capture<Node*> add, branch, heap_number, if_true;
EXPECT_THAT(
r.replacement(),
IsPhi(kMachAnyTagged,
IsFinishRegion(
AllOf(CaptureEq(&heap_number),
IsAllocateHeapNumber(_, CaptureEq(&if_true))),
IsStore(
StoreRepresentation(kMachFloat64, kNoWriteBarrier),
CaptureEq(&heap_number),
IsIntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag),
IsChangeInt32ToFloat64(value), CaptureEq(&heap_number),
CaptureEq(&if_true))),
IsProjection(0, AllOf(CaptureEq(&add),
IsInt32AddWithOverflow(value, value))),
IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
IsIfFalse(AllOf(CaptureEq(&branch),
IsBranch(IsProjection(1, CaptureEq(&add)),
graph()->start()))))));
}
TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToFloat64) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
Node* value = Parameter(Type::Number());
Node* node = graph()->NewNode(simplified()->ChangeTaggedToFloat64(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
Capture<Node*> branch, if_true;
EXPECT_THAT(
r.replacement(),
IsPhi(kMachFloat64, IsLoadHeapNumber(value, CaptureEq(&if_true)),
IsChangeInt32ToFloat64(IsWord32Sar(
value, IsInt32Constant(kSmiTagSize + kSmiShiftSize))),
IsMerge(AllOf(CaptureEq(&if_true),
IsIfTrue(AllOf(
CaptureEq(&branch),
IsBranch(IsWord32And(
value, IsInt32Constant(kSmiTagMask)),
graph()->start())))),
IsIfFalse(CaptureEq(&branch)))));
}
TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToInt32) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
Node* value = Parameter(Type::Signed32());
Node* node = graph()->NewNode(simplified()->ChangeTaggedToInt32(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
Capture<Node*> branch, if_true;
EXPECT_THAT(
r.replacement(),
IsPhi(
kMachInt32,
IsChangeFloat64ToInt32(IsLoadHeapNumber(value, CaptureEq(&if_true))),
IsWord32Sar(value, IsInt32Constant(kSmiTagSize + kSmiShiftSize)),
IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
IsIfFalse(AllOf(
CaptureEq(&branch),
IsBranch(IsWord32And(value, IsInt32Constant(kSmiTagMask)),
graph()->start()))))));
}
TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToUint32) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
Node* value = Parameter(Type::Unsigned32());
Node* node = graph()->NewNode(simplified()->ChangeTaggedToUint32(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
Capture<Node*> branch, if_true;
EXPECT_THAT(
r.replacement(),
IsPhi(
kMachUint32,
IsChangeFloat64ToUint32(IsLoadHeapNumber(value, CaptureEq(&if_true))),
IsWord32Sar(value, IsInt32Constant(kSmiTagSize + kSmiShiftSize)),
IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
IsIfFalse(AllOf(
CaptureEq(&branch),
IsBranch(IsWord32And(value, IsInt32Constant(kSmiTagMask)),
graph()->start()))))));
}
TARGET_TEST_F(ChangeLowering32Test, ChangeUint32ToTagged) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
Node* value = Parameter(Type::Number());
Node* node = graph()->NewNode(simplified()->ChangeUint32ToTagged(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
Capture<Node*> branch, heap_number, if_false;
EXPECT_THAT(
r.replacement(),
IsPhi(
kMachAnyTagged,
IsWord32Shl(value, IsInt32Constant(kSmiTagSize + kSmiShiftSize)),
IsFinishRegion(
AllOf(CaptureEq(&heap_number),
IsAllocateHeapNumber(_, CaptureEq(&if_false))),
IsStore(
StoreRepresentation(kMachFloat64, kNoWriteBarrier),
CaptureEq(&heap_number),
IsInt32Constant(HeapNumber::kValueOffset - kHeapObjectTag),
IsChangeUint32ToFloat64(value), CaptureEq(&heap_number),
CaptureEq(&if_false))),
IsMerge(IsIfTrue(AllOf(
CaptureEq(&branch),
IsBranch(IsUint32LessThanOrEqual(
value, IsInt32Constant(Smi::kMaxValue)),
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* value = Parameter(Type::Signed32());
Node* node = graph()->NewNode(simplified()->ChangeInt32ToTagged(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsChangeInt32ToSmi(value));
}
TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToFloat64) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
Node* value = Parameter(Type::Number());
Node* node = graph()->NewNode(simplified()->ChangeTaggedToFloat64(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
Capture<Node*> branch, if_true;
EXPECT_THAT(
r.replacement(),
IsPhi(kMachFloat64, IsLoadHeapNumber(value, CaptureEq(&if_true)),
IsChangeInt32ToFloat64(IsTruncateInt64ToInt32(IsWord64Sar(
value, IsInt64Constant(kSmiTagSize + kSmiShiftSize)))),
IsMerge(AllOf(CaptureEq(&if_true),
IsIfTrue(AllOf(
CaptureEq(&branch),
IsBranch(IsWord64And(
value, IsInt64Constant(kSmiTagMask)),
graph()->start())))),
IsIfFalse(CaptureEq(&branch)))));
}
TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToInt32) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
Node* value = Parameter(Type::Signed32());
Node* node = graph()->NewNode(simplified()->ChangeTaggedToInt32(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
Capture<Node*> branch, if_true;
EXPECT_THAT(
r.replacement(),
IsPhi(
kMachInt32,
IsChangeFloat64ToInt32(IsLoadHeapNumber(value, CaptureEq(&if_true))),
IsTruncateInt64ToInt32(
IsWord64Sar(value, IsInt64Constant(kSmiTagSize + kSmiShiftSize))),
IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
IsIfFalse(AllOf(
CaptureEq(&branch),
IsBranch(IsWord64And(value, IsInt64Constant(kSmiTagMask)),
graph()->start()))))));
}
TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToUint32) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
Node* value = Parameter(Type::Unsigned32());
Node* node = graph()->NewNode(simplified()->ChangeTaggedToUint32(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
Capture<Node*> branch, if_true;
EXPECT_THAT(
r.replacement(),
IsPhi(
kMachUint32,
IsChangeFloat64ToUint32(IsLoadHeapNumber(value, CaptureEq(&if_true))),
IsTruncateInt64ToInt32(
IsWord64Sar(value, IsInt64Constant(kSmiTagSize + kSmiShiftSize))),
IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))),
IsIfFalse(AllOf(
CaptureEq(&branch),
IsBranch(IsWord64And(value, IsInt64Constant(kSmiTagMask)),
graph()->start()))))));
}
TARGET_TEST_F(ChangeLowering64Test, ChangeUint32ToTagged) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
Node* value = Parameter(Type::Number());
Node* node = graph()->NewNode(simplified()->ChangeUint32ToTagged(), value);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
Capture<Node*> branch, heap_number, if_false;
EXPECT_THAT(
r.replacement(),
IsPhi(
kMachAnyTagged,
IsWord64Shl(IsChangeUint32ToUint64(value),
IsInt64Constant(kSmiTagSize + kSmiShiftSize)),
IsFinishRegion(
AllOf(CaptureEq(&heap_number),
IsAllocateHeapNumber(_, CaptureEq(&if_false))),
IsStore(
StoreRepresentation(kMachFloat64, kNoWriteBarrier),
CaptureEq(&heap_number),
IsInt64Constant(HeapNumber::kValueOffset - kHeapObjectTag),
IsChangeUint32ToFloat64(value), CaptureEq(&heap_number),
CaptureEq(&if_false))),
IsMerge(IsIfTrue(AllOf(
CaptureEq(&branch),
IsBranch(IsUint32LessThanOrEqual(
value, IsInt32Constant(Smi::kMaxValue)),
graph()->start()))),
AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch))))));
}
} // namespace compiler
} // namespace internal
} // namespace v8