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

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: Clean up and relax slot constraints. 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 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
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
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
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