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.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 21 using testing::Capture; 22 23 24 namespace v8 { 25 namespace internal { 26 namespace compiler { 27 28 class 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 103 TEST_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 120 TEST_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 146 TEST_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 164 TEST_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 189 TEST_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 206 TEST_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 232 TEST_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 249 TEST_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 283 TEST_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 300 TEST_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 337