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