Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/gap-resolver.h" | 5 #include "src/compiler/gap-resolver.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <functional> | 8 #include <functional> |
| 9 #include <set> | 9 #include <set> |
| 10 | 10 |
| 11 namespace v8 { | 11 namespace v8 { |
| 12 namespace internal { | 12 namespace internal { |
| 13 namespace compiler { | 13 namespace compiler { |
| 14 | 14 |
| 15 namespace { | 15 namespace { |
| 16 | 16 |
| 17 #define REP_BIT(rep) (1 << static_cast<int>(rep)); | |
| 18 | |
| 19 const int kFloat32Bit = REP_BIT(MachineRepresentation::kFloat32); | |
| 20 const int kFloat64Bit = REP_BIT(MachineRepresentation::kFloat64); | |
| 21 | |
| 17 inline bool Blocks(MoveOperands* move, InstructionOperand destination) { | 22 inline bool Blocks(MoveOperands* move, InstructionOperand destination) { |
| 18 return move->Blocks(destination); | 23 return !move->IsEliminated() && move->source().InterferesWith(destination); |
| 19 } | 24 } |
| 20 | 25 |
| 26 // Splits a FP move between two location operands into the equivalent series of | |
| 27 // moves between smaller sub-operands, e.g. a double move to two single moves. | |
| 28 // This helps reduce the number of cycles that would normally occur under FP | |
| 29 // aliasing, and makes swaps much easier to implement. | |
| 30 MoveOperands* Split(MoveOperands* move, MachineRepresentation smaller_rep, | |
| 31 ParallelMove* moves) { | |
| 32 DCHECK(!kSimpleFPAliasing); | |
| 33 const LocationOperand& src_loc = LocationOperand::cast(move->source()); | |
| 34 const LocationOperand& dst_loc = LocationOperand::cast(move->destination()); | |
| 35 MachineRepresentation dst_rep = dst_loc.representation(); | |
| 36 DCHECK_NE(smaller_rep, dst_rep); | |
| 37 auto src_kind = src_loc.location_kind(); | |
| 38 auto dst_kind = dst_loc.location_kind(); | |
| 21 | 39 |
| 22 inline bool IsRedundant(MoveOperands* move) { return move->IsRedundant(); } | 40 int base = -1; |
| 41 int aliases = RegisterConfiguration::Turbofan()->GetAliases( | |
| 42 dst_rep, 0, smaller_rep, &base); | |
| 43 DCHECK(base::bits::IsPowerOfTwo32(aliases)); | |
| 44 | |
| 45 int src_index = -1; | |
| 46 const int kBaseRep = static_cast<int>(MachineRepresentation::kFloat32); | |
| 47 int slot_size = 1 << (static_cast<int>(smaller_rep) - kBaseRep); | |
| 48 int src_step = 1; | |
| 49 if (src_kind == LocationOperand::REGISTER) { | |
| 50 src_index = src_loc.register_code() * aliases; | |
| 51 } else { | |
| 52 src_index = src_loc.index(); | |
| 53 src_step = -slot_size; | |
| 54 } | |
| 55 int dst_index = -1; | |
| 56 int dst_step = 1; | |
| 57 if (dst_kind == LocationOperand::REGISTER) { | |
| 58 dst_index = dst_loc.register_code() * aliases; | |
| 59 } else { | |
| 60 dst_index = dst_loc.index(); | |
| 61 dst_step = -slot_size; | |
| 62 } | |
| 63 | |
| 64 // Reuse 'move' for the first fragment. It is not pending. | |
| 65 move->set_source(AllocatedOperand(src_kind, smaller_rep, src_index)); | |
| 66 move->set_destination(AllocatedOperand(dst_kind, smaller_rep, dst_index)); | |
| 67 // Add the remaining fragment moves. | |
| 68 for (int i = 1; i < aliases; ++i) { | |
| 69 src_index += src_step; | |
| 70 dst_index += dst_step; | |
| 71 moves->AddMove(AllocatedOperand(src_kind, smaller_rep, src_index), | |
| 72 AllocatedOperand(dst_kind, smaller_rep, dst_index)); | |
| 73 } | |
| 74 // Return the first fragment. | |
| 75 return move; | |
| 76 } | |
| 23 | 77 |
| 24 } // namespace | 78 } // namespace |
| 25 | 79 |
| 80 void GapResolver::Resolve(ParallelMove* moves) { | |
| 81 // Clear redundant moves, and collect FP move representations if aliasing | |
| 82 // is non-simple. | |
| 83 int reps = 0; | |
| 84 for (size_t i = 0; i < moves->size();) { | |
| 85 MoveOperands* move = (*moves)[i]; | |
| 86 if (move->IsRedundant()) { | |
| 87 (*moves)[i] = moves->back(); | |
| 88 moves->pop_back(); | |
| 89 continue; | |
| 90 } | |
| 91 i++; | |
| 92 if (!kSimpleFPAliasing && move->destination().IsFPRegister()) { | |
| 93 reps |= | |
| 94 REP_BIT(LocationOperand::cast(move->destination()).representation()); | |
| 95 } | |
| 96 } | |
| 26 | 97 |
| 27 void GapResolver::Resolve(ParallelMove* moves) const { | 98 if (!kSimpleFPAliasing) { |
| 28 // Clear redundant moves. | 99 if (reps && !base::bits::IsPowerOfTwo32(reps)) { |
| 29 auto it = | 100 // Start with the smallest FP moves, so we never encounter smaller moves |
| 30 std::remove_if(moves->begin(), moves->end(), std::ptr_fun(IsRedundant)); | 101 // in |
| 31 moves->erase(it, moves->end()); | 102 // the middle of a cycle of larger moves. |
| 32 for (MoveOperands* move : *moves) { | 103 if ((reps & kFloat32Bit) != 0) { |
| 104 split_rep_ = MachineRepresentation::kFloat32; | |
| 105 for (size_t i = 0; i < moves->size(); ++i) { | |
| 106 auto move = (*moves)[i]; | |
| 107 if (!move->IsEliminated() && move->destination().IsFloatRegister()) | |
| 108 PerformMove(moves, move); | |
| 109 } | |
| 110 } | |
| 111 if ((reps & kFloat64Bit) != 0) { | |
| 112 split_rep_ = MachineRepresentation::kFloat64; | |
| 113 for (size_t i = 0; i < moves->size(); ++i) { | |
| 114 auto move = (*moves)[i]; | |
| 115 if (!move->IsEliminated() && move->destination().IsDoubleRegister()) | |
| 116 PerformMove(moves, move); | |
| 117 } | |
| 118 } | |
| 119 } | |
| 120 split_rep_ = MachineRepresentation::kSimd128; | |
| 121 } | |
| 122 | |
| 123 for (size_t i = 0; i < moves->size(); ++i) { | |
| 124 auto move = (*moves)[i]; | |
| 33 if (!move->IsEliminated()) PerformMove(moves, move); | 125 if (!move->IsEliminated()) PerformMove(moves, move); |
| 34 } | 126 } |
| 35 } | 127 } |
| 36 | 128 |
| 37 void GapResolver::PerformMove(ParallelMove* moves, MoveOperands* move) const { | 129 void GapResolver::PerformMove(ParallelMove* moves, MoveOperands* move) { |
| 38 // Each call to this function performs a move and deletes it from the move | 130 // Each call to this function performs a move and deletes it from the move |
| 39 // graph. We first recursively perform any move blocking this one. We mark a | 131 // graph. We first recursively perform any move blocking this one. We mark a |
| 40 // move as "pending" on entry to PerformMove in order to detect cycles in the | 132 // move as "pending" on entry to PerformMove in order to detect cycles in the |
| 41 // move graph. We use operand swaps to resolve cycles, which means that a | 133 // move graph. We use operand swaps to resolve cycles, which means that a |
| 42 // call to PerformMove could change any source operand in the move graph. | 134 // call to PerformMove could change any source operand in the move graph. |
| 43 DCHECK(!move->IsPending()); | 135 DCHECK(!move->IsPending()); |
| 44 DCHECK(!move->IsRedundant()); | 136 DCHECK(!move->IsRedundant()); |
| 45 | 137 |
| 46 // Clear this move's destination to indicate a pending move. The actual | 138 // Clear this move's destination to indicate a pending move. The actual |
| 47 // destination is saved on the side. | 139 // destination is saved on the side. |
| 48 DCHECK(!move->source().IsInvalid()); // Or else it will look eliminated. | 140 InstructionOperand source = move->source(); |
| 141 DCHECK(!source.IsInvalid()); // Or else it will look eliminated. | |
| 49 InstructionOperand destination = move->destination(); | 142 InstructionOperand destination = move->destination(); |
| 50 move->SetPending(); | 143 move->SetPending(); |
| 51 | 144 |
| 145 // We may need to split moves between FP locations differently. | |
| 146 bool is_fp_loc_move = !kSimpleFPAliasing && destination.IsFPLocationOperand(); | |
| 147 | |
| 52 // Perform a depth-first traversal of the move graph to resolve dependencies. | 148 // Perform a depth-first traversal of the move graph to resolve dependencies. |
| 53 // Any unperformed, unpending move with a source the same as this one's | 149 // Any unperformed, unpending move with a source the same as this one's |
| 54 // destination blocks this one so recursively perform all such moves. | 150 // destination blocks this one so recursively perform all such moves. |
| 55 for (MoveOperands* other : *moves) { | 151 for (size_t i = 0; i < moves->size(); ++i) { |
| 56 if (other->Blocks(destination) && !other->IsPending()) { | 152 auto other = (*moves)[i]; |
| 153 if (other->IsEliminated()) continue; | |
| 154 if (other->IsPending()) continue; | |
| 155 if (other->source().InterferesWith(destination)) { | |
| 156 if (!kSimpleFPAliasing && is_fp_loc_move && | |
| 157 LocationOperand::cast(other->source()).representation() > | |
| 158 split_rep_) { | |
| 159 // 'other' must also be an FP location move. Break it into fragments | |
| 160 // of the same size as 'move'. 'other' is set to one of the fragments, | |
| 161 // and the rest are appended to 'moves'. | |
| 162 other = Split(other, split_rep_, moves); | |
| 163 // 'other' may not block destination now. | |
| 164 if (!other->source().InterferesWith(destination)) continue; | |
| 165 } | |
| 57 // Though PerformMove can change any source operand in the move graph, | 166 // Though PerformMove can change any source operand in the move graph, |
| 58 // this call cannot create a blocking move via a swap (this loop does not | 167 // this call cannot create a blocking move via a swap (this loop does not |
| 59 // miss any). Assume there is a non-blocking move with source A and this | 168 // miss any). Assume there is a non-blocking move with source A and this |
| 60 // move is blocked on source B and there is a swap of A and B. Then A and | 169 // move is blocked on source B and there is a swap of A and B. Then A and |
| 61 // B must be involved in the same cycle (or they would not be swapped). | 170 // B must be involved in the same cycle (or they would not be swapped). |
| 62 // Since this move's destination is B and there is only a single incoming | 171 // Since this move's destination is B and there is only a single incoming |
| 63 // edge to an operand, this move must also be involved in the same cycle. | 172 // edge to an operand, this move must also be involved in the same cycle. |
| 64 // In that case, the blocking move will be created but will be "pending" | 173 // In that case, the blocking move will be created but will be "pending" |
| 65 // when we return from PerformMove. | 174 // when we return from PerformMove. |
| 66 PerformMove(moves, other); | 175 PerformMove(moves, other); |
| 67 } | 176 } |
| 68 } | 177 } |
| 69 | 178 |
| 179 // This move's source may have changed due to swaps to resolve cycles and so | |
| 180 // it may now be the last move in the cycle. If so remove it. | |
| 181 source = move->source(); | |
| 182 if (source.EqualsCanonicalized(destination)) { | |
| 183 move->Eliminate(); | |
| 184 return; | |
| 185 } | |
| 186 | |
| 70 // We are about to resolve this move and don't need it marked as pending, so | 187 // We are about to resolve this move and don't need it marked as pending, so |
| 71 // restore its destination. | 188 // restore its destination. |
| 72 move->set_destination(destination); | 189 move->set_destination(destination); |
| 73 | 190 |
| 74 // This move's source may have changed due to swaps to resolve cycles and so | |
| 75 // it may now be the last move in the cycle. If so remove it. | |
| 76 InstructionOperand source = move->source(); | |
| 77 if (source.InterferesWith(destination)) { | |
| 78 move->Eliminate(); | |
| 79 return; | |
| 80 } | |
| 81 | |
| 82 // The move may be blocked on a (at most one) pending move, in which case we | 191 // The move may be blocked on a (at most one) pending move, in which case we |
| 83 // have a cycle. Search for such a blocking move and perform a swap to | 192 // have a cycle. Search for such a blocking move and perform a swap to |
| 84 // resolve it. | 193 // resolve it. |
| 85 auto blocker = std::find_if(moves->begin(), moves->end(), | 194 auto blocker = std::find_if(moves->begin(), moves->end(), |
| 86 std::bind2nd(std::ptr_fun(&Blocks), destination)); | 195 std::bind2nd(std::ptr_fun(&Blocks), destination)); |
| 87 if (blocker == moves->end()) { | 196 if (blocker == moves->end()) { |
| 88 // The easy case: This move is not blocked. | 197 // The easy case: This move is not blocked. |
| 89 assembler_->AssembleMove(&source, &destination); | 198 assembler_->AssembleMove(&source, &destination); |
| 90 move->Eliminate(); | 199 move->Eliminate(); |
| 91 return; | 200 return; |
| 92 } | 201 } |
| 93 | 202 |
| 94 DCHECK((*blocker)->IsPending()); | 203 // Ensure source is a register, to limit swap cases. |
|
georgia.kouveli
2016/10/12 17:38:41
Isn't it still possible for both source and regist
bbudge
2016/10/12 23:07:17
I'm not sure why I changed that, but you are corre
| |
| 95 // Ensure source is a register or both are stack slots, to limit swap cases. | |
| 96 if (source.IsStackSlot() || source.IsFPStackSlot()) { | 204 if (source.IsStackSlot() || source.IsFPStackSlot()) { |
| 97 std::swap(source, destination); | 205 std::swap(source, destination); |
| 98 } | 206 } |
| 99 assembler_->AssembleSwap(&source, &destination); | 207 assembler_->AssembleSwap(&source, &destination); |
| 100 move->Eliminate(); | 208 move->Eliminate(); |
| 101 | 209 |
| 102 // Any unperformed (including pending) move with a source of either this | 210 // Update outstanding moves whose source may now have been moved. |
| 103 // move's source or destination needs to have their source changed to | 211 if (!kSimpleFPAliasing && is_fp_loc_move) { |
| 104 // reflect the state of affairs after the swap. | 212 // We may have to split larger moves. |
| 105 for (MoveOperands* other : *moves) { | 213 for (size_t i = 0; i < moves->size(); ++i) { |
| 106 if (other->Blocks(source)) { | 214 auto other = (*moves)[i]; |
| 107 other->set_source(destination); | 215 if (other->IsEliminated()) continue; |
| 108 } else if (other->Blocks(destination)) { | 216 if (source.InterferesWith(other->source())) { |
| 109 other->set_source(source); | 217 if (LocationOperand::cast(other->source()).representation() > |
| 218 split_rep_) { | |
| 219 other = Split(other, split_rep_, moves); | |
| 220 if (!source.InterferesWith(other->source())) continue; | |
| 221 } | |
| 222 other->set_source(destination); | |
| 223 } else if (destination.InterferesWith(other->source())) { | |
| 224 if (LocationOperand::cast(other->source()).representation() > | |
| 225 split_rep_) { | |
| 226 other = Split(other, split_rep_, moves); | |
| 227 if (!destination.InterferesWith(other->source())) continue; | |
| 228 } | |
| 229 other->set_source(source); | |
| 230 } | |
| 231 } | |
| 232 } else { | |
| 233 for (auto other : *moves) { | |
| 234 if (other->IsEliminated()) continue; | |
| 235 if (source.EqualsCanonicalized(other->source())) { | |
| 236 other->set_source(destination); | |
| 237 } else if (destination.EqualsCanonicalized(other->source())) { | |
| 238 other->set_source(source); | |
| 239 } | |
| 110 } | 240 } |
| 111 } | 241 } |
| 112 } | 242 } |
| 113 } // namespace compiler | 243 } // namespace compiler |
| 114 } // namespace internal | 244 } // namespace internal |
| 115 } // namespace v8 | 245 } // namespace v8 |
| OLD | NEW |