// 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/graph-unittest.h" #include "src/compiler/js-builtin-reducer.h" #include "src/compiler/js-graph.h" #include "src/compiler/node-properties-inl.h" #include "src/compiler/typer.h" #include "testing/gmock-support.h" using testing::Capture; namespace v8 { namespace internal { namespace compiler { class JSBuiltinReducerTest : public GraphTest { public: JSBuiltinReducerTest() : javascript_(zone()) {} protected: Reduction Reduce(Node* node) { Typer typer(zone()); MachineOperatorBuilder machine; JSGraph jsgraph(graph(), common(), javascript(), &typer, &machine); JSBuiltinReducer reducer(&jsgraph); return reducer.Reduce(node); } Node* Parameter(Type* t, int32_t index = 0) { Node* n = graph()->NewNode(common()->Parameter(index), graph()->start()); NodeProperties::SetBounds(n, Bounds(Type::None(), t)); return n; } Node* UndefinedConstant() { return HeapConstant( Unique<HeapObject>::CreateImmovable(factory()->undefined_value())); } JSOperatorBuilder* javascript() { return &javascript_; } private: JSOperatorBuilder javascript_; }; namespace { // TODO(mstarzinger): Find a common place and unify with test-js-typed-lowering. Type* const kNumberTypes[] = { Type::UnsignedSmall(), Type::OtherSignedSmall(), Type::OtherUnsigned31(), Type::OtherUnsigned32(), Type::OtherSigned32(), Type::SignedSmall(), Type::Signed32(), Type::Unsigned32(), Type::Integral32(), Type::MinusZero(), Type::NaN(), Type::OtherNumber(), Type::OrderedNumber(), Type::Number()}; } // namespace // ----------------------------------------------------------------------------- // Math.sqrt TEST_F(JSBuiltinReducerTest, MathSqrt) { Handle<JSFunction> f(isolate()->context()->math_sqrt_fun()); TRACED_FOREACH(Type*, t0, kNumberTypes) { Node* p0 = Parameter(t0, 0); Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f)); Node* call = graph()->NewNode(javascript()->Call(3, NO_CALL_FUNCTION_FLAGS), fun, UndefinedConstant(), p0); Reduction r = Reduce(call); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsFloat64Sqrt(p0)); } } // ----------------------------------------------------------------------------- // Math.max TEST_F(JSBuiltinReducerTest, MathMax0) { Handle<JSFunction> f(isolate()->context()->math_max_fun()); Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f)); Node* call = graph()->NewNode(javascript()->Call(2, NO_CALL_FUNCTION_FLAGS), fun, UndefinedConstant()); Reduction r = Reduce(call); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsNumberConstant(-V8_INFINITY)); } TEST_F(JSBuiltinReducerTest, MathMax1) { Handle<JSFunction> f(isolate()->context()->math_max_fun()); TRACED_FOREACH(Type*, t0, kNumberTypes) { Node* p0 = Parameter(t0, 0); Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f)); Node* call = graph()->NewNode(javascript()->Call(3, NO_CALL_FUNCTION_FLAGS), fun, UndefinedConstant(), p0); Reduction r = Reduce(call); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), p0); } } TEST_F(JSBuiltinReducerTest, MathMax2) { Handle<JSFunction> f(isolate()->context()->math_max_fun()); TRACED_FOREACH(Type*, t0, kNumberTypes) { TRACED_FOREACH(Type*, t1, kNumberTypes) { Node* p0 = Parameter(t0, 0); Node* p1 = Parameter(t1, 1); Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f)); Node* call = graph()->NewNode(javascript()->Call(4, NO_CALL_FUNCTION_FLAGS), fun, UndefinedConstant(), p0, p1); Reduction r = Reduce(call); if (t0->Is(Type::Integral32()) && t1->Is(Type::Integral32())) { Capture<Node*> branch; ASSERT_TRUE(r.Changed()); EXPECT_THAT( r.replacement(), IsPhi(kMachNone, p1, p0, IsMerge(IsIfTrue(CaptureEq(&branch)), IsIfFalse(AllOf(CaptureEq(&branch), IsBranch(IsNumberLessThan(p0, p1), graph()->start())))))); } else { ASSERT_FALSE(r.Changed()); EXPECT_EQ(IrOpcode::kJSCallFunction, call->opcode()); } } } } // ----------------------------------------------------------------------------- // Math.imul TEST_F(JSBuiltinReducerTest, MathImul) { Handle<JSFunction> f(isolate()->context()->math_imul_fun()); TRACED_FOREACH(Type*, t0, kNumberTypes) { TRACED_FOREACH(Type*, t1, kNumberTypes) { Node* p0 = Parameter(t0, 0); Node* p1 = Parameter(t1, 1); Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f)); Node* call = graph()->NewNode(javascript()->Call(4, NO_CALL_FUNCTION_FLAGS), fun, UndefinedConstant(), p0, p1); Reduction r = Reduce(call); if (t0->Is(Type::Integral32()) && t1->Is(Type::Integral32())) { ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Mul(p0, p1)); } else { ASSERT_FALSE(r.Changed()); EXPECT_EQ(IrOpcode::kJSCallFunction, call->opcode()); } } } } } // namespace compiler } // namespace internal } // namespace v8