1/* 2 * %CopyrightBegin% 3 4 * 5 * Copyright Ericsson AB 2001-2016. All Rights Reserved. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 * %CopyrightEnd% 20 */ 21 22#define ASM 23#include "hipe_sparc_asm.h" 24#include "hipe_literals.h" 25#include "hipe_mode_switch.h" 26 27 .section ".text" 28 .align 4 29 30/* 31 * Enter Erlang from C. 32 * Switch to a new register window. 33 * Create a new frame on the C stack. 34 * Save C return address in the frame. 35 * Retrieve the process pointer from the C argument registers. 36 */ 37#define ENTER_FROM_C \ 38 save %sp, -112, %sp; \ 39 st %i7, [%sp+96] 40 41/* 42 * Return to the calling C function. 43 * The return value is in %o0. 44 * 45 * .flush_exit saves NSP and other cached P state. 46 * .suspend_exit also saves RA. 47 */ 48.suspend_exit: 49 /* save RA, so we can be resumed */ 50 st RA, [P+P_NRA] 51.flush_exit: 52 /* restore C return address (hoisted to avoid stall) */ 53 ld [%sp+96], %i7 54 /* flush cached P state */ 55 SAVE_CACHED_STATE 56 /* restore callee-save registers, drop frame, return */ 57 jmp %i7+8 /* ret */ 58 restore %g0, %o0, %o0 /* kills P, moves our %o0 to caller's %o0 */ 59 60/* 61 * int hipe_sparc_call_to_native(Process *p); 62 * Emulated code recursively calls native code. 63 */ 64 .global hipe_sparc_call_to_native 65 .type hipe_sparc_call_to_native, #function 66 .proc 04 /* ??? */ 67hipe_sparc_call_to_native: 68 ENTER_FROM_C 69 /* prepare to call the target */ 70 ld [P+P_NCALLEE], TEMP_ARG0 71 /* get argument registers */ 72 LOAD_ARG_REGS 73 /* cache some P state in registers */ 74 RESTORE_CACHED_STATE 75/* FALLTHROUGH 76 * 77 * We export this return address so that hipe_mode_switch() can discover 78 * when native code tailcalls emulated code. 79 * Note: this is SPARC, so the value in the return address register 80 * is the address of the call/jmpl instruction itself. 81 */ 82 .global nbif_return 83nbif_return: 84 /* call the target */ 85 jmpl TEMP_ARG0, RA 86 nop 87/* FALLTHROUGH 88 * 89 * This is where native code returns to emulated code. 90 */ 91 st %o0, [P+P_ARG0] /* save retval */ 92 ba .flush_exit 93 mov HIPE_MODE_SWITCH_RES_RETURN, %o0 94 95/* 96 * int hipe_sparc_return_to_native(Process *p); 97 * Emulated code returns to its native code caller. 98 */ 99 .global hipe_sparc_return_to_native 100 .type hipe_sparc_return_to_native, #function 101 .proc 04 /* ??? */ 102hipe_sparc_return_to_native: 103 ENTER_FROM_C 104 /* restore return address */ 105 ld [P+P_NRA], RA 106 /* cache some P state in registers */ 107 RESTORE_CACHED_STATE 108 /* 109 * Return using the current return address. 110 * The parameters were popped at the original native-to-emulated 111 * call (hipe_call_from_native_is_recursive), so a plain ret suffices. 112 */ 113 jmp RA+8 114 ld [P+P_ARG0], %o0 /* delay slot: get return value */ 115 116/* 117 * int hipe_sparc_tailcall_to_native(Process *); 118 * Emulated code tailcalls native code. 119 */ 120 .global hipe_sparc_tailcall_to_native 121 .type hipe_sparc_tailcall_to_native, #function 122 .proc 04 /* ??? */ 123hipe_sparc_tailcall_to_native: 124 ENTER_FROM_C 125 /* prepare to call the target */ 126 ld [P+P_NCALLEE], TEMP_ARG0 127 /* get argument registers */ 128 LOAD_ARG_REGS 129 /* cache some P state in registers */ 130 RESTORE_CACHED_STATE 131 /* call the target */ 132 jmp TEMP_ARG0 133 ld [P+P_NRA], RA /* delay slot: restore return address */ 134 135/* 136 * int hipe_sparc_throw_to_native(Process *p); 137 * Emulated code throws an exception to its native code caller. 138 */ 139 .align 4 140 .global hipe_sparc_throw_to_native 141 .type hipe_sparc_throw_to_native, #function 142 .proc 04 /* ??? */ 143hipe_sparc_throw_to_native: 144 ENTER_FROM_C 145 /* prepare to invoke handler */ 146 ld [P+P_NCALLEE], TEMP_ARG0 /* set by hipe_find_handler() */ 147 /* cache some P state in registers */ 148 RESTORE_CACHED_STATE 149 /* invoke the handler */ 150 jmp TEMP_ARG0 151 nop 152 153/* 154 * Native code calls emulated code via a stub 155 * which should look as follows: 156 * 157 * stub for f/N: 158 * sethi %hi(f's export entry address), TEMP_ARG0 159 * mov RA, TEMP_RA ! because the call below clobbers RA (%o7) 160 * or TEMP_ARG0, %lo(f's export entry address), TEMP_ARG0 161 * call nbif_callemu ! clobbers RA! 162 * mov N, TEMP_ARG1 ! delay slot: TEMP_ARG1 := ARITY 163 * 164 * XXX. Different stubs for different number of register parameters? 165 */ 166 .global nbif_callemu 167nbif_callemu: 168 st TEMP_ARG0, [P+P_CALLEE_EXP] 169 st TEMP_ARG1, [P+P_ARITY] 170 st TEMP_RA, [P+P_NRA] 171 STORE_ARG_REGS 172 ba .flush_exit 173 mov HIPE_MODE_SWITCH_RES_CALL_EXPORTED, %o0 174 175/* 176 * nbif_apply 177 */ 178 .global nbif_apply 179nbif_apply: 180 STORE_ARG_REGS 181 ba .suspend_exit 182 mov HIPE_MODE_SWITCH_RES_APPLY, %o0 183 184/* 185 * Native code calls an emulated-mode closure via a stub defined below. 186 * 187 * The closure is appended as the last actual parameter, and parameters 188 * beyond the first few passed in registers are pushed onto the stack in 189 * left-to-right order. 190 * Hence, the location of the closure parameter only depends on the number 191 * of parameters in registers, not the total number of parameters. 192 */ 193#if NR_ARG_REGS >= 6 194 .global nbif_ccallemu6 195nbif_ccallemu6: 196 st ARG5, [P+P_ARG5] 197#if NR_ARG_REGS > 6 198 mov ARG6, ARG5 199#else 200 ld [NSP+0], ARG5 201#endif 202 /*FALLTHROUGH*/ 203#endif 204 205#if NR_ARG_REGS >= 5 206 .global nbif_ccallemu5 207nbif_ccallemu5: 208 st ARG4, [P+P_ARG4] 209#if NR_ARG_REGS > 5 210 mov ARG5, ARG4 211#else 212 ld [NSP+0], ARG4 213#endif 214 /*FALLTHROUGH*/ 215#endif 216 217#if NR_ARG_REGS >= 4 218 .global nbif_ccallemu4 219nbif_ccallemu4: 220 st ARG3, [P+P_ARG3] 221#if NR_ARG_REGS > 4 222 mov ARG4, ARG3 223#else 224 ld [NSP+0], ARG3 225#endif 226 /*FALLTHROUGH*/ 227#endif 228 229#if NR_ARG_REGS >= 3 230 .global nbif_ccallemu3 231nbif_ccallemu3: 232 st ARG2, [P+P_ARG2] 233#if NR_ARG_REGS > 3 234 mov ARG3, ARG2 235#else 236 ld [NSP+0], ARG2 237#endif 238 /*FALLTHROUGH*/ 239#endif 240 241#if NR_ARG_REGS >= 2 242 .global nbif_ccallemu2 243nbif_ccallemu2: 244 st ARG1, [P+P_ARG1] 245#if NR_ARG_REGS > 2 246 mov ARG2, ARG1 247#else 248 ld [NSP+0], ARG1 249#endif 250 /*FALLTHROUGH*/ 251#endif 252 253#if NR_ARG_REGS >= 1 254 .global nbif_ccallemu1 255nbif_ccallemu1: 256 st ARG0, [P+P_ARG0] 257#if NR_ARG_REGS > 1 258 mov ARG1, ARG0 259#else 260 ld [NSP+0], ARG0 261#endif 262 /*FALLTHROUGH*/ 263#endif 264 265 .global nbif_ccallemu0 266nbif_ccallemu0: 267 /* We use %o1 not ARG0 here because ARG0 is not 268 defined when NR_ARG_REGS == 0. */ 269#if NR_ARG_REGS == 0 270 ld [NSP+0], %o1 /* get the closure */ 271#endif 272 st %o1, [P+P_CLOSURE] /* save the closure */ 273 ba .suspend_exit 274 mov HIPE_MODE_SWITCH_RES_CALL_CLOSURE, %o0 275 276/* 277 * This is where native code suspends. 278 */ 279 .global nbif_suspend_0 280nbif_suspend_0: 281 ba .suspend_exit 282 mov HIPE_MODE_SWITCH_RES_SUSPEND, %o0 283 284/* 285 * Suspend from a receive (waiting for a message) 286 */ 287 .global nbif_suspend_msg 288nbif_suspend_msg: 289 ba .suspend_exit 290 mov HIPE_MODE_SWITCH_RES_WAIT, %o0 291 292/* 293 * Suspend from a receive with a timeout (waiting for a message) 294 * if (!(p->flags & F_TIMO)) { suspend } 295 * else { return 0; } 296 */ 297 .global nbif_suspend_msg_timeout 298nbif_suspend_msg_timeout: 299 ld [P+P_FLAGS], %o1 300 /* this relies on F_TIMO (1<<2) fitting in a simm13 */ 301 andcc %o1, F_TIMO, %g0 302 bz,a .suspend_exit 303 mov HIPE_MODE_SWITCH_RES_WAIT_TIMEOUT, %o0 /* delay slot */ 304 /* timeout has occurred */ 305 jmp RA+8 306 mov 0, %o0 307 308/* 309 * This is the default exception handler for native code. 310 */ 311 .global nbif_fail 312nbif_fail: 313 ba .flush_exit 314 mov HIPE_MODE_SWITCH_RES_THROW, %o0 315 316 .global nbif_0_gc_after_bif 317 .global nbif_1_gc_after_bif 318 .global nbif_2_gc_after_bif 319 .global nbif_3_gc_after_bif 320 .global nbif_4_gc_after_bif 321nbif_0_gc_after_bif: 322 ba .gc_after_bif 323 mov 0, %o1 /* delay slot */ 324nbif_1_gc_after_bif: 325 ba .gc_after_bif 326 mov 1, %o1 /* delay slot */ 327nbif_2_gc_after_bif: 328 ba .gc_after_bif 329 mov 2, %o1 /* delay slot */ 330nbif_3_gc_after_bif: 331 ba .gc_after_bif 332 mov 3, %o1 /* delay slot */ 333nbif_4_gc_after_bif: 334 mov 4, %o1 335 /*FALLTHROUGH*/ 336.gc_after_bif: 337 st %o1, [P+P_NARITY] 338 st TEMP_RA, [P+P_NRA] 339 st NSP, [P+P_NSP] 340 mov RA, TEMP_RA 341 mov 0, %o3 /* Pass 0 in arity */ 342 mov 0, %o2 /* Pass NULL in regs */ 343 mov %o0, %o1 344 call erts_gc_after_bif_call 345 mov P, %o0 /* delay slot */ 346 mov TEMP_RA, RA 347 ld [P+P_NRA], TEMP_RA 348 jmp RA+8 349 st %g0, [P+P_NARITY] /* delay slot */ 350 351/* 352 * We end up here when a BIF called from native signals an 353 * exceptional condition. 354 * HP has not been read from P. 355 * NSP has not been saved in P. 356 * TEMP_LR contains a copy of LR 357 */ 358 .global nbif_0_simple_exception 359nbif_0_simple_exception: 360 ba .nbif_simple_exception 361 mov 0, %o1 /* delay slot */ 362 .global nbif_1_simple_exception 363nbif_1_simple_exception: 364 ba .nbif_simple_exception 365 mov 1, %o1 /* delay slot */ 366 .global nbif_2_simple_exception 367nbif_2_simple_exception: 368 ba .nbif_simple_exception 369 mov 2, %o1 /* delay slot */ 370 .global nbif_3_simple_exception 371nbif_3_simple_exception: 372 ba .nbif_simple_exception 373 mov 3, %o1 /* delay slot */ 374 .global nbif_4_simple_exception 375nbif_4_simple_exception: 376 mov 4, %o1 377 /*FALLTHROUGH*/ 378.nbif_simple_exception: 379 ld [P+P_FREASON], %o0 380 cmp %o0, FREASON_TRAP 381 beq .handle_trap 382 nop 383 /* 384 * Find and invoke catch handler (it must exist). 385 * HP has not been read from P. 386 * NSP has not been saved in P. 387 * TEMP_RA should contain the current call's return address. 388 * %o1 should contain the current call's arity. 389 */ 390 st NSP, [P+P_NSP] 391 st TEMP_RA, [P+P_NRA] 392 st %o1, [P+P_NARITY] 393 /* find and prepare to invoke the handler */ 394 call hipe_handle_exception /* Note: hipe_handle_exception() conses */ 395 mov P, %o0 /* delay slot */ 396 /* prepare to invoke the handler */ 397 ld [P+P_NCALLEE], %o0 /* set by hipe_find_handler() */ 398 RESTORE_CACHED_STATE 399 /* now invoke the handler */ 400 jmp %o0 401 nop 402 403 /* 404 * A BIF failed with freason TRAP: 405 * - the BIF's arity is in %o1 406 * - the native RA was saved in TEMP_RA before the BIF call 407 * - HP has not been read from P 408 * - NSP has not been saved in P 409 */ 410.handle_trap: 411 mov HIPE_MODE_SWITCH_RES_TRAP, %o0 412.bif_exit: 413 /* restore C return address (hoisted to avoid stall) */ 414 ld [%sp+96], %i7 415 st NSP, [P+P_NSP] 416 st %o1, [P+P_NARITY] 417 st TEMP_RA, [P+P_NRA] 418 jmp %i7+8 419 restore %g0, %o0, %o0 420 421/* 422 * nbif_stack_trap_ra: trap return address for maintaining 423 * the gray/white stack boundary 424 */ 425 .global nbif_stack_trap_ra 426nbif_stack_trap_ra: /* a return address, not a function */ 427 nop /* ditto */ 428 nop /* ditto */ 429 /* This only handles a single return value. 430 If we have more, we need to save them in the PCB. */ 431 mov %o0, TEMP_ARG0 /* save retval */ 432 st NSP, [P+P_NSP] 433 call hipe_handle_stack_trap /* must not cons */ 434 mov P, %o0 /* delay slot */ 435 mov %o0, RA /* original RA */ 436 jmp RA+8 /* resume at original RA */ 437 mov TEMP_ARG0, %o0 /* delay slot: restore retval */ 438 439/* 440 * hipe_sparc_inc_stack 441 * Caller saved its RA in TEMP_RA (== TEMP1) before calling us. 442 */ 443 .global hipe_sparc_inc_stack 444hipe_sparc_inc_stack: 445 STORE_ARG_REGS 446 mov RA, TEMP_ARG0 447 st NSP, [P+P_NSP] 448 /* hipe_inc_nstack reads and writes NSP and NSP_LIMIT, 449 but does not access LR/RA, HP, or FCALLS. */ 450 call hipe_inc_nstack 451 mov P, %o0 /* delay slot */ 452 LOAD_ARG_REGS 453 /* this relies on LOAD_ARG_REGS not clobbering TEMP_ARG0 */ 454 jmp TEMP_ARG0+8 455 ld [P+P_NSP], NSP /* delay slot */ 456 457#if defined(__linux__) && defined(__ELF__) 458.section .note.GNU-stack,"",%progbits 459#endif 460