Clean up RCS ids
[projects/cmucl/cmucl.git] / src / lisp / ppc-arch.c
CommitLineData
eb44537a 1/*
2
eb44537a 3 This code was written as part of the CMU Common Lisp project at
4 Carnegie Mellon University, and has been placed in the public domain.
5
6*/
7
8#include <stdio.h>
9
10#include "arch.h"
11#include "lisp.h"
12#include "internals.h"
13#include "globals.h"
14#include "validate.h"
15#include "os.h"
16#include "lispregs.h"
17#include "signal.h"
18#include "interrupt.h"
19#include "interr.h"
20
21 /* The header files may not define PT_DAR/PT_DSISR. This definition
22 is correct for all versions of ppc linux >= 2.0.30
23
24 As of DR2.1u4, MkLinux doesn't pass these registers to signal
25 handlers correctly; a patch is necessary in order to (partially)
26 correct this.
27
28 Even with the patch, the DSISR may not have its 'write' bit set
29 correctly (it tends not to be set if the fault was caused by
30 something other than a protection violation.)
9a8c1c2f 31
eb44537a 32 Caveat callers. */
33
34#ifndef PT_DAR
35#define PT_DAR 41
36#endif
37
38#ifndef PT_DSISR
39#define PT_DSISR 42
40#endif
41
cabad02a 42/*
43 * A macro to generate the instruction
44 *
45 * twllei r0, code
46 *
47 * This is what the ppc port uses to signal various traps like
48 * breakpoints and stuff.
49 */
50#define TWLLEI_R0(code) ((3<<26) | (6 << 21) | code)
51
9a8c1c2f 52char *
cb786538 53arch_init(fpu_mode_t mode)
eb44537a 54{
9a8c1c2f 55 return "lisp.core";
eb44537a 56}
57
9a8c1c2f 58os_vm_address_t arch_get_bad_addr(HANDLER_ARGS)
eb44537a 59{
838ff560 60 os_context_t *os_context = (os_context_t *) context;
9a8c1c2f 61 os_vm_address_t addr;
62
838ff560 63 addr = (os_vm_address_t) SC_REG(os_context, PT_DAR);
9a8c1c2f 64 return addr;
eb44537a 65}
eb44537a 66
9a8c1c2f 67
68void
69arch_skip_instruction(os_context_t * context)
eb44537a 70{
9a8c1c2f 71 /* Skip the offending instruction */
72 SC_PC(context) += 4;
eb44537a 73}
74
75unsigned char *
9a8c1c2f 76arch_internal_error_arguments(os_context_t * scp)
eb44537a 77{
9a8c1c2f 78 return (unsigned char *) (SC_PC(scp) + 4);
eb44537a 79}
80
9a8c1c2f 81boolean arch_pseudo_atomic_atomic(os_context_t * scp)
eb44537a 82{
9a8c1c2f 83 return (SC_REG(scp, reg_ALLOC) & 4);
eb44537a 84}
85
86#define PSEUDO_ATOMIC_INTERRUPTED_BIAS 0x7f000000
87
9a8c1c2f 88void
89arch_set_pseudo_atomic_interrupted(os_context_t * scp)
eb44537a 90{
ced8187e 91#if 0
9a8c1c2f 92 SC_REG(scp, reg_NL3) += PSEUDO_ATOMIC_INTERRUPTED_BIAS;
ced8187e 93#else
9a8c1c2f 94 SC_REG(scp, reg_ALLOC) |= 1;
ced8187e 95#endif
eb44537a 96}
97
9a8c1c2f 98unsigned long
eb44537a 99arch_install_breakpoint(void *pc)
100{
9a8c1c2f 101 unsigned long *ptr = (unsigned long *) pc;
102 unsigned long result = *ptr;
103
104 /*
105 * Insert a twllei r0, trap_Breakpoint instruction.
106 */
107 *ptr = TWLLEI_R0(trap_Breakpoint);
108 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
109
110 return result;
eb44537a 111}
112
9a8c1c2f 113void
eb44537a 114arch_remove_breakpoint(void *pc, unsigned long orig_inst)
115{
9a8c1c2f 116 *(unsigned long *) pc = orig_inst;
117 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned long));
eb44537a 118}
119
120static unsigned long *skipped_break_addr, displaced_after_inst;
121static sigset_t orig_sigmask;
122
9a8c1c2f 123void
124arch_do_displaced_inst(os_context_t * scp, unsigned long orig_inst)
eb44537a 125{
baacae3a 126 unsigned int *pc = (unsigned int *) SC_PC(scp);
9a8c1c2f 127
128 orig_sigmask = scp->uc_sigmask;
129 sigemptyset(&scp->uc_sigmask);
130 FILLBLOCKSET(&scp->uc_sigmask);
131
132 /* Put the original instruction back */
133 *pc = orig_inst;
134 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
135
baacae3a 136 skipped_break_addr = (unsigned long) pc;
9a8c1c2f 137
138 /*
139 * Replace the next instruction with a
140 * twllei r0, trap_AfterBreakpoint
141 */
142 displaced_after_inst = *++pc;
143 *pc = TWLLEI_R0(trap_AfterBreakpoint);
144 os_flush_icache((os_vm_address_t) pc, sizeof(unsigned int));
145
146 sigreturn(scp);
eb44537a 147}
148
46a4f482 149#ifdef GENCGC
150/*
151 * Return non-zero if the current instruction is an allocation trap
152 */
153static int
154allocation_trap_p(os_context_t * context)
155{
156 int result;
157 unsigned int *pc;
158 unsigned inst;
159 unsigned opcode;
160 unsigned src;
161 unsigned dst;
162
163 result = 0;
164
165 /*
166 * First, the instruction has to be a TWLGE temp, NL3, which as the
167 * format.
168 * | 6| 5| 5 | 5 | 10|1| width
169 * |31|5 |dst|src| 4|0| field
170 */
171 pc = (unsigned int *) SC_PC(context);
172 inst = *pc;
173
174#if 0
175 fprintf(stderr, "allocation_trap_p at %p: inst = 0x%08x\n", pc, inst);
176#endif
177
178 opcode = inst >> 26;
179 src = (inst >> 11) & 0x1f;
180 dst = (inst >> 16) & 0x1f;
181 if ((opcode == 31) && (src == reg_NL3) && (5 == ((inst >> 21) & 0x1f))
182 && (4 == ((inst >> 1) & 0x3ff))) {
183 /*
184 * We got the instruction. Now, look back to make sure it was
185 * proceeded by what we expected. 2 instructions back should be
186 * an ADD or ADDI instruction.
187 */
188 unsigned int add_inst;
189
190 add_inst = pc[-2];
191#if 0
192 fprintf(stderr, " add inst at %p: inst = 0x%08x\n",
193 pc - 2, add_inst);
194#endif
195 opcode = add_inst >> 26;
196 if ((opcode == 31) && (266 == ((add_inst >> 1) & 0x1ff))) {
197 return 1;
198 } else if ((opcode == 14)) {
199 return 1;
200 } else {
201 fprintf(stderr,
202 "Whoa! Got allocation trap not preceeded by an ADD or ADDI instruction: 0x%08x\n",
203 add_inst);
204 }
205 }
206 return 0;
207}
208
209/*
210 * Use this function to enable the minimum number of signals we need
211 * when our trap handler needs to call Lisp code that might cons. For
212 * consing to work with gencgc, we need to be able to trap the SIGILL
213 * signal to perform allocation.
214 */
215void
b8d0dfaf 216enable_some_signals(void)
46a4f482 217{
218 sigset_t sigs;
219
220#if 0
221 fprintf(stderr, "Enabling some signals\n");
222#endif
223#if 0
224 sigprocmask(SIG_SETMASK, &context->uc_sigmask, 0);
225#else
226 sigemptyset(&sigs);
227 sigaddset(&sigs, SIGILL);
228 sigaddset(&sigs, SIGBUS);
229 sigprocmask(SIG_UNBLOCK, &sigs, NULL);
230#endif
231#if 0
232 fprintf(stderr, "Some signals enabled\n");
233#endif
234}
235
236void
237handle_allocation_trap(os_context_t * context)
238{
239 unsigned int *pc;
240 unsigned int inst;
241 unsigned int or_inst;
242 unsigned int target;
243 unsigned int opcode;
244 int size;
245 int immed;
246 boolean were_in_lisp;
247 char *memory;
248 sigset_t block;
249
250 target = 0;
251 size = 0;
252
253#if 0
254 fprintf(stderr, "In handle_allocation_trap\n");
255#endif
256 /*
257 * I don't think it's possible for us NOT to be in lisp when we get
258 * here. Remove this later?
259 */
260 were_in_lisp = !foreign_function_call_active;
261
262 if (were_in_lisp) {
263 fake_foreign_function_call(context);
264 } else {
265 fprintf(stderr, "**** Whoa! allocation trap and we weren't in lisp!\n");
266 }
267
268 /*
269 * Look at current instruction: TWNE temp, NL3. We're here because
270 * temp > NL3 and temp is the end of the allocation, and NL3 is
271 * current-region-end-addr.
272 *
273 * We need to adjust temp and alloc-tn.
274 */
275
276 pc = (unsigned int *) SC_PC(context);
277 inst = pc[0];
278 target = (inst >> 16) & 0x1f;
279
280#if 0
281 fprintf(stderr, "handle_allocation_trap at %p:\n", pc);
282 fprintf(stderr, " trap inst = 0x%08x\n", inst);
283 fprintf(stderr, " target reg = %s\n", lisp_register_names[target]);
284#endif
285 /*
286 * Go back and look at the add/addi instruction. The second src arg
287 * is the size of the allocation. Get it and call alloc to allocate
288 * new space.
289 */
290 inst = pc[-2];
291 opcode = inst >> 26;
292#if 0
293 fprintf(stderr, " add inst = 0x%08x, opcode = %d\n", inst, opcode);
294#endif
295 if (opcode == 14) {
296 /*
297 * ADDI temp-tn, alloc-tn, size
298 *
299 * Extract the size
300 */
301 size = (inst & 0xffff);
302 } else if (opcode == 31) {
303 /*
304 * ADD temp-tn, alloc-tn, size-tn
305 *
306 * Extract the size
307 */
308 int reg;
309
310 reg = (inst >> 11) & 0x1f;
311#if 0
312 fprintf(stderr, " add, reg = %s\n", lisp_register_names[reg]);
313#endif
314 size = SC_REG(context, reg);
315 }
316#if 0
317 fprintf(stderr, "Alloc %d to %s\n", size, lisp_register_names[target]);
318#endif
319
320 /*
321 * Well, maybe not. sigill_handler probably shouldn't be unblocking
322 * all signals. So, let's enable just the signals we need. Since
323 * alloc might call GC, we need to have SIGILL enabled so we can do
324 * allocation. Do we need more?
325 */
326 enable_some_signals();
327
328#if 0
329 fprintf(stderr, "Ready to alloc\n");
330 fprintf(stderr, "free_pointer = 0x%08x\n",
331 current_dynamic_space_free_pointer);
332#endif
333 /*
334 * alloc-tn was incremented by size. Need to decrement it by size to restore it's original value.
335 */
336 current_dynamic_space_free_pointer =
337 (lispobj *) ((long) current_dynamic_space_free_pointer - size);
338#if 0
339 fprintf(stderr, "free_pointer = 0x%08x new\n",
340 current_dynamic_space_free_pointer);
341#endif
342
343 memory = (char *) alloc(size);
344
345#if 0
346 fprintf(stderr, "alloc returned %p\n", memory);
347 fprintf(stderr, "free_pointer = 0x%08x\n",
348 current_dynamic_space_free_pointer);
349#endif
350
351 /*
352 * The allocation macro wants the result to point to the end of the
353 * object!
354 */
355 memory += size;
356#if 0
357 fprintf(stderr, "object end at %p\n", memory);
358#endif
359 SC_REG(context, target) = (unsigned long) memory;
360 SC_REG(context, reg_ALLOC) =
361 (unsigned long) current_dynamic_space_free_pointer;
362
363 if (were_in_lisp) {
364 undo_fake_foreign_function_call(context);
365 }
366
367
368}
369#endif
370
9a8c1c2f 371static void
eb44537a 372sigill_handler(HANDLER_ARGS)
373{
838ff560 374 os_context_t *os_context = (os_context_t *) context;
9a8c1c2f 375 int badinst;
376 int opcode;
eb44537a 377
838ff560
RT
378 sigprocmask(SIG_SETMASK, &os_context->uc_sigmask, 0);
379 opcode = *((int *) SC_PC(os_context));
eb44537a 380
cabad02a 381#if 0
9a8c1c2f 382 printf("SIGILL entry: opcode = 0x%08x\n", opcode);
383 fflush(stdout);
cabad02a 384#endif
385
9a8c1c2f 386 if (opcode == ((3 << 26) | (0x18 << 21) | (reg_NL3 << 16))) {
387 /* Got a twnei reg_NL3,0 - check for deferred interrupt */
ced8187e 388#if 1
9a8c1c2f 389 /* Clear the pseudo-atomic-interrupted bit */
838ff560 390 SC_REG(os_context, reg_ALLOC) &= ~1;
ced8187e 391#else
838ff560 392 (SC_REG(os_context, reg_ALLOC) -= PSEUDO_ATOMIC_INTERRUPTED_BIAS);
ced8187e 393#endif
838ff560
RT
394 arch_skip_instruction(os_context);
395 interrupt_handle_pending(os_context);
eb44537a 396#ifdef DARWIN
9a8c1c2f 397 /* Work around G5 bug; fix courtesy gbyers via chandler */
838ff560 398 sigreturn(os_context);
eb44537a 399#endif
9a8c1c2f 400 return;
401 }
402
e34f6e4e 403 /* Is this an allocation trap? */
46a4f482 404#ifdef GENCGC
838ff560
RT
405 if (allocation_trap_p(os_context)) {
406 handle_allocation_trap(os_context);
407 arch_skip_instruction(os_context);
46a4f482 408#ifdef DARWIN
838ff560 409 sigreturn(os_context);
46a4f482 410#endif
e34f6e4e 411 return;
46a4f482 412 }
413#endif
414
9a8c1c2f 415 if ((opcode >> 16) == ((3 << 10) | (6 << 5))) {
416 /* twllei reg_ZERO,N will always trap if reg_ZERO = 0 */
417 int trap = opcode & 0x1f, extra = (opcode >> 5) & 0x1f;
eb44537a 418
cabad02a 419#if 0
9a8c1c2f 420 printf("SIGILL: TWLLEI, code = %d\n", trap);
421 fflush(stdout);
cabad02a 422#endif
423
9a8c1c2f 424 switch (trap) {
425 case trap_Halt:
838ff560 426 fake_foreign_function_call(os_context);
9a8c1c2f 427 lose("%%primitive halt called; the party is over.\n");
428
429 case trap_Error:
430 case trap_Cerror:
838ff560 431 interrupt_internal_error(signal, code, os_context,
9a8c1c2f 432 trap == trap_Cerror);
433 break;
434
435 case trap_PendingInterrupt:
838ff560
RT
436 arch_skip_instruction(os_context);
437 interrupt_handle_pending(os_context);
9a8c1c2f 438 break;
439
440 case trap_Breakpoint:
cabad02a 441#if 0
9a8c1c2f 442 printf("trap_Breakpoint\n");
443 fflush(stdout);
cabad02a 444#endif
838ff560 445 handle_breakpoint(signal, code, os_context);
9a8c1c2f 446 break;
447
448 case trap_FunctionEndBreakpoint:
cabad02a 449#if 0
9a8c1c2f 450 printf("trap_FunctionEndBreakpoint\n");
451 fflush(stdout);
cabad02a 452#endif
838ff560
RT
453 SC_PC(os_context) =
454 (int) handle_function_end_breakpoint(signal, code, os_context);
9a8c1c2f 455 break;
456
457 case trap_AfterBreakpoint:
336eb20e 458#if 0
459 fprintf(stderr, "trap_AfterBreakpoint: break_addr = %p\n",
460 skipped_break_addr);
e34f6e4e 461 fprintf(stderr, " CSP = %p\n",
838ff560 462 (void *) SC_REG(os_context, reg_CSP));
e34f6e4e 463 fprintf(stderr, " CFP = %p\n",
838ff560 464 (void *) SC_REG(os_context, reg_CFP));
e34f6e4e 465 fprintf(stderr, " OCFP = %p\n",
838ff560 466 (void *) SC_REG(os_context, reg_OCFP));
e34f6e4e 467#endif
9a8c1c2f 468 /* Put our breakpoint instruction back in */
469 *skipped_break_addr = TWLLEI_R0(trap_Breakpoint);
470 skipped_break_addr = NULL;
838ff560
RT
471 *(unsigned long *) SC_PC(os_context) = displaced_after_inst;
472 os_context->uc_sigmask = orig_sigmask;
9a8c1c2f 473
838ff560 474 os_flush_icache((os_vm_address_t) SC_PC(os_context),
9a8c1c2f 475 sizeof(unsigned long));
476 break;
477
478 default:
838ff560 479 interrupt_handle_now(signal, code, os_context);
9a8c1c2f 480 break;
481 }
eb44537a 482#ifdef DARWIN
9a8c1c2f 483 /* Work around G5 bug; fix courtesy gbyers via chandler */
838ff560 484 sigreturn(os_context);
eb44537a 485#endif
9a8c1c2f 486 return;
487 }
488 if (((opcode >> 26) == 3) && (((opcode >> 21) & 31) == 24)) {
838ff560 489 interrupt_internal_error(signal, code, os_context, 0);
eb44537a 490#ifdef DARWIN
9a8c1c2f 491 /* Work around G5 bug; fix courtesy gbyers via chandler */
838ff560 492 sigreturn(os_context);
eb44537a 493#endif
9a8c1c2f 494 return;
495 }
eb44537a 496
838ff560 497 interrupt_handle_now(signal, code, os_context);
eb44537a 498#ifdef DARWIN
9a8c1c2f 499 /* Work around G5 bug; fix courtesy gbyers via chandler */
838ff560 500 sigreturn(os_context);
eb44537a 501#endif
502}
503
504
9a8c1c2f 505void
b8d0dfaf 506arch_install_interrupt_handlers(void)
eb44537a 507{
9a8c1c2f 508 interrupt_install_low_level_handler(SIGILL, sigill_handler);
509 interrupt_install_low_level_handler(SIGTRAP, sigill_handler);
eb44537a 510}
511
512
9a8c1c2f 513extern lispobj call_into_lisp(lispobj fun, lispobj * args, int nargs);
eb44537a 514
9a8c1c2f 515lispobj
516funcall0(lispobj function)
eb44537a 517{
518 lispobj *args = current_control_stack_pointer;
519
520 return call_into_lisp(function, args, 0);
521}
522
9a8c1c2f 523lispobj
524funcall1(lispobj function, lispobj arg0)
eb44537a 525{
526 lispobj *args = current_control_stack_pointer;
527
528 current_control_stack_pointer += 1;
529 args[0] = arg0;
530
531 return call_into_lisp(function, args, 1);
532}
533
9a8c1c2f 534lispobj
535funcall2(lispobj function, lispobj arg0, lispobj arg1)
eb44537a 536{
537 lispobj *args = current_control_stack_pointer;
538
539 current_control_stack_pointer += 2;
540 args[0] = arg0;
541 args[1] = arg1;
542
543 return call_into_lisp(function, args, 2);
544}
545
9a8c1c2f 546lispobj
547funcall3(lispobj function, lispobj arg0, lispobj arg1, lispobj arg2)
eb44537a 548{
549 lispobj *args = current_control_stack_pointer;
550
551 current_control_stack_pointer += 3;
552 args[0] = arg0;
553 args[1] = arg1;
554 args[2] = arg2;
555
556 return call_into_lisp(function, args, 3);
557}
558
559void
560ppc_flush_icache(os_vm_address_t address, os_vm_size_t length)
561{
9a8c1c2f 562 os_vm_address_t end =
563 (os_vm_address_t) ((int) (address + length + (32 - 1)) & ~(32 - 1));
564 extern void ppc_flush_cache_line(os_vm_address_t);
eb44537a 565
9a8c1c2f 566 while (address < end) {
567 ppc_flush_cache_line(address);
568 address += 32;
569 }
eb44537a 570}
6a4fd3c3 571
572#ifdef LINKAGE_TABLE
573/* Linkage tables for PowerPC
574 *
575 * Linkage entry size is 16, because we need at least 4 instructions to
576 * implement a jump.
577 */
578
579/*
580 * This had better match lisp::target-foreign-linkage-entry-size in
581 * ppco/parms.lisp! Each entry is 6 instructions long, so at least
582 * 24 bytes.
583 */
584#ifndef LinkageEntrySize
585#define LinkageEntrySize (8*4)
586#endif
587
588/*
589 * Define the registers to use in the linkage jump table. Can be the
590 * same. Some care must be exercised when choosing these. It has to be
591 * a register that is not otherwise being used. reg_NFP is a good
592 * choice. call_into_c trashes reg_NFP without preserving it, so we can
593 * trash it in the linkage jump table.
594 */
595#define LINKAGE_TEMP_REG reg_NFP
596#define LINKAGE_ADDR_REG reg_A0
597
598/*
599 * Insert the necessary jump instructions at the given address.
600 */
601void
9a8c1c2f 602arch_make_jump_entry(void *reloc_addr, void *target_addr)
6a4fd3c3 603{
9a8c1c2f 604 /*
605 * Make JMP to function entry.
606 *
607 * The instruction sequence is:
608 *
609 * addis temp, 0, (hi part of reloc)
610 * ori temp, temp, (lo part of reloc)
611 * addis addr, 0, (hi part of addr)
612 * ori addr, addr, (low part of addr)
613 * mtctr addr
614 * bctr
615 *
616 */
617 int *inst_ptr;
618 unsigned long hi; /* Top 16 bits of address */
619 unsigned long lo; /* Low 16 bits of address */
620 unsigned int inst;
621
622 inst_ptr = (int *) reloc_addr;
623
624 /*
625 * Split the target address into hi and lo parts for the addis/ori
626 * instructions.
627 */
628 hi = (unsigned long) reloc_addr;
629 lo = hi & 0xffff;
630 hi >>= 16;
631
632 /*
633 * addis 3, 0, (hi part)
634 */
635 inst = (15 << 26) | (LINKAGE_ADDR_REG << 21) | (0 << 16) | hi;
636 *inst_ptr++ = inst;
637
638 /*
639 * ori 3, 3, (lo part)
640 */
641
642 inst =
643 (24 << 26) | (LINKAGE_ADDR_REG << 21) | (LINKAGE_ADDR_REG << 16) | lo;
644 *inst_ptr++ = inst;
645
646 /*
647 * Split the target address into hi and lo parts for the addis/ori
648 * instructions.
649 */
650
651 hi = (unsigned long) target_addr;
652 lo = hi & 0xffff;
653 hi >>= 16;
654
655 /*
656 * addis 13, 0, (hi part)
657 */
658
659 inst = (15 << 26) | (LINKAGE_TEMP_REG << 21) | (0 << 16) | hi;
660 *inst_ptr++ = inst;
661
662 /*
663 * ori 13, 13, (lo part)
664 */
665
666 inst =
667 (24 << 26) | (LINKAGE_TEMP_REG << 21) | (LINKAGE_TEMP_REG << 16) | lo;
668 *inst_ptr++ = inst;
669
670 /*
671 * mtctr 13
672 */
673
674 inst = (31 << 26) | (LINKAGE_TEMP_REG << 21) | (9 << 16) | (467 << 1);
675 *inst_ptr++ = inst;
676
677 /*
678 * bctr
679 */
680
681 inst = (19 << 26) | (20 << 21) | (528 << 1);
682 *inst_ptr++ = inst;
683
684
685 *inst_ptr++ = inst;
686
687 os_flush_icache((os_vm_address_t) reloc_addr,
688 (char *) inst_ptr - (char *) reloc_addr);
6a4fd3c3 689}
690
9a8c1c2f 691void
692arch_make_linkage_entry(long linkage_entry, void *target_addr, long type)
6a4fd3c3 693{
9a8c1c2f 694 int *reloc_addr = (int *) (FOREIGN_LINKAGE_SPACE_START
6a4fd3c3 695
9a8c1c2f 696 + linkage_entry * LinkageEntrySize);
697
698 if (type == 1) { /* code reference */
699 arch_make_jump_entry(reloc_addr, target_addr);
700 } else if (type == 2) {
701 *(unsigned long *) reloc_addr = (unsigned long) target_addr;
6a4fd3c3 702 }
703}
704
705/* Make a the entry a jump to resolve_linkage_tramp. */
706
707extern void resolve_linkage_tramp(void);
708
9a8c1c2f 709void
710arch_make_lazy_linkage(long linkage_entry)
6a4fd3c3 711{
9a8c1c2f 712 arch_make_linkage_entry(linkage_entry, (void *) resolve_linkage_tramp, 1);
6a4fd3c3 713}
714
715/* Get linkage entry. We're given the return address which should be
716 the address of the jmpl instruction (2nd word) of the linkage
717 entry. Figure out which entry this address belong to. */
718
9a8c1c2f 719long
720arch_linkage_entry(unsigned long retaddr)
6a4fd3c3 721{
9a8c1c2f 722 return (retaddr - (FOREIGN_LINKAGE_SPACE_START))
723 / LinkageEntrySize;
6a4fd3c3 724}
725#endif
a459a234 726
727int ieee754_rem_pio2(double x, double *y0, double *y1)
728{
729 extern int __ieee754_rem_pio2(double x, double *y);
730
731 double y[2];
732 int n;
733
734 n = __ieee754_rem_pio2(x, y);
735 *y0 = y[0];
736 *y1 = y[1];
737
738 return n;
739}