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