// 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/js-builtin-reducer.h" #include "src/compiler/js-graph.h" #include "src/compiler/node-properties.h" #include "src/compiler/simplified-operator.h" #include "src/compiler/typer.h" #include "src/isolate-inl.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::Capture; namespace v8 { namespace internal { namespace compiler { class JSBuiltinReducerTest : public TypedGraphTest { public: JSBuiltinReducerTest() : javascript_(zone()) {} protected: Reduction Reduce(Node* node, MachineOperatorBuilder::Flags flags = MachineOperatorBuilder::Flag::kNoFlags) { MachineOperatorBuilder machine(zone(), MachineType::PointerRepresentation(), flags); SimplifiedOperatorBuilder simplified(zone()); JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified, &machine); // TODO(titzer): mock the GraphReducer here for better unit testing. GraphReducer graph_reducer(zone(), graph()); JSBuiltinReducer reducer(&graph_reducer, &jsgraph); return reducer.Reduce(node); } Node* MathFunction(const char* name) { Handle m = JSObject::GetProperty(isolate()->global_object(), isolate()->factory()->NewStringFromAsciiChecked( "Math")).ToHandleChecked(); Handle f = Handle::cast( JSObject::GetProperty( m, isolate()->factory()->NewStringFromAsciiChecked(name)) .ToHandleChecked()); return HeapConstant(f); } JSOperatorBuilder* javascript() { return &javascript_; } private: JSOperatorBuilder javascript_; }; namespace { Type* const kIntegral32Types[] = {Type::UnsignedSmall(), Type::Negative32(), Type::Unsigned31(), Type::SignedSmall(), Type::Signed32(), Type::Unsigned32(), Type::Integral32()}; const LanguageMode kLanguageModes[] = {SLOPPY, STRICT, STRONG}; // TODO(mstarzinger): Find a common place and unify with test-js-typed-lowering. Type* const kNumberTypes[] = { Type::UnsignedSmall(), Type::Negative32(), Type::Unsigned31(), Type::SignedSmall(), Type::Signed32(), Type::Unsigned32(), Type::Integral32(), Type::MinusZero(), Type::NaN(), Type::OrderedNumber(), Type::PlainNumber(), Type::Number()}; } // namespace // ----------------------------------------------------------------------------- // Math.max TEST_F(JSBuiltinReducerTest, MathMax0) { Node* function = MathFunction("max"); Node* effect = graph()->start(); Node* control = graph()->start(); Node* context = UndefinedConstant(); Node* frame_state = graph()->start(); TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { Node* call = graph()->NewNode(javascript()->CallFunction(2, language_mode), function, UndefinedConstant(), context, frame_state, frame_state, effect, control); Reduction r = Reduce(call); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsNumberConstant(-V8_INFINITY)); } } TEST_F(JSBuiltinReducerTest, MathMax1) { Node* function = MathFunction("max"); Node* effect = graph()->start(); Node* control = graph()->start(); Node* context = UndefinedConstant(); Node* frame_state = graph()->start(); TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { TRACED_FOREACH(Type*, t0, kNumberTypes) { Node* p0 = Parameter(t0, 0); Node* call = graph()->NewNode(javascript()->CallFunction(3, language_mode), function, UndefinedConstant(), p0, context, frame_state, frame_state, effect, control); Reduction r = Reduce(call); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), p0); } } } TEST_F(JSBuiltinReducerTest, MathMax2) { Node* function = MathFunction("max"); Node* effect = graph()->start(); Node* control = graph()->start(); Node* context = UndefinedConstant(); Node* frame_state = graph()->start(); TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { TRACED_FOREACH(Type*, t0, kIntegral32Types) { TRACED_FOREACH(Type*, t1, kIntegral32Types) { Node* p0 = Parameter(t0, 0); Node* p1 = Parameter(t1, 1); Node* call = graph()->NewNode(javascript()->CallFunction(4, language_mode), function, UndefinedConstant(), p0, p1, context, frame_state, frame_state, effect, control); Reduction r = Reduce(call); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsSelect(MachineRepresentation::kNone, IsNumberLessThan(p1, p0), p0, p1)); } } } } // ----------------------------------------------------------------------------- // Math.imul TEST_F(JSBuiltinReducerTest, MathImul) { Node* function = MathFunction("imul"); Node* effect = graph()->start(); Node* control = graph()->start(); Node* context = UndefinedConstant(); Node* frame_state = graph()->start(); TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { TRACED_FOREACH(Type*, t0, kIntegral32Types) { TRACED_FOREACH(Type*, t1, kIntegral32Types) { Node* p0 = Parameter(t0, 0); Node* p1 = Parameter(t1, 1); Node* call = graph()->NewNode(javascript()->CallFunction(4, language_mode), function, UndefinedConstant(), p0, p1, context, frame_state, frame_state, effect, control); Reduction r = Reduce(call); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsInt32Mul(p0, p1)); } } } } // ----------------------------------------------------------------------------- // Math.fround TEST_F(JSBuiltinReducerTest, MathFround) { Node* function = MathFunction("fround"); Node* effect = graph()->start(); Node* control = graph()->start(); Node* context = UndefinedConstant(); Node* frame_state = graph()->start(); TRACED_FOREACH(LanguageMode, language_mode, kLanguageModes) { TRACED_FOREACH(Type*, t0, kNumberTypes) { Node* p0 = Parameter(t0, 0); Node* call = graph()->NewNode(javascript()->CallFunction(3, language_mode), function, UndefinedConstant(), p0, context, frame_state, frame_state, effect, control); Reduction r = Reduce(call); ASSERT_TRUE(r.Changed()); EXPECT_THAT(r.replacement(), IsTruncateFloat64ToFloat32(p0)); } } } } // namespace compiler } // namespace internal } // namespace v8