1/* ----------------------------------------------------------------------- 2 o32.S - Copyright (c) 1996, 1998, 2005 Red Hat, Inc. 3 4 MIPS Foreign Function Interface 5 6 Permission is hereby granted, free of charge, to any person obtaining 7 a copy of this software and associated documentation files (the 8 ``Software''), to deal in the Software without restriction, including 9 without limitation the rights to use, copy, modify, merge, publish, 10 distribute, sublicense, and/or sell copies of the Software, and to 11 permit persons to whom the Software is furnished to do so, subject to 12 the following conditions: 13 14 The above copyright notice and this permission notice shall be included 15 in all copies or substantial portions of the Software. 16 17 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 21 ANY CLAIM, DAMAGES OR 22 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 OTHER DEALINGS IN THE SOFTWARE. 25 ----------------------------------------------------------------------- */ 26 27#define LIBFFI_ASM 28#include <fficonfig.h> 29#include <ffi.h> 30 31/* Only build this code if we are compiling for o32 */ 32 33#if defined(FFI_MIPS_O32) 34 35#define callback a0 36#define bytes a2 37#define flags a3 38 39#define SIZEOF_FRAME (4 * FFI_SIZEOF_ARG + 2 * FFI_SIZEOF_ARG) 40#define A3_OFF (SIZEOF_FRAME + 3 * FFI_SIZEOF_ARG) 41#define FP_OFF (SIZEOF_FRAME - 2 * FFI_SIZEOF_ARG) 42#define RA_OFF (SIZEOF_FRAME - 1 * FFI_SIZEOF_ARG) 43 44 .abicalls 45 .text 46 .align 2 47 .globl ffi_call_O32 48 .ent ffi_call_O32 49ffi_call_O32: 50$LFB0: 51 # Prologue 52 SUBU $sp, SIZEOF_FRAME # Frame size 53$LCFI0: 54 REG_S $fp, FP_OFF($sp) # Save frame pointer 55$LCFI1: 56 REG_S ra, RA_OFF($sp) # Save return address 57$LCFI2: 58 move $fp, $sp 59 60$LCFI3: 61 move t9, callback # callback function pointer 62 REG_S flags, A3_OFF($fp) # flags 63 64 # Allocate at least 4 words in the argstack 65 LI v0, 4 * FFI_SIZEOF_ARG 66 blt bytes, v0, sixteen 67 68 ADDU v0, bytes, 7 # make sure it is aligned 69 and v0, -8 # to an 8 byte boundry 70 71sixteen: 72 SUBU $sp, v0 # move the stack pointer to reflect the 73 # arg space 74 75 ADDU a0, $sp, 4 * FFI_SIZEOF_ARG 76 77 jalr t9 78 79 REG_L t0, A3_OFF($fp) # load the flags word 80 SRL t2, t0, 4 # shift our arg info 81 and t0, ((1<<4)-1) # mask out the return type 82 83 ADDU $sp, 4 * FFI_SIZEOF_ARG # adjust $sp to new args 84 85 bnez t0, pass_d # make it quick for int 86 REG_L a0, 0*FFI_SIZEOF_ARG($sp) # just go ahead and load the 87 REG_L a1, 1*FFI_SIZEOF_ARG($sp) # four regs. 88 REG_L a2, 2*FFI_SIZEOF_ARG($sp) 89 REG_L a3, 3*FFI_SIZEOF_ARG($sp) 90 b call_it 91 92pass_d: 93 bne t0, FFI_ARGS_D, pass_f 94 l.d $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args 95 REG_L a2, 2*FFI_SIZEOF_ARG($sp) # passing a double 96 REG_L a3, 3*FFI_SIZEOF_ARG($sp) 97 b call_it 98 99pass_f: 100 bne t0, FFI_ARGS_F, pass_d_d 101 l.s $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args 102 REG_L a1, 1*FFI_SIZEOF_ARG($sp) # passing a float 103 REG_L a2, 2*FFI_SIZEOF_ARG($sp) 104 REG_L a3, 3*FFI_SIZEOF_ARG($sp) 105 b call_it 106 107pass_d_d: 108 bne t0, FFI_ARGS_DD, pass_f_f 109 l.d $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args 110 l.d $f14, 2*FFI_SIZEOF_ARG($sp) # passing two doubles 111 b call_it 112 113pass_f_f: 114 bne t0, FFI_ARGS_FF, pass_d_f 115 l.s $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args 116 l.s $f14, 1*FFI_SIZEOF_ARG($sp) # passing two floats 117 REG_L a2, 2*FFI_SIZEOF_ARG($sp) 118 REG_L a3, 3*FFI_SIZEOF_ARG($sp) 119 b call_it 120 121pass_d_f: 122 bne t0, FFI_ARGS_DF, pass_f_d 123 l.d $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args 124 l.s $f14, 2*FFI_SIZEOF_ARG($sp) # passing double and float 125 REG_L a3, 3*FFI_SIZEOF_ARG($sp) 126 b call_it 127 128pass_f_d: 129 # assume that the only other combination must be float then double 130 # bne t0, FFI_ARGS_F_D, call_it 131 l.s $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args 132 l.d $f14, 2*FFI_SIZEOF_ARG($sp) # passing double and float 133 134call_it: 135 # Load the function pointer 136 REG_L t9, SIZEOF_FRAME + 5*FFI_SIZEOF_ARG($fp) 137 138 # If the return value pointer is NULL, assume no return value. 139 REG_L t1, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp) 140 beqz t1, noretval 141 142 bne t2, FFI_TYPE_INT, retlonglong 143 jalr t9 144 REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp) 145 REG_S v0, 0(t0) 146 b epilogue 147 148retlonglong: 149 # Really any 64-bit int, signed or not. 150 bne t2, FFI_TYPE_UINT64, retfloat 151 jalr t9 152 REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp) 153 REG_S v1, 4(t0) 154 REG_S v0, 0(t0) 155 b epilogue 156 157retfloat: 158 bne t2, FFI_TYPE_FLOAT, retdouble 159 jalr t9 160 REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp) 161 s.s $f0, 0(t0) 162 b epilogue 163 164retdouble: 165 bne t2, FFI_TYPE_DOUBLE, noretval 166 jalr t9 167 REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp) 168 s.d $f0, 0(t0) 169 b epilogue 170 171noretval: 172 jalr t9 173 174 # Epilogue 175epilogue: 176 move $sp, $fp 177 REG_L $fp, FP_OFF($sp) # Restore frame pointer 178 REG_L ra, RA_OFF($sp) # Restore return address 179 ADDU $sp, SIZEOF_FRAME # Fix stack pointer 180 j ra 181 182$LFE0: 183 .end ffi_call_O32 184 185 186/* ffi_closure_O32. Expects address of the passed-in ffi_closure 187 in t4 ($12). Stores any arguments passed in registers onto the 188 stack, then calls ffi_closure_mips_inner_O32, which 189 then decodes them. 190 191 Stack layout: 192 193 3 - a3 save 194 2 - a2 save 195 1 - a1 save 196 0 - a0 save, original sp 197 -1 - ra save 198 -2 - fp save 199 -3 - $16 (s0) save 200 -4 - cprestore 201 -5 - return value high (v1) 202 -6 - return value low (v0) 203 -7 - f14 (le high, be low) 204 -8 - f14 (le low, be high) 205 -9 - f12 (le high, be low) 206 -10 - f12 (le low, be high) 207 -11 - Called function a3 save 208 -12 - Called function a2 save 209 -13 - Called function a1 save 210 -14 - Called function a0 save, our sp and fp point here 211 */ 212 213#define SIZEOF_FRAME2 (14 * FFI_SIZEOF_ARG) 214#define A3_OFF2 (SIZEOF_FRAME2 + 3 * FFI_SIZEOF_ARG) 215#define A2_OFF2 (SIZEOF_FRAME2 + 2 * FFI_SIZEOF_ARG) 216#define A1_OFF2 (SIZEOF_FRAME2 + 1 * FFI_SIZEOF_ARG) 217#define A0_OFF2 (SIZEOF_FRAME2 + 0 * FFI_SIZEOF_ARG) 218#define RA_OFF2 (SIZEOF_FRAME2 - 1 * FFI_SIZEOF_ARG) 219#define FP_OFF2 (SIZEOF_FRAME2 - 2 * FFI_SIZEOF_ARG) 220#define S0_OFF2 (SIZEOF_FRAME2 - 3 * FFI_SIZEOF_ARG) 221#define GP_OFF2 (SIZEOF_FRAME2 - 4 * FFI_SIZEOF_ARG) 222#define V1_OFF2 (SIZEOF_FRAME2 - 5 * FFI_SIZEOF_ARG) 223#define V0_OFF2 (SIZEOF_FRAME2 - 6 * FFI_SIZEOF_ARG) 224#define FA_1_1_OFF2 (SIZEOF_FRAME2 - 7 * FFI_SIZEOF_ARG) 225#define FA_1_0_OFF2 (SIZEOF_FRAME2 - 8 * FFI_SIZEOF_ARG) 226#define FA_0_1_OFF2 (SIZEOF_FRAME2 - 9 * FFI_SIZEOF_ARG) 227#define FA_0_0_OFF2 (SIZEOF_FRAME2 - 10 * FFI_SIZEOF_ARG) 228 229 .text 230 .align 2 231 .globl ffi_closure_O32 232 .ent ffi_closure_O32 233ffi_closure_O32: 234$LFB1: 235 # Prologue 236 .frame $fp, SIZEOF_FRAME2, ra 237 .set noreorder 238 .cpload t9 239 .set reorder 240 SUBU $sp, SIZEOF_FRAME2 241 .cprestore GP_OFF2 242$LCFI4: 243 REG_S $16, S0_OFF2($sp) # Save s0 244 REG_S $fp, FP_OFF2($sp) # Save frame pointer 245 REG_S ra, RA_OFF2($sp) # Save return address 246$LCFI6: 247 move $fp, $sp 248 249$LCFI7: 250 # Store all possible argument registers. If there are more than 251 # four arguments, then they are stored above where we put a3. 252 REG_S a0, A0_OFF2($fp) 253 REG_S a1, A1_OFF2($fp) 254 REG_S a2, A2_OFF2($fp) 255 REG_S a3, A3_OFF2($fp) 256 257 # Load ABI enum to s0 258 REG_L $16, 20($12) # cif pointer follows tramp. 259 REG_L $16, 0($16) # abi is first member. 260 261 li $13, 1 # FFI_O32 262 bne $16, $13, 1f # Skip fp save if FFI_O32_SOFT_FLOAT 263 264 # Store all possible float/double registers. 265 s.d $f12, FA_0_0_OFF2($fp) 266 s.d $f14, FA_1_0_OFF2($fp) 2671: 268 # Call ffi_closure_mips_inner_O32 to do the work. 269 la t9, ffi_closure_mips_inner_O32 270 move a0, $12 # Pointer to the ffi_closure 271 addu a1, $fp, V0_OFF2 272 addu a2, $fp, A0_OFF2 273 addu a3, $fp, FA_0_0_OFF2 274 jalr t9 275 276 # Load the return value into the appropriate register. 277 move $8, $2 278 li $9, FFI_TYPE_VOID 279 beq $8, $9, closure_done 280 281 li $13, 1 # FFI_O32 282 bne $16, $13, 1f # Skip fp restore if FFI_O32_SOFT_FLOAT 283 284 li $9, FFI_TYPE_FLOAT 285 l.s $f0, V0_OFF2($fp) 286 beq $8, $9, closure_done 287 288 li $9, FFI_TYPE_DOUBLE 289 l.d $f0, V0_OFF2($fp) 290 beq $8, $9, closure_done 2911: 292 REG_L $3, V1_OFF2($fp) 293 REG_L $2, V0_OFF2($fp) 294 295closure_done: 296 # Epilogue 297 move $sp, $fp 298 REG_L $16, S0_OFF2($sp) # Restore s0 299 REG_L $fp, FP_OFF2($sp) # Restore frame pointer 300 REG_L ra, RA_OFF2($sp) # Restore return address 301 ADDU $sp, SIZEOF_FRAME2 302 j ra 303$LFE1: 304 .end ffi_closure_O32 305 306/* DWARF-2 unwind info. */ 307 308 .section .eh_frame,"a",@progbits 309$Lframe0: 310 .4byte $LECIE0-$LSCIE0 # Length of Common Information Entry 311$LSCIE0: 312 .4byte 0x0 # CIE Identifier Tag 313 .byte 0x1 # CIE Version 314 .ascii "zR\0" # CIE Augmentation 315 .uleb128 0x1 # CIE Code Alignment Factor 316 .sleb128 4 # CIE Data Alignment Factor 317 .byte 0x1f # CIE RA Column 318 .uleb128 0x1 # Augmentation size 319 .byte 0x00 # FDE Encoding (absptr) 320 .byte 0xc # DW_CFA_def_cfa 321 .uleb128 0x1d 322 .uleb128 0x0 323 .align 2 324$LECIE0: 325$LSFDE0: 326 .4byte $LEFDE0-$LASFDE0 # FDE Length 327$LASFDE0: 328 .4byte $LASFDE0-$Lframe0 # FDE CIE offset 329 .4byte $LFB0 # FDE initial location 330 .4byte $LFE0-$LFB0 # FDE address range 331 .uleb128 0x0 # Augmentation size 332 .byte 0x4 # DW_CFA_advance_loc4 333 .4byte $LCFI0-$LFB0 334 .byte 0xe # DW_CFA_def_cfa_offset 335 .uleb128 0x18 336 .byte 0x4 # DW_CFA_advance_loc4 337 .4byte $LCFI2-$LCFI0 338 .byte 0x11 # DW_CFA_offset_extended_sf 339 .uleb128 0x1e # $fp 340 .sleb128 -2 # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp) 341 .byte 0x11 # DW_CFA_offset_extended_sf 342 .uleb128 0x1f # $ra 343 .sleb128 -1 # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp) 344 .byte 0x4 # DW_CFA_advance_loc4 345 .4byte $LCFI3-$LCFI2 346 .byte 0xc # DW_CFA_def_cfa 347 .uleb128 0x1e 348 .uleb128 0x18 349 .align 2 350$LEFDE0: 351$LSFDE1: 352 .4byte $LEFDE1-$LASFDE1 # FDE Length 353$LASFDE1: 354 .4byte $LASFDE1-$Lframe0 # FDE CIE offset 355 .4byte $LFB1 # FDE initial location 356 .4byte $LFE1-$LFB1 # FDE address range 357 .uleb128 0x0 # Augmentation size 358 .byte 0x4 # DW_CFA_advance_loc4 359 .4byte $LCFI4-$LFB1 360 .byte 0xe # DW_CFA_def_cfa_offset 361 .uleb128 0x38 362 .byte 0x4 # DW_CFA_advance_loc4 363 .4byte $LCFI6-$LCFI4 364 .byte 0x11 # DW_CFA_offset_extended_sf 365 .uleb128 0x10 # $16 366 .sleb128 -3 # SIZEOF_FRAME2 - 3*FFI_SIZEOF_ARG($sp) 367 .byte 0x11 # DW_CFA_offset_extended_sf 368 .uleb128 0x1e # $fp 369 .sleb128 -2 # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp) 370 .byte 0x11 # DW_CFA_offset_extended_sf 371 .uleb128 0x1f # $ra 372 .sleb128 -1 # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp) 373 .byte 0x4 # DW_CFA_advance_loc4 374 .4byte $LCFI7-$LCFI6 375 .byte 0xc # DW_CFA_def_cfa 376 .uleb128 0x1e 377 .uleb128 0x38 378 .align 2 379$LEFDE1: 380 381#endif 382