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 |