Raghu Gandham | a8b91c5 | 2012-05-02 14:27:16 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (C) 2008 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 | * JNI method invocation. This is used to call a C/C++ JNI method. The |
| 19 | * argument list has to be pushed onto the native stack according to |
| 20 | * local calling conventions. |
| 21 | * |
| 22 | * This version supports the MIPS O32 ABI. |
| 23 | */ |
| 24 | |
| 25 | /* |
| 26 | Function prototype: |
| 27 | |
| 28 | void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc, |
| 29 | const u4* argv, const char* signature, void* func, JValue* pReturn) |
| 30 | |
| 31 | The method we are calling has the form: |
| 32 | |
| 33 | return_type func(JNIEnv* pEnv, ClassObject* clazz, ...) |
| 34 | -or- |
| 35 | return_type func(JNIEnv* pEnv, Object* this, ...) |
| 36 | |
| 37 | We receive a collection of 32-bit values which correspond to arguments from |
| 38 | the interpreter (e.g. float occupies one, double occupies two). It's up to |
| 39 | us to convert these into local calling conventions. |
| 40 | |
| 41 | Please notice that argc in dvmPlatformInvoke does NOT include pEnv and clazz/this. |
| 42 | */ |
| 43 | |
| 44 | .text |
| 45 | .align 2 |
| 46 | .globl dvmPlatformInvoke |
| 47 | .ent dvmPlatformInvoke |
| 48 | /* |
| 49 | * On entry: |
| 50 | * a0 JNIEnv (can be left alone) |
| 51 | * a1 clazz (NULL for virtual method calls, non-NULL for static) |
| 52 | * a2 argInfo |
| 53 | * a3 argc (number of 32-bit values in argv) |
| 54 | * MIPS reservers 16 bytes on stack even if the first 4 args are passed by |
| 55 | * reg a0-a3. That's different from ARM. |
| 56 | * [sp + 16] argv |
| 57 | * [sp + 20] short signature |
| 58 | * [sp + 24] func |
| 59 | * [sp + 28] pReturn |
| 60 | * |
| 61 | * For a virtual method call, the "this" reference is in argv[0]. |
| 62 | * |
| 63 | * argInfo (32-bit int) layout: |
| 64 | * SRRRLLLL FFFFFFFF FFFFFFFF FFFFFFFF |
| 65 | * |
| 66 | * S - if set, do things the hard way (scan the signature) |
| 67 | * R - return type enumeration, really only important for hardware FP |
| 68 | * L - number of double-words (64 bits!) of storage required on stack (0-30 words) |
| 69 | * F - pad flag -- if set, write a pad word to the stack |
| 70 | * |
| 71 | * With this arrangement we can efficiently push up to 24 words of arguments |
| 72 | * onto the stack. Anything requiring more than that -- which should happen |
| 73 | * rarely to never -- can do the slow signature scan. |
| 74 | * |
| 75 | * (We could pack the Fs more efficiently -- we know we never push two pads |
| 76 | * in a row, and the first word can never be a pad -- but there's really |
| 77 | * no need for it.) |
| 78 | * |
| 79 | * NOTE: if the called function has more than 4 words of arguments, gdb |
| 80 | * will not be able to unwind the stack past this method. The only way |
| 81 | * around this is to convince gdb to respect an explicit frame pointer. |
| 82 | */ |
| 83 | |
| 84 | /* Stack: |
| 85 | * High |
| 86 | * ____________ |
| 87 | * |__28______| pReturn |
| 88 | * |__24______| func |
| 89 | * |__20______| short signature |
| 90 | * |__16______| argv |
| 91 | * |__12______| reserved (a3: argc) |
| 92 | * |__8_______| reserved (a2: arg) |
| 93 | * |__4_______| reserved (a1: clazz) |
| 94 | *__sp on entry_->_|__0_______|_reserved (a0: JNIenv) |
| 95 | * |__________| saved ra |
| 96 | * |__________| saved fp |
| 97 | * |__________| saved s0 |
| 98 | * |__________| spare |
| 99 | * |__________| saved s2 |
| 100 | *"framepointer"->_|__________| pad for 8 bytes aligned |
| 101 | * |__________| other argv or pad |
| 102 | * |__________| other argv or pad |
| 103 | * |__________| other argv or pad |
| 104 | * |__________| other argv or pad |
| 105 | * |__________| other argv or pad |
| 106 | * |__________| other argv or pad |
| 107 | * |__________| reserved for a3 |
| 108 | * |__________| reserved for a2 |
| 109 | * |__________| reserved for a1 |
| 110 | *_____new sp___-> |__________| reserved for a0 |
| 111 | * (new sp: sp when call native method) |
| 112 | */ |
| 113 | |
| 114 | /* Register usage: |
| 115 | * |
| 116 | * s0: pReturn |
| 117 | * s2: Return type |
| 118 | * These registers should be saved to and restored from stack. |
| 119 | * |
| 120 | * t0: argv |
| 121 | * t9: func |
| 122 | * These registers do not need to be saved. |
| 123 | * |
| 124 | * We put the stack size into register s1 because we can not know the size |
| 125 | * of stack at the beginning. This size can be calculated with the help |
| 126 | * of hints in jniarginfo. |
| 127 | * |
| 128 | */ |
| 129 | |
| 130 | dvmPlatformInvoke: |
| 131 | .set noreorder |
| 132 | .cpload $t9 |
| 133 | .set reorder |
| 134 | |
| 135 | /* Do we have arg padding flags in "argInfo"? Check bit 31 */ |
| 136 | bltz $a2,.Lno_arginfo |
| 137 | |
| 138 | /* Fast path. We have hints. */ |
| 139 | /* save fp and ra to stack */ |
| 140 | #define FSIZE 24 |
| 141 | subu $sp,FSIZE |
| 142 | sw $ra,20($sp) |
| 143 | sw $fp,16($sp) |
| 144 | sw $s0,12($sp) |
| 145 | sw $s2,4($sp) |
| 146 | move $fp,$sp |
| 147 | |
| 148 | lw $t0,FSIZE+16($sp) /* t0 <- argv */ |
| 149 | lw $t9,FSIZE+24($sp) /* t9 <- func */ |
| 150 | lw $s0,FSIZE+28($sp) /* s0 <- pReturn */ |
| 151 | |
| 152 | /* Is the method static? */ |
| 153 | bnez $a1,1f |
| 154 | /* Not static: a1 <- *argv++ ("this"), argc-- */ |
| 155 | lw $a1,($t0) |
| 156 | addiu $t0,4 |
| 157 | addiu $a3,-1 |
| 158 | 1: |
| 159 | /* expand the stack for args */ |
| 160 | srl $s2,$a2,28 /* s2 <- returnType */ |
| 161 | srl $t1,$a2,21 |
| 162 | andi $t1,0x78 /* t1 <- stackSize in bytes */ |
| 163 | |
| 164 | addiu $t1,16 /* include space for a0/a1/a2/a3 */ |
| 165 | subu $sp,$t1 |
| 166 | addiu $t1,$sp,8 |
| 167 | |
| 168 | /* |
| 169 | * t0 :argv |
| 170 | * t1 :sp+8(first arg position in stack except pEnv and clazz/this) |
| 171 | * a2 :argInfo |
| 172 | * a3 :argc |
| 173 | * sp :new stack bottom |
| 174 | */ |
| 175 | |
| 176 | /* first two args or one args and pad */ |
| 177 | blez $a3,.Largs_done |
| 178 | lw $t2,($t0) |
| 179 | addiu $t0,4 |
| 180 | addiu $a3,-1 |
| 181 | sw $t2,($t1) |
| 182 | addiu $t1,4 |
| 183 | srl $a2,1 |
| 184 | blez $a3,.Largs_done |
| 185 | |
| 186 | andi $t3,$a2,0x1 /* the second position is a pad? */ |
| 187 | bnez $t3,.Lpad0 |
| 188 | |
| 189 | lw $t2,($t0) |
| 190 | addiu $t0,4 |
| 191 | addiu $a3,-1 |
| 192 | sw $t2,($t1) |
| 193 | .Lpad0: |
| 194 | addiu $t1,4 |
| 195 | srl $a2,1 |
| 196 | blez $a3,.Largs_done |
| 197 | |
| 198 | .Lloop1: |
| 199 | /* copy other args |
| 200 | * $fp: sp top for args |
| 201 | * $t1: sp for next arg |
| 202 | */ |
| 203 | beq $t1,$fp,.Largs_done |
| 204 | andi $t3,$a2,0x1 |
| 205 | srl $a2,1 |
| 206 | bnez $t3,.Lpad |
| 207 | lw $t2,($t0) |
| 208 | addiu $t0,4 |
| 209 | sw $t2,($t1) |
| 210 | .Lpad: |
| 211 | addiu $t1,4 |
| 212 | b .Lloop1 |
| 213 | |
| 214 | .Largs_done: |
| 215 | |
| 216 | /* |
| 217 | * We have copied args into stacks. Then copy argv[0]/argv[1] into |
| 218 | * reg a2/a3. You may find that if argv[0] is 32 bits and argv[1] |
| 219 | * is 64 bits, then we do not need to set reg a3 since it is a pad. |
| 220 | * However, copy a3 from argv is harmless. We do not need to set |
| 221 | * a0(pEnv)/a1(clazz/this) since they are already there. |
| 222 | */ |
| 223 | |
| 224 | /* |
| 225 | * sp: new stack |
| 226 | * s0: pReturn |
| 227 | * s2: Return type |
| 228 | * |
| 229 | */ |
| 230 | lw $a2,8($sp) |
| 231 | lw $a3,12($sp) |
| 232 | |
| 233 | /* Linux/PIC needs $t9 points to function address. |
| 234 | * call the function |
| 235 | */ |
| 236 | jalr $t9 |
| 237 | |
| 238 | /* function call return */ |
| 239 | /* 1. check the return type |
| 240 | * 2. if the return type is not DALVIK_JNI_RETURN_VOID then copy v0/v1 |
| 241 | * to pReturn |
| 242 | */ |
| 243 | beqz $s2,.Lend /* don't set result if return type is void */ |
| 244 | |
| 245 | #ifdef __mips_hard_float |
| 246 | mfc1 $t0,$f0 /* Get float ($f0) or double ($f1$f0) result */ |
| 247 | mfc1 $t1,$f1 |
| 248 | sltiu $t2,$s2,3 /* set t2 if return type is float or double */ |
| 249 | #ifdef HAVE_LITTLE_ENDIAN |
| 250 | /* Note: for little endian, the double result is in $v1:$v0 and float result is in $v0 */ |
| 251 | movn $v0,$t0,$t2 /* If the result type is float or double overwrite $v1/$v0 */ |
| 252 | movn $v1,$t1,$t2 |
| 253 | #else |
| 254 | /* Note: for big endian, the double result is in $v0:$v1 and float result is in $v0 */ |
| 255 | movn $v1,$t0,$t2 /* If the result type is float or double overwrite $v0/$v1 */ |
| 256 | movn $v0,$t1,$t2 |
| 257 | sltiu $t3,$s2,2 /* set t3 if return type is float */ |
| 258 | movn $v0,$t0,$t3 /* If the result type is float overwrite $v0 */ |
| 259 | #endif |
| 260 | #endif |
| 261 | |
| 262 | /* Store the result */ |
| 263 | sw $v0,0($s0) |
| 264 | sw $v1,4($s0) |
| 265 | |
| 266 | .Lend: |
| 267 | /* restore saved registers */ |
| 268 | move $sp,$fp |
| 269 | lw $ra,20($sp) |
| 270 | lw $fp,16($sp) |
| 271 | lw $s0,12($sp) |
| 272 | lw $s2,4($sp) |
| 273 | addiu $sp,FSIZE |
| 274 | jr $ra |
| 275 | |
| 276 | /* Slow path - just tail call the generic routine */ |
| 277 | .Lno_arginfo: |
| 278 | |
| 279 | la $t9,dvmPlatformInvokeFFI |
| 280 | j $t9 |
| 281 | |
| 282 | .end dvmPlatformInvoke |