1/*
2 * Copyright (C) 2009 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/*
18 * This file contains codegen and support common to all supported
19 * Mips variants.  It is included by:
20 *
21 *        Codegen-$(TARGET_ARCH_VARIANT).c
22 *
23 * which combines this common code with specific support found in the
24 * applicable directory below this one.
25 */
26
27
28/* Load a word at base + displacement.  Displacement must be word multiple */
29static MipsLIR *loadWordDisp(CompilationUnit *cUnit, int rBase, int displacement,
30                            int rDest)
31{
32    return loadBaseDisp(cUnit, NULL, rBase, displacement, rDest, kWord,
33                        INVALID_SREG);
34}
35
36static MipsLIR *storeWordDisp(CompilationUnit *cUnit, int rBase,
37                             int displacement, int rSrc)
38{
39    return storeBaseDisp(cUnit, rBase, displacement, rSrc, kWord);
40}
41
42/*
43 * Load a Dalvik register into a physical register.  Take care when
44 * using this routine, as it doesn't perform any bookkeeping regarding
45 * register liveness.  That is the responsibility of the caller.
46 */
47static void loadValueDirect(CompilationUnit *cUnit, RegLocation rlSrc,
48                                int reg1)
49{
50    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
51    if (rlSrc.location == kLocPhysReg) {
52        genRegCopy(cUnit, reg1, rlSrc.lowReg);
53    } else  if (rlSrc.location == kLocRetval) {
54        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), reg1);
55    } else {
56        assert(rlSrc.location == kLocDalvikFrame);
57        loadWordDisp(cUnit, rFP, dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
58                     reg1);
59    }
60}
61
62/*
63 * Similar to loadValueDirect, but clobbers and allocates the target
64 * register.  Should be used when loading to a fixed register (for example,
65 * loading arguments to an out of line call.
66 */
67static void loadValueDirectFixed(CompilationUnit *cUnit, RegLocation rlSrc,
68                                 int reg1)
69{
70    dvmCompilerClobber(cUnit, reg1);
71    dvmCompilerMarkInUse(cUnit, reg1);
72    loadValueDirect(cUnit, rlSrc, reg1);
73}
74
75/*
76 * Load a Dalvik register pair into a physical register[s].  Take care when
77 * using this routine, as it doesn't perform any bookkeeping regarding
78 * register liveness.  That is the responsibility of the caller.
79 */
80static void loadValueDirectWide(CompilationUnit *cUnit, RegLocation rlSrc,
81                                int regLo, int regHi)
82{
83    rlSrc = dvmCompilerUpdateLocWide(cUnit, rlSrc);
84    if (rlSrc.location == kLocPhysReg) {
85        genRegCopyWide(cUnit, regLo, regHi, rlSrc.lowReg, rlSrc.highReg);
86    } else if (rlSrc.location == kLocRetval) {
87        loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval),
88                         regLo, regHi, INVALID_SREG);
89    } else {
90        assert(rlSrc.location == kLocDalvikFrame);
91            loadBaseDispWide(cUnit, NULL, rFP,
92                             dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2,
93                             regLo, regHi, INVALID_SREG);
94    }
95}
96
97/*
98 * Similar to loadValueDirect, but clobbers and allocates the target
99 * registers.  Should be used when loading to a fixed registers (for example,
100 * loading arguments to an out of line call.
101 */
102static void loadValueDirectWideFixed(CompilationUnit *cUnit, RegLocation rlSrc,
103                                     int regLo, int regHi)
104{
105    dvmCompilerClobber(cUnit, regLo);
106    dvmCompilerClobber(cUnit, regHi);
107    dvmCompilerMarkInUse(cUnit, regLo);
108    dvmCompilerMarkInUse(cUnit, regHi);
109    loadValueDirectWide(cUnit, rlSrc, regLo, regHi);
110}
111
112static RegLocation loadValue(CompilationUnit *cUnit, RegLocation rlSrc,
113                             RegisterClass opKind)
114{
115    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
116    if (rlSrc.location == kLocDalvikFrame) {
117        loadValueDirect(cUnit, rlSrc, rlSrc.lowReg);
118        rlSrc.location = kLocPhysReg;
119        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
120    } else if (rlSrc.location == kLocRetval) {
121        loadWordDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval), rlSrc.lowReg);
122        rlSrc.location = kLocPhysReg;
123        dvmCompilerClobber(cUnit, rlSrc.lowReg);
124    }
125    return rlSrc;
126}
127
128static void storeValue(CompilationUnit *cUnit, RegLocation rlDest,
129                       RegLocation rlSrc)
130{
131    LIR *defStart;
132    LIR *defEnd;
133    assert(!rlDest.wide);
134    assert(!rlSrc.wide);
135    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
136    rlSrc = dvmCompilerUpdateLoc(cUnit, rlSrc);
137    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
138    if (rlSrc.location == kLocPhysReg) {
139        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
140            (rlDest.location == kLocPhysReg)) {
141            // Src is live or Dest has assigned reg.
142            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
143            genRegCopy(cUnit, rlDest.lowReg, rlSrc.lowReg);
144        } else {
145            // Just re-assign the registers.  Dest gets Src's regs
146            rlDest.lowReg = rlSrc.lowReg;
147            dvmCompilerClobber(cUnit, rlSrc.lowReg);
148        }
149    } else {
150        // Load Src either into promoted Dest or temps allocated for Dest
151        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
152        loadValueDirect(cUnit, rlSrc, rlDest.lowReg);
153    }
154
155    // Dest is now live and dirty (until/if we flush it to home location)
156    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
157    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
158
159
160    if (rlDest.location == kLocRetval) {
161        storeBaseDisp(cUnit, rSELF, offsetof(Thread, interpSave.retval),
162                      rlDest.lowReg, kWord);
163        dvmCompilerClobber(cUnit, rlDest.lowReg);
164    } else {
165        dvmCompilerResetDefLoc(cUnit, rlDest);
166        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow)) {
167            defStart = (LIR *)cUnit->lastLIRInsn;
168            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
169            storeBaseDisp(cUnit, rFP, vReg << 2, rlDest.lowReg, kWord);
170            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
171            defEnd = (LIR *)cUnit->lastLIRInsn;
172            dvmCompilerMarkDef(cUnit, rlDest, defStart, defEnd);
173        }
174    }
175}
176
177static RegLocation loadValueWide(CompilationUnit *cUnit, RegLocation rlSrc,
178                                 RegisterClass opKind)
179{
180    assert(rlSrc.wide);
181    rlSrc = dvmCompilerEvalLoc(cUnit, rlSrc, opKind, false);
182    if (rlSrc.location == kLocDalvikFrame) {
183        loadValueDirectWide(cUnit, rlSrc, rlSrc.lowReg, rlSrc.highReg);
184        rlSrc.location = kLocPhysReg;
185        dvmCompilerMarkLive(cUnit, rlSrc.lowReg, rlSrc.sRegLow);
186        dvmCompilerMarkLive(cUnit, rlSrc.highReg,
187                            dvmCompilerSRegHi(rlSrc.sRegLow));
188    } else if (rlSrc.location == kLocRetval) {
189        loadBaseDispWide(cUnit, NULL, rSELF, offsetof(Thread, interpSave.retval),
190                         rlSrc.lowReg, rlSrc.highReg, INVALID_SREG);
191        rlSrc.location = kLocPhysReg;
192        dvmCompilerClobber(cUnit, rlSrc.lowReg);
193        dvmCompilerClobber(cUnit, rlSrc.highReg);
194    }
195    return rlSrc;
196}
197
198static void storeValueWide(CompilationUnit *cUnit, RegLocation rlDest,
199                       RegLocation rlSrc)
200{
201    LIR *defStart;
202    LIR *defEnd;
203    assert(FPREG(rlSrc.lowReg)==FPREG(rlSrc.highReg));
204    assert(rlDest.wide);
205    assert(rlSrc.wide);
206    dvmCompilerKillNullCheckedLoc(cUnit, rlDest);
207    if (rlSrc.location == kLocPhysReg) {
208        if (dvmCompilerIsLive(cUnit, rlSrc.lowReg) ||
209            dvmCompilerIsLive(cUnit, rlSrc.highReg) ||
210            (rlDest.location == kLocPhysReg)) {
211            // Src is live or Dest has assigned reg.
212            rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
213            genRegCopyWide(cUnit, rlDest.lowReg, rlDest.highReg,
214                           rlSrc.lowReg, rlSrc.highReg);
215        } else {
216            // Just re-assign the registers.  Dest gets Src's regs
217            rlDest.lowReg = rlSrc.lowReg;
218            rlDest.highReg = rlSrc.highReg;
219            dvmCompilerClobber(cUnit, rlSrc.lowReg);
220            dvmCompilerClobber(cUnit, rlSrc.highReg);
221        }
222    } else {
223        // Load Src either into promoted Dest or temps allocated for Dest
224        rlDest = dvmCompilerEvalLoc(cUnit, rlDest, kAnyReg, false);
225        loadValueDirectWide(cUnit, rlSrc, rlDest.lowReg,
226                            rlDest.highReg);
227    }
228
229    // Dest is now live and dirty (until/if we flush it to home location)
230    dvmCompilerMarkLive(cUnit, rlDest.lowReg, rlDest.sRegLow);
231    dvmCompilerMarkLive(cUnit, rlDest.highReg,
232                        dvmCompilerSRegHi(rlDest.sRegLow));
233    dvmCompilerMarkDirty(cUnit, rlDest.lowReg);
234    dvmCompilerMarkDirty(cUnit, rlDest.highReg);
235    dvmCompilerMarkPair(cUnit, rlDest.lowReg, rlDest.highReg);
236
237
238    if (rlDest.location == kLocRetval) {
239        storeBaseDispWide(cUnit, rSELF, offsetof(Thread, interpSave.retval),
240                          rlDest.lowReg, rlDest.highReg);
241        dvmCompilerClobber(cUnit, rlDest.lowReg);
242        dvmCompilerClobber(cUnit, rlDest.highReg);
243    } else {
244        dvmCompilerResetDefLocWide(cUnit, rlDest);
245        if (dvmCompilerLiveOut(cUnit, rlDest.sRegLow) ||
246            dvmCompilerLiveOut(cUnit, dvmCompilerSRegHi(rlDest.sRegLow))) {
247            defStart = (LIR *)cUnit->lastLIRInsn;
248            int vReg = dvmCompilerS2VReg(cUnit, rlDest.sRegLow);
249            assert((vReg+1) == dvmCompilerS2VReg(cUnit,
250                                     dvmCompilerSRegHi(rlDest.sRegLow)));
251            storeBaseDispWide(cUnit, rFP, vReg << 2, rlDest.lowReg,
252                              rlDest.highReg);
253            dvmCompilerMarkClean(cUnit, rlDest.lowReg);
254            dvmCompilerMarkClean(cUnit, rlDest.highReg);
255            defEnd = (LIR *)cUnit->lastLIRInsn;
256            dvmCompilerMarkDefWide(cUnit, rlDest, defStart, defEnd);
257        }
258    }
259}
260/*
261 * Perform null-check on a register. sReg is the ssa register being checked,
262 * and mReg is the machine register holding the actual value. If internal state
263 * indicates that sReg has been checked before the check request is ignored.
264 */
265static MipsLIR *genNullCheck(CompilationUnit *cUnit, int sReg, int mReg,
266                                int dOffset, MipsLIR *pcrLabel)
267{
268    /* This particular Dalvik register has been null-checked */
269    if (dvmIsBitSet(cUnit->regPool->nullCheckedRegs, sReg)) {
270        return pcrLabel;
271    }
272    dvmSetBit(cUnit->regPool->nullCheckedRegs, sReg);
273    return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel);
274}
275
276
277
278/*
279 * Perform a "reg cmp reg" operation and jump to the PCR region if condition
280 * satisfies.
281 */
282static MipsLIR *genRegRegCheck(CompilationUnit *cUnit,
283                              MipsConditionCode cond,
284                              int reg1, int reg2, int dOffset,
285                              MipsLIR *pcrLabel)
286{
287    MipsLIR *res = NULL;
288    if (cond == kMipsCondGe) { /* signed >= case */
289        int tReg = dvmCompilerAllocTemp(cUnit);
290        res = newLIR3(cUnit, kMipsSlt, tReg, reg1, reg2);
291        MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1);
292        genCheckCommon(cUnit, dOffset, branch, pcrLabel);
293    } else if (cond == kMipsCondCs) {  /* unsigned >= case */
294        int tReg = dvmCompilerAllocTemp(cUnit);
295        res = newLIR3(cUnit, kMipsSltu, tReg, reg1, reg2);
296        MipsLIR *branch = opCompareBranch(cUnit, kMipsBeqz, tReg, -1);
297        genCheckCommon(cUnit, dOffset, branch, pcrLabel);
298    } else {
299        ALOGE("Unexpected condition in genRegRegCheck: %d\n", (int) cond);
300        dvmAbort();
301    }
302    return res;
303}
304
305/*
306 * Perform zero-check on a register. Similar to genNullCheck but the value being
307 * checked does not have a corresponding Dalvik register.
308 */
309static MipsLIR *genZeroCheck(CompilationUnit *cUnit, int mReg,
310                                int dOffset, MipsLIR *pcrLabel)
311{
312    return genRegImmCheck(cUnit, kMipsCondEq, mReg, 0, dOffset, pcrLabel);
313}
314
315/* Perform bound check on two registers */
316static MipsLIR *genBoundsCheck(CompilationUnit *cUnit, int rIndex,
317                                  int rBound, int dOffset, MipsLIR *pcrLabel)
318{
319    return genRegRegCheck(cUnit, kMipsCondCs, rIndex, rBound, dOffset,
320                            pcrLabel);
321}
322
323/*
324 * Jump to the out-of-line handler to finish executing the
325 * remaining of more complex instructions.
326 */
327static void genDispatchToHandler(CompilationUnit *cUnit, TemplateOpcode opCode)
328{
329    /*
330     * We're jumping from a trace to a template. Using jal is preferable to jalr,
331     * but we need to ensure source and target addresses allow the use of jal.
332     * This should almost always be the case, but if source and target are in
333     * different 256mb regions then use jalr.  The test below is very conservative
334     * since we don't have a source address yet, but this is ok for now given that
335     * we expect this case to be very rare. The test can be made less conservative
336     * as needed in the future in coordination with address assignment during
337     * the assembly process.
338     */
339    dvmCompilerClobberHandlerRegs(cUnit);
340    int targetAddr = (int) gDvmJit.codeCache + templateEntryOffsets[opCode];
341    int maxSourceAddr = (int) gDvmJit.codeCache + gDvmJit.codeCacheSize;
342
343    if ((targetAddr & 0xF0000000) == (maxSourceAddr & 0xF0000000)) {
344        newLIR1(cUnit, kMipsJal, targetAddr);
345    } else {
346        loadConstant(cUnit, r_T9, targetAddr);
347        newLIR2(cUnit, kMipsJalr, r_RA, r_T9);
348    }
349}
350