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/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 |