| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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/common-operator.h" | |
| 6 #include "src/compiler/common-operator-reducer.h" | |
| 7 #include "src/compiler/control-reducer.h" | |
| 8 #include "src/compiler/graph.h" | |
| 9 #include "src/compiler/graph-reducer.h" | |
| 10 #include "src/compiler/js-graph.h" | |
| 11 #include "src/compiler/node-marker.h" | |
| 12 #include "src/compiler/node-matchers.h" | |
| 13 #include "src/compiler/node-properties.h" | |
| 14 #include "src/zone-containers.h" | |
| 15 | |
| 16 namespace v8 { | |
| 17 namespace internal { | |
| 18 namespace compiler { | |
| 19 | |
| 20 #define TRACE(...) \ | |
| 21 do { \ | |
| 22 if (FLAG_trace_turbo_reduction) PrintF(__VA_ARGS__); \ | |
| 23 } while (false) | |
| 24 | |
| 25 enum Decision { kFalse, kUnknown, kTrue }; | |
| 26 | |
| 27 class ControlReducerImpl final : public AdvancedReducer { | |
| 28 public: | |
| 29 Zone* zone_; | |
| 30 JSGraph* jsgraph_; | |
| 31 int max_phis_for_select_; | |
| 32 | |
| 33 ControlReducerImpl(Editor* editor, Zone* zone, JSGraph* jsgraph) | |
| 34 : AdvancedReducer(editor), | |
| 35 zone_(zone), | |
| 36 jsgraph_(jsgraph), | |
| 37 max_phis_for_select_(0) {} | |
| 38 | |
| 39 Graph* graph() { return jsgraph_->graph(); } | |
| 40 CommonOperatorBuilder* common() { return jsgraph_->common(); } | |
| 41 Node* dead() { return jsgraph_->DeadControl(); } | |
| 42 | |
| 43 //=========================================================================== | |
| 44 // Reducer implementation: perform reductions on a node. | |
| 45 //=========================================================================== | |
| 46 Reduction Reduce(Node* node) override { | |
| 47 if (node->op()->ControlInputCount() == 1 || | |
| 48 node->opcode() == IrOpcode::kLoop) { | |
| 49 // If a node has only one control input and it is dead, replace with dead. | |
| 50 Node* control = NodeProperties::GetControlInput(node); | |
| 51 if (control->opcode() == IrOpcode::kDeadControl) { | |
| 52 TRACE("ControlDead: #%d:%s\n", node->id(), node->op()->mnemonic()); | |
| 53 return Replace(control); | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 Node* result = node; | |
| 58 // Reduce branches, phis, and merges. | |
| 59 switch (node->opcode()) { | |
| 60 case IrOpcode::kBranch: | |
| 61 result = ReduceBranch(node); | |
| 62 break; | |
| 63 case IrOpcode::kIfTrue: | |
| 64 result = ReduceIfProjection(node, kTrue); | |
| 65 break; | |
| 66 case IrOpcode::kIfFalse: | |
| 67 result = ReduceIfProjection(node, kFalse); | |
| 68 break; | |
| 69 case IrOpcode::kLoop: // fallthrough | |
| 70 case IrOpcode::kMerge: | |
| 71 result = ReduceMerge(node); | |
| 72 break; | |
| 73 case IrOpcode::kEnd: | |
| 74 result = ReduceEnd(node); | |
| 75 break; | |
| 76 default: | |
| 77 break; | |
| 78 } | |
| 79 | |
| 80 return result == node ? NoChange() : Replace(result); | |
| 81 } | |
| 82 | |
| 83 // Try to statically fold a condition. | |
| 84 Decision DecideCondition(Node* cond) { | |
| 85 switch (cond->opcode()) { | |
| 86 case IrOpcode::kInt32Constant: | |
| 87 return Int32Matcher(cond).Is(0) ? kFalse : kTrue; | |
| 88 case IrOpcode::kInt64Constant: | |
| 89 return Int64Matcher(cond).Is(0) ? kFalse : kTrue; | |
| 90 case IrOpcode::kHeapConstant: { | |
| 91 Handle<Object> object = | |
| 92 HeapObjectMatcher<Object>(cond).Value().handle(); | |
| 93 return object->BooleanValue() ? kTrue : kFalse; | |
| 94 } | |
| 95 default: | |
| 96 break; | |
| 97 } | |
| 98 return kUnknown; | |
| 99 } | |
| 100 | |
| 101 // Reduce branches. | |
| 102 Node* ReduceBranch(Node* branch) { | |
| 103 if (DecideCondition(branch->InputAt(0)) != kUnknown) { | |
| 104 for (Node* use : branch->uses()) Revisit(use); | |
| 105 } | |
| 106 return branch; | |
| 107 } | |
| 108 | |
| 109 // Reduce end by trimming away dead inputs. | |
| 110 Node* ReduceEnd(Node* node) { | |
| 111 // Count the number of live inputs. | |
| 112 int live = 0; | |
| 113 for (int index = 0; index < node->InputCount(); ++index) { | |
| 114 // Skip dead inputs. | |
| 115 if (node->InputAt(index)->opcode() == IrOpcode::kDeadControl) continue; | |
| 116 // Compact live inputs. | |
| 117 if (index != live) node->ReplaceInput(live, node->InputAt(index)); | |
| 118 ++live; | |
| 119 } | |
| 120 | |
| 121 TRACE("ReduceEnd: #%d:%s (%d of %d live)\n", node->id(), | |
| 122 node->op()->mnemonic(), live, node->InputCount()); | |
| 123 | |
| 124 if (live == 0) return dead(); // No remaining inputs. | |
| 125 | |
| 126 if (live < node->InputCount()) { | |
| 127 node->set_op(common()->End(live)); | |
| 128 node->TrimInputCount(live); | |
| 129 } | |
| 130 | |
| 131 return node; | |
| 132 } | |
| 133 | |
| 134 // Reduce merges by trimming away dead inputs from the merge and phis. | |
| 135 Node* ReduceMerge(Node* node) { | |
| 136 // Count the number of live inputs. | |
| 137 int live = 0; | |
| 138 int index = 0; | |
| 139 int live_index = 0; | |
| 140 for (Node* const input : node->inputs()) { | |
| 141 if (input->opcode() != IrOpcode::kDeadControl) { | |
| 142 live++; | |
| 143 live_index = index; | |
| 144 } | |
| 145 index++; | |
| 146 } | |
| 147 | |
| 148 TRACE("ReduceMerge: #%d:%s (%d of %d live)\n", node->id(), | |
| 149 node->op()->mnemonic(), live, index); | |
| 150 | |
| 151 if (live == 0) return dead(); // no remaining inputs. | |
| 152 | |
| 153 // Gather terminates, phis and effect phis to be edited. | |
| 154 NodeVector phis(zone_); | |
| 155 Node* terminate = nullptr; | |
| 156 for (Node* const use : node->uses()) { | |
| 157 if (NodeProperties::IsPhi(use)) { | |
| 158 phis.push_back(use); | |
| 159 } else if (use->opcode() == IrOpcode::kTerminate) { | |
| 160 DCHECK_NULL(terminate); | |
| 161 terminate = use; | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 if (live == 1) { | |
| 166 // All phis are redundant. Replace them with their live input. | |
| 167 for (Node* const phi : phis) { | |
| 168 Replace(phi, phi->InputAt(live_index)); | |
| 169 } | |
| 170 // The terminate is not needed anymore. | |
| 171 if (terminate) Replace(terminate, dead()); | |
| 172 // The merge itself is redundant. | |
| 173 return node->InputAt(live_index); | |
| 174 } | |
| 175 | |
| 176 DCHECK_LE(2, live); | |
| 177 | |
| 178 if (live < node->InputCount()) { | |
| 179 // Edit phis in place, removing dead inputs and revisiting them. | |
| 180 for (Node* const phi : phis) { | |
| 181 TRACE(" PhiInMerge: #%d:%s (%d live)\n", phi->id(), | |
| 182 phi->op()->mnemonic(), live); | |
| 183 RemoveDeadInputs(node, phi); | |
| 184 Revisit(phi); | |
| 185 } | |
| 186 // Edit the merge in place, removing dead inputs. | |
| 187 RemoveDeadInputs(node, node); | |
| 188 } | |
| 189 | |
| 190 DCHECK_EQ(live, node->InputCount()); | |
| 191 | |
| 192 // Try to remove dead diamonds or introduce selects. | |
| 193 if (live == 2 && CheckPhisForSelect(phis)) { | |
| 194 DiamondMatcher matcher(node); | |
| 195 if (matcher.Matched() && matcher.IfProjectionsAreOwned()) { | |
| 196 // Dead diamond, i.e. neither the IfTrue nor the IfFalse nodes | |
| 197 // have uses except for the Merge. Remove the branch if there | |
| 198 // are no phis or replace phis with selects. | |
| 199 Node* control = NodeProperties::GetControlInput(matcher.Branch()); | |
| 200 if (phis.size() == 0) { | |
| 201 // No phis. Remove the branch altogether. | |
| 202 TRACE(" DeadDiamond: #%d:Branch #%d:IfTrue #%d:IfFalse\n", | |
| 203 matcher.Branch()->id(), matcher.IfTrue()->id(), | |
| 204 matcher.IfFalse()->id()); | |
| 205 return control; | |
| 206 } else { | |
| 207 // A small number of phis. Replace with selects. | |
| 208 Node* cond = matcher.Branch()->InputAt(0); | |
| 209 for (Node* phi : phis) { | |
| 210 Node* select = graph()->NewNode( | |
| 211 common()->Select(OpParameter<MachineType>(phi), | |
| 212 BranchHintOf(matcher.Branch()->op())), | |
| 213 cond, matcher.TrueInputOf(phi), matcher.FalseInputOf(phi)); | |
| 214 TRACE(" MatchSelect: #%d:Branch #%d:IfTrue #%d:IfFalse -> #%d\n", | |
| 215 matcher.Branch()->id(), matcher.IfTrue()->id(), | |
| 216 matcher.IfFalse()->id(), select->id()); | |
| 217 Replace(phi, select); | |
| 218 } | |
| 219 return control; | |
| 220 } | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 return node; | |
| 225 } | |
| 226 | |
| 227 bool CheckPhisForSelect(const NodeVector& phis) { | |
| 228 if (phis.size() > static_cast<size_t>(max_phis_for_select_)) return false; | |
| 229 for (Node* phi : phis) { | |
| 230 if (phi->opcode() != IrOpcode::kPhi) return false; // no EffectPhis. | |
| 231 } | |
| 232 return true; | |
| 233 } | |
| 234 | |
| 235 // Reduce if projections if the branch has a constant input. | |
| 236 Node* ReduceIfProjection(Node* node, Decision decision) { | |
| 237 Node* branch = node->InputAt(0); | |
| 238 DCHECK_EQ(IrOpcode::kBranch, branch->opcode()); | |
| 239 Decision result = DecideCondition(branch->InputAt(0)); | |
| 240 if (result == decision) { | |
| 241 // Fold a branch by replacing IfTrue/IfFalse with the branch control. | |
| 242 TRACE(" BranchReduce: #%d:%s => #%d:%s\n", branch->id(), | |
| 243 branch->op()->mnemonic(), node->id(), node->op()->mnemonic()); | |
| 244 return branch->InputAt(1); | |
| 245 } | |
| 246 return result == kUnknown ? node : dead(); | |
| 247 } | |
| 248 | |
| 249 // Remove inputs to {node} corresponding to the dead inputs to {merge} | |
| 250 // and compact the remaining inputs, updating the operator. | |
| 251 void RemoveDeadInputs(Node* merge, Node* node) { | |
| 252 int live = 0; | |
| 253 for (int i = 0; i < merge->InputCount(); i++) { | |
| 254 // skip dead inputs. | |
| 255 if (merge->InputAt(i)->opcode() == IrOpcode::kDeadControl) continue; | |
| 256 // compact live inputs. | |
| 257 if (live != i) node->ReplaceInput(live, node->InputAt(i)); | |
| 258 live++; | |
| 259 } | |
| 260 // compact remaining inputs. | |
| 261 int total = live; | |
| 262 for (int i = merge->InputCount(); i < node->InputCount(); i++) { | |
| 263 if (total != i) node->ReplaceInput(total, node->InputAt(i)); | |
| 264 total++; | |
| 265 } | |
| 266 DCHECK_EQ(total, live + node->InputCount() - merge->InputCount()); | |
| 267 DCHECK_NE(total, node->InputCount()); | |
| 268 node->TrimInputCount(total); | |
| 269 node->set_op(common()->ResizeMergeOrPhi(node->op(), live)); | |
| 270 } | |
| 271 }; | |
| 272 | |
| 273 | |
| 274 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, | |
| 275 int max_phis_for_select) { | |
| 276 GraphReducer graph_reducer(zone, jsgraph->graph()); | |
| 277 CommonOperatorReducer common(&graph_reducer, jsgraph->graph(), | |
| 278 jsgraph->common(), jsgraph->machine()); | |
| 279 ControlReducerImpl impl(&graph_reducer, zone, jsgraph); | |
| 280 impl.max_phis_for_select_ = max_phis_for_select; | |
| 281 graph_reducer.AddReducer(&impl); | |
| 282 graph_reducer.AddReducer(&common); | |
| 283 graph_reducer.ReduceGraph(); | |
| 284 } | |
| 285 | |
| 286 | |
| 287 namespace { | |
| 288 | |
| 289 class DummyEditor final : public AdvancedReducer::Editor { | |
| 290 public: | |
| 291 void Replace(Node* node, Node* replacement) final { | |
| 292 node->ReplaceUses(replacement); | |
| 293 } | |
| 294 void Revisit(Node* node) final {} | |
| 295 void ReplaceWithValue(Node* node, Node* value, Node* effect, | |
| 296 Node* control) final {} | |
| 297 }; | |
| 298 | |
| 299 } // namespace | |
| 300 | |
| 301 | |
| 302 Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, Node* node, | |
| 303 int max_phis_for_select) { | |
| 304 Zone zone; | |
| 305 DummyEditor editor; | |
| 306 ControlReducerImpl impl(&editor, &zone, jsgraph); | |
| 307 impl.max_phis_for_select_ = max_phis_for_select; | |
| 308 return impl.ReduceMerge(node); | |
| 309 } | |
| 310 | |
| 311 | |
| 312 Node* ControlReducer::ReduceIfNodeForTesting(JSGraph* jsgraph, Node* node) { | |
| 313 Zone zone; | |
| 314 DummyEditor editor; | |
| 315 ControlReducerImpl impl(&editor, &zone, jsgraph); | |
| 316 switch (node->opcode()) { | |
| 317 case IrOpcode::kIfTrue: | |
| 318 return impl.ReduceIfProjection(node, kTrue); | |
| 319 case IrOpcode::kIfFalse: | |
| 320 return impl.ReduceIfProjection(node, kFalse); | |
| 321 default: | |
| 322 return node; | |
| 323 } | |
| 324 } | |
| 325 | |
| 326 } // namespace compiler | |
| 327 } // namespace internal | |
| 328 } // namespace v8 | |
| OLD | NEW |