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/bit-vector.h" 6 #include "src/compiler/escape-analysis.h" 7 #include "src/compiler/escape-analysis-reducer.h" 8 #include "src/compiler/graph-visualizer.h" 9 #include "src/compiler/js-graph.h" 10 #include "src/compiler/node-properties.h" 11 #include "src/compiler/simplified-operator.h" 12 #include "src/types-inl.h" 13 #include "src/zone-containers.h" 14 #include "test/unittests/compiler/graph-unittest.h" 15 16 namespace v8 { 17 namespace internal { 18 namespace compiler { 19 20 class EscapeAnalysisTest : public GraphTest { 21 public: 22 EscapeAnalysisTest() 23 : simplified_(zone()), 24 jsgraph_(isolate(), graph(), common(), nullptr, nullptr, nullptr), 25 escape_analysis_(graph(), common(), zone()), 26 effect_(graph()->start()), 27 control_(graph()->start()) {} 28 29 ~EscapeAnalysisTest() {} 30 31 EscapeAnalysis* escape_analysis() { return &escape_analysis_; } 32 33 protected: 34 void Analysis() { escape_analysis_.Run(); } 35 36 void Transformation() { 37 GraphReducer graph_reducer(zone(), graph()); 38 EscapeAnalysisReducer escape_reducer(&graph_reducer, &jsgraph_, 39 &escape_analysis_, zone()); 40 graph_reducer.AddReducer(&escape_reducer); 41 graph_reducer.ReduceGraph(); 42 } 43 44 // ---------------------------------Node Creation Helper---------------------- 45 46 Node* BeginRegion(Node* effect = nullptr) { 47 if (!effect) { 48 effect = effect_; 49 } 50 51 return effect_ = graph()->NewNode(common()->BeginRegion(), effect); 52 } 53 54 Node* FinishRegion(Node* value, Node* effect = nullptr) { 55 if (!effect) { 56 effect = effect_; 57 } 58 return effect_ = graph()->NewNode(common()->FinishRegion(), value, effect); 59 } 60 61 Node* Allocate(Node* size, Node* effect = nullptr, Node* control = nullptr) { 62 if (!effect) { 63 effect = effect_; 64 } 65 if (!control) { 66 control = control_; 67 } 68 return effect_ = graph()->NewNode(simplified()->Allocate(), size, effect, 69 control); 70 } 71 72 Node* Constant(int num) { 73 return graph()->NewNode(common()->NumberConstant(num)); 74 } 75 76 Node* Store(const FieldAccess& access, Node* allocation, Node* value, 77 Node* effect = nullptr, Node* control = nullptr) { 78 if (!effect) { 79 effect = effect_; 80 } 81 if (!control) { 82 control = control_; 83 } 84 return effect_ = graph()->NewNode(simplified()->StoreField(access), 85 allocation, value, effect, control); 86 } 87 88 Node* Load(const FieldAccess& access, Node* from, Node* effect = nullptr, 89 Node* control = nullptr) { 90 if (!effect) { 91 effect = effect_; 92 } 93 if (!control) { 94 control = control_; 95 } 96 return graph()->NewNode(simplified()->LoadField(access), from, effect, 97 control); 98 } 99 100 Node* Return(Node* value, Node* effect = nullptr, Node* control = nullptr) { 101 if (!effect) { 102 effect = effect_; 103 } 104 if (!control) { 105 control = control_; 106 } 107 return control_ = 108 graph()->NewNode(common()->Return(), value, effect, control); 109 } 110 111 void EndGraph() { 112 for (Edge edge : graph()->end()->input_edges()) { 113 if (NodeProperties::IsControlEdge(edge)) { 114 edge.UpdateTo(control_); 115 } 116 } 117 } 118 119 Node* Branch() { 120 return control_ = 121 graph()->NewNode(common()->Branch(), Constant(0), control_); 122 } 123 124 Node* IfTrue() { 125 return control_ = graph()->NewNode(common()->IfTrue(), control_); 126 } 127 128 Node* IfFalse() { return graph()->NewNode(common()->IfFalse(), control_); } 129 130 Node* Merge2(Node* control1, Node* control2) { 131 return control_ = graph()->NewNode(common()->Merge(2), control1, control2); 132 } 133 134 FieldAccess AccessAtIndex(int offset) { 135 FieldAccess access = {kTaggedBase, offset, MaybeHandle<Name>(), Type::Any(), 136 MachineType::AnyTagged()}; 137 return access; 138 } 139 140 // ---------------------------------Assertion Helper-------------------------- 141 142 void ExpectReplacement(Node* node, Node* rep) { 143 EXPECT_EQ(rep, escape_analysis()->GetReplacement(node)); 144 } 145 146 void ExpectReplacementPhi(Node* node, Node* left, Node* right) { 147 Node* rep = escape_analysis()->GetReplacement(node); 148 ASSERT_NE(nullptr, rep); 149 ASSERT_EQ(IrOpcode::kPhi, rep->opcode()); 150 EXPECT_EQ(left, NodeProperties::GetValueInput(rep, 0)); 151 EXPECT_EQ(right, NodeProperties::GetValueInput(rep, 1)); 152 } 153 154 void ExpectVirtual(Node* node) { 155 EXPECT_TRUE(node->opcode() == IrOpcode::kAllocate || 156 node->opcode() == IrOpcode::kFinishRegion); 157 EXPECT_TRUE(escape_analysis()->IsVirtual(node)); 158 } 159 160 void ExpectEscaped(Node* node) { 161 EXPECT_TRUE(node->opcode() == IrOpcode::kAllocate || 162 node->opcode() == IrOpcode::kFinishRegion); 163 EXPECT_TRUE(escape_analysis()->IsEscaped(node)); 164 } 165 166 SimplifiedOperatorBuilder* simplified() { return &simplified_; } 167 168 Node* effect() { return effect_; } 169 170 private: 171 SimplifiedOperatorBuilder simplified_; 172 JSGraph jsgraph_; 173 EscapeAnalysis escape_analysis_; 174 175 Node* effect_; 176 Node* control_; 177 }; 178 179 180 // ----------------------------------------------------------------------------- 181 // Test cases. 182 183 184 TEST_F(EscapeAnalysisTest, StraightNonEscape) { 185 Node* object1 = Constant(1); 186 BeginRegion(); 187 Node* allocation = Allocate(Constant(kPointerSize)); 188 Store(AccessAtIndex(0), allocation, object1); 189 Node* finish = FinishRegion(allocation); 190 Node* load = Load(AccessAtIndex(0), finish); 191 Node* result = Return(load); 192 EndGraph(); 193 194 Analysis(); 195 196 ExpectVirtual(allocation); 197 ExpectReplacement(load, object1); 198 199 Transformation(); 200 201 ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0)); 202 } 203 204 205 TEST_F(EscapeAnalysisTest, StraightEscape) { 206 Node* object1 = Constant(1); 207 BeginRegion(); 208 Node* allocation = Allocate(Constant(kPointerSize)); 209 Store(AccessAtIndex(0), allocation, object1); 210 Node* finish = FinishRegion(allocation); 211 Node* load = Load(AccessAtIndex(0), finish); 212 Node* result = Return(allocation); 213 EndGraph(); 214 graph()->end()->AppendInput(zone(), load); 215 216 Analysis(); 217 218 ExpectEscaped(allocation); 219 ExpectReplacement(load, object1); 220 221 Transformation(); 222 223 ASSERT_EQ(allocation, NodeProperties::GetValueInput(result, 0)); 224 } 225 226 227 TEST_F(EscapeAnalysisTest, StoreLoadEscape) { 228 Node* object1 = Constant(1); 229 230 BeginRegion(); 231 Node* allocation1 = Allocate(Constant(kPointerSize)); 232 Store(AccessAtIndex(0), allocation1, object1); 233 Node* finish1 = FinishRegion(allocation1); 234 235 BeginRegion(); 236 Node* allocation2 = Allocate(Constant(kPointerSize)); 237 Store(AccessAtIndex(0), allocation2, finish1); 238 Node* finish2 = FinishRegion(allocation2); 239 240 Node* load = Load(AccessAtIndex(0), finish2); 241 Node* result = Return(load); 242 EndGraph(); 243 Analysis(); 244 245 ExpectEscaped(allocation1); 246 ExpectVirtual(allocation2); 247 ExpectReplacement(load, finish1); 248 249 Transformation(); 250 251 ASSERT_EQ(finish1, NodeProperties::GetValueInput(result, 0)); 252 } 253 254 255 TEST_F(EscapeAnalysisTest, BranchNonEscape) { 256 Node* object1 = Constant(1); 257 Node* object2 = Constant(2); 258 BeginRegion(); 259 Node* allocation = Allocate(Constant(kPointerSize)); 260 Store(AccessAtIndex(0), allocation, object1); 261 Node* finish = FinishRegion(allocation); 262 Branch(); 263 Node* ifFalse = IfFalse(); 264 Node* ifTrue = IfTrue(); 265 Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish, ifFalse); 266 Node* effect2 = Store(AccessAtIndex(0), allocation, object2, finish, ifTrue); 267 Node* merge = Merge2(ifFalse, ifTrue); 268 Node* phi = graph()->NewNode(common()->EffectPhi(2), effect1, effect2, merge); 269 Node* load = Load(AccessAtIndex(0), finish, phi, merge); 270 Node* result = Return(load, phi); 271 EndGraph(); 272 graph()->end()->AppendInput(zone(), result); 273 274 Analysis(); 275 276 ExpectVirtual(allocation); 277 ExpectReplacementPhi(load, object1, object2); 278 Node* replacement_phi = escape_analysis()->GetReplacement(load); 279 280 Transformation(); 281 282 ASSERT_EQ(replacement_phi, NodeProperties::GetValueInput(result, 0)); 283 } 284 285 286 TEST_F(EscapeAnalysisTest, DanglingLoadOrder) { 287 Node* object1 = Constant(1); 288 Node* object2 = Constant(2); 289 Node* allocation = Allocate(Constant(kPointerSize)); 290 Node* store1 = Store(AccessAtIndex(0), allocation, object1); 291 Node* load1 = Load(AccessAtIndex(0), allocation); 292 Node* store2 = Store(AccessAtIndex(0), allocation, object2); 293 Node* load2 = Load(AccessAtIndex(0), allocation, store1); 294 Node* result = Return(load2); 295 EndGraph(); 296 graph()->end()->AppendInput(zone(), store2); 297 graph()->end()->AppendInput(zone(), load1); 298 299 Analysis(); 300 301 ExpectVirtual(allocation); 302 ExpectReplacement(load1, object1); 303 ExpectReplacement(load2, object1); 304 305 Transformation(); 306 307 ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0)); 308 } 309 310 311 TEST_F(EscapeAnalysisTest, DeoptReplacement) { 312 Node* object1 = Constant(1); 313 BeginRegion(); 314 Node* allocation = Allocate(Constant(kPointerSize)); 315 Store(AccessAtIndex(0), allocation, object1); 316 Node* finish = FinishRegion(allocation); 317 Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish); 318 Branch(); 319 Node* ifFalse = IfFalse(); 320 Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish); 321 Node* state_values2 = graph()->NewNode(common()->StateValues(0)); 322 Node* state_values3 = graph()->NewNode(common()->StateValues(0)); 323 Node* frame_state = graph()->NewNode( 324 common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(), 325 nullptr), 326 state_values1, state_values2, state_values3, UndefinedConstant(), 327 graph()->start(), graph()->start()); 328 Node* deopt = graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager), 329 frame_state, effect1, ifFalse); 330 Node* ifTrue = IfTrue(); 331 Node* load = Load(AccessAtIndex(0), finish, effect1, ifTrue); 332 Node* result = Return(load, effect1, ifTrue); 333 EndGraph(); 334 graph()->end()->AppendInput(zone(), deopt); 335 Analysis(); 336 337 ExpectVirtual(allocation); 338 ExpectReplacement(load, object1); 339 340 Transformation(); 341 342 ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0)); 343 Node* object_state = NodeProperties::GetValueInput(state_values1, 0); 344 ASSERT_EQ(object_state->opcode(), IrOpcode::kObjectState); 345 ASSERT_EQ(1, object_state->op()->ValueInputCount()); 346 ASSERT_EQ(object1, NodeProperties::GetValueInput(object_state, 0)); 347 } 348 349 350 TEST_F(EscapeAnalysisTest, DeoptReplacementIdentity) { 351 Node* object1 = Constant(1); 352 BeginRegion(); 353 Node* allocation = Allocate(Constant(kPointerSize * 2)); 354 Store(AccessAtIndex(0), allocation, object1); 355 Store(AccessAtIndex(kPointerSize), allocation, allocation); 356 Node* finish = FinishRegion(allocation); 357 Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish); 358 Branch(); 359 Node* ifFalse = IfFalse(); 360 Node* state_values1 = graph()->NewNode(common()->StateValues(1), finish); 361 Node* state_values2 = graph()->NewNode(common()->StateValues(1), finish); 362 Node* state_values3 = graph()->NewNode(common()->StateValues(0)); 363 Node* frame_state = graph()->NewNode( 364 common()->FrameState(BailoutId::None(), OutputFrameStateCombine::Ignore(), 365 nullptr), 366 state_values1, state_values2, state_values3, UndefinedConstant(), 367 graph()->start(), graph()->start()); 368 Node* deopt = graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager), 369 frame_state, effect1, ifFalse); 370 Node* ifTrue = IfTrue(); 371 Node* load = Load(AccessAtIndex(0), finish, effect1, ifTrue); 372 Node* result = Return(load, effect1, ifTrue); 373 EndGraph(); 374 graph()->end()->AppendInput(zone(), deopt); 375 Analysis(); 376 377 ExpectVirtual(allocation); 378 ExpectReplacement(load, object1); 379 380 Transformation(); 381 382 ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0)); 383 384 Node* object_state = NodeProperties::GetValueInput(state_values1, 0); 385 ASSERT_EQ(object_state->opcode(), IrOpcode::kObjectState); 386 ASSERT_EQ(2, object_state->op()->ValueInputCount()); 387 ASSERT_EQ(object1, NodeProperties::GetValueInput(object_state, 0)); 388 ASSERT_EQ(object_state, NodeProperties::GetValueInput(object_state, 1)); 389 390 Node* object_state2 = NodeProperties::GetValueInput(state_values1, 0); 391 ASSERT_EQ(object_state, object_state2); 392 } 393 394 } // namespace compiler 395 } // namespace internal 396 } // namespace v8 397