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(int 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, int 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      int temp_reg = zero_reg;
59      if (temp_reg == INVALID_REG) {
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(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 == INVALID_REG) {
71        FreeTemp(temp_reg);
72      }
73    }
74  }
75}
76
77/* Load a word at base + displacement.  Displacement must be word multiple */
78LIR* Mir2Lir::LoadWordDisp(int rBase, int displacement, int r_dest) {
79  return LoadBaseDisp(rBase, displacement, r_dest, kWord,
80                      INVALID_SREG);
81}
82
83LIR* Mir2Lir::StoreWordDisp(int rBase, int displacement, int r_src) {
84  return StoreBaseDisp(rBase, displacement, r_src, kWord);
85}
86
87/*
88 * Load a Dalvik register into a physical register.  Take care when
89 * using this routine, as it doesn't perform any bookkeeping regarding
90 * register liveness.  That is the responsibility of the caller.
91 */
92void Mir2Lir::LoadValueDirect(RegLocation rl_src, int r_dest) {
93  rl_src = UpdateLoc(rl_src);
94  if (rl_src.location == kLocPhysReg) {
95    OpRegCopy(r_dest, rl_src.low_reg);
96  } else if (IsInexpensiveConstant(rl_src)) {
97    LoadConstantNoClobber(r_dest, mir_graph_->ConstantValue(rl_src));
98  } else {
99    DCHECK((rl_src.location == kLocDalvikFrame) ||
100           (rl_src.location == kLocCompilerTemp));
101    LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest);
102  }
103}
104
105/*
106 * Similar to LoadValueDirect, but clobbers and allocates the target
107 * register.  Should be used when loading to a fixed register (for example,
108 * loading arguments to an out of line call.
109 */
110void Mir2Lir::LoadValueDirectFixed(RegLocation rl_src, int r_dest) {
111  Clobber(r_dest);
112  MarkInUse(r_dest);
113  LoadValueDirect(rl_src, r_dest);
114}
115
116/*
117 * Load a Dalvik register pair into a physical register[s].  Take care when
118 * using this routine, as it doesn't perform any bookkeeping regarding
119 * register liveness.  That is the responsibility of the caller.
120 */
121void Mir2Lir::LoadValueDirectWide(RegLocation rl_src, int reg_lo,
122             int reg_hi) {
123  rl_src = UpdateLocWide(rl_src);
124  if (rl_src.location == kLocPhysReg) {
125    OpRegCopyWide(reg_lo, reg_hi, rl_src.low_reg, rl_src.high_reg);
126  } else if (IsInexpensiveConstant(rl_src)) {
127    LoadConstantWide(reg_lo, reg_hi, mir_graph_->ConstantValueWide(rl_src));
128  } else {
129    DCHECK((rl_src.location == kLocDalvikFrame) ||
130           (rl_src.location == kLocCompilerTemp));
131    LoadBaseDispWide(TargetReg(kSp), SRegOffset(rl_src.s_reg_low),
132                     reg_lo, reg_hi, INVALID_SREG);
133  }
134}
135
136/*
137 * Similar to LoadValueDirect, but clobbers and allocates the target
138 * registers.  Should be used when loading to a fixed registers (for example,
139 * loading arguments to an out of line call.
140 */
141void Mir2Lir::LoadValueDirectWideFixed(RegLocation rl_src, int reg_lo,
142                                       int reg_hi) {
143  Clobber(reg_lo);
144  Clobber(reg_hi);
145  MarkInUse(reg_lo);
146  MarkInUse(reg_hi);
147  LoadValueDirectWide(rl_src, reg_lo, reg_hi);
148}
149
150RegLocation Mir2Lir::LoadValue(RegLocation rl_src, RegisterClass op_kind) {
151  rl_src = EvalLoc(rl_src, op_kind, false);
152  if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) {
153    LoadValueDirect(rl_src, rl_src.low_reg);
154    rl_src.location = kLocPhysReg;
155    MarkLive(rl_src.low_reg, rl_src.s_reg_low);
156  }
157  return rl_src;
158}
159
160void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src) {
161  /*
162   * Sanity checking - should never try to store to the same
163   * ssa name during the compilation of a single instruction
164   * without an intervening ClobberSReg().
165   */
166  if (kIsDebugBuild) {
167    DCHECK((live_sreg_ == INVALID_SREG) ||
168           (rl_dest.s_reg_low != live_sreg_));
169    live_sreg_ = rl_dest.s_reg_low;
170  }
171  LIR* def_start;
172  LIR* def_end;
173  DCHECK(!rl_dest.wide);
174  DCHECK(!rl_src.wide);
175  rl_src = UpdateLoc(rl_src);
176  rl_dest = UpdateLoc(rl_dest);
177  if (rl_src.location == kLocPhysReg) {
178    if (IsLive(rl_src.low_reg) ||
179      IsPromoted(rl_src.low_reg) ||
180      (rl_dest.location == kLocPhysReg)) {
181      // Src is live/promoted or Dest has assigned reg.
182      rl_dest = EvalLoc(rl_dest, kAnyReg, false);
183      OpRegCopy(rl_dest.low_reg, rl_src.low_reg);
184    } else {
185      // Just re-assign the registers.  Dest gets Src's regs
186      rl_dest.low_reg = rl_src.low_reg;
187      Clobber(rl_src.low_reg);
188    }
189  } else {
190    // Load Src either into promoted Dest or temps allocated for Dest
191    rl_dest = EvalLoc(rl_dest, kAnyReg, false);
192    LoadValueDirect(rl_src, rl_dest.low_reg);
193  }
194
195  // Dest is now live and dirty (until/if we flush it to home location)
196  MarkLive(rl_dest.low_reg, rl_dest.s_reg_low);
197  MarkDirty(rl_dest);
198
199
200  ResetDefLoc(rl_dest);
201  if (IsDirty(rl_dest.low_reg) &&
202      oat_live_out(rl_dest.s_reg_low)) {
203    def_start = last_lir_insn_;
204    StoreBaseDisp(TargetReg(kSp), SRegOffset(rl_dest.s_reg_low),
205                  rl_dest.low_reg, kWord);
206    MarkClean(rl_dest);
207    def_end = last_lir_insn_;
208    if (!rl_dest.ref) {
209      // Exclude references from store elimination
210      MarkDef(rl_dest, def_start, def_end);
211    }
212  }
213}
214
215RegLocation Mir2Lir::LoadValueWide(RegLocation rl_src, RegisterClass op_kind) {
216  DCHECK(rl_src.wide);
217  rl_src = EvalLoc(rl_src, op_kind, false);
218  if (IsInexpensiveConstant(rl_src) || rl_src.location != kLocPhysReg) {
219    LoadValueDirectWide(rl_src, rl_src.low_reg, rl_src.high_reg);
220    rl_src.location = kLocPhysReg;
221    MarkLive(rl_src.low_reg, rl_src.s_reg_low);
222    MarkLive(rl_src.high_reg, GetSRegHi(rl_src.s_reg_low));
223  }
224  return rl_src;
225}
226
227void Mir2Lir::StoreValueWide(RegLocation rl_dest, RegLocation rl_src) {
228  /*
229   * Sanity checking - should never try to store to the same
230   * ssa name during the compilation of a single instruction
231   * without an intervening ClobberSReg().
232   */
233  if (kIsDebugBuild) {
234    DCHECK((live_sreg_ == INVALID_SREG) ||
235           (rl_dest.s_reg_low != live_sreg_));
236    live_sreg_ = rl_dest.s_reg_low;
237  }
238  LIR* def_start;
239  LIR* def_end;
240  DCHECK_EQ(IsFpReg(rl_src.low_reg), IsFpReg(rl_src.high_reg));
241  DCHECK(rl_dest.wide);
242  DCHECK(rl_src.wide);
243  if (rl_src.location == kLocPhysReg) {
244    if (IsLive(rl_src.low_reg) ||
245        IsLive(rl_src.high_reg) ||
246        IsPromoted(rl_src.low_reg) ||
247        IsPromoted(rl_src.high_reg) ||
248        (rl_dest.location == kLocPhysReg)) {
249      // Src is live or promoted or Dest has assigned reg.
250      rl_dest = EvalLoc(rl_dest, kAnyReg, false);
251      OpRegCopyWide(rl_dest.low_reg, rl_dest.high_reg,
252                    rl_src.low_reg, rl_src.high_reg);
253    } else {
254      // Just re-assign the registers.  Dest gets Src's regs
255      rl_dest.low_reg = rl_src.low_reg;
256      rl_dest.high_reg = rl_src.high_reg;
257      Clobber(rl_src.low_reg);
258      Clobber(rl_src.high_reg);
259    }
260  } else {
261    // Load Src either into promoted Dest or temps allocated for Dest
262    rl_dest = EvalLoc(rl_dest, kAnyReg, false);
263    LoadValueDirectWide(rl_src, rl_dest.low_reg, rl_dest.high_reg);
264  }
265
266  // Dest is now live and dirty (until/if we flush it to home location)
267  MarkLive(rl_dest.low_reg, rl_dest.s_reg_low);
268  MarkLive(rl_dest.high_reg, GetSRegHi(rl_dest.s_reg_low));
269  MarkDirty(rl_dest);
270  MarkPair(rl_dest.low_reg, rl_dest.high_reg);
271
272
273  ResetDefLocWide(rl_dest);
274  if ((IsDirty(rl_dest.low_reg) ||
275      IsDirty(rl_dest.high_reg)) &&
276      (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),
282                      rl_dest.low_reg, rl_dest.high_reg);
283    MarkClean(rl_dest);
284    def_end = last_lir_insn_;
285    MarkDefWide(rl_dest, def_start, def_end);
286  }
287}
288
289/* Utilities to load the current Method* */
290void Mir2Lir::LoadCurrMethodDirect(int r_tgt) {
291  LoadValueDirectFixed(mir_graph_->GetMethodLoc(), r_tgt);
292}
293
294RegLocation Mir2Lir::LoadCurrMethod() {
295  return LoadValue(mir_graph_->GetMethodLoc(), kCoreReg);
296}
297
298}  // namespace art
299