| 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 |