1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "src/compiler/js-builtin-reducer.h"
6 #include "src/compiler/js-graph.h"
7 #include "src/compiler/node-properties.h"
8 #include "src/compiler/typer.h"
9 #include "test/unittests/compiler/graph-unittest.h"
10 #include "test/unittests/compiler/node-test-utils.h"
11 #include "testing/gmock-support.h"
14 using testing::Capture;
20 class JSBuiltinReducerTest : public TypedGraphTest {
22 JSBuiltinReducerTest() : javascript_(zone()) {}
25 Reduction Reduce(Node* node, MachineOperatorBuilder::Flags flags =
26 MachineOperatorBuilder::Flag::kNoFlags) {
27 MachineOperatorBuilder machine(zone(), kMachPtr, flags);
28 JSGraph jsgraph(isolate(), graph(), common(), javascript(), &machine);
29 JSBuiltinReducer reducer(&jsgraph);
30 return reducer.Reduce(node);
33 Handle<JSFunction> MathFunction(const char* name) {
35 JSObject::GetProperty(isolate()->global_object(),
36 isolate()->factory()->NewStringFromAsciiChecked(
37 "Math")).ToHandleChecked();
38 Handle<JSFunction> f = Handle<JSFunction>::cast(
39 JSObject::GetProperty(
40 m, isolate()->factory()->NewStringFromAsciiChecked(name))
45 JSOperatorBuilder* javascript() { return &javascript_; }
48 JSOperatorBuilder javascript_;
54 // TODO(mstarzinger): Find a common place and unify with test-js-typed-lowering.
55 Type* const kNumberTypes[] = {
56 Type::UnsignedSmall(), Type::Negative32(), Type::Unsigned31(),
57 Type::SignedSmall(), Type::Signed32(), Type::Unsigned32(),
58 Type::Integral32(), Type::MinusZero(), Type::NaN(),
59 Type::OrderedNumber(), Type::PlainNumber(), Type::Number()};
64 // -----------------------------------------------------------------------------
68 TEST_F(JSBuiltinReducerTest, MathAbs) {
69 Handle<JSFunction> f = MathFunction("abs");
71 TRACED_FOREACH(Type*, t0, kNumberTypes) {
72 Node* p0 = Parameter(t0, 0);
73 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
75 graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS),
76 fun, UndefinedConstant(), p0);
77 Reduction r = Reduce(call);
79 if (t0->Is(Type::Unsigned32())) {
80 ASSERT_TRUE(r.Changed());
81 EXPECT_THAT(r.replacement(), p0);
83 Capture<Node*> branch;
84 ASSERT_TRUE(r.Changed());
88 IsNumberLessThan(IsNumberConstant(BitEq(0.0)), p0), p0,
89 IsNumberSubtract(IsNumberConstant(BitEq(0.0)), p0)));
95 // -----------------------------------------------------------------------------
99 TEST_F(JSBuiltinReducerTest, MathSqrt) {
100 Handle<JSFunction> f = MathFunction("sqrt");
102 TRACED_FOREACH(Type*, t0, kNumberTypes) {
103 Node* p0 = Parameter(t0, 0);
104 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
106 graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS),
107 fun, UndefinedConstant(), p0);
108 Reduction r = Reduce(call);
110 ASSERT_TRUE(r.Changed());
111 EXPECT_THAT(r.replacement(), IsFloat64Sqrt(p0));
116 // -----------------------------------------------------------------------------
120 TEST_F(JSBuiltinReducerTest, MathMax0) {
121 Handle<JSFunction> f = MathFunction("max");
123 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
125 graph()->NewNode(javascript()->CallFunction(2, NO_CALL_FUNCTION_FLAGS),
126 fun, UndefinedConstant());
127 Reduction r = Reduce(call);
129 ASSERT_TRUE(r.Changed());
130 EXPECT_THAT(r.replacement(), IsNumberConstant(-V8_INFINITY));
134 TEST_F(JSBuiltinReducerTest, MathMax1) {
135 Handle<JSFunction> f = MathFunction("max");
137 TRACED_FOREACH(Type*, t0, kNumberTypes) {
138 Node* p0 = Parameter(t0, 0);
139 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
141 graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS),
142 fun, UndefinedConstant(), p0);
143 Reduction r = Reduce(call);
145 ASSERT_TRUE(r.Changed());
146 EXPECT_THAT(r.replacement(), p0);
151 TEST_F(JSBuiltinReducerTest, MathMax2) {
152 Handle<JSFunction> f = MathFunction("max");
154 TRACED_FOREACH(Type*, t0, kNumberTypes) {
155 TRACED_FOREACH(Type*, t1, kNumberTypes) {
156 Node* p0 = Parameter(t0, 0);
157 Node* p1 = Parameter(t1, 1);
158 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
159 Node* call = graph()->NewNode(
160 javascript()->CallFunction(4, NO_CALL_FUNCTION_FLAGS), fun,
161 UndefinedConstant(), p0, p1);
162 Reduction r = Reduce(call);
164 if (t0->Is(Type::Integral32()) && t1->Is(Type::Integral32())) {
165 ASSERT_TRUE(r.Changed());
166 EXPECT_THAT(r.replacement(),
167 IsSelect(kMachNone, IsNumberLessThan(p1, p0), p0, p1));
169 ASSERT_FALSE(r.Changed());
170 EXPECT_EQ(IrOpcode::kJSCallFunction, call->opcode());
177 // -----------------------------------------------------------------------------
181 TEST_F(JSBuiltinReducerTest, MathImul) {
182 Handle<JSFunction> f = MathFunction("imul");
184 TRACED_FOREACH(Type*, t0, kNumberTypes) {
185 TRACED_FOREACH(Type*, t1, kNumberTypes) {
186 Node* p0 = Parameter(t0, 0);
187 Node* p1 = Parameter(t1, 1);
188 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
189 Node* call = graph()->NewNode(
190 javascript()->CallFunction(4, NO_CALL_FUNCTION_FLAGS), fun,
191 UndefinedConstant(), p0, p1);
192 Reduction r = Reduce(call);
194 if (t0->Is(Type::Integral32()) && t1->Is(Type::Integral32())) {
195 ASSERT_TRUE(r.Changed());
196 EXPECT_THAT(r.replacement(), IsInt32Mul(p0, p1));
198 ASSERT_FALSE(r.Changed());
199 EXPECT_EQ(IrOpcode::kJSCallFunction, call->opcode());
206 // -----------------------------------------------------------------------------
210 TEST_F(JSBuiltinReducerTest, MathFround) {
211 Handle<JSFunction> f = MathFunction("fround");
213 TRACED_FOREACH(Type*, t0, kNumberTypes) {
214 Node* p0 = Parameter(t0, 0);
215 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
217 graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS),
218 fun, UndefinedConstant(), p0);
219 Reduction r = Reduce(call);
221 ASSERT_TRUE(r.Changed());
222 EXPECT_THAT(r.replacement(), IsTruncateFloat64ToFloat32(p0));
227 // -----------------------------------------------------------------------------
231 TEST_F(JSBuiltinReducerTest, MathFloorAvailable) {
232 Handle<JSFunction> f = MathFunction("floor");
234 TRACED_FOREACH(Type*, t0, kNumberTypes) {
235 Node* p0 = Parameter(t0, 0);
236 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
238 graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS),
239 fun, UndefinedConstant(), p0);
240 Reduction r = Reduce(call, MachineOperatorBuilder::Flag::kFloat64Floor);
242 ASSERT_TRUE(r.Changed());
243 EXPECT_THAT(r.replacement(), IsFloat64Floor(p0));
248 TEST_F(JSBuiltinReducerTest, MathFloorUnavailable) {
249 Handle<JSFunction> f = MathFunction("floor");
251 TRACED_FOREACH(Type*, t0, kNumberTypes) {
252 Node* p0 = Parameter(t0, 0);
253 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
255 graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS),
256 fun, UndefinedConstant(), p0);
257 Reduction r = Reduce(call, MachineOperatorBuilder::Flag::kNoFlags);
259 ASSERT_FALSE(r.Changed());
264 // -----------------------------------------------------------------------------
268 TEST_F(JSBuiltinReducerTest, MathCeilAvailable) {
269 Handle<JSFunction> f = MathFunction("ceil");
271 TRACED_FOREACH(Type*, t0, kNumberTypes) {
272 Node* p0 = Parameter(t0, 0);
273 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
275 graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS),
276 fun, UndefinedConstant(), p0);
277 Reduction r = Reduce(call, MachineOperatorBuilder::Flag::kFloat64Ceil);
279 ASSERT_TRUE(r.Changed());
280 EXPECT_THAT(r.replacement(), IsFloat64Ceil(p0));
285 TEST_F(JSBuiltinReducerTest, MathCeilUnavailable) {
286 Handle<JSFunction> f = MathFunction("ceil");
288 TRACED_FOREACH(Type*, t0, kNumberTypes) {
289 Node* p0 = Parameter(t0, 0);
290 Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
292 graph()->NewNode(javascript()->CallFunction(3, NO_CALL_FUNCTION_FLAGS),
293 fun, UndefinedConstant(), p0);
294 Reduction r = Reduce(call, MachineOperatorBuilder::Flag::kNoFlags);
296 ASSERT_FALSE(r.Changed());
299 } // namespace compiler
300 } // namespace internal