📄 i860.c
字号:
because if the current function calls alloca(), the stack pointer will get moved, and it will be impossible to restore the registers properly again after that. Both compilers handled parameter registers (i.e. r16-r27 and f8-f15) by copying their values either into various "preserved" registers or into stack slots in the lower part of the current frame (as seemed appropriate, depending upon subsequent usage of these values). Here we want to save the preserved registers at some offset from the frame pointer register so as to avoid any possible problems arising from calls to alloca(). We can either save them at small positive offsets from the frame pointer, or at small negative offsets from the frame pointer. If we save them at small negative offsets from the frame pointer (i.e. in the lower part of the frame) then we must tell the rest of GCC (via STARTING_FRAME_OFFSET) exactly how many bytes of space we plan to use in the lower part of the frame for this purpose. Since other parts of the compiler reference the value of STARTING_FRAME_OFFSET long before final() calls this function, we would have to go ahead and assume the worst-case storage requirements for saving all of the "preserved" registers (and use that number, i.e. `80', to define STARTING_FRAME_OFFSET) if we wanted to save them in the lower part of the frame. That could potentially be very wasteful, and that wastefulness could really hamper people compiling for embedded i860 targets with very tight limits on stack space. Thus, we choose here to save the preserved registers in the upper part of the frame, so that we can decide at the very last minute how much (or how little) space we must allocate for this purpose. To satisfy the needs of the SVR4 ABI "tdesc" scheme, preserved registers must always be saved so that the saved values of registers with higher numbers are at higher addresses. We obey that restriction here. There are two somewhat different ways that you can generate prologues here... i.e. pedantically ABI-compliant, and the "other" way. The "other" way is more consistent with what is currently generated by the "native" SVR4 C compiler for the i860. That's important if you want to use the current (as of 8/91) incarnation of SVR4 SDB for the i860. The SVR4 SDB for the i860 insists on having function prologues be non-ABI-compliant! To get fully ABI-compliant prologues, define I860_STRICT_ABI_PROLOGUES in the i860/sysv4.h file. (By default this is *not* defined). The differences between the ABI-compliant and non-ABI-compliant prologues are that (a) the ABI version seems to require the use of *signed* (rather than unsigned) adds and subtracts, and (b) the ordering of the various steps (e.g. saving preserved registers, saving the return address, setting up the new frame pointer value) is different. For strict ABI compliance, it seems to be the case that the very last thing that is supposed to happen in the prologue is getting the frame pointer set to its new value (but only after everything else has already been properly setup). We do that here, but only if the symbol I860_STRICT_ABI_PROLOGUES is defined. */#ifndef STACK_ALIGNMENT#define STACK_ALIGNMENT 16#endifconst char *current_function_original_name;static int must_preserve_r1;static unsigned must_preserve_bytes;static voidi860_output_function_prologue (FILE *asm_file, HOST_WIDE_INT local_bytes){ register HOST_WIDE_INT frame_lower_bytes; register HOST_WIDE_INT frame_upper_bytes; register HOST_WIDE_INT total_fsize; register unsigned preserved_reg_bytes = 0; register unsigned i; register unsigned preserved_so_far = 0; must_preserve_r1 = (optimize < 2 || ! leaf_function_p ()); must_preserve_bytes = 4 + (must_preserve_r1 ? 4 : 0); /* Count registers that need preserving. Ignore r0. It never needs preserving. */ for (i = 1; i < FIRST_PSEUDO_REGISTER; i++) { if (regs_ever_live[i] && ! call_used_regs[i]) preserved_reg_bytes += 4; } /* Round-up the frame_lower_bytes so that it's a multiple of 16. */ frame_lower_bytes = (local_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT; /* The upper part of each frame will contain the saved fp, the saved r1, and stack slots for all of the other "preserved" registers that we find we will need to save & restore. */ frame_upper_bytes = must_preserve_bytes + preserved_reg_bytes; /* Round-up the frame_upper_bytes so that it's a multiple of 16. */ frame_upper_bytes = (frame_upper_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT; total_fsize = frame_upper_bytes + frame_lower_bytes;#ifndef I860_STRICT_ABI_PROLOGUES /* There are two kinds of function prologues. You use the "small" version if the total frame size is small enough so that it can fit into an immediate 16-bit value in one instruction. Otherwise, you use the "large" version of the function prologue. */ if (total_fsize > 0x7fff) { /* Adjust the stack pointer. The ABI specifies using `adds' for this, but the native C compiler on SVR4 uses `addu'. */ fprintf (asm_file, "\taddu -" HOST_WIDE_INT_PRINT_DEC ",%ssp,%ssp\n", frame_upper_bytes, i860_reg_prefix, i860_reg_prefix); /* Save the old frame pointer. */ fprintf (asm_file, "\tst.l %sfp,0(%ssp)\n", i860_reg_prefix, i860_reg_prefix); /* Setup the new frame pointer. The ABI specifies that this is to be done after preserving registers (using `adds'), but that's not what the native C compiler on SVR4 does. */ fprintf (asm_file, "\taddu 0,%ssp,%sfp\n", i860_reg_prefix, i860_reg_prefix); /* Get the value of frame_lower_bytes into r31. */ fprintf (asm_file, "\torh " HOST_WIDE_INT_PRINT_DEC ",%sr0,%sr31\n", frame_lower_bytes >> 16, i860_reg_prefix, i860_reg_prefix); fprintf (asm_file, "\tor " HOST_WIDE_INT_PRINT_DEC ",%sr31,%sr31\n", frame_lower_bytes & 0xffff, i860_reg_prefix, i860_reg_prefix); /* Now re-adjust the stack pointer using the value in r31. The ABI specifies that this is done with `subs' but SDB may prefer `subu'. */ fprintf (asm_file, "\tsubu %ssp,%sr31,%ssp\n", i860_reg_prefix, i860_reg_prefix, i860_reg_prefix); /* Preserve registers. The ABI specifies that this is to be done before setting up the new frame pointer, but that's not what the native C compiler on SVR4 does. */ for (i = 1; i < 32; i++) if (regs_ever_live[i] && ! call_used_regs[i]) fprintf (asm_file, "\tst.l %s%s,%d(%sfp)\n", i860_reg_prefix, reg_names[i], must_preserve_bytes + (4 * preserved_so_far++), i860_reg_prefix); for (i = 32; i < 64; i++) if (regs_ever_live[i] && ! call_used_regs[i]) fprintf (asm_file, "\tfst.l %s%s,%d(%sfp)\n", i860_reg_prefix, reg_names[i], must_preserve_bytes + (4 * preserved_so_far++), i860_reg_prefix); /* Save the return address. */ if (must_preserve_r1) fprintf (asm_file, "\tst.l %sr1,4(%sfp)\n", i860_reg_prefix, i860_reg_prefix); } else { /* Adjust the stack pointer. The ABI specifies using `adds' for this, but the native C compiler on SVR4 uses `addu'. */ fprintf (asm_file, "\taddu -" HOST_WIDE_INT_PRINT_DEC ",%ssp,%ssp\n", total_fsize, i860_reg_prefix, i860_reg_prefix); /* Save the old frame pointer. */ fprintf (asm_file, "\tst.l %sfp," HOST_WIDE_INT_PRINT_DEC "(%ssp)\n", i860_reg_prefix, frame_lower_bytes, i860_reg_prefix); /* Setup the new frame pointer. The ABI specifies that this is to be done after preserving registers and after saving the return address, (and to do it using `adds'), but that's not what the native C compiler on SVR4 does. */ fprintf (asm_file, "\taddu " HOST_WIDE_INT_PRINT_DEC ",%ssp,%sfp\n", frame_lower_bytes, i860_reg_prefix, i860_reg_prefix); /* Preserve registers. The ABI specifies that this is to be done before setting up the new frame pointer, but that's not what the native compiler on SVR4 does. */ for (i = 1; i < 32; i++) if (regs_ever_live[i] && ! call_used_regs[i]) fprintf (asm_file, "\tst.l %s%s,%d(%sfp)\n", i860_reg_prefix, reg_names[i], must_preserve_bytes + (4 * preserved_so_far++), i860_reg_prefix); for (i = 32; i < 64; i++) if (regs_ever_live[i] && ! call_used_regs[i]) fprintf (asm_file, "\tfst.l %s%s,%d(%sfp)\n", i860_reg_prefix, reg_names[i], must_preserve_bytes + (4 * preserved_so_far++), i860_reg_prefix); /* Save the return address. The ABI specifies that this is to be done earlier, and also via an offset from %sp, but the native C compiler on SVR4 does it later (i.e. now) and uses an offset from %fp. */ if (must_preserve_r1) fprintf (asm_file, "\tst.l %sr1,4(%sfp)\n", i860_reg_prefix, i860_reg_prefix); }#else /* defined(I860_STRICT_ABI_PROLOGUES) */ /* There are two kinds of function prologues. You use the "small" version if the total frame size is small enough so that it can fit into an immediate 16-bit value in one instruction. Otherwise, you use the "large" version of the function prologue. */ if (total_fsize > 0x7fff) { /* Adjust the stack pointer (thereby allocating a new frame). */ fprintf (asm_file, "\tadds -%d,%ssp,%ssp\n", frame_upper_bytes, i860_reg_prefix, i860_reg_prefix); /* Save the caller's frame pointer. */ fprintf (asm_file, "\tst.l %sfp,0(%ssp)\n", i860_reg_prefix, i860_reg_prefix); /* Save return address. */ if (must_preserve_r1) fprintf (asm_file, "\tst.l %sr1,4(%ssp)\n", i860_reg_prefix, i860_reg_prefix); /* Get the value of frame_lower_bytes into r31 for later use. */ fprintf (asm_file, "\torh %d,%sr0,%sr31\n", frame_lower_bytes >> 16, i860_reg_prefix, i860_reg_prefix); fprintf (asm_file, "\tor %d,%sr31,%sr31\n", frame_lower_bytes & 0xffff, i860_reg_prefix, i860_reg_prefix); /* Now re-adjust the stack pointer using the value in r31. */ fprintf (asm_file, "\tsubs %ssp,%sr31,%ssp\n", i860_reg_prefix, i860_reg_prefix, i860_reg_prefix); /* Pre-compute value to be used as the new frame pointer. */ fprintf (asm_file, "\tadds %ssp,%sr31,%sr31\n", i860_reg_prefix, i860_reg_prefix, i860_reg_prefix); /* Preserve registers. */ for (i = 1; i < 32; i++) if (regs_ever_live[i] && ! call_used_regs[i]) fprintf (asm_file, "\tst.l %s%s,%d(%sr31)\n", i860_reg_prefix, reg_names[i], must_preserve_bytes + (4 * preserved_so_far++), i860_reg_prefix); for (i = 32; i < 64; i++) if (regs_ever_live[i] && ! call_used_regs[i]) fprintf (asm_file, "\tfst.l %s%s,%d(%sr31)\n", i860_reg_prefix, reg_names[i], must_preserve_bytes + (4 * preserved_so_far++), i860_reg_prefix); /* Actually set the new value of the frame pointer. */ fprintf (asm_file, "\tmov %sr31,%sfp\n", i860_reg_prefix, i860_reg_prefix); } else { /* Adjust the stack pointer. */ fprintf (asm_file, "\tadds -%d,%ssp,%ssp\n", total_fsize, i860_reg_prefix, i860_reg_prefix); /* Save the caller's frame pointer. */ fprintf (asm_file, "\tst.l %sfp,%d(%ssp)\n", i860_reg_prefix, frame_lower_bytes, i860_reg_prefix); /* Save the return address. */ if (must_preserve_r1) fprintf (asm_file, "\tst.l %sr1,%d(%ssp)\n", i860_reg_prefix, frame_lower_bytes + 4, i860_reg_prefix); /* Preserve registers. */ for (i = 1; i < 32; i++) if (regs_ever_live[i] && ! call_used_regs[i]) fprintf (asm_file, "\tst.l %s%s,%d(%ssp)\n", i860_reg_prefix, reg_names[i], frame_lower_bytes + must_preserve_bytes + (4 * preserved_so_far++), i860_reg_prefix); for (i = 32; i < 64; i++) if (regs_ever_live[i] && ! call_used_regs[i]) fprintf (asm_file, "\tfst.l %s%s,%d(%ssp)\n", i860_reg_prefix, reg_names[i], frame_lower_bytes + must_preserve_bytes + (4 * preserved_so_far++), i860_reg_prefix); /* Setup the new frame pointer. */ fprintf (asm_file, "\tadds %d,%ssp,%sfp\n", frame_lower_bytes, i860_reg_prefix, i860_reg_prefix); }#endif /* defined(I860_STRICT_ABI_PROLOGUES) */#ifdef ASM_OUTPUT_PROLOGUE_SUFFIX ASM_OUTPUT_PROLOGUE_SUFFIX (asm_file);#endif /* defined(ASM_OUTPUT_PROLOGUE_SUFFIX) */}/* This function generates the assembly code for function exit. ASM_FILE is a stdio stream to output the code to. SIZE is an int: how many units of temporary storage to allocate. The function epilogue should not depend on the current stack pointer! It should use the frame pointer only. This is mandatory because of alloca; we also take advantage of it to omit stack adjustments before returning. Note that when we go to restore the preserved register values we must not try to address their slots by using offsets from the stack pointer. That's because the stack pointer may have been moved during the function execution due to a call to alloca(). Rather, we must restore all preserved registers via offsets from the frame pointer value. Note also that when the current frame is being "popped" (by adjusting the value of the stack pointer) on function exit, we must (for the sake of alloca) set the new value of the stack pointer based upon the current value of the frame pointer. We can't just add what we believe to be the (static) frame size to the stack pointer because if we did that, and alloca() had been called during this function, we would end up returning *without* having fully deallocated all of the space grabbed by alloca. If that happened, and a function containing one or more alloca() calls was called over and over again, then the stack would grow without limit! Finally note that the epilogues generated here are completely ABI compliant. They go out of their way to insure that the value in the frame pointer register is never less than the value in the stack pointer register. It's not clear why this relationship needs to be maintained at all times, but maintaining it only costs one extra instruction, so what the hell. *//* This corresponds to a version 4 TDESC structure. Lower numbered versions successively omit the last word of the structure. We don't try to handle version 5 here. */typedef struct TDESC_flags { int version:4; int reg_packing:1; int callable_block:1; int reserved:4; int fregs:6; /* fp regs 2-7 */ int iregs:16; /* regs 0-15 */} TDESC_flags;typedef struct TDESC { TDESC_flags flags; int integer_reg_offset; /* same as must_preserve_bytes */ int floating_point_reg_offset; unsigned int positive_frame_size; /* same as frame_upper_bytes */ unsigned int negative_frame_size; /* same as frame_lower_bytes */} TDESC;static voidi860_output_function_epilogue (FILE *asm_file, HOST_WIDE_INT local_bytes){ register HOST_WIDE_INT frame_upper_bytes; register HOST_WIDE_INT frame_lower_bytes; register HOST_WIDE_INT preserved_reg_bytes = 0; register unsigned i; register unsigned restored_so_far = 0; register unsigned int_restored; register unsigned mask; unsigned intflags=0; register TDESC_flags *flags = (TDESC_flags *) &intflags;#ifdef OUTPUT_TDESC /* Output an ABI-compliant TDESC entry */ const char *long_op = integer_asm_op (4, TRUE);#endif flags->version = 4; flags->reg_packing = 1; flags->iregs = 8; /* old fp always gets saved */ /* Round-up the frame_lower_bytes so that it's a multiple of 16. */ frame_lower_bytes = (local_bytes + STACK_ALIGNMENT - 1) & -STACK_ALIGNMENT; /* Count the number of registers that were preserved in the prologue. Ignore r0. It is never preserved. */ for (i = 1; i < FIRST_PSEUDO_REGISTER; i++) { if (regs_ever_live[i] && ! call_used_regs[i]) preserved_reg_bytes += 4; } /* The upper part of each frame will contain only saved fp, the saved r1, and stack slots for all of the other "preserved" registers that we find we will need to save & restore. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -