Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(458)

Side by Side Diff: test/cctest/compiler/test-gap-resolver.cc

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

Powered by Google App Engine
This is Rietveld 408576698