blob: 97ff106329770269274b74bfeef435ed7e63e0a3 [file] [log] [blame]
// 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/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/js-typed-lowering.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-properties-inl.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::BitEq;
using testing::IsNaN;
namespace v8 {
namespace internal {
namespace compiler {
namespace {
const ExternalArrayType kExternalArrayTypes[] = {
kExternalUint8Array, kExternalInt8Array, kExternalUint16Array,
kExternalInt16Array, kExternalUint32Array, kExternalInt32Array,
kExternalFloat32Array, kExternalFloat64Array};
const double kFloat64Values[] = {
-V8_INFINITY, -4.23878e+275, -5.82632e+265, -6.60355e+220, -6.26172e+212,
-2.56222e+211, -4.82408e+201, -1.84106e+157, -1.63662e+127, -1.55772e+100,
-1.67813e+72, -2.3382e+55, -3.179e+30, -1.441e+09, -1.0647e+09,
-7.99361e+08, -5.77375e+08, -2.20984e+08, -32757, -13171, -9970, -3984,
-107, -105, -92, -77, -61, -0.000208163, -1.86685e-06, -1.17296e-10,
-9.26358e-11, -5.08004e-60, -1.74753e-65, -1.06561e-71, -5.67879e-79,
-5.78459e-130, -2.90989e-171, -7.15489e-243, -3.76242e-252, -1.05639e-263,
-4.40497e-267, -2.19666e-273, -4.9998e-276, -5.59821e-278, -2.03855e-282,
-5.99335e-283, -7.17554e-284, -3.11744e-309, -0.0, 0.0, 2.22507e-308,
1.30127e-270, 7.62898e-260, 4.00313e-249, 3.16829e-233, 1.85244e-228,
2.03544e-129, 1.35126e-110, 1.01182e-106, 5.26333e-94, 1.35292e-90,
2.85394e-83, 1.78323e-77, 5.4967e-57, 1.03207e-25, 4.57401e-25, 1.58738e-05,
2, 125, 2310, 9636, 14802, 17168, 28945, 29305, 4.81336e+07, 1.41207e+08,
4.65962e+08, 1.40499e+09, 2.12648e+09, 8.80006e+30, 1.4446e+45, 1.12164e+54,
2.48188e+89, 6.71121e+102, 3.074e+112, 4.9699e+152, 5.58383e+166,
4.30654e+172, 7.08824e+185, 9.6586e+214, 2.028e+223, 6.63277e+243,
1.56192e+261, 1.23202e+269, 5.72883e+289, 8.5798e+290, 1.40256e+294,
1.79769e+308, V8_INFINITY};
const size_t kIndices[] = {0, 1, 42, 100, 1024};
const double kIntegerValues[] = {-V8_INFINITY, INT_MIN, -1000.0, -42.0,
-1.0, 0.0, 1.0, 42.0,
1000.0, INT_MAX, UINT_MAX, V8_INFINITY};
Type* const kJSTypes[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
Type::Number(), Type::String(), Type::Object()};
const StrictMode kStrictModes[] = {SLOPPY, STRICT};
} // namespace
class JSTypedLoweringTest : public TypedGraphTest {
public:
JSTypedLoweringTest() : TypedGraphTest(3), javascript_(zone()) {}
~JSTypedLoweringTest() OVERRIDE {}
protected:
Reduction Reduce(Node* node) {
MachineOperatorBuilder machine(zone());
JSGraph jsgraph(graph(), common(), javascript(), &machine);
JSTypedLowering reducer(&jsgraph, zone());
return reducer.Reduce(node);
}
Handle<JSArrayBuffer> NewArrayBuffer(void* bytes, size_t byte_length) {
Handle<JSArrayBuffer> buffer = factory()->NewJSArrayBuffer();
Runtime::SetupArrayBuffer(isolate(), buffer, true, bytes, byte_length);
return buffer;
}
Matcher<Node*> IsIntPtrConstant(intptr_t value) {
return sizeof(value) == 4 ? IsInt32Constant(static_cast<int32_t>(value))
: IsInt64Constant(static_cast<int64_t>(value));
}
JSOperatorBuilder* javascript() { return &javascript_; }
private:
JSOperatorBuilder javascript_;
};
// -----------------------------------------------------------------------------
// JSUnaryNot
TEST_F(JSTypedLoweringTest, JSUnaryNotWithBoolean) {
Node* input = Parameter(Type::Boolean(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(input));
}
TEST_F(JSTypedLoweringTest, JSUnaryNotWithFalsish) {
Handle<Object> zero = factory()->NewNumber(0);
Node* input = Parameter(
Type::Union(
Type::MinusZero(),
Type::Union(
Type::NaN(),
Type::Union(
Type::Null(),
Type::Union(
Type::Undefined(),
Type::Union(
Type::Undetectable(),
Type::Union(
Type::Constant(factory()->false_value(), zone()),
Type::Range(zero, zero, zone()), zone()),
zone()),
zone()),
zone()),
zone()),
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(JSTypedLoweringTest, JSUnaryNotWithTruish) {
Node* input = Parameter(
Type::Union(
Type::Constant(factory()->true_value(), zone()),
Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()),
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
TEST_F(JSTypedLoweringTest, JSUnaryNotWithNonZeroPlainNumber) {
Node* input = Parameter(
Type::Range(factory()->NewNumber(1), factory()->NewNumber(42), zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
TEST_F(JSTypedLoweringTest, JSUnaryNotWithAny) {
Node* input = Parameter(Type::Any(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->UnaryNot(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(IsAnyToBoolean(input)));
}
// -----------------------------------------------------------------------------
// Constant propagation
TEST_F(JSTypedLoweringTest, ParameterWithMinusZero) {
{
Reduction r = Reduce(
Parameter(Type::Constant(factory()->minus_zero_value(), zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(-0.0));
}
{
Reduction r = Reduce(Parameter(Type::MinusZero()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(-0.0));
}
{
Reduction r = Reduce(Parameter(
Type::Union(Type::MinusZero(),
Type::Constant(factory()->NewNumber(0), zone()), zone())));
EXPECT_FALSE(r.Changed());
}
}
TEST_F(JSTypedLoweringTest, ParameterWithNull) {
Handle<HeapObject> null = factory()->null_value();
{
Reduction r = Reduce(Parameter(Type::Constant(null, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsHeapConstant(Unique<HeapObject>::CreateImmovable(null)));
}
{
Reduction r = Reduce(Parameter(Type::Null()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsHeapConstant(Unique<HeapObject>::CreateImmovable(null)));
}
}
TEST_F(JSTypedLoweringTest, ParameterWithNaN) {
const double kNaNs[] = {base::OS::nan_value(),
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::signaling_NaN()};
TRACED_FOREACH(double, nan, kNaNs) {
Handle<Object> constant = factory()->NewNumber(nan);
Reduction r = Reduce(Parameter(Type::Constant(constant, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
{
Reduction r =
Reduce(Parameter(Type::Constant(factory()->nan_value(), zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
{
Reduction r = Reduce(Parameter(Type::NaN()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(IsNaN()));
}
}
TEST_F(JSTypedLoweringTest, ParameterWithPlainNumber) {
TRACED_FOREACH(double, value, kFloat64Values) {
Handle<Object> constant = factory()->NewNumber(value);
Reduction r = Reduce(Parameter(Type::Constant(constant, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(value));
}
TRACED_FOREACH(double, value, kIntegerValues) {
Handle<Object> constant = factory()->NewNumber(value);
Reduction r = Reduce(Parameter(Type::Range(constant, constant, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberConstant(value));
}
}
TEST_F(JSTypedLoweringTest, ParameterWithUndefined) {
Handle<HeapObject> undefined = factory()->undefined_value();
{
Reduction r = Reduce(Parameter(Type::Undefined()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsHeapConstant(Unique<HeapObject>::CreateImmovable(undefined)));
}
{
Reduction r = Reduce(Parameter(Type::Constant(undefined, zone())));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsHeapConstant(Unique<HeapObject>::CreateImmovable(undefined)));
}
}
// -----------------------------------------------------------------------------
// JSToBoolean
TEST_F(JSTypedLoweringTest, JSToBooleanWithBoolean) {
Node* input = Parameter(Type::Boolean(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_EQ(input, r.replacement());
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithFalsish) {
Handle<Object> zero = factory()->NewNumber(0);
Node* input = Parameter(
Type::Union(
Type::MinusZero(),
Type::Union(
Type::NaN(),
Type::Union(
Type::Null(),
Type::Union(
Type::Undefined(),
Type::Union(
Type::Undetectable(),
Type::Union(
Type::Constant(factory()->false_value(), zone()),
Type::Range(zero, zero, zone()), zone()),
zone()),
zone()),
zone()),
zone()),
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithTruish) {
Node* input = Parameter(
Type::Union(
Type::Constant(factory()->true_value(), zone()),
Type::Union(Type::DetectableReceiver(), Type::Symbol(), zone()),
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithNonZeroPlainNumber) {
Node* input =
Parameter(Type::Range(factory()->NewNumber(1),
factory()->NewNumber(V8_INFINITY), zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
TEST_F(JSTypedLoweringTest, JSToBooleanWithAny) {
Node* input = Parameter(Type::Any(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(), input, context));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsAnyToBoolean(input));
}
// -----------------------------------------------------------------------------
// JSToNumber
TEST_F(JSTypedLoweringTest, JSToNumberWithPlainPrimitive) {
Node* const input = Parameter(Type::PlainPrimitive(), 0);
Node* const context = Parameter(Type::Any(), 1);
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->ToNumber(), input,
context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsToNumber(input, IsNumberConstant(BitEq(0.0)),
graph()->start(), control));
}
// -----------------------------------------------------------------------------
// JSStrictEqual
TEST_F(JSTypedLoweringTest, JSStrictEqualWithTheHole) {
Node* const the_hole = HeapConstant(factory()->the_hole_value());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
TRACED_FOREACH(Type*, type, kJSTypes) {
Node* const lhs = Parameter(type);
Reduction r = Reduce(graph()->NewNode(javascript()->StrictEqual(), lhs,
the_hole, context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
}
// -----------------------------------------------------------------------------
// JSShiftLeft
TEST_F(JSTypedLoweringTest, JSShiftLeftWithSigned32AndConstant) {
Node* const lhs = Parameter(Type::Signed32());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
TRACED_FORRANGE(double, rhs, 0, 31) {
Reduction r =
Reduce(graph()->NewNode(javascript()->ShiftLeft(), lhs,
NumberConstant(rhs), context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsWord32Shl(lhs, IsNumberConstant(BitEq(rhs))));
}
}
TEST_F(JSTypedLoweringTest, JSShiftLeftWithSigned32AndUnsigned32) {
Node* const lhs = Parameter(Type::Signed32());
Node* const rhs = Parameter(Type::Unsigned32());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->ShiftLeft(), lhs, rhs,
context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsWord32Shl(lhs, IsWord32And(rhs, IsInt32Constant(0x1f))));
}
// -----------------------------------------------------------------------------
// JSShiftRight
TEST_F(JSTypedLoweringTest, JSShiftRightWithSigned32AndConstant) {
Node* const lhs = Parameter(Type::Signed32());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
TRACED_FORRANGE(double, rhs, 0, 31) {
Reduction r =
Reduce(graph()->NewNode(javascript()->ShiftRight(), lhs,
NumberConstant(rhs), context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsWord32Sar(lhs, IsNumberConstant(BitEq(rhs))));
}
}
TEST_F(JSTypedLoweringTest, JSShiftRightWithSigned32AndUnsigned32) {
Node* const lhs = Parameter(Type::Signed32());
Node* const rhs = Parameter(Type::Unsigned32());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->ShiftRight(), lhs, rhs,
context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsWord32Sar(lhs, IsWord32And(rhs, IsInt32Constant(0x1f))));
}
// -----------------------------------------------------------------------------
// JSShiftRightLogical
TEST_F(JSTypedLoweringTest, JSShiftRightLogicalWithUnsigned32AndConstant) {
Node* const lhs = Parameter(Type::Unsigned32());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
TRACED_FORRANGE(double, rhs, 0, 31) {
Reduction r =
Reduce(graph()->NewNode(javascript()->ShiftRightLogical(), lhs,
NumberConstant(rhs), context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsWord32Shr(lhs, IsNumberConstant(BitEq(rhs))));
}
}
TEST_F(JSTypedLoweringTest, JSShiftRightLogicalWithUnsigned32AndUnsigned32) {
Node* const lhs = Parameter(Type::Unsigned32());
Node* const rhs = Parameter(Type::Unsigned32());
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->ShiftRightLogical(), lhs,
rhs, context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsWord32Shr(lhs, IsWord32And(rhs, IsInt32Constant(0x1f))));
}
// -----------------------------------------------------------------------------
// JSLoadContext
TEST_F(JSTypedLoweringTest, JSLoadContext) {
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
static bool kBooleans[] = {false, true};
TRACED_FOREACH(size_t, index, kIndices) {
TRACED_FOREACH(bool, immutable, kBooleans) {
Reduction const r1 = Reduce(
graph()->NewNode(javascript()->LoadContext(0, index, immutable),
context, context, effect));
ASSERT_TRUE(r1.Changed());
EXPECT_THAT(r1.replacement(),
IsLoadField(AccessBuilder::ForContextSlot(index), context,
effect, graph()->start()));
Reduction const r2 = Reduce(
graph()->NewNode(javascript()->LoadContext(1, index, immutable),
context, context, effect));
ASSERT_TRUE(r2.Changed());
EXPECT_THAT(r2.replacement(),
IsLoadField(AccessBuilder::ForContextSlot(index),
IsLoadField(AccessBuilder::ForContextSlot(
Context::PREVIOUS_INDEX),
context, effect, graph()->start()),
effect, graph()->start()));
}
}
}
// -----------------------------------------------------------------------------
// JSStoreContext
TEST_F(JSTypedLoweringTest, JSStoreContext) {
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Node* const control = graph()->start();
TRACED_FOREACH(size_t, index, kIndices) {
TRACED_FOREACH(Type*, type, kJSTypes) {
Node* const value = Parameter(type);
Reduction const r1 =
Reduce(graph()->NewNode(javascript()->StoreContext(0, index), context,
value, context, effect, control));
ASSERT_TRUE(r1.Changed());
EXPECT_THAT(r1.replacement(),
IsStoreField(AccessBuilder::ForContextSlot(index), context,
value, effect, control));
Reduction const r2 =
Reduce(graph()->NewNode(javascript()->StoreContext(1, index), context,
value, context, effect, control));
ASSERT_TRUE(r2.Changed());
EXPECT_THAT(r2.replacement(),
IsStoreField(AccessBuilder::ForContextSlot(index),
IsLoadField(AccessBuilder::ForContextSlot(
Context::PREVIOUS_INDEX),
context, effect, graph()->start()),
value, effect, control));
}
}
}
// -----------------------------------------------------------------------------
// JSLoadProperty
TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArray) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
VectorSlotPair feedback(Handle<TypeFeedbackVector>::null(),
FeedbackVectorICSlot::Invalid());
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
int const element_size = static_cast<int>(array->element_size());
Node* key = Parameter(
Type::Range(factory()->NewNumber(kMinInt / element_size),
factory()->NewNumber(kMaxInt / element_size), zone()));
Node* base = HeapConstant(array);
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
Node* node = graph()->NewNode(javascript()->LoadProperty(feedback), base,
key, context);
if (FLAG_turbo_deoptimization) {
node->AppendInput(zone(), UndefinedConstant());
}
node->AppendInput(zone(), effect);
node->AppendInput(zone(), control);
Reduction r = Reduce(node);
Matcher<Node*> offset_matcher =
element_size == 1
? key
: IsWord32Shl(key, IsInt32Constant(WhichPowerOf2(element_size)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsLoadBuffer(BufferAccess(type),
IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher,
IsNumberConstant(array->byte_length()->Number()), effect,
control));
}
}
TEST_F(JSTypedLoweringTest, JSLoadPropertyFromExternalTypedArrayWithSafeKey) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
VectorSlotPair feedback(Handle<TypeFeedbackVector>::null(),
FeedbackVectorICSlot::Invalid());
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
ElementAccess access = AccessBuilder::ForTypedArrayElement(type, true);
int min = random_number_generator()->NextInt(static_cast<int>(kLength));
int max = random_number_generator()->NextInt(static_cast<int>(kLength));
if (min > max) std::swap(min, max);
Node* key = Parameter(Type::Range(factory()->NewNumber(min),
factory()->NewNumber(max), zone()));
Node* base = HeapConstant(array);
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
Node* node = graph()->NewNode(javascript()->LoadProperty(feedback), base,
key, context);
if (FLAG_turbo_deoptimization) {
node->AppendInput(zone(), UndefinedConstant());
}
node->AppendInput(zone(), effect);
node->AppendInput(zone(), control);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsLoadElement(access,
IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
key, effect, control));
}
}
// -----------------------------------------------------------------------------
// JSStoreProperty
TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArray) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
TRACED_FOREACH(StrictMode, strict_mode, kStrictModes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
int const element_size = static_cast<int>(array->element_size());
Node* key = Parameter(
Type::Range(factory()->NewNumber(kMinInt / element_size),
factory()->NewNumber(kMaxInt / element_size), zone()));
Node* base = HeapConstant(array);
Node* value =
Parameter(AccessBuilder::ForTypedArrayElement(type, true).type);
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
Node* node = graph()->NewNode(javascript()->StoreProperty(strict_mode),
base, key, value, context);
if (FLAG_turbo_deoptimization) {
node->AppendInput(zone(), UndefinedConstant());
}
node->AppendInput(zone(), effect);
node->AppendInput(zone(), control);
Reduction r = Reduce(node);
Matcher<Node*> offset_matcher =
element_size == 1
? key
: IsWord32Shl(key, IsInt32Constant(WhichPowerOf2(element_size)));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsStoreBuffer(BufferAccess(type),
IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher,
IsNumberConstant(array->byte_length()->Number()), value,
effect, control));
}
}
}
TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithConversion) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
TRACED_FOREACH(StrictMode, strict_mode, kStrictModes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
int const element_size = static_cast<int>(array->element_size());
Node* key = Parameter(
Type::Range(factory()->NewNumber(kMinInt / element_size),
factory()->NewNumber(kMaxInt / element_size), zone()));
Node* base = HeapConstant(array);
Node* value = Parameter(Type::Any());
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
Node* node = graph()->NewNode(javascript()->StoreProperty(strict_mode),
base, key, value, context);
if (FLAG_turbo_deoptimization) {
node->AppendInput(zone(), UndefinedConstant());
}
node->AppendInput(zone(), effect);
node->AppendInput(zone(), control);
Reduction r = Reduce(node);
Matcher<Node*> offset_matcher =
element_size == 1
? key
: IsWord32Shl(key, IsInt32Constant(WhichPowerOf2(element_size)));
Matcher<Node*> value_matcher =
IsToNumber(value, context, effect, control);
Matcher<Node*> effect_matcher = value_matcher;
if (AccessBuilder::ForTypedArrayElement(type, true)
.type->Is(Type::Signed32())) {
value_matcher = IsNumberToInt32(value_matcher);
} else if (AccessBuilder::ForTypedArrayElement(type, true)
.type->Is(Type::Unsigned32())) {
value_matcher = IsNumberToUint32(value_matcher);
}
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsStoreBuffer(BufferAccess(type),
IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
offset_matcher,
IsNumberConstant(array->byte_length()->Number()),
value_matcher, effect_matcher, control));
}
}
}
TEST_F(JSTypedLoweringTest, JSStorePropertyToExternalTypedArrayWithSafeKey) {
const size_t kLength = 17;
double backing_store[kLength];
Handle<JSArrayBuffer> buffer =
NewArrayBuffer(backing_store, sizeof(backing_store));
TRACED_FOREACH(ExternalArrayType, type, kExternalArrayTypes) {
TRACED_FOREACH(StrictMode, strict_mode, kStrictModes) {
Handle<JSTypedArray> array =
factory()->NewJSTypedArray(type, buffer, 0, kLength);
ElementAccess access = AccessBuilder::ForTypedArrayElement(type, true);
int min = random_number_generator()->NextInt(static_cast<int>(kLength));
int max = random_number_generator()->NextInt(static_cast<int>(kLength));
if (min > max) std::swap(min, max);
Node* key = Parameter(Type::Range(factory()->NewNumber(min),
factory()->NewNumber(max), zone()));
Node* base = HeapConstant(array);
Node* value = Parameter(access.type);
Node* context = UndefinedConstant();
Node* effect = graph()->start();
Node* control = graph()->start();
Node* node = graph()->NewNode(javascript()->StoreProperty(strict_mode),
base, key, value, context);
if (FLAG_turbo_deoptimization) {
node->AppendInput(zone(), UndefinedConstant());
}
node->AppendInput(zone(), effect);
node->AppendInput(zone(), control);
Reduction r = Reduce(node);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsStoreElement(
access, IsIntPtrConstant(bit_cast<intptr_t>(&backing_store[0])),
key, value, effect, control));
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8