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 "mir_to_lir-inl.h"
18 
19 #include "dex/compiler_ir.h"
20 #include "dex/mir_graph.h"
21 #include "invoke_type.h"
22 
23 namespace art {
24 
25 /* This file contains target-independent codegen and support. */
26 
27 /*
28  * Load an immediate value into a fixed or temp register.  Target
29  * register is clobbered, and marked in_use.
30  */
LoadConstant(RegStorage r_dest,int value)31 LIR* Mir2Lir::LoadConstant(RegStorage r_dest, int value) {
32   if (IsTemp(r_dest)) {
33     Clobber(r_dest);
34     MarkInUse(r_dest);
35   }
36   return LoadConstantNoClobber(r_dest, value);
37 }
38 
39 /*
40  * Load a Dalvik register into a physical register.  Take care when
41  * using this routine, as it doesn't perform any bookkeeping regarding
42  * register liveness.  That is the responsibility of the caller.
43  */
LoadValueDirect(RegLocation rl_src,RegStorage r_dest)44 void Mir2Lir::LoadValueDirect(RegLocation rl_src, RegStorage r_dest) {
45   rl_src = rl_src.wide ? UpdateLocWide(rl_src) : UpdateLoc(rl_src);
46   if (rl_src.location == kLocPhysReg) {
47     OpRegCopy(r_dest, rl_src.reg);
48   } else if (IsInexpensiveConstant(rl_src)) {
49     // On 64-bit targets, will sign extend.  Make sure constant reference is always null.
50     DCHECK(!rl_src.ref || (mir_graph_->ConstantValue(rl_src) == 0));
51     LoadConstantNoClobber(r_dest, mir_graph_->ConstantValue(rl_src));
52   } else {
53     DCHECK((rl_src.location == kLocDalvikFrame) ||
54            (rl_src.location == kLocCompilerTemp));
55     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
56     OpSize op_size;
57     if (rl_src.ref) {
58       op_size = kReference;
59     } else if (rl_src.wide) {
60       op_size = k64;
61     } else {
62       op_size = k32;
63     }
64     LoadBaseDisp(TargetPtrReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest, op_size, kNotVolatile);
65   }
66 }
67 
68 /*
69  * Similar to LoadValueDirect, but clobbers and allocates the target
70  * register.  Should be used when loading to a fixed register (for example,
71  * loading arguments to an out of line call.
72  */
LoadValueDirectFixed(RegLocation rl_src,RegStorage r_dest)73 void Mir2Lir::LoadValueDirectFixed(RegLocation rl_src, RegStorage r_dest) {
74   Clobber(r_dest);
75   MarkInUse(r_dest);
76   LoadValueDirect(rl_src, r_dest);
77 }
78 
79 /*
80  * Load a Dalvik register pair into a physical register[s].  Take care when
81  * using this routine, as it doesn't perform any bookkeeping regarding
82  * register liveness.  That is the responsibility of the caller.
83  */
LoadValueDirectWide(RegLocation rl_src,RegStorage r_dest)84 void Mir2Lir::LoadValueDirectWide(RegLocation rl_src, RegStorage r_dest) {
85   rl_src = UpdateLocWide(rl_src);
86   if (rl_src.location == kLocPhysReg) {
87     OpRegCopyWide(r_dest, rl_src.reg);
88   } else if (IsInexpensiveConstant(rl_src)) {
89     LoadConstantWide(r_dest, mir_graph_->ConstantValueWide(rl_src));
90   } else {
91     DCHECK((rl_src.location == kLocDalvikFrame) ||
92            (rl_src.location == kLocCompilerTemp));
93     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
94     LoadBaseDisp(TargetPtrReg(kSp), SRegOffset(rl_src.s_reg_low), r_dest, k64, kNotVolatile);
95   }
96 }
97 
98 /*
99  * Similar to LoadValueDirect, but clobbers and allocates the target
100  * registers.  Should be used when loading to a fixed registers (for example,
101  * loading arguments to an out of line call.
102  */
LoadValueDirectWideFixed(RegLocation rl_src,RegStorage r_dest)103 void Mir2Lir::LoadValueDirectWideFixed(RegLocation rl_src, RegStorage r_dest) {
104   Clobber(r_dest);
105   MarkInUse(r_dest);
106   LoadValueDirectWide(rl_src, r_dest);
107 }
108 
LoadValue(RegLocation rl_src,RegisterClass op_kind)109 RegLocation Mir2Lir::LoadValue(RegLocation rl_src, RegisterClass op_kind) {
110   DCHECK(!rl_src.ref || op_kind == kRefReg);
111   rl_src = UpdateLoc(rl_src);
112   if (rl_src.location == kLocPhysReg) {
113     if (!RegClassMatches(op_kind, rl_src.reg)) {
114       // Wrong register class, realloc, copy and transfer ownership.
115       RegStorage new_reg = AllocTypedTemp(rl_src.fp, op_kind);
116       OpRegCopy(new_reg, rl_src.reg);
117       // Clobber the old regs and free it.
118       Clobber(rl_src.reg);
119       FreeTemp(rl_src.reg);
120       // ...and mark the new one live.
121       rl_src.reg = new_reg;
122       MarkLive(rl_src);
123     }
124     return rl_src;
125   }
126 
127   DCHECK_NE(rl_src.s_reg_low, INVALID_SREG);
128   rl_src.reg = AllocTypedTemp(rl_src.fp, op_kind);
129   LoadValueDirect(rl_src, rl_src.reg);
130   rl_src.location = kLocPhysReg;
131   MarkLive(rl_src);
132   return rl_src;
133 }
134 
StoreValue(RegLocation rl_dest,RegLocation rl_src)135 void Mir2Lir::StoreValue(RegLocation rl_dest, RegLocation rl_src) {
136   /*
137    * Sanity checking - should never try to store to the same
138    * ssa name during the compilation of a single instruction
139    * without an intervening ClobberSReg().
140    */
141   if (kIsDebugBuild) {
142     DCHECK((live_sreg_ == INVALID_SREG) ||
143            (rl_dest.s_reg_low != live_sreg_));
144     live_sreg_ = rl_dest.s_reg_low;
145   }
146   LIR* def_start;
147   LIR* def_end;
148   DCHECK(!rl_dest.wide);
149   DCHECK(!rl_src.wide);
150   rl_src = UpdateLoc(rl_src);
151   rl_dest = UpdateLoc(rl_dest);
152   if (rl_src.location == kLocPhysReg) {
153     if (IsLive(rl_src.reg) ||
154       IsPromoted(rl_src.reg) ||
155       (rl_dest.location == kLocPhysReg)) {
156       // Src is live/promoted or Dest has assigned reg.
157       rl_dest = EvalLoc(rl_dest, rl_dest.ref || rl_src.ref ? kRefReg : kAnyReg, false);
158       OpRegCopy(rl_dest.reg, rl_src.reg);
159     } else {
160       // Just re-assign the registers.  Dest gets Src's regs
161       rl_dest.reg = rl_src.reg;
162       Clobber(rl_src.reg);
163     }
164   } else {
165     // Load Src either into promoted Dest or temps allocated for Dest
166     rl_dest = EvalLoc(rl_dest, rl_dest.ref ? kRefReg : kAnyReg, false);
167     LoadValueDirect(rl_src, rl_dest.reg);
168   }
169 
170   // Dest is now live and dirty (until/if we flush it to home location)
171   MarkLive(rl_dest);
172   MarkDirty(rl_dest);
173 
174 
175   ResetDefLoc(rl_dest);
176   if (IsDirty(rl_dest.reg) && LiveOut(rl_dest.s_reg_low)) {
177     def_start = last_lir_insn_;
178     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
179     if (rl_dest.ref) {
180       StoreRefDisp(TargetPtrReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, kNotVolatile);
181     } else {
182       Store32Disp(TargetPtrReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg);
183     }
184     MarkClean(rl_dest);
185     def_end = last_lir_insn_;
186     if (!rl_dest.ref) {
187       // Exclude references from store elimination
188       MarkDef(rl_dest, def_start, def_end);
189     }
190   }
191 }
192 
LoadValueWide(RegLocation rl_src,RegisterClass op_kind)193 RegLocation Mir2Lir::LoadValueWide(RegLocation rl_src, RegisterClass op_kind) {
194   DCHECK(rl_src.wide);
195   rl_src = UpdateLocWide(rl_src);
196   if (rl_src.location == kLocPhysReg) {
197     if (!RegClassMatches(op_kind, rl_src.reg)) {
198       // Wrong register class, realloc, copy and transfer ownership.
199       RegStorage new_regs = AllocTypedTempWide(rl_src.fp, op_kind);
200       OpRegCopyWide(new_regs, rl_src.reg);
201       // Clobber the old regs and free it.
202       Clobber(rl_src.reg);
203       FreeTemp(rl_src.reg);
204       // ...and mark the new ones live.
205       rl_src.reg = new_regs;
206       MarkLive(rl_src);
207     }
208     return rl_src;
209   }
210 
211   DCHECK_NE(rl_src.s_reg_low, INVALID_SREG);
212   DCHECK_NE(GetSRegHi(rl_src.s_reg_low), INVALID_SREG);
213   rl_src.reg = AllocTypedTempWide(rl_src.fp, op_kind);
214   LoadValueDirectWide(rl_src, rl_src.reg);
215   rl_src.location = kLocPhysReg;
216   MarkLive(rl_src);
217   return rl_src;
218 }
219 
StoreValueWide(RegLocation rl_dest,RegLocation rl_src)220 void Mir2Lir::StoreValueWide(RegLocation rl_dest, RegLocation rl_src) {
221   /*
222    * Sanity checking - should never try to store to the same
223    * ssa name during the compilation of a single instruction
224    * without an intervening ClobberSReg().
225    */
226   if (kIsDebugBuild) {
227     DCHECK((live_sreg_ == INVALID_SREG) ||
228            (rl_dest.s_reg_low != live_sreg_));
229     live_sreg_ = rl_dest.s_reg_low;
230   }
231   LIR* def_start;
232   LIR* def_end;
233   DCHECK(rl_dest.wide);
234   DCHECK(rl_src.wide);
235   rl_src = UpdateLocWide(rl_src);
236   rl_dest = UpdateLocWide(rl_dest);
237   if (rl_src.location == kLocPhysReg) {
238     if (IsLive(rl_src.reg) ||
239         IsPromoted(rl_src.reg) ||
240         (rl_dest.location == kLocPhysReg)) {
241       /*
242        * If src reg[s] are tied to the original Dalvik vreg via liveness or promotion, we
243        * can't repurpose them.  Similarly, if the dest reg[s] are tied to Dalvik vregs via
244        * promotion, we can't just re-assign.  In these cases, we have to copy.
245        */
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);
261   MarkWide(rl_dest.reg);
262   MarkDirty(rl_dest);
263 
264   ResetDefLocWide(rl_dest);
265   if (IsDirty(rl_dest.reg) && (LiveOut(rl_dest.s_reg_low) ||
266       LiveOut(GetSRegHi(rl_dest.s_reg_low)))) {
267     def_start = last_lir_insn_;
268     DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1),
269               mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low)));
270     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
271     StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, k64, kNotVolatile);
272     MarkClean(rl_dest);
273     def_end = last_lir_insn_;
274     MarkDefWide(rl_dest, def_start, def_end);
275   }
276 }
277 
StoreFinalValue(RegLocation rl_dest,RegLocation rl_src)278 void Mir2Lir::StoreFinalValue(RegLocation rl_dest, RegLocation rl_src) {
279   DCHECK_EQ(rl_src.location, kLocPhysReg);
280 
281   if (rl_dest.location == kLocPhysReg) {
282     OpRegCopy(rl_dest.reg, rl_src.reg);
283   } else {
284     // Just re-assign the register.  Dest gets Src's reg.
285     rl_dest.location = kLocPhysReg;
286     rl_dest.reg = rl_src.reg;
287     Clobber(rl_src.reg);
288   }
289 
290   // Dest is now live and dirty (until/if we flush it to home location)
291   MarkLive(rl_dest);
292   MarkDirty(rl_dest);
293 
294 
295   ResetDefLoc(rl_dest);
296   if (IsDirty(rl_dest.reg) && LiveOut(rl_dest.s_reg_low)) {
297     LIR *def_start = last_lir_insn_;
298     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
299     Store32Disp(TargetPtrReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg);
300     MarkClean(rl_dest);
301     LIR *def_end = last_lir_insn_;
302     if (!rl_dest.ref) {
303       // Exclude references from store elimination
304       MarkDef(rl_dest, def_start, def_end);
305     }
306   }
307 }
308 
StoreFinalValueWide(RegLocation rl_dest,RegLocation rl_src)309 void Mir2Lir::StoreFinalValueWide(RegLocation rl_dest, RegLocation rl_src) {
310   DCHECK(rl_dest.wide);
311   DCHECK(rl_src.wide);
312   DCHECK_EQ(rl_src.location, kLocPhysReg);
313 
314   if (rl_dest.location == kLocPhysReg) {
315     OpRegCopyWide(rl_dest.reg, rl_src.reg);
316   } else {
317     // Just re-assign the registers.  Dest gets Src's regs.
318     rl_dest.location = kLocPhysReg;
319     rl_dest.reg = rl_src.reg;
320     Clobber(rl_src.reg);
321   }
322 
323   // Dest is now live and dirty (until/if we flush it to home location).
324   MarkLive(rl_dest);
325   MarkWide(rl_dest.reg);
326   MarkDirty(rl_dest);
327 
328   ResetDefLocWide(rl_dest);
329   if (IsDirty(rl_dest.reg) && (LiveOut(rl_dest.s_reg_low) ||
330       LiveOut(GetSRegHi(rl_dest.s_reg_low)))) {
331     LIR *def_start = last_lir_insn_;
332     DCHECK_EQ((mir_graph_->SRegToVReg(rl_dest.s_reg_low)+1),
333               mir_graph_->SRegToVReg(GetSRegHi(rl_dest.s_reg_low)));
334     ScopedMemRefType mem_ref_type(this, ResourceMask::kDalvikReg);
335     StoreBaseDisp(TargetPtrReg(kSp), SRegOffset(rl_dest.s_reg_low), rl_dest.reg, k64, kNotVolatile);
336     MarkClean(rl_dest);
337     LIR *def_end = last_lir_insn_;
338     MarkDefWide(rl_dest, def_start, def_end);
339   }
340 }
341 
342 /* Utilities to load the current Method* */
LoadCurrMethodDirect(RegStorage r_tgt)343 void Mir2Lir::LoadCurrMethodDirect(RegStorage r_tgt) {
344   if (GetCompilationUnit()->target64) {
345     LoadValueDirectWideFixed(mir_graph_->GetMethodLoc(), r_tgt);
346   } else {
347     LoadValueDirectFixed(mir_graph_->GetMethodLoc(), r_tgt);
348   }
349 }
350 
LoadCurrMethodWithHint(RegStorage r_hint)351 RegStorage Mir2Lir::LoadCurrMethodWithHint(RegStorage r_hint) {
352   // If the method is promoted to a register, return that register, otherwise load it to r_hint.
353   // (Replacement for LoadCurrMethod() usually used when LockCallTemps() is in effect.)
354   DCHECK(r_hint.Valid());
355   RegLocation rl_method = mir_graph_->GetMethodLoc();
356   if (rl_method.location == kLocPhysReg) {
357     DCHECK(!IsTemp(rl_method.reg));
358     return rl_method.reg;
359   } else {
360     LoadCurrMethodDirect(r_hint);
361     return r_hint;
362   }
363 }
364 
LoadCurrMethod()365 RegLocation Mir2Lir::LoadCurrMethod() {
366   return GetCompilationUnit()->target64 ?
367       LoadValueWide(mir_graph_->GetMethodLoc(), kCoreReg) :
368       LoadValue(mir_graph_->GetMethodLoc(), kRefReg);
369 }
370 
ForceTemp(RegLocation loc)371 RegLocation Mir2Lir::ForceTemp(RegLocation loc) {
372   DCHECK(!loc.wide);
373   DCHECK(loc.location == kLocPhysReg);
374   DCHECK(!loc.reg.IsFloat());
375   if (IsTemp(loc.reg)) {
376     Clobber(loc.reg);
377   } else {
378     RegStorage temp_low = AllocTemp();
379     OpRegCopy(temp_low, loc.reg);
380     loc.reg = temp_low;
381   }
382 
383   // Ensure that this doesn't represent the original SR any more.
384   loc.s_reg_low = INVALID_SREG;
385   return loc;
386 }
387 
ForceTempWide(RegLocation loc)388 RegLocation Mir2Lir::ForceTempWide(RegLocation loc) {
389   DCHECK(loc.wide);
390   DCHECK(loc.location == kLocPhysReg);
391   DCHECK(!loc.reg.IsFloat());
392 
393   if (!loc.reg.IsPair()) {
394     if (IsTemp(loc.reg)) {
395       Clobber(loc.reg);
396     } else {
397       RegStorage temp = AllocTempWide();
398       OpRegCopy(temp, loc.reg);
399       loc.reg = temp;
400     }
401   } else {
402     if (IsTemp(loc.reg.GetLow())) {
403       Clobber(loc.reg.GetLow());
404     } else {
405       RegStorage temp_low = AllocTemp();
406       OpRegCopy(temp_low, loc.reg.GetLow());
407       loc.reg.SetLowReg(temp_low.GetReg());
408     }
409     if (IsTemp(loc.reg.GetHigh())) {
410       Clobber(loc.reg.GetHigh());
411     } else {
412       RegStorage temp_high = AllocTemp();
413       OpRegCopy(temp_high, loc.reg.GetHigh());
414       loc.reg.SetHighReg(temp_high.GetReg());
415     }
416   }
417 
418   // Ensure that this doesn't represent the original SR any more.
419   loc.s_reg_low = INVALID_SREG;
420   return loc;
421 }
422 
423 }  // namespace art
424