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 "src/base/utils/random-number-generator.h" | 7 #include "src/base/utils/random-number-generator.h" |
| 8 #include "test/cctest/cctest.h" | 8 #include "test/cctest/cctest.h" |
| 9 | 9 |
| 10 namespace v8 { | 10 namespace v8 { |
| 11 namespace internal { | 11 namespace internal { |
| 12 namespace compiler { | 12 namespace compiler { |
| 13 | 13 |
| 14 const auto GetRegConfig = RegisterConfiguration::Turbofan; | |
| 15 | |
| 16 bool IsFPLocation(const InstructionOperand& op) { | |
|
Mircea Trofin
2016/09/30 04:56:13
don't we have this defined on InstructionOperand?
bbudge
2016/09/30 18:16:31
Since the only usage is in this test, I just imple
bbudge
2016/10/03 23:45:50
I merged in the IsFPLocationOperand method from my
| |
| 17 return op.IsFPStackSlot() || op.IsFPRegister(); | |
| 18 } | |
| 19 | |
| 20 // Fragments the given operand into an equivalent set of operands to simplify | |
| 21 // ParallelMove equivalence testing. | |
| 22 void GetCanonicalOperands(const InstructionOperand& op, | |
| 23 std::vector<InstructionOperand>* fragments) { | |
| 24 CHECK(!kSimpleFPAliasing); | |
| 25 CHECK(IsFPLocation(op)); | |
| 26 // TODO(bbudge) Split into float operands on platforms with non-simple FP | |
| 27 // register aliasing. | |
| 28 fragments->push_back(op); | |
| 29 } | |
| 30 | |
| 14 // The state of our move interpreter is the mapping of operands to values. Note | 31 // The state of our move interpreter is the mapping of operands to values. Note |
| 15 // that the actual values don't really matter, all we care about is equality. | 32 // that the actual values don't really matter, all we care about is equality. |
| 16 class InterpreterState { | 33 class InterpreterState { |
| 17 public: | 34 public: |
| 18 void ExecuteInParallel(const ParallelMove* moves) { | 35 void ExecuteInParallel(const ParallelMove* moves) { |
| 19 InterpreterState copy(*this); | 36 InterpreterState copy(*this); |
| 20 for (const auto m : *moves) { | 37 for (const auto m : *moves) { |
| 21 if (!m->IsRedundant()) write(m->destination(), copy.read(m->source())); | 38 CHECK(!m->IsRedundant()); |
| 39 const InstructionOperand& src = m->source(); | |
| 40 const InstructionOperand& dst = m->destination(); | |
| 41 if (!kSimpleFPAliasing && IsFPLocation(src) && IsFPLocation(dst)) { | |
| 42 // Canonicalize FP location-location moves. | |
| 43 std::vector<InstructionOperand> src_fragments; | |
| 44 GetCanonicalOperands(src, &src_fragments); | |
| 45 CHECK(!src_fragments.empty()); | |
| 46 std::vector<InstructionOperand> dst_fragments; | |
| 47 GetCanonicalOperands(dst, &dst_fragments); | |
| 48 CHECK_EQ(src_fragments.size(), dst_fragments.size()); | |
| 49 | |
| 50 for (size_t i = 0; i < src_fragments.size(); ++i) { | |
| 51 write(dst_fragments[i], copy.read(src_fragments[i])); | |
| 52 } | |
| 53 continue; | |
| 54 } | |
| 55 // All other moves. | |
| 56 write(dst, copy.read(src)); | |
| 22 } | 57 } |
| 23 } | 58 } |
| 24 | 59 |
| 25 bool operator==(const InterpreterState& other) const { | 60 bool operator==(const InterpreterState& other) const { |
| 26 return values_ == other.values_; | 61 return values_ == other.values_; |
| 27 } | 62 } |
| 28 | 63 |
| 29 bool operator!=(const InterpreterState& other) const { | |
| 30 return values_ != other.values_; | |
| 31 } | |
| 32 | |
| 33 private: | 64 private: |
| 65 // struct for mapping operands to a unique value, that makes it easier to | |
| 66 // detect illegal parallel moves, and to evaluate moves for equivalence. This | |
| 67 // is a one way transformation. All general register and slot operands are | |
| 68 // mapped to the default representation. FP registers and slots are mapped to | |
| 69 // float64 except on architectures with non-simple FP register aliasing, where | |
| 70 // the actual representation is used. | |
| 34 struct Key { | 71 struct Key { |
| 35 bool is_constant; | 72 bool is_constant; |
| 36 MachineRepresentation rep; | 73 MachineRepresentation rep; |
| 37 LocationOperand::LocationKind kind; | 74 LocationOperand::LocationKind kind; |
| 38 int index; | 75 int index; |
| 39 | 76 |
| 40 bool operator<(const Key& other) const { | 77 bool operator<(const Key& other) const { |
| 41 if (this->is_constant != other.is_constant) { | 78 if (this->is_constant != other.is_constant) { |
| 42 return this->is_constant; | 79 return this->is_constant; |
| 43 } | 80 } |
| 44 if (this->rep != other.rep) { | 81 if (this->rep != other.rep) { |
| 45 return static_cast<int>(this->rep) < static_cast<int>(other.rep); | 82 return this->rep < other.rep; |
| 46 } | 83 } |
| 47 if (this->kind != other.kind) { | 84 if (this->kind != other.kind) { |
| 48 return this->kind < other.kind; | 85 return this->kind < other.kind; |
| 49 } | 86 } |
| 50 return this->index < other.index; | 87 return this->index < other.index; |
| 51 } | 88 } |
| 52 | 89 |
| 53 bool operator==(const Key& other) const { | 90 bool operator==(const Key& other) const { |
| 54 return this->is_constant == other.is_constant && this->rep == other.rep && | 91 return this->is_constant == other.is_constant && this->rep == other.rep && |
| 55 this->kind == other.kind && this->index == other.index; | 92 this->kind == other.kind && this->index == other.index; |
| 56 } | 93 } |
| 57 }; | 94 }; |
| 58 | 95 |
| 59 // Internally, the state is a normalized permutation of (kind,index) pairs. | 96 // Internally, the state is a normalized permutation of Value pairs. |
| 60 typedef Key Value; | 97 typedef Key Value; |
| 61 typedef std::map<Key, Value> OperandMap; | 98 typedef std::map<Key, Value> OperandMap; |
| 62 | 99 |
| 63 Value read(const InstructionOperand& op) const { | 100 Value read(const InstructionOperand& op) const { |
| 64 OperandMap::const_iterator it = values_.find(KeyFor(op)); | 101 OperandMap::const_iterator it = values_.find(KeyFor(op)); |
| 65 return (it == values_.end()) ? ValueFor(op) : it->second; | 102 return (it == values_.end()) ? ValueFor(op) : it->second; |
| 66 } | 103 } |
| 67 | 104 |
| 68 void write(const InstructionOperand& op, Value v) { | 105 void write(const InstructionOperand& dst, Value v) { |
| 69 if (v == ValueFor(op)) { | 106 if (v == ValueFor(dst)) { |
| 70 values_.erase(KeyFor(op)); | 107 values_.erase(KeyFor(dst)); |
| 71 } else { | 108 } else { |
| 72 values_[KeyFor(op)] = v; | 109 values_[KeyFor(dst)] = v; |
| 73 } | 110 } |
| 74 } | 111 } |
| 75 | 112 |
| 76 static Key KeyFor(const InstructionOperand& op) { | 113 static Key KeyFor(const InstructionOperand& op) { |
| 77 bool is_constant = op.IsConstant(); | 114 bool is_constant = op.IsConstant(); |
| 78 MachineRepresentation rep = | 115 MachineRepresentation rep = |
| 79 v8::internal::compiler::InstructionSequence::DefaultRepresentation(); | 116 v8::internal::compiler::InstructionSequence::DefaultRepresentation(); |
| 80 LocationOperand::LocationKind kind; | 117 LocationOperand::LocationKind kind; |
| 81 int index; | 118 int index; |
| 82 if (!is_constant) { | 119 if (!is_constant) { |
| 83 const LocationOperand& loc_op = LocationOperand::cast(op); | 120 const LocationOperand& loc_op = LocationOperand::cast(op); |
| 121 // Canonicalize FP location operand representations to kFloat64. | |
| 122 if (IsFloatingPoint(loc_op.representation())) { | |
| 123 rep = MachineRepresentation::kFloat64; | |
| 124 } | |
| 84 if (loc_op.IsAnyRegister()) { | 125 if (loc_op.IsAnyRegister()) { |
| 85 if (loc_op.IsFPRegister()) { | |
| 86 rep = MachineRepresentation::kFloat64; | |
| 87 } | |
| 88 index = loc_op.register_code(); | 126 index = loc_op.register_code(); |
| 89 } else { | 127 } else { |
| 90 index = loc_op.index(); | 128 index = loc_op.index(); |
| 91 } | 129 } |
| 92 kind = loc_op.location_kind(); | 130 kind = loc_op.location_kind(); |
| 93 } else { | 131 } else { |
| 94 index = ConstantOperand::cast(op).virtual_register(); | 132 index = ConstantOperand::cast(op).virtual_register(); |
| 95 kind = LocationOperand::REGISTER; | 133 kind = LocationOperand::REGISTER; |
| 96 } | 134 } |
| 97 Key key = {is_constant, rep, kind, index}; | 135 Key key = {is_constant, rep, kind, index}; |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 108 } | 146 } |
| 109 | 147 |
| 110 friend std::ostream& operator<<(std::ostream& os, | 148 friend std::ostream& operator<<(std::ostream& os, |
| 111 const InterpreterState& is) { | 149 const InterpreterState& is) { |
| 112 for (OperandMap::const_iterator it = is.values_.begin(); | 150 for (OperandMap::const_iterator it = is.values_.begin(); |
| 113 it != is.values_.end(); ++it) { | 151 it != is.values_.end(); ++it) { |
| 114 if (it != is.values_.begin()) os << " "; | 152 if (it != is.values_.begin()) os << " "; |
| 115 InstructionOperand source = FromKey(it->second); | 153 InstructionOperand source = FromKey(it->second); |
| 116 InstructionOperand destination = FromKey(it->first); | 154 InstructionOperand destination = FromKey(it->first); |
| 117 MoveOperands mo(source, destination); | 155 MoveOperands mo(source, destination); |
| 118 PrintableMoveOperands pmo = {RegisterConfiguration::Turbofan(), &mo}; | 156 PrintableMoveOperands pmo = {GetRegConfig(), &mo}; |
| 119 os << pmo; | 157 os << pmo; |
| 120 } | 158 } |
| 121 return os; | 159 return os; |
| 122 } | 160 } |
| 123 | 161 |
| 124 OperandMap values_; | 162 OperandMap values_; |
| 125 }; | 163 }; |
| 126 | 164 |
| 127 | |
| 128 // An abstract interpreter for moves, swaps and parallel moves. | 165 // An abstract interpreter for moves, swaps and parallel moves. |
| 129 class MoveInterpreter : public GapResolver::Assembler { | 166 class MoveInterpreter : public GapResolver::Assembler { |
| 130 public: | 167 public: |
| 131 explicit MoveInterpreter(Zone* zone) : zone_(zone) {} | 168 explicit MoveInterpreter(Zone* zone) : zone_(zone) {} |
| 132 | 169 |
| 133 void AssembleMove(InstructionOperand* source, | 170 void AssembleMove(InstructionOperand* source, |
| 134 InstructionOperand* destination) override { | 171 InstructionOperand* destination) override { |
| 135 ParallelMove* moves = new (zone_) ParallelMove(zone_); | 172 ParallelMove* moves = new (zone_) ParallelMove(zone_); |
| 136 moves->AddMove(*source, *destination); | 173 moves->AddMove(*source, *destination); |
| 137 state_.ExecuteInParallel(moves); | 174 state_.ExecuteInParallel(moves); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 154 private: | 191 private: |
| 155 Zone* const zone_; | 192 Zone* const zone_; |
| 156 InterpreterState state_; | 193 InterpreterState state_; |
| 157 }; | 194 }; |
| 158 | 195 |
| 159 | 196 |
| 160 class ParallelMoveCreator : public HandleAndZoneScope { | 197 class ParallelMoveCreator : public HandleAndZoneScope { |
| 161 public: | 198 public: |
| 162 ParallelMoveCreator() : rng_(CcTest::random_number_generator()) {} | 199 ParallelMoveCreator() : rng_(CcTest::random_number_generator()) {} |
| 163 | 200 |
| 201 // Creates a ParallelMove with 'size' random MoveOperands. Note that illegal | |
| 202 // moves will be rejected, so the actual number of MoveOperands may be less. | |
| 164 ParallelMove* Create(int size) { | 203 ParallelMove* Create(int size) { |
| 165 ParallelMove* parallel_move = new (main_zone()) ParallelMove(main_zone()); | 204 ParallelMove* parallel_move = new (main_zone()) ParallelMove(main_zone()); |
| 166 std::set<InstructionOperand, CompareOperandModuloType> seen; | 205 // Valid ParallelMoves can't have interfering destination ops. |
| 206 std::set<InstructionOperand, CompareOperandModuloType> destinations; | |
| 207 // Valid ParallelMoves can't have interfering source ops of different reps. | |
| 208 std::map<InstructionOperand, MachineRepresentation, | |
| 209 CompareOperandModuloType> | |
| 210 sources; | |
| 167 for (int i = 0; i < size; ++i) { | 211 for (int i = 0; i < size; ++i) { |
| 168 MachineRepresentation rep = RandomRepresentation(); | 212 MachineRepresentation rep = RandomRepresentation(); |
| 169 MoveOperands mo(CreateRandomOperand(true, rep), | 213 MoveOperands mo(CreateRandomOperand(true, rep), |
| 170 CreateRandomOperand(false, rep)); | 214 CreateRandomOperand(false, rep)); |
| 171 if (!mo.IsRedundant() && seen.find(mo.destination()) == seen.end()) { | 215 if (mo.IsRedundant()) continue; |
| 216 | |
| 217 const InstructionOperand& dst = mo.destination(); | |
| 218 bool reject = false; | |
| 219 // On architectures where FP register aliasing is non-simple, update the | |
| 220 // destinations set with the float equivalents of the operand and check | |
| 221 // that all destinations are unique and do not alias each other. | |
| 222 if (!kSimpleFPAliasing && IsFPLocation(mo.destination())) { | |
| 223 std::vector<InstructionOperand> fragments; | |
| 224 GetCanonicalOperands(dst, &fragments); | |
| 225 CHECK(!fragments.empty()); | |
| 226 for (size_t i = 0; i < fragments.size(); ++i) { | |
| 227 if (destinations.find(fragments[i]) == destinations.end()) { | |
| 228 destinations.insert(fragments[i]); | |
| 229 } else { | |
| 230 reject = true; | |
| 231 break; | |
| 232 } | |
| 233 } | |
| 234 // Update the sources map, and check that no FP source has multiple | |
| 235 // representations. | |
| 236 const InstructionOperand& src = mo.source(); | |
| 237 if (src.IsFPRegister()) { | |
| 238 std::vector<InstructionOperand> fragments; | |
| 239 MachineRepresentation src_rep = | |
| 240 LocationOperand::cast(src).representation(); | |
| 241 GetCanonicalOperands(src, &fragments); | |
| 242 CHECK(!fragments.empty()); | |
| 243 for (size_t i = 0; i < fragments.size(); ++i) { | |
| 244 auto find_it = sources.find(fragments[i]); | |
| 245 if (find_it != sources.end() && find_it->second != src_rep) { | |
| 246 reject = true; | |
| 247 break; | |
| 248 } | |
| 249 sources.insert(std::make_pair(fragments[i], src_rep)); | |
| 250 } | |
| 251 } | |
| 252 } else { | |
| 253 if (destinations.find(dst) == destinations.end()) { | |
| 254 destinations.insert(dst); | |
| 255 } else { | |
| 256 reject = true; | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 if (!reject) { | |
| 172 parallel_move->AddMove(mo.source(), mo.destination()); | 261 parallel_move->AddMove(mo.source(), mo.destination()); |
| 173 seen.insert(mo.destination()); | |
| 174 } | 262 } |
| 175 } | 263 } |
| 176 return parallel_move; | 264 return parallel_move; |
| 177 } | 265 } |
| 178 | 266 |
| 267 // Creates a ParallelMove from a list of operand pairs. Even operands are | |
| 268 // destinations, odd ones are sources. | |
| 269 ParallelMove* Create(const std::vector<InstructionOperand>& operand_pairs) { | |
| 270 ParallelMove* parallel_move = new (main_zone()) ParallelMove(main_zone()); | |
| 271 for (size_t i = 0; i < operand_pairs.size(); i += 2) { | |
| 272 const InstructionOperand& dst = operand_pairs[i]; | |
| 273 const InstructionOperand& src = operand_pairs[i + 1]; | |
| 274 parallel_move->AddMove(src, dst); | |
| 275 } | |
| 276 return parallel_move; | |
| 277 } | |
| 278 | |
| 179 private: | 279 private: |
| 180 MachineRepresentation RandomRepresentation() { | 280 MachineRepresentation RandomRepresentation() { |
| 181 int index = rng_->NextInt(5); | 281 int index = rng_->NextInt(6); |
| 182 switch (index) { | 282 switch (index) { |
| 183 case 0: | 283 case 0: |
| 184 return MachineRepresentation::kWord32; | 284 return MachineRepresentation::kWord32; |
| 185 case 1: | 285 case 1: |
| 186 return MachineRepresentation::kWord64; | 286 return MachineRepresentation::kWord64; |
| 187 case 2: | 287 case 2: |
| 188 return MachineRepresentation::kFloat32; | 288 return MachineRepresentation::kFloat32; |
| 189 case 3: | 289 case 3: |
| 190 return MachineRepresentation::kFloat64; | 290 return MachineRepresentation::kFloat64; |
| 191 case 4: | 291 case 4: |
| 292 return MachineRepresentation::kSimd128; | |
| 293 case 5: | |
| 192 return MachineRepresentation::kTagged; | 294 return MachineRepresentation::kTagged; |
| 193 } | 295 } |
| 194 UNREACHABLE(); | 296 UNREACHABLE(); |
| 195 return MachineRepresentation::kNone; | 297 return MachineRepresentation::kNone; |
| 196 } | 298 } |
| 197 | 299 |
| 300 const int kMaxIndex = 7; | |
| 301 const int kMaxIndices = kMaxIndex + 1; | |
| 302 | |
| 303 // Non-FP slots shouldn't overlap FP slots. | |
| 304 // FP slots with different representations shouldn't overlap. | |
| 305 int GetValidSlotIndex(MachineRepresentation rep, int index) { | |
| 306 DCHECK_GE(kMaxIndex, index); | |
| 307 // The first group of slots are for non-FP values. | |
| 308 if (!IsFloatingPoint(rep)) return index; | |
| 309 // The next group are for float values. | |
| 310 int base = kMaxIndices; | |
| 311 if (rep == MachineRepresentation::kFloat32) return base + index; | |
| 312 // Double values. | |
| 313 base += kMaxIndices; | |
| 314 if (rep == MachineRepresentation::kFloat64) return base + index * 2; | |
| 315 // SIMD values | |
| 316 base += kMaxIndices * 2; | |
| 317 CHECK_EQ(MachineRepresentation::kSimd128, rep); | |
| 318 return base + index * 4; | |
| 319 } | |
| 320 | |
| 198 InstructionOperand CreateRandomOperand(bool is_source, | 321 InstructionOperand CreateRandomOperand(bool is_source, |
| 199 MachineRepresentation rep) { | 322 MachineRepresentation rep) { |
| 200 auto conf = RegisterConfiguration::Turbofan(); | 323 auto conf = RegisterConfiguration::Turbofan(); |
| 201 auto GetRegisterCode = [&conf](MachineRepresentation rep, int index) { | 324 auto GetValidRegisterCode = [&conf](MachineRepresentation rep, int index) { |
| 202 switch (rep) { | 325 switch (rep) { |
| 203 case MachineRepresentation::kFloat32: | 326 case MachineRepresentation::kFloat32: |
| 204 #if V8_TARGET_ARCH_ARM | |
| 205 // Only even number float registers are used on Arm. | |
| 206 // TODO(bbudge) Eliminate this when FP register aliasing works. | |
| 207 return conf->RegisterConfiguration::GetAllocatableDoubleCode(index) * | |
| 208 2; | |
| 209 #endif | |
| 210 // Fall through on non-Arm targets. | |
| 211 case MachineRepresentation::kFloat64: | 327 case MachineRepresentation::kFloat64: |
| 328 case MachineRepresentation::kSimd128: | |
| 212 return conf->RegisterConfiguration::GetAllocatableDoubleCode(index); | 329 return conf->RegisterConfiguration::GetAllocatableDoubleCode(index); |
| 213 | |
| 214 default: | 330 default: |
| 215 return conf->RegisterConfiguration::GetAllocatableGeneralCode(index); | 331 return conf->RegisterConfiguration::GetAllocatableGeneralCode(index); |
| 216 } | 332 } |
| 217 UNREACHABLE(); | 333 UNREACHABLE(); |
| 218 return static_cast<int>(Register::kCode_no_reg); | 334 return static_cast<int>(Register::kCode_no_reg); |
| 219 }; | 335 }; |
| 220 int index = rng_->NextInt(7); | 336 int index = rng_->NextInt(kMaxIndex); |
| 221 // destination can't be Constant. | 337 // destination can't be Constant. |
| 222 switch (rng_->NextInt(is_source ? 5 : 4)) { | 338 switch (rng_->NextInt(is_source ? 5 : 4)) { |
| 223 case 0: | 339 case 0: |
| 224 return AllocatedOperand(LocationOperand::STACK_SLOT, rep, index); | 340 return AllocatedOperand(LocationOperand::STACK_SLOT, rep, |
| 341 GetValidSlotIndex(rep, index)); | |
| 225 case 1: | 342 case 1: |
| 226 return AllocatedOperand(LocationOperand::REGISTER, rep, index); | 343 return AllocatedOperand(LocationOperand::REGISTER, rep, |
| 344 GetValidRegisterCode(rep, index)); | |
| 227 case 2: | 345 case 2: |
| 228 return ExplicitOperand(LocationOperand::REGISTER, rep, | 346 return ExplicitOperand(LocationOperand::REGISTER, rep, |
| 229 GetRegisterCode(rep, 1)); | 347 GetValidRegisterCode(rep, 1)); |
| 230 case 3: | 348 case 3: |
| 231 return ExplicitOperand(LocationOperand::STACK_SLOT, rep, | 349 return ExplicitOperand(LocationOperand::STACK_SLOT, rep, |
| 232 GetRegisterCode(rep, index)); | 350 GetValidSlotIndex(rep, index)); |
| 233 case 4: | 351 case 4: |
| 234 return ConstantOperand(index); | 352 return ConstantOperand(index); |
| 235 } | 353 } |
| 236 UNREACHABLE(); | 354 UNREACHABLE(); |
| 237 return InstructionOperand(); | 355 return InstructionOperand(); |
| 238 } | 356 } |
| 239 | 357 |
| 240 private: | 358 private: |
| 241 v8::base::RandomNumberGenerator* rng_; | 359 v8::base::RandomNumberGenerator* rng_; |
| 242 }; | 360 }; |
| 243 | 361 |
| 362 void RunTest(ParallelMove* pm, Zone* zone) { | |
| 363 // Note: The gap resolver modifies the ParallelMove, so interpret first. | |
| 364 MoveInterpreter mi1(zone); | |
| 365 mi1.AssembleParallelMove(pm); | |
| 366 | |
| 367 MoveInterpreter mi2(zone); | |
| 368 GapResolver resolver(&mi2); | |
| 369 resolver.Resolve(pm); | |
| 370 | |
| 371 CHECK_EQ(mi1.state(), mi2.state()); | |
| 372 } | |
| 244 | 373 |
| 245 TEST(FuzzResolver) { | 374 TEST(FuzzResolver) { |
| 246 ParallelMoveCreator pmc; | 375 ParallelMoveCreator pmc; |
| 247 for (int size = 0; size < 20; ++size) { | 376 for (int size = 0; size < 80; ++size) { |
| 248 for (int repeat = 0; repeat < 50; ++repeat) { | 377 for (int repeat = 0; repeat < 50; ++repeat) { |
| 249 ParallelMove* pm = pmc.Create(size); | 378 RunTest(pmc.Create(size), pmc.main_zone()); |
| 250 | |
| 251 // Note: The gap resolver modifies the ParallelMove, so interpret first. | |
| 252 MoveInterpreter mi1(pmc.main_zone()); | |
| 253 mi1.AssembleParallelMove(pm); | |
| 254 | |
| 255 MoveInterpreter mi2(pmc.main_zone()); | |
| 256 GapResolver resolver(&mi2); | |
| 257 resolver.Resolve(pm); | |
| 258 | |
| 259 CHECK_EQ(mi1.state(), mi2.state()); | |
| 260 } | 379 } |
| 261 } | 380 } |
| 262 } | 381 } |
| 263 | 382 |
| 264 } // namespace compiler | 383 } // namespace compiler |
| 265 } // namespace internal | 384 } // namespace internal |
| 266 } // namespace v8 | 385 } // namespace v8 |
| OLD | NEW |