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/move-optimizer.h" | 5 #include "src/compiler/move-optimizer.h" |
6 | 6 |
7 namespace v8 { | 7 namespace v8 { |
8 namespace internal { | 8 namespace internal { |
9 namespace compiler { | 9 namespace compiler { |
10 | 10 |
11 namespace { | 11 namespace { |
12 | 12 |
13 struct MoveKey { | 13 struct MoveKey { |
14 InstructionOperand source; | 14 InstructionOperand source; |
15 InstructionOperand destination; | 15 InstructionOperand destination; |
16 }; | 16 }; |
17 | 17 |
18 struct MoveKeyCompare { | 18 struct MoveKeyCompare { |
19 bool operator()(const MoveKey& a, const MoveKey& b) const { | 19 bool operator()(const MoveKey& a, const MoveKey& b) const { |
20 if (a.source.EqualsCanonicalized(b.source)) { | 20 if (a.source.EqualsCanonicalized(b.source)) { |
21 return a.destination.CompareCanonicalized(b.destination); | 21 return a.destination.CompareCanonicalized(b.destination); |
22 } | 22 } |
23 return a.source.CompareCanonicalized(b.source); | 23 return a.source.CompareCanonicalized(b.source); |
24 } | 24 } |
25 }; | 25 }; |
26 | 26 |
27 typedef ZoneMap<MoveKey, unsigned, MoveKeyCompare> MoveMap; | 27 typedef ZoneMap<MoveKey, unsigned, MoveKeyCompare> MoveMap; |
28 typedef ZoneSet<InstructionOperand, CompareOperandModuloType> OperandSet; | 28 typedef ZoneSet<InstructionOperand, CompareOperandModuloType> OperandSet; |
29 | 29 |
30 bool Blocks(const OperandSet& set, const InstructionOperand& operand) { | 30 #define REP_BIT(rep) (1 << static_cast<int>(rep)); |
31 return set.find(operand) != set.end(); | 31 |
32 bool HasMixedFPReps(int reps) { | |
33 return reps && !base::bits::IsPowerOfTwo32(reps); | |
34 } | |
35 | |
36 void InsertOp(OperandSet* set, const InstructionOperand& op, int* fp_reg_reps) { | |
Mircea Trofin
2016/10/12 21:09:26
would it be overkill to define InsertOp as a membe
bbudge
2016/10/12 23:07:17
OperandSet is only a typedef. We could have it inh
Mircea Trofin
2016/10/14 20:58:39
Mind adding a todo on the typedef to investigate d
bbudge
2016/10/15 01:30:25
I replaced the typedef with a class. It's much cle
| |
37 set->insert(op); | |
38 if (!kSimpleFPAliasing && op.IsFPRegister()) | |
39 *fp_reg_reps |= REP_BIT(LocationOperand::cast(op).representation()); | |
40 } | |
41 | |
42 bool ContainsOpOrAlias(const OperandSet& set, const InstructionOperand& op, | |
Mircea Trofin
2016/10/12 21:09:26
same for this API
bbudge
2016/10/12 23:07:17
Ditto
| |
43 int fp_reg_reps) { | |
44 if (set.find(op) != set.end()) return true; | |
45 | |
46 if (!kSimpleFPAliasing && op.IsFPRegister()) { | |
47 // Platforms where FP registers have complex aliasing need extra checks. | |
48 const LocationOperand& loc = LocationOperand::cast(op); | |
49 MachineRepresentation rep = loc.representation(); | |
50 // If we haven't encountered mixed FP registers, skip the extra checks. | |
51 fp_reg_reps |= REP_BIT(rep); | |
52 if (!HasMixedFPReps(fp_reg_reps)) return false; | |
53 | |
54 // Check register against aliasing registers of other FP representations. | |
55 MachineRepresentation other_rep1, other_rep2; | |
56 switch (rep) { | |
57 case MachineRepresentation::kFloat32: | |
58 other_rep1 = MachineRepresentation::kFloat64; | |
59 other_rep2 = MachineRepresentation::kSimd128; | |
60 break; | |
61 case MachineRepresentation::kFloat64: | |
62 other_rep1 = MachineRepresentation::kFloat32; | |
63 other_rep2 = MachineRepresentation::kSimd128; | |
64 break; | |
65 case MachineRepresentation::kSimd128: | |
66 other_rep1 = MachineRepresentation::kFloat32; | |
67 other_rep2 = MachineRepresentation::kFloat64; | |
68 break; | |
69 default: | |
70 UNREACHABLE(); | |
71 break; | |
72 } | |
73 const RegisterConfiguration* config = RegisterConfiguration::Turbofan(); | |
74 int base = -1; | |
75 int aliases = | |
76 config->GetAliases(rep, loc.register_code(), other_rep1, &base); | |
77 DCHECK(aliases > 0 || (aliases == 0 && base == -1)); | |
78 while (aliases--) { | |
79 if (set.find(AllocatedOperand(LocationOperand::REGISTER, other_rep1, | |
80 base + aliases)) != set.end()) | |
81 return true; | |
82 } | |
83 aliases = config->GetAliases(rep, loc.register_code(), other_rep2, &base); | |
84 DCHECK(aliases > 0 || (aliases == 0 && base == -1)); | |
85 while (aliases--) { | |
86 if (set.find(AllocatedOperand(LocationOperand::REGISTER, other_rep2, | |
87 base + aliases)) != set.end()) | |
88 return true; | |
89 } | |
90 } | |
91 return false; | |
32 } | 92 } |
33 | 93 |
34 int FindFirstNonEmptySlot(const Instruction* instr) { | 94 int FindFirstNonEmptySlot(const Instruction* instr) { |
35 int i = Instruction::FIRST_GAP_POSITION; | 95 int i = Instruction::FIRST_GAP_POSITION; |
36 for (; i <= Instruction::LAST_GAP_POSITION; i++) { | 96 for (; i <= Instruction::LAST_GAP_POSITION; i++) { |
37 ParallelMove* moves = instr->parallel_moves()[i]; | 97 ParallelMove* moves = instr->parallel_moves()[i]; |
38 if (moves == nullptr) continue; | 98 if (moves == nullptr) continue; |
39 for (MoveOperands* move : *moves) { | 99 for (MoveOperands* move : *moves) { |
40 if (!move->IsRedundant()) return i; | 100 if (!move->IsRedundant()) return i; |
41 move->Eliminate(); | 101 move->Eliminate(); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
87 void MoveOptimizer::RemoveClobberedDestinations(Instruction* instruction) { | 147 void MoveOptimizer::RemoveClobberedDestinations(Instruction* instruction) { |
88 if (instruction->IsCall()) return; | 148 if (instruction->IsCall()) return; |
89 ParallelMove* moves = instruction->parallel_moves()[0]; | 149 ParallelMove* moves = instruction->parallel_moves()[0]; |
90 if (moves == nullptr) return; | 150 if (moves == nullptr) return; |
91 | 151 |
92 DCHECK(instruction->parallel_moves()[1] == nullptr || | 152 DCHECK(instruction->parallel_moves()[1] == nullptr || |
93 instruction->parallel_moves()[1]->empty()); | 153 instruction->parallel_moves()[1]->empty()); |
94 | 154 |
95 OperandSet outputs(local_zone()); | 155 OperandSet outputs(local_zone()); |
96 OperandSet inputs(local_zone()); | 156 OperandSet inputs(local_zone()); |
157 int input_fp_reg_reps = 0; | |
158 int output_fp_reg_reps = 0; | |
97 | 159 |
98 // Outputs and temps are treated together as potentially clobbering a | 160 // Outputs and temps are treated together as potentially clobbering a |
99 // destination operand. | 161 // destination operand. |
100 for (size_t i = 0; i < instruction->OutputCount(); ++i) { | 162 for (size_t i = 0; i < instruction->OutputCount(); ++i) { |
101 outputs.insert(*instruction->OutputAt(i)); | 163 InsertOp(&outputs, *instruction->OutputAt(i), &output_fp_reg_reps); |
102 } | 164 } |
103 for (size_t i = 0; i < instruction->TempCount(); ++i) { | 165 for (size_t i = 0; i < instruction->TempCount(); ++i) { |
104 outputs.insert(*instruction->TempAt(i)); | 166 InsertOp(&outputs, *instruction->TempAt(i), &output_fp_reg_reps); |
105 } | 167 } |
106 | 168 |
107 // Input operands block elisions. | 169 // Input operands block elisions. |
108 for (size_t i = 0; i < instruction->InputCount(); ++i) { | 170 for (size_t i = 0; i < instruction->InputCount(); ++i) { |
109 inputs.insert(*instruction->InputAt(i)); | 171 InsertOp(&inputs, *instruction->InputAt(i), &input_fp_reg_reps); |
110 } | 172 } |
111 | 173 |
112 // Elide moves made redundant by the instruction. | 174 // Elide moves made redundant by the instruction. |
113 for (MoveOperands* move : *moves) { | 175 for (MoveOperands* move : *moves) { |
114 if (outputs.find(move->destination()) != outputs.end() && | 176 if (ContainsOpOrAlias(outputs, move->destination(), output_fp_reg_reps) && |
115 inputs.find(move->destination()) == inputs.end()) { | 177 !ContainsOpOrAlias(inputs, move->destination(), input_fp_reg_reps)) { |
116 move->Eliminate(); | 178 move->Eliminate(); |
117 } | 179 } |
118 } | 180 } |
119 | 181 |
120 // The ret instruction makes any assignment before it unnecessary, except for | 182 // The ret instruction makes any assignment before it unnecessary, except for |
121 // the one for its input. | 183 // the one for its input. |
122 if (instruction->IsRet() || instruction->IsTailCall()) { | 184 if (instruction->IsRet() || instruction->IsTailCall()) { |
123 for (MoveOperands* move : *moves) { | 185 for (MoveOperands* move : *moves) { |
124 if (inputs.find(move->destination()) == inputs.end()) { | 186 if (!ContainsOpOrAlias(inputs, move->destination(), input_fp_reg_reps)) { |
125 move->Eliminate(); | 187 move->Eliminate(); |
126 } | 188 } |
127 } | 189 } |
128 } | 190 } |
129 } | 191 } |
130 | 192 |
131 void MoveOptimizer::MigrateMoves(Instruction* to, Instruction* from) { | 193 void MoveOptimizer::MigrateMoves(Instruction* to, Instruction* from) { |
132 if (from->IsCall()) return; | 194 if (from->IsCall()) return; |
133 | 195 |
134 ParallelMove* from_moves = from->parallel_moves()[0]; | 196 ParallelMove* from_moves = from->parallel_moves()[0]; |
135 if (from_moves == nullptr || from_moves->empty()) return; | 197 if (from_moves == nullptr || from_moves->empty()) return; |
136 | 198 |
137 OperandSet dst_cant_be(local_zone()); | 199 OperandSet dst_cant_be(local_zone()); |
138 OperandSet src_cant_be(local_zone()); | 200 OperandSet src_cant_be(local_zone()); |
201 int dst_fp_reg_reps = 0; | |
202 int src_fp_reg_reps = 0; | |
139 | 203 |
140 // If an operand is an input to the instruction, we cannot move assignments | 204 // If an operand is an input to the instruction, we cannot move assignments |
141 // where it appears on the LHS. | 205 // where it appears on the LHS. |
142 for (size_t i = 0; i < from->InputCount(); ++i) { | 206 for (size_t i = 0; i < from->InputCount(); ++i) { |
143 dst_cant_be.insert(*from->InputAt(i)); | 207 InsertOp(&dst_cant_be, *from->InputAt(i), &dst_fp_reg_reps); |
144 } | 208 } |
145 // If an operand is output to the instruction, we cannot move assignments | 209 // If an operand is output to the instruction, we cannot move assignments |
146 // where it appears on the RHS, because we would lose its value before the | 210 // where it appears on the RHS, because we would lose its value before the |
147 // instruction. | 211 // instruction. |
148 // Same for temp operands. | 212 // Same for temp operands. |
149 // The output can't appear on the LHS because we performed | 213 // The output can't appear on the LHS because we performed |
150 // RemoveClobberedDestinations for the "from" instruction. | 214 // RemoveClobberedDestinations for the "from" instruction. |
151 for (size_t i = 0; i < from->OutputCount(); ++i) { | 215 for (size_t i = 0; i < from->OutputCount(); ++i) { |
152 src_cant_be.insert(*from->OutputAt(i)); | 216 InsertOp(&src_cant_be, *from->OutputAt(i), &src_fp_reg_reps); |
153 } | 217 } |
154 for (size_t i = 0; i < from->TempCount(); ++i) { | 218 for (size_t i = 0; i < from->TempCount(); ++i) { |
155 src_cant_be.insert(*from->TempAt(i)); | 219 InsertOp(&src_cant_be, *from->TempAt(i), &src_fp_reg_reps); |
156 } | 220 } |
157 for (MoveOperands* move : *from_moves) { | 221 for (MoveOperands* move : *from_moves) { |
158 if (move->IsRedundant()) continue; | 222 if (move->IsRedundant()) continue; |
159 // Assume dest has a value "V". If we have a "dest = y" move, then we can't | 223 // Assume dest has a value "V". If we have a "dest = y" move, then we can't |
160 // move "z = dest", because z would become y rather than "V". | 224 // move "z = dest", because z would become y rather than "V". |
161 // We assume CompressMoves has happened before this, which means we don't | 225 // We assume CompressMoves has happened before this, which means we don't |
162 // have more than one assignment to dest. | 226 // have more than one assignment to dest. |
163 src_cant_be.insert(move->destination()); | 227 InsertOp(&src_cant_be, move->destination(), &src_fp_reg_reps); |
164 } | 228 } |
165 | 229 |
166 ZoneSet<MoveKey, MoveKeyCompare> move_candidates(local_zone()); | 230 ZoneSet<MoveKey, MoveKeyCompare> move_candidates(local_zone()); |
167 // We start with all the moves that don't have conflicting source or | 231 // We start with all the moves that don't have conflicting source or |
168 // destination operands are eligible for being moved down. | 232 // destination operands are eligible for being moved down. |
169 for (MoveOperands* move : *from_moves) { | 233 for (MoveOperands* move : *from_moves) { |
170 if (move->IsRedundant()) continue; | 234 if (move->IsRedundant()) continue; |
171 if (!Blocks(dst_cant_be, move->destination())) { | 235 if (!ContainsOpOrAlias(dst_cant_be, move->destination(), dst_fp_reg_reps)) { |
172 MoveKey key = {move->source(), move->destination()}; | 236 MoveKey key = {move->source(), move->destination()}; |
173 move_candidates.insert(key); | 237 move_candidates.insert(key); |
174 } | 238 } |
175 } | 239 } |
176 if (move_candidates.empty()) return; | 240 if (move_candidates.empty()) return; |
177 | 241 |
178 // Stabilize the candidate set. | 242 // Stabilize the candidate set. |
179 bool changed = false; | 243 bool changed = false; |
180 do { | 244 do { |
181 changed = false; | 245 changed = false; |
182 for (auto iter = move_candidates.begin(); iter != move_candidates.end();) { | 246 for (auto iter = move_candidates.begin(); iter != move_candidates.end();) { |
183 auto current = iter; | 247 auto current = iter; |
184 ++iter; | 248 ++iter; |
185 InstructionOperand src = current->source; | 249 InstructionOperand src = current->source; |
186 if (Blocks(src_cant_be, src)) { | 250 if (ContainsOpOrAlias(src_cant_be, src, src_fp_reg_reps)) { |
187 src_cant_be.insert(current->destination); | 251 InsertOp(&src_cant_be, current->destination, &src_fp_reg_reps); |
188 move_candidates.erase(current); | 252 move_candidates.erase(current); |
189 changed = true; | 253 changed = true; |
190 } | 254 } |
191 } | 255 } |
192 } while (changed); | 256 } while (changed); |
193 | 257 |
194 ParallelMove to_move(local_zone()); | 258 ParallelMove to_move(local_zone()); |
195 for (MoveOperands* move : *from_moves) { | 259 for (MoveOperands* move : *from_moves) { |
196 if (move->IsRedundant()) continue; | 260 if (move->IsRedundant()) continue; |
197 MoveKey key = {move->source(), move->destination()}; | 261 MoveKey key = {move->source(), move->destination()}; |
(...skipping 18 matching lines...) Expand all Loading... | |
216 if (right == nullptr) return; | 280 if (right == nullptr) return; |
217 | 281 |
218 MoveOpVector& eliminated = local_vector(); | 282 MoveOpVector& eliminated = local_vector(); |
219 DCHECK(eliminated.empty()); | 283 DCHECK(eliminated.empty()); |
220 | 284 |
221 if (!left->empty()) { | 285 if (!left->empty()) { |
222 // Modify the right moves in place and collect moves that will be killed by | 286 // Modify the right moves in place and collect moves that will be killed by |
223 // merging the two gaps. | 287 // merging the two gaps. |
224 for (MoveOperands* move : *right) { | 288 for (MoveOperands* move : *right) { |
225 if (move->IsRedundant()) continue; | 289 if (move->IsRedundant()) continue; |
226 MoveOperands* to_eliminate = left->PrepareInsertAfter(move); | 290 left->PrepareInsertAfter(move, &eliminated); |
227 if (to_eliminate != nullptr) eliminated.push_back(to_eliminate); | |
228 } | 291 } |
229 // Eliminate dead moves. | 292 // Eliminate dead moves. |
230 for (MoveOperands* to_eliminate : eliminated) { | 293 for (MoveOperands* to_eliminate : eliminated) { |
231 to_eliminate->Eliminate(); | 294 to_eliminate->Eliminate(); |
232 } | 295 } |
233 eliminated.clear(); | 296 eliminated.clear(); |
234 } | 297 } |
235 // Add all possibly modified moves from right side. | 298 // Add all possibly modified moves from right side. |
236 for (MoveOperands* move : *right) { | 299 for (MoveOperands* move : *right) { |
237 if (move->IsRedundant()) continue; | 300 if (move->IsRedundant()) continue; |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
474 static_cast<Instruction::GapPosition>(1), code_zone()); | 537 static_cast<Instruction::GapPosition>(1), code_zone()); |
475 slot_1->AddMove(group_begin->destination(), load->destination()); | 538 slot_1->AddMove(group_begin->destination(), load->destination()); |
476 load->Eliminate(); | 539 load->Eliminate(); |
477 } | 540 } |
478 loads.clear(); | 541 loads.clear(); |
479 } | 542 } |
480 | 543 |
481 } // namespace compiler | 544 } // namespace compiler |
482 } // namespace internal | 545 } // namespace internal |
483 } // namespace v8 | 546 } // namespace v8 |
OLD | NEW |