| 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<HeapObject> object = HeapObjectMatcher(cond).Value().handle(); | |
| 92 return object->BooleanValue() ? kTrue : kFalse; | |
| 93 } | |
| 94 default: | |
| 95 break; | |
| 96 } | |
| 97 return kUnknown; | |
| 98 } | |
| 99 | |
| 100 // Reduce branches. | |
| 101 Node* ReduceBranch(Node* branch) { | |
| 102 if (DecideCondition(branch->InputAt(0)) != kUnknown) { | |
| 103 for (Node* use : branch->uses()) Revisit(use); | |
| 104 } | |
| 105 return branch; | |
| 106 } | |
| 107 | |
| 108 // Reduce end by trimming away dead inputs. | |
| 109 Node* ReduceEnd(Node* node) { | |
| 110 // Count the number of live inputs. | |
| 111 int live = 0; | |
| 112 for (int index = 0; index < node->InputCount(); ++index) { | |
| 113 // Skip dead inputs. | |
| 114 if (node->InputAt(index)->opcode() == IrOpcode::kDeadControl) continue; | |
| 115 // Compact live inputs. | |
| 116 if (index != live) node->ReplaceInput(live, node->InputAt(index)); | |
| 117 ++live; | |
| 118 } | |
| 119 | |
| 120 TRACE("ReduceEnd: #%d:%s (%d of %d live)\n", node->id(), | |
| 121 node->op()->mnemonic(), live, node->InputCount()); | |
| 122 | |
| 123 if (live == 0) return dead(); // No remaining inputs. | |
| 124 | |
| 125 if (live < node->InputCount()) { | |
| 126 node->set_op(common()->End(live)); | |
| 127 node->TrimInputCount(live); | |
| 128 } | |
| 129 | |
| 130 return node; | |
| 131 } | |
| 132 | |
| 133 // Reduce merges by trimming away dead inputs from the merge and phis. | |
| 134 Node* ReduceMerge(Node* node) { | |
| 135 // Count the number of live inputs. | |
| 136 int live = 0; | |
| 137 int index = 0; | |
| 138 int live_index = 0; | |
| 139 for (Node* const input : node->inputs()) { | |
| 140 if (input->opcode() != IrOpcode::kDeadControl) { | |
| 141 live++; | |
| 142 live_index = index; | |
| 143 } | |
| 144 index++; | |
| 145 } | |
| 146 | |
| 147 TRACE("ReduceMerge: #%d:%s (%d of %d live)\n", node->id(), | |
| 148 node->op()->mnemonic(), live, index); | |
| 149 | |
| 150 if (live == 0) return dead(); // no remaining inputs. | |
| 151 | |
| 152 // Gather terminates, phis and effect phis to be edited. | |
| 153 NodeVector phis(zone_); | |
| 154 Node* terminate = nullptr; | |
| 155 for (Node* const use : node->uses()) { | |
| 156 if (NodeProperties::IsPhi(use)) { | |
| 157 phis.push_back(use); | |
| 158 } else if (use->opcode() == IrOpcode::kTerminate) { | |
| 159 DCHECK_NULL(terminate); | |
| 160 terminate = use; | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 if (live == 1) { | |
| 165 // All phis are redundant. Replace them with their live input. | |
| 166 for (Node* const phi : phis) { | |
| 167 Replace(phi, phi->InputAt(live_index)); | |
| 168 } | |
| 169 // The terminate is not needed anymore. | |
| 170 if (terminate) Replace(terminate, dead()); | |
| 171 // The merge itself is redundant. | |
| 172 return node->InputAt(live_index); | |
| 173 } | |
| 174 | |
| 175 DCHECK_LE(2, live); | |
| 176 | |
| 177 if (live < node->InputCount()) { | |
| 178 // Edit phis in place, removing dead inputs and revisiting them. | |
| 179 for (Node* const phi : phis) { | |
| 180 TRACE(" PhiInMerge: #%d:%s (%d live)\n", phi->id(), | |
| 181 phi->op()->mnemonic(), live); | |
| 182 RemoveDeadInputs(node, phi); | |
| 183 Revisit(phi); | |
| 184 } | |
| 185 // Edit the merge in place, removing dead inputs. | |
| 186 RemoveDeadInputs(node, node); | |
| 187 } | |
| 188 | |
| 189 DCHECK_EQ(live, node->InputCount()); | |
| 190 | |
| 191 // Try to remove dead diamonds or introduce selects. | |
| 192 if (live == 2 && CheckPhisForSelect(phis)) { | |
| 193 DiamondMatcher matcher(node); | |
| 194 if (matcher.Matched() && matcher.IfProjectionsAreOwned()) { | |
| 195 // Dead diamond, i.e. neither the IfTrue nor the IfFalse nodes | |
| 196 // have uses except for the Merge. Remove the branch if there | |
| 197 // are no phis or replace phis with selects. | |
| 198 Node* control = NodeProperties::GetControlInput(matcher.Branch()); | |
| 199 if (phis.size() == 0) { | |
| 200 // No phis. Remove the branch altogether. | |
| 201 TRACE(" DeadDiamond: #%d:Branch #%d:IfTrue #%d:IfFalse\n", | |
| 202 matcher.Branch()->id(), matcher.IfTrue()->id(), | |
| 203 matcher.IfFalse()->id()); | |
| 204 return control; | |
| 205 } else { | |
| 206 // A small number of phis. Replace with selects. | |
| 207 Node* cond = matcher.Branch()->InputAt(0); | |
| 208 for (Node* phi : phis) { | |
| 209 Node* select = graph()->NewNode( | |
| 210 common()->Select(OpParameter<MachineType>(phi), | |
| 211 BranchHintOf(matcher.Branch()->op())), | |
| 212 cond, matcher.TrueInputOf(phi), matcher.FalseInputOf(phi)); | |
| 213 TRACE(" MatchSelect: #%d:Branch #%d:IfTrue #%d:IfFalse -> #%d\n", | |
| 214 matcher.Branch()->id(), matcher.IfTrue()->id(), | |
| 215 matcher.IfFalse()->id(), select->id()); | |
| 216 Replace(phi, select); | |
| 217 } | |
| 218 return control; | |
| 219 } | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 return node; | |
| 224 } | |
| 225 | |
| 226 bool CheckPhisForSelect(const NodeVector& phis) { | |
| 227 if (phis.size() > static_cast<size_t>(max_phis_for_select_)) return false; | |
| 228 for (Node* phi : phis) { | |
| 229 if (phi->opcode() != IrOpcode::kPhi) return false; // no EffectPhis. | |
| 230 } | |
| 231 return true; | |
| 232 } | |
| 233 | |
| 234 // Reduce if projections if the branch has a constant input. | |
| 235 Node* ReduceIfProjection(Node* node, Decision decision) { | |
| 236 Node* branch = node->InputAt(0); | |
| 237 DCHECK_EQ(IrOpcode::kBranch, branch->opcode()); | |
| 238 Decision result = DecideCondition(branch->InputAt(0)); | |
| 239 if (result == decision) { | |
| 240 // Fold a branch by replacing IfTrue/IfFalse with the branch control. | |
| 241 TRACE(" BranchReduce: #%d:%s => #%d:%s\n", branch->id(), | |
| 242 branch->op()->mnemonic(), node->id(), node->op()->mnemonic()); | |
| 243 return branch->InputAt(1); | |
| 244 } | |
| 245 return result == kUnknown ? node : dead(); | |
| 246 } | |
| 247 | |
| 248 // Remove inputs to {node} corresponding to the dead inputs to {merge} | |
| 249 // and compact the remaining inputs, updating the operator. | |
| 250 void RemoveDeadInputs(Node* merge, Node* node) { | |
| 251 int live = 0; | |
| 252 for (int i = 0; i < merge->InputCount(); i++) { | |
| 253 // skip dead inputs. | |
| 254 if (merge->InputAt(i)->opcode() == IrOpcode::kDeadControl) continue; | |
| 255 // compact live inputs. | |
| 256 if (live != i) node->ReplaceInput(live, node->InputAt(i)); | |
| 257 live++; | |
| 258 } | |
| 259 // compact remaining inputs. | |
| 260 int total = live; | |
| 261 for (int i = merge->InputCount(); i < node->InputCount(); i++) { | |
| 262 if (total != i) node->ReplaceInput(total, node->InputAt(i)); | |
| 263 total++; | |
| 264 } | |
| 265 DCHECK_EQ(total, live + node->InputCount() - merge->InputCount()); | |
| 266 DCHECK_NE(total, node->InputCount()); | |
| 267 node->TrimInputCount(total); | |
| 268 node->set_op(common()->ResizeMergeOrPhi(node->op(), live)); | |
| 269 } | |
| 270 }; | |
| 271 | |
| 272 | |
| 273 void ControlReducer::ReduceGraph(Zone* zone, JSGraph* jsgraph, | |
| 274 int max_phis_for_select) { | |
| 275 GraphReducer graph_reducer(zone, jsgraph->graph()); | |
| 276 CommonOperatorReducer common(&graph_reducer, jsgraph->graph(), | |
| 277 jsgraph->common(), jsgraph->machine()); | |
| 278 ControlReducerImpl impl(&graph_reducer, zone, jsgraph); | |
| 279 impl.max_phis_for_select_ = max_phis_for_select; | |
| 280 graph_reducer.AddReducer(&impl); | |
| 281 graph_reducer.AddReducer(&common); | |
| 282 graph_reducer.ReduceGraph(); | |
| 283 } | |
| 284 | |
| 285 | |
| 286 namespace { | |
| 287 | |
| 288 class DummyEditor final : public AdvancedReducer::Editor { | |
| 289 public: | |
| 290 void Replace(Node* node, Node* replacement) final { | |
| 291 node->ReplaceUses(replacement); | |
| 292 } | |
| 293 void Revisit(Node* node) final {} | |
| 294 void ReplaceWithValue(Node* node, Node* value, Node* effect, | |
| 295 Node* control) final {} | |
| 296 }; | |
| 297 | |
| 298 } // namespace | |
| 299 | |
| 300 | |
| 301 Node* ControlReducer::ReduceMerge(JSGraph* jsgraph, Node* node, | |
| 302 int max_phis_for_select) { | |
| 303 Zone zone; | |
| 304 DummyEditor editor; | |
| 305 ControlReducerImpl impl(&editor, &zone, jsgraph); | |
| 306 impl.max_phis_for_select_ = max_phis_for_select; | |
| 307 return impl.ReduceMerge(node); | |
| 308 } | |
| 309 | |
| 310 | |
| 311 Node* ControlReducer::ReduceIfNodeForTesting(JSGraph* jsgraph, Node* node) { | |
| 312 Zone zone; | |
| 313 DummyEditor editor; | |
| 314 ControlReducerImpl impl(&editor, &zone, jsgraph); | |
| 315 switch (node->opcode()) { | |
| 316 case IrOpcode::kIfTrue: | |
| 317 return impl.ReduceIfProjection(node, kTrue); | |
| 318 case IrOpcode::kIfFalse: | |
| 319 return impl.ReduceIfProjection(node, kFalse); | |
| 320 default: | |
| 321 return node; | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 } // namespace compiler | |
| 326 } // namespace internal | |
| 327 } // namespace v8 | |
| OLD | NEW |