1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/crankshaft/s390/lithium-gap-resolver-s390.h"
6
7#include "src/crankshaft/s390/lithium-codegen-s390.h"
8
9namespace v8 {
10namespace internal {
11
12static const Register kSavedValueRegister = {1};
13
14LGapResolver::LGapResolver(LCodeGen* owner)
15    : cgen_(owner),
16      moves_(32, owner->zone()),
17      root_index_(0),
18      in_cycle_(false),
19      saved_destination_(NULL) {}
20
21void LGapResolver::Resolve(LParallelMove* parallel_move) {
22  DCHECK(moves_.is_empty());
23  // Build up a worklist of moves.
24  BuildInitialMoveList(parallel_move);
25
26  for (int i = 0; i < moves_.length(); ++i) {
27    LMoveOperands move = moves_[i];
28    // Skip constants to perform them last.  They don't block other moves
29    // and skipping such moves with register destinations keeps those
30    // registers free for the whole algorithm.
31    if (!move.IsEliminated() && !move.source()->IsConstantOperand()) {
32      root_index_ = i;  // Any cycle is found when by reaching this move again.
33      PerformMove(i);
34      if (in_cycle_) {
35        RestoreValue();
36      }
37    }
38  }
39
40  // Perform the moves with constant sources.
41  for (int i = 0; i < moves_.length(); ++i) {
42    if (!moves_[i].IsEliminated()) {
43      DCHECK(moves_[i].source()->IsConstantOperand());
44      EmitMove(i);
45    }
46  }
47
48  moves_.Rewind(0);
49}
50
51void LGapResolver::BuildInitialMoveList(LParallelMove* parallel_move) {
52  // Perform a linear sweep of the moves to add them to the initial list of
53  // moves to perform, ignoring any move that is redundant (the source is
54  // the same as the destination, the destination is ignored and
55  // unallocated, or the move was already eliminated).
56  const ZoneList<LMoveOperands>* moves = parallel_move->move_operands();
57  for (int i = 0; i < moves->length(); ++i) {
58    LMoveOperands move = moves->at(i);
59    if (!move.IsRedundant()) moves_.Add(move, cgen_->zone());
60  }
61  Verify();
62}
63
64void LGapResolver::PerformMove(int index) {
65  // Each call to this function performs a move and deletes it from the move
66  // graph.  We first recursively perform any move blocking this one.  We
67  // mark a move as "pending" on entry to PerformMove in order to detect
68  // cycles in the move graph.
69
70  // We can only find a cycle, when doing a depth-first traversal of moves,
71  // be encountering the starting move again. So by spilling the source of
72  // the starting move, we break the cycle.  All moves are then unblocked,
73  // and the starting move is completed by writing the spilled value to
74  // its destination.  All other moves from the spilled source have been
75  // completed prior to breaking the cycle.
76  // An additional complication is that moves to MemOperands with large
77  // offsets (more than 1K or 4K) require us to spill this spilled value to
78  // the stack, to free up the register.
79  DCHECK(!moves_[index].IsPending());
80  DCHECK(!moves_[index].IsRedundant());
81
82  // Clear this move's destination to indicate a pending move.  The actual
83  // destination is saved in a stack allocated local.  Multiple moves can
84  // be pending because this function is recursive.
85  DCHECK(moves_[index].source() != NULL);  // Or else it will look eliminated.
86  LOperand* destination = moves_[index].destination();
87  moves_[index].set_destination(NULL);
88
89  // Perform a depth-first traversal of the move graph to resolve
90  // dependencies.  Any unperformed, unpending move with a source the same
91  // as this one's destination blocks this one so recursively perform all
92  // such moves.
93  for (int i = 0; i < moves_.length(); ++i) {
94    LMoveOperands other_move = moves_[i];
95    if (other_move.Blocks(destination) && !other_move.IsPending()) {
96      PerformMove(i);
97      // If there is a blocking, pending move it must be moves_[root_index_]
98      // and all other moves with the same source as moves_[root_index_] are
99      // sucessfully executed (because they are cycle-free) by this loop.
100    }
101  }
102
103  // We are about to resolve this move and don't need it marked as
104  // pending, so restore its destination.
105  moves_[index].set_destination(destination);
106
107  // The move may be blocked on a pending move, which must be the starting move.
108  // In this case, we have a cycle, and we save the source of this move to
109  // a scratch register to break it.
110  LMoveOperands other_move = moves_[root_index_];
111  if (other_move.Blocks(destination)) {
112    DCHECK(other_move.IsPending());
113    BreakCycle(index);
114    return;
115  }
116
117  // This move is no longer blocked.
118  EmitMove(index);
119}
120
121void LGapResolver::Verify() {
122#ifdef ENABLE_SLOW_DCHECKS
123  // No operand should be the destination for more than one move.
124  for (int i = 0; i < moves_.length(); ++i) {
125    LOperand* destination = moves_[i].destination();
126    for (int j = i + 1; j < moves_.length(); ++j) {
127      SLOW_DCHECK(!destination->Equals(moves_[j].destination()));
128    }
129  }
130#endif
131}
132
133#define __ ACCESS_MASM(cgen_->masm())
134
135void LGapResolver::BreakCycle(int index) {
136  // We save in a register the value that should end up in the source of
137  // moves_[root_index].  After performing all moves in the tree rooted
138  // in that move, we save the value to that source.
139  DCHECK(moves_[index].destination()->Equals(moves_[root_index_].source()));
140  DCHECK(!in_cycle_);
141  in_cycle_ = true;
142  LOperand* source = moves_[index].source();
143  saved_destination_ = moves_[index].destination();
144  if (source->IsRegister()) {
145    __ LoadRR(kSavedValueRegister, cgen_->ToRegister(source));
146  } else if (source->IsStackSlot()) {
147    __ LoadP(kSavedValueRegister, cgen_->ToMemOperand(source));
148  } else if (source->IsDoubleRegister()) {
149    __ ldr(kScratchDoubleReg, cgen_->ToDoubleRegister(source));
150  } else if (source->IsDoubleStackSlot()) {
151    __ LoadDouble(kScratchDoubleReg, cgen_->ToMemOperand(source));
152  } else {
153    UNREACHABLE();
154  }
155  // This move will be done by restoring the saved value to the destination.
156  moves_[index].Eliminate();
157}
158
159void LGapResolver::RestoreValue() {
160  DCHECK(in_cycle_);
161  DCHECK(saved_destination_ != NULL);
162
163  // Spilled value is in kSavedValueRegister or kSavedDoubleValueRegister.
164  if (saved_destination_->IsRegister()) {
165    __ LoadRR(cgen_->ToRegister(saved_destination_), kSavedValueRegister);
166  } else if (saved_destination_->IsStackSlot()) {
167    __ StoreP(kSavedValueRegister, cgen_->ToMemOperand(saved_destination_));
168  } else if (saved_destination_->IsDoubleRegister()) {
169    __ ldr(cgen_->ToDoubleRegister(saved_destination_), kScratchDoubleReg);
170  } else if (saved_destination_->IsDoubleStackSlot()) {
171    __ StoreDouble(kScratchDoubleReg, cgen_->ToMemOperand(saved_destination_));
172  } else {
173    UNREACHABLE();
174  }
175
176  in_cycle_ = false;
177  saved_destination_ = NULL;
178}
179
180void LGapResolver::EmitMove(int index) {
181  LOperand* source = moves_[index].source();
182  LOperand* destination = moves_[index].destination();
183
184  // Dispatch on the source and destination operand kinds.  Not all
185  // combinations are possible.
186
187  if (source->IsRegister()) {
188    Register source_register = cgen_->ToRegister(source);
189    if (destination->IsRegister()) {
190      __ LoadRR(cgen_->ToRegister(destination), source_register);
191    } else {
192      DCHECK(destination->IsStackSlot());
193      __ StoreP(source_register, cgen_->ToMemOperand(destination));
194    }
195  } else if (source->IsStackSlot()) {
196    MemOperand source_operand = cgen_->ToMemOperand(source);
197    if (destination->IsRegister()) {
198      __ LoadP(cgen_->ToRegister(destination), source_operand);
199    } else {
200      DCHECK(destination->IsStackSlot());
201      MemOperand destination_operand = cgen_->ToMemOperand(destination);
202      if (in_cycle_) {
203        __ LoadP(ip, source_operand);
204        __ StoreP(ip, destination_operand);
205      } else {
206        __ LoadP(kSavedValueRegister, source_operand);
207        __ StoreP(kSavedValueRegister, destination_operand);
208      }
209    }
210
211  } else if (source->IsConstantOperand()) {
212    LConstantOperand* constant_source = LConstantOperand::cast(source);
213    if (destination->IsRegister()) {
214      Register dst = cgen_->ToRegister(destination);
215      if (cgen_->IsInteger32(constant_source)) {
216        cgen_->EmitLoadIntegerConstant(constant_source, dst);
217      } else {
218        __ Move(dst, cgen_->ToHandle(constant_source));
219      }
220    } else if (destination->IsDoubleRegister()) {
221      DoubleRegister result = cgen_->ToDoubleRegister(destination);
222      double v = cgen_->ToDouble(constant_source);
223      __ LoadDoubleLiteral(result, v, ip);
224    } else {
225      DCHECK(destination->IsStackSlot());
226      DCHECK(!in_cycle_);  // Constant moves happen after all cycles are gone.
227      if (cgen_->IsInteger32(constant_source)) {
228        cgen_->EmitLoadIntegerConstant(constant_source, kSavedValueRegister);
229      } else {
230        __ Move(kSavedValueRegister, cgen_->ToHandle(constant_source));
231      }
232      __ StoreP(kSavedValueRegister, cgen_->ToMemOperand(destination));
233    }
234
235  } else if (source->IsDoubleRegister()) {
236    DoubleRegister source_register = cgen_->ToDoubleRegister(source);
237    if (destination->IsDoubleRegister()) {
238      __ ldr(cgen_->ToDoubleRegister(destination), source_register);
239    } else {
240      DCHECK(destination->IsDoubleStackSlot());
241      __ StoreDouble(source_register, cgen_->ToMemOperand(destination));
242    }
243
244  } else if (source->IsDoubleStackSlot()) {
245    MemOperand source_operand = cgen_->ToMemOperand(source);
246    if (destination->IsDoubleRegister()) {
247      __ LoadDouble(cgen_->ToDoubleRegister(destination), source_operand);
248    } else {
249      DCHECK(destination->IsDoubleStackSlot());
250      MemOperand destination_operand = cgen_->ToMemOperand(destination);
251      if (in_cycle_) {
252// kSavedDoubleValueRegister was used to break the cycle,
253// but kSavedValueRegister is free.
254#if V8_TARGET_ARCH_S390X
255        __ lg(kSavedValueRegister, source_operand);
256        __ stg(kSavedValueRegister, destination_operand);
257#else
258        MemOperand source_high_operand = cgen_->ToHighMemOperand(source);
259        MemOperand destination_high_operand =
260            cgen_->ToHighMemOperand(destination);
261        __ LoadlW(kSavedValueRegister, source_operand);
262        __ StoreW(kSavedValueRegister, destination_operand);
263        __ LoadlW(kSavedValueRegister, source_high_operand);
264        __ StoreW(kSavedValueRegister, destination_high_operand);
265#endif
266      } else {
267        __ LoadDouble(kScratchDoubleReg, source_operand);
268        __ StoreDouble(kScratchDoubleReg, destination_operand);
269      }
270    }
271  } else {
272    UNREACHABLE();
273  }
274
275  moves_[index].Eliminate();
276}
277
278#undef __
279}  // namespace internal
280}  // namespace v8
281