gen_loadstore.cc revision 30adc7383a74eb3cb6db3bf42cea3a5595055ce1
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "dex/compiler_ir.h"
18#include "dex/compiler_internals.h"
19#include "dex/quick/mir_to_lir-inl.h"
20#include "invoke_type.h"
21
22namespace art {
23
24/* This file contains target-independent codegen and support. */
25
26/*
27 * Load an immediate value into a fixed or temp register.  Target
28 * register is clobbered, and marked in_use.
29 */
30LIR* Mir2Lir::LoadConstant(RegStorage r_dest, int value) {
31  if (IsTemp(r_dest)) {
32    Clobber(r_dest);
33    MarkInUse(r_dest);
34  }
35  return LoadConstantNoClobber(r_dest, value);
36}
37
38/*
39 * Temporary workaround for Issue 7250540.  If we're loading a constant zero into a
40 * promoted floating point register, also copy a zero into the int/ref identity of
41 * that sreg.
42 */
43void Mir2Lir::Workaround7250540(RegLocation rl_dest, RegStorage zero_reg) {
44  if (rl_dest.fp) {
45    int pmap_index = SRegToPMap(rl_dest.s_reg_low);
46    if (promotion_map_[pmap_index].fp_location == kLocPhysReg) {
47      // Now, determine if this vreg is ever used as a reference.  If not, we're done.
48      bool used_as_reference = false;
49      int base_vreg = mir_graph_->SRegToVReg(rl_dest.s_reg_low);
50      for (int i = 0; !used_as_reference && (i < mir_graph_->GetNumSSARegs()); i++) {
51        if (mir_graph_->SRegToVReg(mir_graph_->reg_location_[i].s_reg_low) == base_vreg) {
52          used_as_reference |= mir_graph_->reg_location_[i].ref;
53        }
54      }
55      if (!used_as_reference) {
56        return;
57      }
58      RegStorage temp_reg = zero_reg;
59      if (!temp_reg.Valid()) {
60        temp_reg = AllocTemp();
61        LoadConstant(temp_reg, 0);
62      }
63      if (promotion_map_[pmap_index].core_location == kLocPhysReg) {
64        // Promoted - just copy in a zero
65        OpRegCopy(RegStorage::Solo32(promotion_map_[pmap_index].core_reg), temp_reg);
66      } else {
67        // Lives in the frame, need to store.
68        StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), temp_reg, k32);
69      }
70      if (!zero_reg.Valid()) {
71        FreeTemp(temp_reg);
72      }
73    }
74  }
75}
76
77/*
78 * Load a Dalvik register into a physical register.  Take care when
79 * using this routine, as it doesn't perform any bookkeeping regarding
80 * register liveness.  That is the responsibility of the caller.
81 */
82void Mir2Lir::LoadValueDirect(RegLocation rl_src, RegStorage r_dest) {
83  rl_src = UpdateLoc(rl_src);
84  if (rl_src.location == kLocPhysReg) {
85    OpRegCopy(r_dest, rl_src.reg);
86  } else if (IsInexpensiveConstant(rl_src)) {
87    // On 64-bit targets, will sign extend.  Make sure constant reference is always NULL.
88    DCHECK(!rl_src.ref || (mir_graph_->ConstantValue(rl_src) == 0));
89    LoadConstantNoClobber(r_dest, mir_graph_->ConstantValue(rl_src));
90  } else {
91    DCHECK((rl_src.location == kLocDalvikFrame) ||
92           (rl_src.location == kLocCompilerTemp));
93    if (rl_src.ref) {
94      LoadRefDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest);
95    } else {
96      Load32Disp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest);
97    }
98  }
99}
100
101/*
102 * Similar to LoadValueDirect, but clobbers and allocates the target
103 * register.  Should be used when loading to a fixed register (for example,
104 * loading arguments to an out of line call.
105 */
106void Mir2Lir::LoadValueDirectFixed(RegLocation rl_src, RegStorage r_dest) {
107  Clobber(r_dest);
108  MarkInUse(r_dest);
109  LoadValueDirect(rl_src, r_dest);
110}
111
112/*
113 * Load a Dalvik register pair into a physical register[s].  Take care when
114 * using this routine, as it doesn't perform any bookkeeping regarding
115 * register liveness.  That is the responsibility of the caller.
116 */
117void Mir2Lir::LoadValueDirectWide(RegLocation rl_src, RegStorage r_dest) {
118  rl_src = UpdateLocWide(rl_src);
119  if (rl_src.location == kLocPhysReg) {
120    OpRegCopyWide(r_dest, rl_src.reg);
121  } else if (IsInexpensiveConstant(rl_src)) {
122    LoadConstantWide(r_dest, mir_graph_->ConstantValueWide(rl_src));
123  } else {
124    DCHECK((rl_src.location == kLocDalvikFrame) ||
125           (rl_src.location == kLocCompilerTemp));
126    LoadBaseDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest, k64);
127  }
128}
129
130/*
131 * Similar to LoadValueDirect, but clobbers and allocates the target
132 * registers.  Should be used when loading to a fixed registers (for example,
133 * loading arguments to an out of line call.
134 */
135void Mir2Lir::LoadValueDirectWideFixed(RegLocation rl_src, RegStorage r_dest) {
136  Clobber(r_dest);
137  MarkInUse(r_dest);
138  LoadValueDirectWide(rl_src, r_dest);
139}
140
141RegLocation Mir2Lir::LoadValue(RegLocation rl_src, RegisterClass op_kind) {
142  rl_src = EvalLoc(rl_src, op_kind, false);
143  if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) {
144    LoadValueDirect(rl_src, rl_src.reg);
145    rl_src.location = kLocPhysReg;
146    MarkLive(rl_src);
147  }
148  return rl_src;
149}
150
151void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src) {
152  /*
153   * Sanity checking - should never try to store to the same
154   * ssa name during the compilation of a single instruction
155   * without an intervening ClobberSReg().
156   */
157  if (kIsDebugBuild) {
158    DCHECK((live_sreg_ == INVALID_SREG) ||
159           (rl_dest.s_reg_low != live_sreg_));
160    live_sreg_ = rl_dest.s_reg_low;
161  }
162  LIR* def_start;
163  LIR* def_end;
164  DCHECK(!rl_dest.wide);
165  DCHECK(!rl_src.wide);
166  rl_src = UpdateLoc(rl_src);
167  rl_dest = UpdateLoc(rl_dest);
168  if (rl_src.location == kLocPhysReg) {
169    if (IsLive(rl_src.reg) ||
170      IsPromoted(rl_src.reg) ||
171      (rl_dest.location == kLocPhysReg)) {
172      // Src is live/promoted or Dest has assigned reg.
173      rl_dest = EvalLoc(rl_dest, kAnyReg, false);
174      OpRegCopy(rl_dest.reg, rl_src.reg);
175    } else {
176      // Just re-assign the registers.  Dest gets Src's regs
177      rl_dest.reg = rl_src.reg;
178      Clobber(rl_src.reg);
179    }
180  } else {
181    // Load Src either into promoted Dest or temps allocated for Dest
182    rl_dest = EvalLoc(rl_dest, kAnyReg, false);
183    LoadValueDirect(rl_src, rl_dest.reg);
184  }
185
186  // Dest is now live and dirty (until/if we flush it to home location)
187  MarkLive(rl_dest);
188  MarkDirty(rl_dest);
189
190
191  ResetDefLoc(rl_dest);
192  if (IsDirty(rl_dest.reg) && LiveOut(rl_dest.s_reg_low)) {
193    def_start = last_lir_insn_;
194    Store32Disp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg);
195    MarkClean(rl_dest);
196    def_end = last_lir_insn_;
197    if (!rl_dest.ref) {
198      // Exclude references from store elimination
199      MarkDef(rl_dest, def_start, def_end);
200    }
201  }
202}
203
204RegLocation Mir2Lir::LoadValueWide(RegLocation rl_src, RegisterClass op_kind) {
205  DCHECK(rl_src.wide);
206  rl_src = EvalLoc(rl_src, op_kind, false);
207  if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) {
208    LoadValueDirectWide(rl_src, rl_src.reg);
209    rl_src.location = kLocPhysReg;
210    MarkLive(rl_src);
211  }
212  return rl_src;
213}
214
215void Mir2Lir::StoreValueWide(RegLocation rl_dest, RegLocation rl_src) {
216  /*
217   * Sanity checking - should never try to store to the same
218   * ssa name during the compilation of a single instruction
219   * without an intervening ClobberSReg().
220   */
221  if (kIsDebugBuild) {
222    DCHECK((live_sreg_ == INVALID_SREG) ||
223           (rl_dest.s_reg_low != live_sreg_));
224    live_sreg_ = rl_dest.s_reg_low;
225  }
226  LIR* def_start;
227  LIR* def_end;
228  DCHECK(rl_dest.wide);
229  DCHECK(rl_src.wide);
230  rl_src = UpdateLocWide(rl_src);
231  rl_dest = UpdateLocWide(rl_dest);
232  if (rl_src.location == kLocPhysReg) {
233    if (IsLive(rl_src.reg) ||
234        IsPromoted(rl_src.reg) ||
235        (rl_dest.location == kLocPhysReg)) {
236      /*
237       * If src reg[s] are tied to the original Dalvik vreg via liveness or promotion, we
238       * can't repurpose them.  Similarly, if the dest reg[s] are tied to Dalvik vregs via
239       * promotion, we can't just re-assign.  In these cases, we have to copy.
240       */
241      rl_dest = EvalLoc(rl_dest, kAnyReg, false);
242      OpRegCopyWide(rl_dest.reg, rl_src.reg);
243    } else {
244      // Just re-assign the registers.  Dest gets Src's regs
245      rl_dest.reg = rl_src.reg;
246      Clobber(rl_src.reg);
247    }
248  } else {
249    // Load Src either into promoted Dest or temps allocated for Dest
250    rl_dest = EvalLoc(rl_dest, kAnyReg, false);
251    LoadValueDirectWide(rl_src, rl_dest.reg);
252  }
253
254  // Dest is now live and dirty (until/if we flush it to home location)
255  MarkLive(rl_dest);
256  MarkWide(rl_dest.reg);
257  MarkDirty(rl_dest);
258
259  ResetDefLocWide(rl_dest);
260  if (IsDirty(rl_dest.reg) && (LiveOut(rl_dest.s_reg_low) ||
261      LiveOut(GetSRegHi(rl_dest.s_reg_low)))) {
262    def_start = last_lir_insn_;
263    DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1),
264              mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low)));
265    StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, k64);
266    MarkClean(rl_dest);
267    def_end = last_lir_insn_;
268    MarkDefWide(rl_dest, def_start, def_end);
269  }
270}
271
272void Mir2Lir::StoreFinalValue(RegLocation rl_dest, RegLocation rl_src) {
273  DCHECK_EQ(rl_src.location, kLocPhysReg);
274
275  if (rl_dest.location == kLocPhysReg) {
276    OpRegCopy(rl_dest.reg, rl_src.reg);
277  } else {
278    // Just re-assign the register.  Dest gets Src's reg.
279    rl_dest.location = kLocPhysReg;
280    rl_dest.reg = rl_src.reg;
281    Clobber(rl_src.reg);
282  }
283
284  // Dest is now live and dirty (until/if we flush it to home location)
285  MarkLive(rl_dest);
286  MarkDirty(rl_dest);
287
288
289  ResetDefLoc(rl_dest);
290  if (IsDirty(rl_dest.reg) && LiveOut(rl_dest.s_reg_low)) {
291    LIR *def_start = last_lir_insn_;
292    Store32Disp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg);
293    MarkClean(rl_dest);
294    LIR *def_end = last_lir_insn_;
295    if (!rl_dest.ref) {
296      // Exclude references from store elimination
297      MarkDef(rl_dest, def_start, def_end);
298    }
299  }
300}
301
302void Mir2Lir::StoreFinalValueWide(RegLocation rl_dest, RegLocation rl_src) {
303  DCHECK(rl_dest.wide);
304  DCHECK(rl_src.wide);
305  DCHECK_EQ(rl_src.location, kLocPhysReg);
306
307  if (rl_dest.location == kLocPhysReg) {
308    OpRegCopyWide(rl_dest.reg, rl_src.reg);
309  } else {
310    // Just re-assign the registers.  Dest gets Src's regs.
311    rl_dest.location = kLocPhysReg;
312    rl_dest.reg = rl_src.reg;
313    Clobber(rl_src.reg);
314  }
315
316  // Dest is now live and dirty (until/if we flush it to home location).
317  MarkLive(rl_dest);
318  MarkWide(rl_dest.reg);
319  MarkDirty(rl_dest);
320
321  ResetDefLocWide(rl_dest);
322  if (IsDirty(rl_dest.reg) && (LiveOut(rl_dest.s_reg_low) ||
323      LiveOut(GetSRegHi(rl_dest.s_reg_low)))) {
324    LIR *def_start = last_lir_insn_;
325    DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1),
326              mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low)));
327    StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, k64);
328    MarkClean(rl_dest);
329    LIR *def_end = last_lir_insn_;
330    MarkDefWide(rl_dest, def_start, def_end);
331  }
332}
333
334/* Utilities to load the current Method* */
335void Mir2Lir::LoadCurrMethodDirect(RegStorage r_tgt) {
336  LoadValueDirectFixed(mir_graph_->GetMethodLoc(), r_tgt);
337}
338
339RegLocation Mir2Lir::LoadCurrMethod() {
340  return LoadValue(mir_graph_->GetMethodLoc(), kCoreReg);
341}
342
343RegLocation Mir2Lir::ForceTemp(RegLocation loc) {
344  DCHECK(!loc.wide);
345  DCHECK(loc.location == kLocPhysReg);
346  DCHECK(!loc.reg.IsFloat());
347  if (IsTemp(loc.reg)) {
348    Clobber(loc.reg);
349  } else {
350    RegStorage temp_low = AllocTemp();
351    OpRegCopy(temp_low, loc.reg);
352    loc.reg = temp_low;
353  }
354
355  // Ensure that this doesn't represent the original SR any more.
356  loc.s_reg_low = INVALID_SREG;
357  return loc;
358}
359
360// FIXME: will need an update for 64-bit core regs.
361RegLocation Mir2Lir::ForceTempWide(RegLocation loc) {
362  DCHECK(loc.wide);
363  DCHECK(loc.location == kLocPhysReg);
364  DCHECK(!loc.reg.IsFloat());
365  if (IsTemp(loc.reg.GetLow())) {
366    Clobber(loc.reg.GetLow());
367  } else {
368    RegStorage temp_low = AllocTemp();
369    OpRegCopy(temp_low, loc.reg.GetLow());
370    loc.reg.SetLowReg(temp_low.GetReg());
371  }
372  if (IsTemp(loc.reg.GetHigh())) {
373    Clobber(loc.reg.GetHigh());
374  } else {
375    RegStorage temp_high = AllocTemp();
376    OpRegCopy(temp_high, loc.reg.GetHigh());
377    loc.reg.SetHighReg(temp_high.GetReg());
378  }
379
380  // Ensure that this doesn't represent the original SR any more.
381  loc.s_reg_low = INVALID_SREG;
382  return loc;
383}
384
385}  // namespace art
386