gen_loadstore.cc revision cbd18b7f4da7677ca48c48c23ed9a7de3a8b0354
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, kWord);
69      }
70      if (!zero_reg.Valid()) {
71        FreeTemp(temp_reg);
72      }
73    }
74  }
75}
76
77/* Load a word at base + displacement.  Displacement must be word multiple */
78LIR* Mir2Lir::LoadWordDisp(RegStorage r_base, int displacement, RegStorage r_dest) {
79  return LoadBaseDisp(r_base, displacement, r_dest, kWord, INVALID_SREG);
80}
81
82LIR* Mir2Lir::StoreWordDisp(RegStorage r_base, int displacement, RegStorage r_src) {
83  return StoreBaseDisp(r_base, displacement, r_src, kWord);
84}
85
86/*
87 * Load a Dalvik register into a physical register.  Take care when
88 * using this routine, as it doesn't perform any bookkeeping regarding
89 * register liveness.  That is the responsibility of the caller.
90 */
91void Mir2Lir::LoadValueDirect(RegLocation rl_src, RegStorage r_dest) {
92  rl_src = UpdateLoc(rl_src);
93  if (rl_src.location == kLocPhysReg) {
94    OpRegCopy(r_dest, rl_src.reg);
95  } else if (IsInexpensiveConstant(rl_src)) {
96    LoadConstantNoClobber(r_dest, mir_graph_->ConstantValue(rl_src));
97  } else {
98    DCHECK((rl_src.location == kLocDalvikFrame) ||
99           (rl_src.location == kLocCompilerTemp));
100    LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest);
101  }
102}
103
104/*
105 * Similar to LoadValueDirect, but clobbers and allocates the target
106 * register.  Should be used when loading to a fixed register (for example,
107 * loading arguments to an out of line call.
108 */
109void Mir2Lir::LoadValueDirectFixed(RegLocation rl_src, RegStorage r_dest) {
110  Clobber(r_dest);
111  MarkInUse(r_dest);
112  LoadValueDirect(rl_src, r_dest);
113}
114
115/*
116 * Load a Dalvik register pair into a physical register[s].  Take care when
117 * using this routine, as it doesn't perform any bookkeeping regarding
118 * register liveness.  That is the responsibility of the caller.
119 */
120void Mir2Lir::LoadValueDirectWide(RegLocation rl_src, RegStorage r_dest) {
121  rl_src = UpdateLocWide(rl_src);
122  if (rl_src.location == kLocPhysReg) {
123    OpRegCopyWide(r_dest, rl_src.reg);
124  } else if (IsInexpensiveConstant(rl_src)) {
125    LoadConstantWide(r_dest, mir_graph_->ConstantValueWide(rl_src));
126  } else {
127    DCHECK((rl_src.location == kLocDalvikFrame) ||
128           (rl_src.location == kLocCompilerTemp));
129    LoadBaseDispWide(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest, INVALID_SREG);
130  }
131}
132
133/*
134 * Similar to LoadValueDirect, but clobbers and allocates the target
135 * registers.  Should be used when loading to a fixed registers (for example,
136 * loading arguments to an out of line call.
137 */
138void Mir2Lir::LoadValueDirectWideFixed(RegLocation rl_src, RegStorage r_dest) {
139  Clobber(r_dest);
140  MarkInUse(r_dest);
141  LoadValueDirectWide(rl_src, r_dest);
142}
143
144RegLocation Mir2Lir::LoadValue(RegLocation rl_src, RegisterClass op_kind) {
145  rl_src = EvalLoc(rl_src, op_kind, false);
146  if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) {
147    LoadValueDirect(rl_src, rl_src.reg);
148    rl_src.location = kLocPhysReg;
149    MarkLive(rl_src.reg, rl_src.s_reg_low);
150  }
151  return rl_src;
152}
153
154void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src) {
155  /*
156   * Sanity checking - should never try to store to the same
157   * ssa name during the compilation of a single instruction
158   * without an intervening ClobberSReg().
159   */
160  if (kIsDebugBuild) {
161    DCHECK((live_sreg_ == INVALID_SREG) ||
162           (rl_dest.s_reg_low != live_sreg_));
163    live_sreg_ = rl_dest.s_reg_low;
164  }
165  LIR* def_start;
166  LIR* def_end;
167  DCHECK(!rl_dest.wide);
168  DCHECK(!rl_src.wide);
169  rl_src = UpdateLoc(rl_src);
170  rl_dest = UpdateLoc(rl_dest);
171  if (rl_src.location == kLocPhysReg) {
172    if (IsLive(rl_src.reg) ||
173      IsPromoted(rl_src.reg) ||
174      (rl_dest.location == kLocPhysReg)) {
175      // Src is live/promoted or Dest has assigned reg.
176      rl_dest = EvalLoc(rl_dest, kAnyReg, false);
177      OpRegCopy(rl_dest.reg, rl_src.reg);
178    } else {
179      // Just re-assign the registers.  Dest gets Src's regs
180      rl_dest.reg = rl_src.reg;
181      Clobber(rl_src.reg);
182    }
183  } else {
184    // Load Src either into promoted Dest or temps allocated for Dest
185    rl_dest = EvalLoc(rl_dest, kAnyReg, false);
186    LoadValueDirect(rl_src, rl_dest.reg);
187  }
188
189  // Dest is now live and dirty (until/if we flush it to home location)
190  MarkLive(rl_dest.reg, rl_dest.s_reg_low);
191  MarkDirty(rl_dest);
192
193
194  ResetDefLoc(rl_dest);
195  if (IsDirty(rl_dest.reg) && oat_live_out(rl_dest.s_reg_low)) {
196    def_start = last_lir_insn_;
197    StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kWord);
198    MarkClean(rl_dest);
199    def_end = last_lir_insn_;
200    if (!rl_dest.ref) {
201      // Exclude references from store elimination
202      MarkDef(rl_dest, def_start, def_end);
203    }
204  }
205}
206
207RegLocation Mir2Lir::LoadValueWide(RegLocation rl_src, RegisterClass op_kind) {
208  DCHECK(rl_src.wide);
209  rl_src = EvalLoc(rl_src, op_kind, false);
210  if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) {
211    LoadValueDirectWide(rl_src, rl_src.reg);
212    rl_src.location = kLocPhysReg;
213    MarkLive(rl_src.reg.GetLow(), rl_src.s_reg_low);
214    if (rl_src.reg.GetLowReg() != rl_src.reg.GetHighReg()) {
215      MarkLive(rl_src.reg.GetHigh(), GetSRegHi(rl_src.s_reg_low));
216    } else {
217      // This must be an x86 vector register value.
218      DCHECK(IsFpReg(rl_src.reg) && (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64));
219    }
220  }
221  return rl_src;
222}
223
224void Mir2Lir::StoreValueWide(RegLocation rl_dest, RegLocation rl_src) {
225  /*
226   * Sanity checking - should never try to store to the same
227   * ssa name during the compilation of a single instruction
228   * without an intervening ClobberSReg().
229   */
230  if (kIsDebugBuild) {
231    DCHECK((live_sreg_ == INVALID_SREG) ||
232           (rl_dest.s_reg_low != live_sreg_));
233    live_sreg_ = rl_dest.s_reg_low;
234  }
235  LIR* def_start;
236  LIR* def_end;
237  DCHECK(rl_dest.wide);
238  DCHECK(rl_src.wide);
239  rl_src = UpdateLocWide(rl_src);
240  rl_dest = UpdateLocWide(rl_dest);
241  if (rl_src.location == kLocPhysReg) {
242    if (IsLive(rl_src.reg) ||
243        IsPromoted(rl_src.reg) ||
244        (rl_dest.location == kLocPhysReg)) {
245      // Src is live or promoted or Dest has assigned reg.
246      rl_dest = EvalLoc(rl_dest, kAnyReg, false);
247      OpRegCopyWide(rl_dest.reg, rl_src.reg);
248    } else {
249      // Just re-assign the registers.  Dest gets Src's regs
250      rl_dest.reg = rl_src.reg;
251      Clobber(rl_src.reg);
252    }
253  } else {
254    // Load Src either into promoted Dest or temps allocated for Dest
255    rl_dest = EvalLoc(rl_dest, kAnyReg, false);
256    LoadValueDirectWide(rl_src, rl_dest.reg);
257  }
258
259  // Dest is now live and dirty (until/if we flush it to home location)
260  MarkLive(rl_dest.reg.GetLow(), rl_dest.s_reg_low);
261
262  // Does this wide value live in two registers (or one vector one)?
263  // FIXME: wide reg update.
264  if (rl_dest.reg.GetLowReg() != rl_dest.reg.GetHighReg()) {
265    MarkLive(rl_dest.reg.GetHigh(), GetSRegHi(rl_dest.s_reg_low));
266    MarkDirty(rl_dest);
267    MarkPair(rl_dest.reg.GetLowReg(), rl_dest.reg.GetHighReg());
268  } else {
269    // This must be an x86 vector register value,
270    DCHECK(IsFpReg(rl_dest.reg) && (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64));
271    MarkDirty(rl_dest);
272  }
273
274
275  ResetDefLocWide(rl_dest);
276  if (IsDirty(rl_dest.reg) && (oat_live_out(rl_dest.s_reg_low) ||
277      oat_live_out(GetSRegHi(rl_dest.s_reg_low)))) {
278    def_start = last_lir_insn_;
279    DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1),
280              mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low)));
281    StoreBaseDispWide(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg);
282    MarkClean(rl_dest);
283    def_end = last_lir_insn_;
284    MarkDefWide(rl_dest, def_start, def_end);
285  }
286}
287
288void Mir2Lir::StoreFinalValue(RegLocation rl_dest, RegLocation rl_src) {
289  DCHECK_EQ(rl_src.location, kLocPhysReg);
290
291  if (rl_dest.location == kLocPhysReg) {
292    OpRegCopy(rl_dest.reg, rl_src.reg);
293  } else {
294    // Just re-assign the register.  Dest gets Src's reg.
295    rl_dest.location = kLocPhysReg;
296    rl_dest.reg = rl_src.reg;
297    Clobber(rl_src.reg);
298  }
299
300  // Dest is now live and dirty (until/if we flush it to home location)
301  MarkLive(rl_dest.reg, rl_dest.s_reg_low);
302  MarkDirty(rl_dest);
303
304
305  ResetDefLoc(rl_dest);
306  if (IsDirty(rl_dest.reg) &&
307      oat_live_out(rl_dest.s_reg_low)) {
308    LIR *def_start = last_lir_insn_;
309    StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kWord);
310    MarkClean(rl_dest);
311    LIR *def_end = last_lir_insn_;
312    if (!rl_dest.ref) {
313      // Exclude references from store elimination
314      MarkDef(rl_dest, def_start, def_end);
315    }
316  }
317}
318
319void Mir2Lir::StoreFinalValueWide(RegLocation rl_dest, RegLocation rl_src) {
320  DCHECK_EQ(IsFpReg(rl_src.reg.GetLowReg()), IsFpReg(rl_src.reg.GetHighReg()));
321  DCHECK(rl_dest.wide);
322  DCHECK(rl_src.wide);
323  DCHECK_EQ(rl_src.location, kLocPhysReg);
324
325  if (rl_dest.location == kLocPhysReg) {
326    OpRegCopyWide(rl_dest.reg, rl_src.reg);
327  } else {
328    // Just re-assign the registers.  Dest gets Src's regs.
329    rl_dest.location = kLocPhysReg;
330    rl_dest.reg = rl_src.reg;
331    Clobber(rl_src.reg.GetLowReg());
332    Clobber(rl_src.reg.GetHighReg());
333  }
334
335  // Dest is now live and dirty (until/if we flush it to home location).
336  MarkLive(rl_dest.reg.GetLow(), rl_dest.s_reg_low);
337
338  // Does this wide value live in two registers (or one vector one)?
339  // FIXME: wide reg.
340  if (rl_dest.reg.GetLowReg() != rl_dest.reg.GetHighReg()) {
341    MarkLive(rl_dest.reg.GetHigh(), GetSRegHi(rl_dest.s_reg_low));
342    MarkDirty(rl_dest);
343    MarkPair(rl_dest.reg.GetLowReg(), rl_dest.reg.GetHighReg());
344  } else {
345    // This must be an x86 vector register value,
346    DCHECK(IsFpReg(rl_dest.reg) && (cu_->instruction_set == kX86 || cu_->instruction_set == kX86_64));
347    MarkDirty(rl_dest);
348  }
349
350  ResetDefLocWide(rl_dest);
351  if (IsDirty(rl_dest.reg) && (oat_live_out(rl_dest.s_reg_low) ||
352      oat_live_out(GetSRegHi(rl_dest.s_reg_low)))) {
353    LIR *def_start = last_lir_insn_;
354    DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1),
355              mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low)));
356    StoreBaseDispWide(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg);
357    MarkClean(rl_dest);
358    LIR *def_end = last_lir_insn_;
359    MarkDefWide(rl_dest, def_start, def_end);
360  }
361}
362
363/* Utilities to load the current Method* */
364void Mir2Lir::LoadCurrMethodDirect(RegStorage r_tgt) {
365  LoadValueDirectFixed(mir_graph_->GetMethodLoc(), r_tgt);
366}
367
368RegLocation Mir2Lir::LoadCurrMethod() {
369  return LoadValue(mir_graph_->GetMethodLoc(), kCoreReg);
370}
371
372RegLocation Mir2Lir::ForceTemp(RegLocation loc) {
373  DCHECK(!loc.wide);
374  DCHECK(loc.location == kLocPhysReg);
375  DCHECK(!IsFpReg(loc.reg));
376  if (IsTemp(loc.reg)) {
377    Clobber(loc.reg);
378  } else {
379    RegStorage temp_low = AllocTemp();
380    OpRegCopy(temp_low, loc.reg);
381    loc.reg = temp_low;
382  }
383
384  // Ensure that this doesn't represent the original SR any more.
385  loc.s_reg_low = INVALID_SREG;
386  return loc;
387}
388
389// FIXME: wide regs.
390RegLocation Mir2Lir::ForceTempWide(RegLocation loc) {
391  DCHECK(loc.wide);
392  DCHECK(loc.location == kLocPhysReg);
393  DCHECK(!IsFpReg(loc.reg.GetLowReg()));
394  DCHECK(!IsFpReg(loc.reg.GetHighReg()));
395  if (IsTemp(loc.reg.GetLowReg())) {
396    Clobber(loc.reg.GetLowReg());
397  } else {
398    RegStorage temp_low = AllocTemp();
399    OpRegCopy(temp_low, loc.reg.GetLow());
400    loc.reg.SetLowReg(temp_low.GetReg());
401  }
402  if (IsTemp(loc.reg.GetHighReg())) {
403    Clobber(loc.reg.GetHighReg());
404  } else {
405    RegStorage temp_high = AllocTemp();
406    OpRegCopy(temp_high, loc.reg.GetHigh());
407    loc.reg.SetHighReg(temp_high.GetReg());
408  }
409
410  // Ensure that this doesn't represent the original SR any more.
411  loc.s_reg_low = INVALID_SREG;
412  return loc;
413}
414
415}  // namespace art
416