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 is included by Codegen-armv5te-vfp.c, and implements architecture
19 * variant-specific code.
20 */
21
22extern void dvmCompilerFlushRegWideForV5TEVFP(CompilationUnit *cUnit,
23                                              int reg1, int reg2);
24extern void dvmCompilerFlushRegForV5TEVFP(CompilationUnit *cUnit, int reg);
25
26/* First, flush any registers associated with this value */
27static void loadValueAddress(CompilationUnit *cUnit, RegLocation rlSrc,
28                             int rDest)
29{
30     rlSrc = rlSrc.wide ? dvmCompilerUpdateLocWide(cUnit, rlSrc) :
31                          dvmCompilerUpdateLoc(cUnit, rlSrc);
32     if (rlSrc.location == kLocPhysReg) {
33         if (rlSrc.wide) {
34             dvmCompilerFlushRegWideForV5TEVFP(cUnit, rlSrc.lowReg,
35                                               rlSrc.highReg);
36         } else {
37             dvmCompilerFlushRegForV5TEVFP(cUnit, rlSrc.lowReg);
38         }
39     }
40     opRegRegImm(cUnit, kOpAdd, rDest, rFP,
41                 dvmCompilerS2VReg(cUnit, rlSrc.sRegLow) << 2);
42}
43
44static bool genInlineSqrt(CompilationUnit *cUnit, MIR *mir)
45{
46    RegLocation rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
47#ifdef __mips_hard_float
48    RegLocation rlResult = LOC_C_RETURN_WIDE_ALT;
49#else
50    RegLocation rlResult = LOC_C_RETURN_WIDE;
51#endif
52    RegLocation rlDest = LOC_DALVIK_RETURN_VAL_WIDE;
53    loadValueAddress(cUnit, rlSrc, r_A2);
54    genDispatchToHandler(cUnit, TEMPLATE_SQRT_DOUBLE_VFP);
55    storeValueWide(cUnit, rlDest, rlResult);
56    return false;
57}
58
59/*
60 * TUNING: On some implementations, it is quicker to pass addresses
61 * to the handlers rather than load the operands into core registers
62 * and then move the values to FP regs in the handlers.  Other implementations
63 * may prefer passing data in registers (and the latter approach would
64 * yeild cleaner register handling - avoiding the requirement that operands
65 * be flushed to memory prior to the call).
66 */
67static bool genArithOpFloat(CompilationUnit *cUnit, MIR *mir,
68                            RegLocation rlDest, RegLocation rlSrc1,
69                            RegLocation rlSrc2)
70{
71#ifdef __mips_hard_float
72    int op = kMipsNop;
73    RegLocation rlResult;
74
75    /*
76     * Don't attempt to optimize register usage since these opcodes call out to
77     * the handlers.
78     */
79    switch (mir->dalvikInsn.opcode) {
80        case OP_ADD_FLOAT_2ADDR:
81        case OP_ADD_FLOAT:
82            op = kMipsFadds;
83            break;
84        case OP_SUB_FLOAT_2ADDR:
85        case OP_SUB_FLOAT:
86            op = kMipsFsubs;
87            break;
88        case OP_DIV_FLOAT_2ADDR:
89        case OP_DIV_FLOAT:
90            op = kMipsFdivs;
91            break;
92        case OP_MUL_FLOAT_2ADDR:
93        case OP_MUL_FLOAT:
94            op = kMipsFmuls;
95            break;
96        case OP_REM_FLOAT_2ADDR:
97        case OP_REM_FLOAT:
98        case OP_NEG_FLOAT: {
99            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
100        }
101        default:
102            return true;
103    }
104    rlSrc1 = loadValue(cUnit, rlSrc1, kFPReg);
105    rlSrc2 = loadValue(cUnit, rlSrc2, kFPReg);
106    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
107    newLIR3(cUnit, (MipsOpCode)op, rlResult.lowReg, rlSrc1.lowReg, rlSrc2.lowReg);
108    storeValue(cUnit, rlDest, rlResult);
109
110    return false;
111#else
112    TemplateOpcode opcode;
113
114    /*
115     * Don't attempt to optimize register usage since these opcodes call out to
116     * the handlers.
117     */
118    switch (mir->dalvikInsn.opcode) {
119        case OP_ADD_FLOAT_2ADDR:
120        case OP_ADD_FLOAT:
121            opcode = TEMPLATE_ADD_FLOAT_VFP;
122            break;
123        case OP_SUB_FLOAT_2ADDR:
124        case OP_SUB_FLOAT:
125            opcode = TEMPLATE_SUB_FLOAT_VFP;
126            break;
127        case OP_DIV_FLOAT_2ADDR:
128        case OP_DIV_FLOAT:
129            opcode = TEMPLATE_DIV_FLOAT_VFP;
130            break;
131        case OP_MUL_FLOAT_2ADDR:
132        case OP_MUL_FLOAT:
133            opcode = TEMPLATE_MUL_FLOAT_VFP;
134            break;
135        case OP_REM_FLOAT_2ADDR:
136        case OP_REM_FLOAT:
137        case OP_NEG_FLOAT: {
138            return genArithOpFloatPortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
139        }
140        default:
141            return true;
142    }
143    loadValueAddress(cUnit, rlDest, r_A0);
144    dvmCompilerClobber(cUnit, r_A0);
145    loadValueAddress(cUnit, rlSrc1, r_A1);
146    dvmCompilerClobber(cUnit, r_A1);
147    loadValueAddress(cUnit, rlSrc2, r_A2);
148    genDispatchToHandler(cUnit, opcode);
149    rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
150    if (rlDest.location == kLocPhysReg) {
151        dvmCompilerClobber(cUnit, rlDest.lowReg);
152    }
153    return false;
154#endif
155}
156
157static bool genArithOpDouble(CompilationUnit *cUnit, MIR *mir,
158                             RegLocation rlDest, RegLocation rlSrc1,
159                             RegLocation rlSrc2)
160{
161#ifdef __mips_hard_float
162    int op = kMipsNop;
163    RegLocation rlResult;
164
165    switch (mir->dalvikInsn.opcode) {
166        case OP_ADD_DOUBLE_2ADDR:
167        case OP_ADD_DOUBLE:
168            op = kMipsFaddd;
169            break;
170        case OP_SUB_DOUBLE_2ADDR:
171        case OP_SUB_DOUBLE:
172            op = kMipsFsubd;
173            break;
174        case OP_DIV_DOUBLE_2ADDR:
175        case OP_DIV_DOUBLE:
176            op = kMipsFdivd;
177            break;
178        case OP_MUL_DOUBLE_2ADDR:
179        case OP_MUL_DOUBLE:
180            op = kMipsFmuld;
181            break;
182        case OP_REM_DOUBLE_2ADDR:
183        case OP_REM_DOUBLE:
184        case OP_NEG_DOUBLE: {
185            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1, rlSrc2);
186        }
187        default:
188            return true;
189    }
190    rlSrc1 = loadValueWide(cUnit, rlSrc1, kFPReg);
191    assert(rlSrc1.wide);
192    rlSrc2 = loadValueWide(cUnit, rlSrc2, kFPReg);
193    assert(rlSrc2.wide);
194    rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
195    assert(rlDest.wide);
196    assert(rlResult.wide);
197    newLIR3(cUnit, (MipsOpCode)op, S2D(rlResult.lowReg, rlResult.highReg),
198            S2D(rlSrc1.lowReg, rlSrc1.highReg),
199            S2D(rlSrc2.lowReg, rlSrc2.highReg));
200    storeValueWide(cUnit, rlDest, rlResult);
201    return false;
202#else
203    TemplateOpcode opcode;
204
205    switch (mir->dalvikInsn.opcode) {
206        case OP_ADD_DOUBLE_2ADDR:
207        case OP_ADD_DOUBLE:
208            opcode = TEMPLATE_ADD_DOUBLE_VFP;
209            break;
210        case OP_SUB_DOUBLE_2ADDR:
211        case OP_SUB_DOUBLE:
212            opcode = TEMPLATE_SUB_DOUBLE_VFP;
213            break;
214        case OP_DIV_DOUBLE_2ADDR:
215        case OP_DIV_DOUBLE:
216            opcode = TEMPLATE_DIV_DOUBLE_VFP;
217            break;
218        case OP_MUL_DOUBLE_2ADDR:
219        case OP_MUL_DOUBLE:
220            opcode = TEMPLATE_MUL_DOUBLE_VFP;
221            break;
222        case OP_REM_DOUBLE_2ADDR:
223        case OP_REM_DOUBLE:
224        case OP_NEG_DOUBLE: {
225            return genArithOpDoublePortable(cUnit, mir, rlDest, rlSrc1,
226                                               rlSrc2);
227        }
228        default:
229            return true;
230    }
231    loadValueAddress(cUnit, rlDest, r_A0);
232    dvmCompilerClobber(cUnit, r_A0);
233    loadValueAddress(cUnit, rlSrc1, r_A1);
234    dvmCompilerClobber(cUnit, r_A1);
235    loadValueAddress(cUnit, rlSrc2, r_A2);
236    genDispatchToHandler(cUnit, opcode);
237    rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
238    if (rlDest.location == kLocPhysReg) {
239        dvmCompilerClobber(cUnit, rlDest.lowReg);
240        dvmCompilerClobber(cUnit, rlDest.highReg);
241    }
242    return false;
243#endif
244}
245
246static bool genConversion(CompilationUnit *cUnit, MIR *mir)
247{
248    Opcode opcode = mir->dalvikInsn.opcode;
249    bool longSrc = false;
250    bool longDest = false;
251    RegLocation rlSrc;
252    RegLocation rlDest;
253#ifdef __mips_hard_float
254    int op = kMipsNop;
255    int srcReg;
256    RegLocation rlResult;
257
258    switch (opcode) {
259        case OP_INT_TO_FLOAT:
260            longSrc = false;
261            longDest = false;
262            op = kMipsFcvtsw;
263            break;
264        case OP_DOUBLE_TO_FLOAT:
265            longSrc = true;
266            longDest = false;
267            op = kMipsFcvtsd;
268            break;
269        case OP_FLOAT_TO_DOUBLE:
270            longSrc = false;
271            longDest = true;
272            op = kMipsFcvtds;
273            break;
274        case OP_INT_TO_DOUBLE:
275            longSrc = false;
276            longDest = true;
277            op = kMipsFcvtdw;
278            break;
279        case OP_FLOAT_TO_INT:
280        case OP_DOUBLE_TO_INT:
281        case OP_LONG_TO_DOUBLE:
282        case OP_FLOAT_TO_LONG:
283        case OP_LONG_TO_FLOAT:
284        case OP_DOUBLE_TO_LONG:
285            return genConversionPortable(cUnit, mir);
286        default:
287            return true;
288    }
289    if (longSrc) {
290        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
291        rlSrc = loadValueWide(cUnit, rlSrc, kFPReg);
292        srcReg = S2D(rlSrc.lowReg, rlSrc.highReg);
293    } else {
294        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
295        rlSrc = loadValue(cUnit, rlSrc, kFPReg);
296        srcReg = rlSrc.lowReg;
297    }
298    if (longDest) {
299        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
300        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
301        newLIR2(cUnit, (MipsOpCode)op, S2D(rlResult.lowReg, rlResult.highReg), srcReg);
302        storeValueWide(cUnit, rlDest, rlResult);
303    } else {
304        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
305        rlResult = dvmCompilerEvalLoc(cUnit, rlDest, kFPReg, true);
306        newLIR2(cUnit, (MipsOpCode)op, rlResult.lowReg, srcReg);
307        storeValue(cUnit, rlDest, rlResult);
308    }
309    return false;
310#else
311    TemplateOpcode templateOpcode;
312    switch (opcode) {
313        case OP_INT_TO_FLOAT:
314            longSrc = false;
315            longDest = false;
316            templateOpcode = TEMPLATE_INT_TO_FLOAT_VFP;
317            break;
318        case OP_FLOAT_TO_INT:
319            longSrc = false;
320            longDest = false;
321            templateOpcode = TEMPLATE_FLOAT_TO_INT_VFP;
322            break;
323        case OP_DOUBLE_TO_FLOAT:
324            longSrc = true;
325            longDest = false;
326            templateOpcode = TEMPLATE_DOUBLE_TO_FLOAT_VFP;
327            break;
328        case OP_FLOAT_TO_DOUBLE:
329            longSrc = false;
330            longDest = true;
331            templateOpcode = TEMPLATE_FLOAT_TO_DOUBLE_VFP;
332            break;
333        case OP_INT_TO_DOUBLE:
334            longSrc = false;
335            longDest = true;
336            templateOpcode = TEMPLATE_INT_TO_DOUBLE_VFP;
337            break;
338        case OP_DOUBLE_TO_INT:
339            longSrc = true;
340            longDest = false;
341            templateOpcode = TEMPLATE_DOUBLE_TO_INT_VFP;
342            break;
343        case OP_LONG_TO_DOUBLE:
344        case OP_FLOAT_TO_LONG:
345        case OP_LONG_TO_FLOAT:
346        case OP_DOUBLE_TO_LONG:
347            return genConversionPortable(cUnit, mir);
348        default:
349            return true;
350    }
351
352    if (longSrc) {
353        rlSrc = dvmCompilerGetSrcWide(cUnit, mir, 0, 1);
354    } else {
355        rlSrc = dvmCompilerGetSrc(cUnit, mir, 0);
356    }
357
358    if (longDest) {
359        rlDest = dvmCompilerGetDestWide(cUnit, mir, 0, 1);
360    } else {
361        rlDest = dvmCompilerGetDest(cUnit, mir, 0);
362    }
363    loadValueAddress(cUnit, rlDest, r_A0);
364    dvmCompilerClobber(cUnit, r_A0);
365    loadValueAddress(cUnit, rlSrc, r_A1);
366    genDispatchToHandler(cUnit, templateOpcode);
367    if (rlDest.wide) {
368        rlDest = dvmCompilerUpdateLocWide(cUnit, rlDest);
369        dvmCompilerClobber(cUnit, rlDest.highReg);
370    } else {
371        rlDest = dvmCompilerUpdateLoc(cUnit, rlDest);
372    }
373    dvmCompilerClobber(cUnit, rlDest.lowReg);
374    return false;
375#endif
376}
377
378static bool genCmpFP(CompilationUnit *cUnit, MIR *mir, RegLocation rlDest,
379                     RegLocation rlSrc1, RegLocation rlSrc2)
380{
381    TemplateOpcode templateOpcode;
382    RegLocation rlResult = dvmCompilerGetReturn(cUnit);
383    bool wide = true;
384
385    switch(mir->dalvikInsn.opcode) {
386        case OP_CMPL_FLOAT:
387            templateOpcode = TEMPLATE_CMPL_FLOAT_VFP;
388            wide = false;
389            break;
390        case OP_CMPG_FLOAT:
391            templateOpcode = TEMPLATE_CMPG_FLOAT_VFP;
392            wide = false;
393            break;
394        case OP_CMPL_DOUBLE:
395            templateOpcode = TEMPLATE_CMPL_DOUBLE_VFP;
396            break;
397        case OP_CMPG_DOUBLE:
398            templateOpcode = TEMPLATE_CMPG_DOUBLE_VFP;
399            break;
400        default:
401            return true;
402    }
403    loadValueAddress(cUnit, rlSrc1, r_A0);
404    dvmCompilerClobber(cUnit, r_A0);
405    loadValueAddress(cUnit, rlSrc2, r_A1);
406    genDispatchToHandler(cUnit, templateOpcode);
407    storeValue(cUnit, rlDest, rlResult);
408    return false;
409}
410