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/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_objects_(graph(), common(), zone()), |
| 26 escape_status_(&escape_objects_, graph(), zone()), |
| 27 effect_(graph()->start()), |
| 28 control_(graph()->start()) {} |
| 29 |
| 30 ~EscapeAnalysisTest() {} |
| 31 |
| 32 EscapeStatusAnalysis* escape_status() { return &escape_status_; } |
| 33 EscapeObjectAnalysis* escape_objects() { return &escape_objects_; } |
| 34 |
| 35 protected: |
| 36 void Analysis() { |
| 37 escape_objects_.Run(); |
| 38 escape_status_.Run(); |
| 39 } |
| 40 |
| 41 void Transformation() { |
| 42 GraphReducer graph_reducer(zone(), graph()); |
| 43 EscapeAnalysisReducer escape_reducer( |
| 44 &graph_reducer, &jsgraph_, &escape_status_, &escape_objects_, zone()); |
| 45 graph_reducer.AddReducer(&escape_reducer); |
| 46 graph_reducer.ReduceGraph(); |
| 47 } |
| 48 |
| 49 // ---------------------------------Node Creation Helper---------------------- |
| 50 |
| 51 Node* BeginRegion(Node* effect = nullptr) { |
| 52 if (!effect) { |
| 53 effect = effect_; |
| 54 } |
| 55 |
| 56 return effect_ = graph()->NewNode(common()->BeginRegion(), effect); |
| 57 } |
| 58 |
| 59 Node* FinishRegion(Node* value, Node* effect = nullptr) { |
| 60 if (!effect) { |
| 61 effect = effect_; |
| 62 } |
| 63 return effect_ = graph()->NewNode(common()->FinishRegion(), value, effect); |
| 64 } |
| 65 |
| 66 Node* Allocate(Node* size, Node* effect = nullptr, Node* control = nullptr) { |
| 67 if (!effect) { |
| 68 effect = effect_; |
| 69 } |
| 70 if (!control) { |
| 71 control = control_; |
| 72 } |
| 73 return effect_ = graph()->NewNode(simplified()->Allocate(), size, effect, |
| 74 control); |
| 75 } |
| 76 |
| 77 Node* Constant(int num) { |
| 78 return graph()->NewNode(common()->NumberConstant(num)); |
| 79 } |
| 80 |
| 81 Node* Store(const FieldAccess& access, Node* allocation, Node* value, |
| 82 Node* effect = nullptr, Node* control = nullptr) { |
| 83 if (!effect) { |
| 84 effect = effect_; |
| 85 } |
| 86 if (!control) { |
| 87 control = control_; |
| 88 } |
| 89 return effect_ = graph()->NewNode(simplified()->StoreField(access), |
| 90 allocation, value, effect, control); |
| 91 } |
| 92 |
| 93 Node* Load(const FieldAccess& access, Node* from, Node* effect = nullptr, |
| 94 Node* control = nullptr) { |
| 95 if (!effect) { |
| 96 effect = effect_; |
| 97 } |
| 98 if (!control) { |
| 99 control = control_; |
| 100 } |
| 101 return graph()->NewNode(simplified()->LoadField(access), from, effect, |
| 102 control); |
| 103 } |
| 104 |
| 105 Node* Return(Node* value, Node* effect = nullptr, Node* control = nullptr) { |
| 106 if (!effect) { |
| 107 effect = effect_; |
| 108 } |
| 109 if (!control) { |
| 110 control = control_; |
| 111 } |
| 112 return control_ = |
| 113 graph()->NewNode(common()->Return(), value, effect, control); |
| 114 } |
| 115 |
| 116 void EndGraph() { |
| 117 for (Edge edge : graph()->end()->input_edges()) { |
| 118 if (NodeProperties::IsControlEdge(edge)) { |
| 119 edge.UpdateTo(control_); |
| 120 } |
| 121 } |
| 122 } |
| 123 |
| 124 Node* Branch() { |
| 125 return control_ = |
| 126 graph()->NewNode(common()->Branch(), Constant(0), control_); |
| 127 } |
| 128 |
| 129 Node* IfTrue() { |
| 130 return control_ = graph()->NewNode(common()->IfTrue(), control_); |
| 131 } |
| 132 |
| 133 Node* IfFalse() { return graph()->NewNode(common()->IfFalse(), control_); } |
| 134 |
| 135 Node* Merge2(Node* control1, Node* control2) { |
| 136 return control_ = graph()->NewNode(common()->Merge(2), control1, control2); |
| 137 } |
| 138 |
| 139 FieldAccess AccessAtIndex(int offset) { |
| 140 FieldAccess access = {kTaggedBase, offset, MaybeHandle<Name>(), Type::Any(), |
| 141 kMachAnyTagged}; |
| 142 return access; |
| 143 } |
| 144 |
| 145 // ---------------------------------Assertion Helper-------------------------- |
| 146 |
| 147 void ExpectReplacement(Node* node, Node* rep) { |
| 148 EXPECT_EQ(rep, escape_objects()->GetReplacement(node, node->id())); |
| 149 } |
| 150 |
| 151 void ExpectReplacementPhi(Node* node, Node* left, Node* right) { |
| 152 Node* rep = escape_objects()->GetReplacement(node, node->id()); |
| 153 ASSERT_NE(nullptr, rep); |
| 154 ASSERT_EQ(IrOpcode::kPhi, rep->opcode()); |
| 155 EXPECT_EQ(left, NodeProperties::GetValueInput(rep, 0)); |
| 156 EXPECT_EQ(right, NodeProperties::GetValueInput(rep, 1)); |
| 157 } |
| 158 |
| 159 void ExpectVirtual(Node* node) { |
| 160 EXPECT_TRUE(node->opcode() == IrOpcode::kAllocate || |
| 161 node->opcode() == IrOpcode::kFinishRegion); |
| 162 EXPECT_TRUE(escape_status()->IsVirtual(node)); |
| 163 } |
| 164 |
| 165 void ExpectEscaped(Node* node) { |
| 166 EXPECT_TRUE(node->opcode() == IrOpcode::kAllocate || |
| 167 node->opcode() == IrOpcode::kFinishRegion); |
| 168 EXPECT_TRUE(escape_status()->IsEscaped(node)); |
| 169 } |
| 170 |
| 171 SimplifiedOperatorBuilder* simplified() { return &simplified_; } |
| 172 |
| 173 Node* effect() { return effect_; } |
| 174 |
| 175 private: |
| 176 SimplifiedOperatorBuilder simplified_; |
| 177 JSGraph jsgraph_; |
| 178 EscapeObjectAnalysis escape_objects_; |
| 179 EscapeStatusAnalysis escape_status_; |
| 180 |
| 181 Node* effect_; |
| 182 Node* control_; |
| 183 }; |
| 184 |
| 185 |
| 186 // ----------------------------------------------------------------------------- |
| 187 // Test cases. |
| 188 |
| 189 |
| 190 TEST_F(EscapeAnalysisTest, StraightNonEscape) { |
| 191 Node* object1 = Constant(1); |
| 192 BeginRegion(); |
| 193 Node* allocation = Allocate(Constant(kPointerSize)); |
| 194 Store(AccessAtIndex(0), allocation, object1); |
| 195 Node* finish = FinishRegion(allocation); |
| 196 Node* load = Load(AccessAtIndex(0), finish); |
| 197 Node* result = Return(load); |
| 198 EndGraph(); |
| 199 Analysis(); |
| 200 |
| 201 ExpectVirtual(allocation); |
| 202 ExpectReplacement(load, object1); |
| 203 |
| 204 Transformation(); |
| 205 |
| 206 ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0)); |
| 207 } |
| 208 |
| 209 |
| 210 TEST_F(EscapeAnalysisTest, StraightEscape) { |
| 211 Node* object1 = Constant(1); |
| 212 BeginRegion(); |
| 213 Node* allocation = Allocate(Constant(kPointerSize)); |
| 214 Store(AccessAtIndex(0), allocation, object1); |
| 215 Node* finish = FinishRegion(allocation); |
| 216 Node* load = Load(AccessAtIndex(0), finish); |
| 217 Node* result = Return(allocation); |
| 218 EndGraph(); |
| 219 Analysis(); |
| 220 |
| 221 ExpectEscaped(allocation); |
| 222 ExpectReplacement(load, object1); |
| 223 |
| 224 Transformation(); |
| 225 |
| 226 ASSERT_EQ(allocation, NodeProperties::GetValueInput(result, 0)); |
| 227 } |
| 228 |
| 229 |
| 230 TEST_F(EscapeAnalysisTest, StoreLoadEscape) { |
| 231 Node* object1 = Constant(1); |
| 232 |
| 233 BeginRegion(); |
| 234 Node* allocation1 = Allocate(Constant(kPointerSize)); |
| 235 Store(AccessAtIndex(0), allocation1, object1); |
| 236 Node* finish1 = FinishRegion(allocation1); |
| 237 |
| 238 BeginRegion(); |
| 239 Node* allocation2 = Allocate(Constant(kPointerSize)); |
| 240 Store(AccessAtIndex(0), allocation2, finish1); |
| 241 Node* finish2 = FinishRegion(allocation2); |
| 242 |
| 243 Node* load = Load(AccessAtIndex(0), finish2); |
| 244 Node* result = Return(load); |
| 245 EndGraph(); |
| 246 Analysis(); |
| 247 |
| 248 ExpectEscaped(allocation1); |
| 249 ExpectVirtual(allocation2); |
| 250 ExpectReplacement(load, finish1); |
| 251 |
| 252 Transformation(); |
| 253 |
| 254 ASSERT_EQ(finish1, NodeProperties::GetValueInput(result, 0)); |
| 255 } |
| 256 |
| 257 |
| 258 TEST_F(EscapeAnalysisTest, BranchNonEscape) { |
| 259 Node* object1 = Constant(1); |
| 260 Node* object2 = Constant(2); |
| 261 BeginRegion(); |
| 262 Node* allocation = Allocate(Constant(kPointerSize)); |
| 263 Store(AccessAtIndex(0), allocation, object1); |
| 264 Node* finish = FinishRegion(allocation); |
| 265 Branch(); |
| 266 Node* ifFalse = IfFalse(); |
| 267 Node* ifTrue = IfTrue(); |
| 268 Node* effect1 = Store(AccessAtIndex(0), allocation, object1, finish, ifFalse); |
| 269 Node* effect2 = Store(AccessAtIndex(0), allocation, object2, finish, ifTrue); |
| 270 Node* merge = Merge2(ifFalse, ifTrue); |
| 271 Node* phi = graph()->NewNode(common()->EffectPhi(2), effect1, effect2, merge); |
| 272 Node* load = Load(AccessAtIndex(0), finish, phi, merge); |
| 273 Node* result = Return(load, phi); |
| 274 EndGraph(); |
| 275 Analysis(); |
| 276 |
| 277 ExpectVirtual(allocation); |
| 278 ExpectReplacementPhi(load, object1, object2); |
| 279 Node* replacement_phi = escape_objects()->GetReplacement(load, load->id()); |
| 280 |
| 281 Transformation(); |
| 282 |
| 283 ASSERT_EQ(replacement_phi, NodeProperties::GetValueInput(result, 0)); |
| 284 } |
| 285 |
| 286 |
| 287 TEST_F(EscapeAnalysisTest, DanglingLoadOrder) { |
| 288 Node* object1 = Constant(1); |
| 289 Node* object2 = Constant(2); |
| 290 Node* allocation = Allocate(Constant(kPointerSize)); |
| 291 Node* store1 = Store(AccessAtIndex(0), allocation, object1); |
| 292 Node* load1 = Load(AccessAtIndex(0), allocation); |
| 293 Store(AccessAtIndex(0), allocation, object2); |
| 294 Node* load2 = Load(AccessAtIndex(0), allocation, store1); |
| 295 Node* result = Return(load2); |
| 296 EndGraph(); |
| 297 |
| 298 Analysis(); |
| 299 |
| 300 ExpectVirtual(allocation); |
| 301 ExpectReplacement(load1, object1); |
| 302 ExpectReplacement(load2, object1); |
| 303 |
| 304 Transformation(); |
| 305 |
| 306 ASSERT_EQ(object1, NodeProperties::GetValueInput(result, 0)); |
| 307 } |
| 308 |
| 309 } // namespace compiler |
| 310 } // namespace internal |
| 311 } // namespace v8 |
OLD | NEW |