📄 romp.h
字号:
stack and set PRETEND_SIZE to the length of the registers pushed. */#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \{ if (TARGET_FP_REGS) \ error ("can't have varargs with -mfp-arg-in-fp-regs"); \ else if ((CUM).gregs < 4) \ { \ int first_reg_offset = (CUM).gregs; \ \ if (MUST_PASS_IN_STACK (MODE, TYPE)) \ first_reg_offset += ROMP_ARG_SIZE (TYPE_MODE (TYPE), TYPE, 1); \ \ if (first_reg_offset > 4) \ first_reg_offset = 4; \ \ if (! NO_RTL && first_reg_offset != 4) \ move_block_from_reg \ (2 + first_reg_offset, \ gen_rtx (MEM, BLKmode, \ plus_constant (virtual_incoming_args_rtx, \ first_reg_offset * 4)), \ 4 - first_reg_offset, (4 - first_reg_offset) * UNITS_PER_WORD); \ PRETEND_SIZE = (4 - first_reg_offset) * UNITS_PER_WORD; \ } \}/* This macro produces the initial definition of a function name. On the ROMP, we need to place an extra '.' in the function name. */#define ASM_DECLARE_FUNCTION_NAME(FILE,NAME,DECL) \{ if (TREE_PUBLIC(DECL)) \ fprintf (FILE, "\t.globl _.%s\n", NAME); \ fprintf (FILE, "_.%s:\n", NAME); \}/* This macro is used to output the start of the data area. On the ROMP, the _name is a pointer to the data area. At that location is the address of _.name, which is really the name of the function. We need to set all this up here. The global declaration of the data area, if needed, is done in `assemble_function', where it thinks it is globalizing the function itself. */#define ASM_OUTPUT_POOL_PROLOGUE(FILE, NAME, DECL, SIZE) \{ extern int data_offset; \ data_section (); \ fprintf (FILE, "\t.align 2\n"); \ ASM_OUTPUT_LABEL (FILE, NAME); \ fprintf (FILE, "\t.long _.%s, 0, ", NAME); \ if (current_function_calls_alloca) \ fprintf (FILE, "0x%x\n", \ 0xf6900000 + current_function_outgoing_args_size); \ else \ fprintf (FILE, "0\n"); \ data_offset = ((SIZE) + 12 + 3) / 4; \}/* Select section for constant in constant pool. On ROMP, all constants are in the data area. */#define SELECT_RTX_SECTION(MODE, X) data_section ()/* This macro generates the assembly code for function entry. FILE is a stdio stream to output the code to. SIZE is an int: how many units of temporary storage to allocate. Refer to the array `regs_ever_live' to determine which registers to save; `regs_ever_live[I]' is nonzero if register number I is ever used in the function. This macro is responsible for knowing which registers should not be saved even if used. */#define FUNCTION_PROLOGUE(FILE, SIZE) output_prolog (FILE, SIZE)/* Output assembler code to FILE to increment profiler label # LABELNO for profiling a function entry. */#define FUNCTION_PROFILER(FILE, LABELNO) \ fprintf(FILE, "\tcas r0,r15,r0\n\tbali r15,mcount\n");/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, the stack pointer does not matter. The value is tested only in functions that have frame pointers. No definition is equivalent to always zero. *//* #define EXIT_IGNORE_STACK 1 *//* This macro generates the assembly code for function exit, on machines that need it. If FUNCTION_EPILOGUE is not defined then individual return instructions are generated for each return statement. Args are same as for FUNCTION_PROLOGUE. 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. */#define FUNCTION_EPILOGUE(FILE, SIZE) output_epilog (FILE, SIZE)/* Output assembler code for a block containing the constant parts of a trampoline, leaving space for the variable parts. The trampoline should set the static chain pointer to value placed into the trampoline and should branch to the specified routine. On the ROMP, we have a problem. There are no free registers to use to construct the static chain and function addresses. Hence we use the following kludge: r15 (the return address) is first saved in mq. Then we use r15 to form the function address. We then branch to the function and restore r15 in the delay slot. This makes it appear that the function was called directly from the caller. (Note that the function address built is actually that of the data block. This is passed in r0 and the actual routine address is loaded into r15.) In addition, note that the address of the "called function", in this case the trampoline, is actually the address of the data area. So we need to make a fake data area that will contain the address of the trampoline. Note that this must be defined as two half-words, since the trampoline template (as opposed to the trampoline on the stack) is only half-word aligned. */#define TRAMPOLINE_TEMPLATE(FILE) \{ \ fprintf (FILE, "\t.short 0,0\n"); \ fprintf (FILE, "\tcau r0,0(r0)\n"); \ fprintf (FILE, "\toil r0,r0,0\n"); \ fprintf (FILE, "\tmts r10,r15\n"); \ fprintf (FILE, "\tst r0,-36(r1)\n"); \ fprintf (FILE, "\tcau r15,0(r0)\n"); \ fprintf (FILE, "\toil r15,r15,0\n"); \ fprintf (FILE, "\tcas r0,r15,r0\n"); \ fprintf (FILE, "\tls r15,0(r15)\n"); \ fprintf (FILE, "\tbrx r15\n"); \ fprintf (FILE, "\tmfs r10,r15\n"); \}/* Length in units of the trampoline for entering a nested function. */#define TRAMPOLINE_SIZE 36/* Emit RTL insns to initialize the variable parts of a trampoline. FNADDR is an RTX for the address of the function's pure code. CXT is an RTX for the static chain value for the function. On the RT, the static chain and function addresses are written in two 16-bit sections. We also need to write the address of the first instruction in the trampoline into the first word of the trampoline to simulate a data area. */#define INITIALIZE_TRAMPOLINE(ADDR, FNADDR, CXT) \{ \ rtx _addr, _temp; \ rtx _val; \ \ _temp = expand_binop (SImode, add_optab, ADDR, \ GEN_INT (4), \ 0, 1, OPTAB_LIB_WIDEN); \ emit_move_insn (gen_rtx (MEM, SImode, \ memory_address (SImode, ADDR)), _temp); \ \ _val = force_reg (SImode, CXT); \ _addr = memory_address (HImode, plus_constant (ADDR, 10)); \ emit_move_insn (gen_rtx (MEM, HImode, _addr), \ gen_lowpart (HImode, _val)); \ _temp = expand_shift (RSHIFT_EXPR, SImode, _val, \ build_int_2 (16, 0), 0, 1); \ _addr = memory_address (HImode, plus_constant (ADDR, 6)); \ emit_move_insn (gen_rtx (MEM, HImode, _addr), \ gen_lowpart (HImode, _temp)); \ \ _val = force_reg (SImode, FNADDR); \ _addr = memory_address (HImode, plus_constant (ADDR, 24)); \ emit_move_insn (gen_rtx (MEM, HImode, _addr), \ gen_lowpart (HImode, _val)); \ _temp = expand_shift (RSHIFT_EXPR, SImode, _val, \ build_int_2 (16, 0), 0, 1); \ _addr = memory_address (HImode, plus_constant (ADDR, 20)); \ emit_move_insn (gen_rtx (MEM, HImode, _addr), \ gen_lowpart (HImode, _temp)); \ \}/* Definitions for register eliminations. We have two registers that can be eliminated on the ROMP. First, the frame pointer register can often be eliminated in favor of the stack pointer register. Secondly, the argument pointer register can always be eliminated; it is replaced with either the stack or frame pointer. In addition, we use the elimination mechanism to see if r14 is needed. Initially we assume that it isn't. If it is, we spill it. This is done by making it an eliminable register. It doesn't matter what we replace it with, since it will never occur in the rtl at this point. *//* This is an array of structures. Each structure initializes one pair of eliminable registers. The "from" register number is given first, followed by "to". Eliminations of the same "from" register are listed in order of preference. */#define ELIMINABLE_REGS \{{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ { ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ { 14, 0}}/* Given FROM and TO register numbers, say whether this elimination is allowed. Frame pointer elimination is automatically handled. For the ROMP, if frame pointer elimination is being done, we would like to convert ap into fp, not sp. We need r14 if various conditions (tested in romp_using_r14) are true. All other eliminations are valid. */#define CAN_ELIMINATE(FROM, TO) \ ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM \ ? ! frame_pointer_needed \ : (FROM) == 14 ? ! romp_using_r14 () \ : 1)/* Define the offset between two registers, one to be eliminated, and the other its replacement, at the start of a routine. */#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \{ if ((FROM) == FRAME_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \ { \ if (romp_pushes_stack ()) \ (OFFSET) = ((get_frame_size () - 64) \ + current_function_outgoing_args_size); \ else \ (OFFSET) = - (romp_sa_size () + 64); \ } \ else if ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM) \ (OFFSET) = romp_sa_size () - 16 + 64; \ else if ((FROM) == ARG_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM) \ { \ if (romp_pushes_stack ()) \ (OFFSET) = (get_frame_size () + (romp_sa_size () - 16) \ + current_function_outgoing_args_size); \ else \ (OFFSET) = -16; \ } \ else if ((FROM) == 14) \ (OFFSET) = 0; \ else \ abort (); \}/* Addressing modes, and classification of registers for them. *//* #define HAVE_POST_INCREMENT 0 *//* #define HAVE_POST_DECREMENT 0 *//* #define HAVE_PRE_DECREMENT 0 *//* #define HAVE_PRE_INCREMENT 0 *//* Macros to check register numbers against specific register classes. *//* These assume that REGNO is a hard or pseudo reg number. They give nonzero only if REGNO is a hard reg of the suitable class or a pseudo reg currently allocated to a suitable hard reg. Since they use reg_renumber, they are safe only once reg_renumber has been allocated, which happens in local-alloc.c. */#define REGNO_OK_FOR_INDEX_P(REGNO) 0#define REGNO_OK_FOR_BASE_P(REGNO) \((REGNO) < FIRST_PSEUDO_REGISTER \ ? (REGNO) < 16 && (REGNO) != 0 && (REGNO) != 16 \ : (reg_renumber[REGNO] < 16 && reg_renumber[REGNO] >= 0 \ && reg_renumber[REGNO] != 16))/* Maximum number of registers that can appear in a valid memory address. */#define MAX_REGS_PER_ADDRESS 1/* Recognize any constant value that is a valid address. */#define CONSTANT_ADDRESS_P(X) \ (GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF \ || GET_CODE (X) == CONST_INT || GET_CODE (X) == CONST \ || GET_CODE (X) == HIGH)/* Nonzero if the constant value X is a legitimate general operand. It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. On the ROMP, there is a bit of a hack here. Basically, we wish to only issue instructions that are not `as' macros. However, in the case of `get', `load', and `store', if the operand is a relocatable symbol (possibly +/- an integer), there is no way to express the resulting split-relocation except with the macro. Therefore, allow either a constant valid in a normal (sign-extended) D-format insn or a relocatable expression. Also, for DFmode and DImode, we must ensure that both words are addressable. We define two macros: The first is given an offset (0 or 4) and indicates that the operand is a CONST_INT that is valid for that offset. The second indicates a valid non-CONST_INT constant. */#define LEGITIMATE_ADDRESS_INTEGER_P(X,OFFSET) \ (GET_CODE (X) == CONST_INT \ && (unsigned) (INTVAL (X) + (OFFSET) + 0x8000) < 0x10000)#define LEGITIMATE_ADDRESS_CONSTANT_P(X) \ (GET_CODE (X) == SYMBOL_REF \ || GET_CODE (X) == LABEL_REF \ || (GET_CODE (X) == CONST \ && (GET_CODE (XEXP (XEXP (X, 0), 0)) == SYMBOL_REF \ || GET_CODE (XEXP (XEXP (X, 0), 0)) == LABEL_REF) \ && GET_CODE (XEXP (XEXP (X, 0), 1)) == CONST_INT))/* Include all constant integers and constant double, but exclude SYMBOL_REFs that are to be obtained from the data area (see below). */#define LEGITIMATE_CONSTANT_P(X) \ ((LEGITIMATE_ADDRESS_CONSTANT_P (X) \ || GET_CODE (X) == CONST_INT \ || GET_CODE (X) == CONST_DOUBLE) \ && ! (GET_CODE (X) == SYMBOL_REF && SYMBOL_REF_FLAG (X)))/* For no good reason, we do the same as the other RT compilers and load the addresses of data areas for a function from our data area. That means that we need to mark such SYMBOL_REFs. We do so here. */#define ENCODE_SECTION_INFO(DECL) \ if (TREE_CODE (TREE_TYPE (DECL)) == FUNCTION_TYPE) \ SYMBOL_REF_FLAG (XEXP (DECL_RTL (DECL), 0)) = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -