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