blob: dece25def1367409e3cf257121aafca366fec4f8 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2015 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.
4
5#include "src/compiler.h"
6
7#include "src/compiler/access-builder.h"
8#include "src/compiler/js-graph.h"
9#include "src/compiler/js-operator.h"
10#include "src/compiler/js-type-feedback.h"
11#include "src/compiler/machine-operator.h"
12#include "src/compiler/node-matchers.h"
13#include "src/compiler/node-properties.h"
14#include "src/compiler/operator-properties.h"
15
16#include "test/unittests/compiler/compiler-test-utils.h"
17#include "test/unittests/compiler/graph-unittest.h"
18#include "test/unittests/compiler/node-test-utils.h"
19#include "testing/gmock-support.h"
20
21using testing::Capture;
22
23
24namespace v8 {
25namespace internal {
26namespace compiler {
27
28class JSTypeFeedbackTest : public TypedGraphTest {
29 public:
30 JSTypeFeedbackTest()
31 : TypedGraphTest(3),
32 javascript_(zone()),
33 dependencies_(isolate(), zone()) {}
34 ~JSTypeFeedbackTest() override { dependencies_.Rollback(); }
35
36 protected:
37 Reduction Reduce(Node* node,
38 JSTypeFeedbackSpecializer::DeoptimizationMode mode) {
39 Handle<GlobalObject> global_object(
40 isolate()->native_context()->global_object(), isolate());
41
42 MachineOperatorBuilder machine(zone());
43 SimplifiedOperatorBuilder simplified(zone());
44 JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified,
45 &machine);
46 JSTypeFeedbackTable table(zone());
47 // TODO(titzer): mock the GraphReducer here for better unit testing.
48 GraphReducer graph_reducer(zone(), graph());
49 JSTypeFeedbackSpecializer reducer(&graph_reducer, &jsgraph, &table, nullptr,
50 global_object, mode, &dependencies_);
51 return reducer.Reduce(node);
52 }
53
54 Node* EmptyFrameState() {
55 MachineOperatorBuilder machine(zone());
56 JSGraph jsgraph(isolate(), graph(), common(), javascript(), nullptr,
57 &machine);
58 return jsgraph.EmptyFrameState();
59 }
60
61 JSOperatorBuilder* javascript() { return &javascript_; }
62
63 void SetGlobalProperty(const char* string, int value) {
64 SetGlobalProperty(string, Handle<Smi>(Smi::FromInt(value), isolate()));
65 }
66
67 void SetGlobalProperty(const char* string, double value) {
68 SetGlobalProperty(string, isolate()->factory()->NewNumber(value));
69 }
70
71 void SetGlobalProperty(const char* string, Handle<Object> value) {
72 Handle<JSObject> global(isolate()->context()->global_object(), isolate());
73 Handle<String> name =
74 isolate()->factory()->NewStringFromAsciiChecked(string);
75 MaybeHandle<Object> result =
76 JSReceiver::SetProperty(global, name, value, SLOPPY);
77 result.Assert();
78 }
79
80 Node* ReturnLoadNamedFromGlobal(
81 const char* string, Node* effect, Node* control,
82 JSTypeFeedbackSpecializer::DeoptimizationMode mode) {
83 VectorSlotPair feedback;
84 Node* vector = UndefinedConstant();
85 Node* context = UndefinedConstant();
86
87 Handle<Name> name = isolate()->factory()->InternalizeUtf8String(string);
88 const Operator* op = javascript()->LoadGlobal(name, feedback);
89 Node* load = graph()->NewNode(op, vector, context, EmptyFrameState(),
90 EmptyFrameState(), effect, control);
91 Node* if_success = graph()->NewNode(common()->IfSuccess(), load);
92 return graph()->NewNode(common()->Return(), load, load, if_success);
93 }
94
95 CompilationDependencies* dependencies() { return &dependencies_; }
96
97 private:
98 JSOperatorBuilder javascript_;
99 CompilationDependencies dependencies_;
100};
101
102
103TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstSmi) {
104 const int kValue = 111;
105 const char* kName = "banana";
106 SetGlobalProperty(kName, kValue);
107
108 Node* ret = ReturnLoadNamedFromGlobal(
109 kName, graph()->start(), graph()->start(),
110 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
111 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
112
113 Reduction r = Reduce(ret->InputAt(0),
114 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
115 EXPECT_FALSE(r.Changed());
116 EXPECT_TRUE(dependencies()->IsEmpty());
117}
118
119
120TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstSmiWithDeoptimization) {
121 const int kValue = 111;
122 const char* kName = "banana";
123 SetGlobalProperty(kName, kValue);
124
125 Node* ret = ReturnLoadNamedFromGlobal(
126 kName, graph()->start(), graph()->start(),
127 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
128 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
129
130 Reduction r = Reduce(ret->InputAt(0),
131 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
132
133 // Check LoadNamed(global) => HeapConstant[kValue]
134 ASSERT_TRUE(r.Changed());
135 EXPECT_THAT(r.replacement(), IsNumberConstant(kValue));
136
137 EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), graph()->start(),
138 graph()->start()));
139 EXPECT_THAT(graph()->end(), IsEnd(ret));
140
141 EXPECT_FALSE(dependencies()->IsEmpty());
142 dependencies()->Rollback();
143}
144
145
146TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstNumber) {
147 const double kValue = -11.25;
148 const char* kName = "kiwi";
149 SetGlobalProperty(kName, kValue);
150
151 Node* ret = ReturnLoadNamedFromGlobal(
152 kName, graph()->start(), graph()->start(),
153 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
154 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
155
156 Reduction r = Reduce(ret->InputAt(0),
157 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
158
159 EXPECT_FALSE(r.Changed());
160 EXPECT_TRUE(dependencies()->IsEmpty());
161}
162
163
164TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstNumberWithDeoptimization) {
165 const double kValue = -11.25;
166 const char* kName = "kiwi";
167 SetGlobalProperty(kName, kValue);
168
169 Node* ret = ReturnLoadNamedFromGlobal(
170 kName, graph()->start(), graph()->start(),
171 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
172 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
173
174 Reduction r = Reduce(ret->InputAt(0),
175 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
176
177 // Check LoadNamed(global) => HeapConstant[kValue]
178 ASSERT_TRUE(r.Changed());
179 EXPECT_THAT(r.replacement(), IsNumberConstant(kValue));
180
181 EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), graph()->start(),
182 graph()->start()));
183 EXPECT_THAT(graph()->end(), IsEnd(ret));
184
185 EXPECT_FALSE(dependencies()->IsEmpty());
186}
187
188
189TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstString) {
190 Handle<HeapObject> kValue = isolate()->factory()->undefined_string();
191 const char* kName = "mango";
192 SetGlobalProperty(kName, kValue);
193
194 Node* ret = ReturnLoadNamedFromGlobal(
195 kName, graph()->start(), graph()->start(),
196 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
197 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
198
199 Reduction r = Reduce(ret->InputAt(0),
200 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
201 ASSERT_FALSE(r.Changed());
202 EXPECT_TRUE(dependencies()->IsEmpty());
203}
204
205
206TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstStringWithDeoptimization) {
207 Handle<HeapObject> kValue = isolate()->factory()->undefined_string();
208 const char* kName = "mango";
209 SetGlobalProperty(kName, kValue);
210
211 Node* ret = ReturnLoadNamedFromGlobal(
212 kName, graph()->start(), graph()->start(),
213 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
214 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
215
216 Reduction r = Reduce(ret->InputAt(0),
217 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
218
219 // Check LoadNamed(global) => HeapConstant[kValue]
220 ASSERT_TRUE(r.Changed());
221 EXPECT_THAT(r.replacement(), IsHeapConstant(kValue));
222
223 EXPECT_THAT(ret, IsReturn(IsHeapConstant(kValue), graph()->start(),
224 graph()->start()));
225 EXPECT_THAT(graph()->end(), IsEnd(ret));
226
227 EXPECT_FALSE(dependencies()->IsEmpty());
228 dependencies()->Rollback();
229}
230
231
232TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellSmi) {
233 const char* kName = "melon";
234 SetGlobalProperty(kName, 123);
235 SetGlobalProperty(kName, 124);
236
237 Node* ret = ReturnLoadNamedFromGlobal(
238 kName, graph()->start(), graph()->start(),
239 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
240 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
241
242 Reduction r = Reduce(ret->InputAt(0),
243 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
244 ASSERT_FALSE(r.Changed());
245 EXPECT_TRUE(dependencies()->IsEmpty());
246}
247
248
249TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellSmiWithDeoptimization) {
250 const char* kName = "melon";
251 SetGlobalProperty(kName, 123);
252 SetGlobalProperty(kName, 124);
253
254 Node* ret = ReturnLoadNamedFromGlobal(
255 kName, graph()->start(), graph()->start(),
256 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
257 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
258
259 Reduction r = Reduce(ret->InputAt(0),
260 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
261
262 // Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
263 ASSERT_TRUE(r.Changed());
264 FieldAccess access = AccessBuilder::ForPropertyCellValue();
265 Capture<Node*> cell_capture;
266 Matcher<Node*> load_field_match = IsLoadField(
267 access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
268 EXPECT_THAT(r.replacement(), load_field_match);
269
270 HeapObjectMatcher cell(cell_capture.value());
271 EXPECT_TRUE(cell.HasValue());
272 EXPECT_TRUE(cell.Value()->IsPropertyCell());
273
274 EXPECT_THAT(ret,
275 IsReturn(load_field_match, load_field_match, graph()->start()));
276 EXPECT_THAT(graph()->end(), IsEnd(ret));
277
278 EXPECT_FALSE(dependencies()->IsEmpty());
279 dependencies()->Rollback();
280}
281
282
283TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellString) {
284 const char* kName = "pineapple";
285 SetGlobalProperty(kName, isolate()->factory()->undefined_string());
286 SetGlobalProperty(kName, isolate()->factory()->undefined_value());
287
288 Node* ret = ReturnLoadNamedFromGlobal(
289 kName, graph()->start(), graph()->start(),
290 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
291 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
292
293 Reduction r = Reduce(ret->InputAt(0),
294 JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
295 ASSERT_FALSE(r.Changed());
296 EXPECT_TRUE(dependencies()->IsEmpty());
297}
298
299
300TEST_F(JSTypeFeedbackTest,
301 JSLoadNamedGlobalPropertyCellStringWithDeoptimization) {
302 const char* kName = "pineapple";
303 SetGlobalProperty(kName, isolate()->factory()->undefined_string());
304 SetGlobalProperty(kName, isolate()->factory()->undefined_value());
305
306 Node* ret = ReturnLoadNamedFromGlobal(
307 kName, graph()->start(), graph()->start(),
308 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
309 graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
310
311 Reduction r = Reduce(ret->InputAt(0),
312 JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
313
314 // Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
315 ASSERT_TRUE(r.Changed());
316 FieldAccess access = AccessBuilder::ForPropertyCellValue();
317 Capture<Node*> cell_capture;
318 Matcher<Node*> load_field_match = IsLoadField(
319 access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
320 EXPECT_THAT(r.replacement(), load_field_match);
321
322 HeapObjectMatcher cell(cell_capture.value());
323 EXPECT_TRUE(cell.HasValue());
324 EXPECT_TRUE(cell.Value()->IsPropertyCell());
325
326 EXPECT_THAT(ret,
327 IsReturn(load_field_match, load_field_match, graph()->start()));
328 EXPECT_THAT(graph()->end(), IsEnd(ret));
329
330 EXPECT_FALSE(dependencies()->IsEmpty());
331 dependencies()->Rollback();
332}
333
334} // namespace compiler
335} // namespace internal
336} // namespace v8