📄 d30v.c
字号:
int adjust = (bytes > UNITS_PER_WORD && (*cum & 1) != 0); int arg_num = *cum + adjust; int ret; ret = ((arg_num <= GPR_ARG_LAST && arg_num + words > GPR_ARG_LAST+1) ? GPR_ARG_LAST - arg_num + 1 : 0); if (TARGET_DEBUG_ARG && ret) fprintf (stderr, "function_arg_partial_nregs: %d\n", ret); return ret;}/* A C expression that indicates when an argument must be passed by reference. If nonzero for an argument, a copy of that argument is made in memory and a pointer to the argument is passed instead of the argument itself. The pointer is passed in whatever way is appropriate for passing a pointer to that type. On machines where `REG_PARM_STACK_SPACE' is not defined, a suitable definition of this macro might be #define FUNCTION_ARG_PASS_BY_REFERENCE\ (CUM, MODE, TYPE, NAMED) \ MUST_PASS_IN_STACK (MODE, TYPE) */intd30v_function_arg_pass_by_reference (cum, mode, type, named) CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED; enum machine_mode mode; tree type; int named ATTRIBUTE_UNUSED;{ int ret = MUST_PASS_IN_STACK (mode, type); if (TARGET_DEBUG_ARG && ret) fprintf (stderr, "function_arg_pass_by_reference: %d\n", ret); return ret;}/* A C statement (sans semicolon) to update the summarizer variable CUM to advance past an argument in the argument list. The values MODE, TYPE and NAMED describe that argument. Once this is done, the variable CUM is suitable for analyzing the *following* argument with `FUNCTION_ARG', etc. This macro need not do anything if the argument in question was passed on the stack. The compiler knows how to track the amount of stack space used for arguments without any special help. */voidd30v_function_arg_advance (cum, mode, type, named) CUMULATIVE_ARGS *cum; enum machine_mode mode; tree type; int named;{ int bytes = ((mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode)); int words = D30V_ALIGN (bytes, UNITS_PER_WORD) / UNITS_PER_WORD; int adjust = (bytes > UNITS_PER_WORD && (*cum & 1) != 0); *cum += words + adjust; if (TARGET_DEBUG_ARG) fprintf (stderr, "function_adv: words = %2d, mode = %4s, named = %d, size = %3d, adjust = %1d\n", *cum, GET_MODE_NAME (mode), named, words * UNITS_PER_WORD, adjust);}/* If defined, is a C expression that produces the machine-specific code for a call to `__builtin_saveregs'. This code will be moved to the very beginning of the function, before any parameter access are made. The return value of this function should be an RTX that contains the value to use as the return of `__builtin_saveregs'. If this macro is not defined, the compiler will output an ordinary call to the library function `__builtin_saveregs'. */rtxd30v_expand_builtin_saveregs (){ int offset = UNITS_PER_WORD * (GPR_ARG_LAST + 1 - GPR_ARG_FIRST); if (TARGET_DEBUG_ARG) fprintf (stderr, "expand_builtin_saveregs: offset from ap = %d\n", offset); return gen_rtx (PLUS, Pmode, virtual_incoming_args_rtx, GEN_INT (- offset));}/* This macro offers an alternative to using `__builtin_saveregs' and defining the macro `EXPAND_BUILTIN_SAVEREGS'. Use it to store the anonymous register arguments into the stack so that all the arguments appear to have been passed consecutively on the stack. Once this is done, you can use the standard implementation of varargs that works for machines that pass all their arguments on the stack. The argument ARGS_SO_FAR is the `CUMULATIVE_ARGS' data structure, containing the values that obtain after processing of the named arguments. The arguments MODE and TYPE describe the last named argument--its machine mode and its data type as a tree node. The macro implementation should do two things: first, push onto the stack all the argument registers *not* used for the named arguments, and second, store the size of the data thus pushed into the `int'-valued variable whose name is supplied as the argument PRETEND_ARGS_SIZE. The value that you store here will serve as additional offset for setting up the stack frame. Because you must generate code to push the anonymous arguments at compile time without knowing their data types, `SETUP_INCOMING_VARARGS' is only useful on machines that have just a single category of argument register and use it uniformly for all data types. If the argument SECOND_TIME is nonzero, it means that the arguments of the function are being analyzed for the second time. This happens for an inline function, which is not actually compiled until the end of the source file. The macro `SETUP_INCOMING_VARARGS' should not generate any instructions in this case. */voidd30v_setup_incoming_varargs (cum, mode, type, pretend_size, second_time) CUMULATIVE_ARGS *cum; enum machine_mode mode; tree type ATTRIBUTE_UNUSED; int *pretend_size ATTRIBUTE_UNUSED; int second_time;{ if (TARGET_DEBUG_ARG) fprintf (stderr, "setup_vararg: words = %2d, mode = %4s, second_time = %d\n", *cum, GET_MODE_NAME (mode), second_time);}/* Create the va_list data type. */treed30v_build_va_list (){ tree f_arg_ptr, f_arg_num, record, type_decl; tree int_type_node; record = (*lang_hooks.types.make_type) (RECORD_TYPE); type_decl = build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record); int_type_node = make_signed_type (INT_TYPE_SIZE); f_arg_ptr = build_decl (FIELD_DECL, get_identifier ("__va_arg_ptr"), ptr_type_node); f_arg_num = build_decl (FIELD_DECL, get_identifier ("__va_arg_num"), int_type_node); DECL_FIELD_CONTEXT (f_arg_ptr) = record; DECL_FIELD_CONTEXT (f_arg_num) = record; TREE_CHAIN (record) = type_decl; TYPE_NAME (record) = type_decl; TYPE_FIELDS (record) = f_arg_ptr; TREE_CHAIN (f_arg_ptr) = f_arg_num; layout_type (record); /* The correct type is an array type of one element. */ return build_array_type (record, build_index_type (size_zero_node));}/* Expand __builtin_va_start to do the va_start macro. */void d30v_expand_builtin_va_start (valist, nextarg) tree valist; rtx nextarg ATTRIBUTE_UNUSED;{ HOST_WIDE_INT words; tree f_arg_ptr, f_arg_num; tree arg_ptr, arg_num, saveregs, t; f_arg_ptr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); f_arg_num = TREE_CHAIN (f_arg_ptr); valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist); arg_ptr = build (COMPONENT_REF, TREE_TYPE (f_arg_ptr), valist, f_arg_ptr); arg_num = build (COMPONENT_REF, TREE_TYPE (f_arg_num), valist, f_arg_num); words = current_function_args_info; /* __builtin_args_info (0) */ /* (AP)->__va_arg_ptr = (int *) __builtin_saveregs (); */ saveregs = make_tree (TREE_TYPE (arg_ptr), d30v_expand_builtin_saveregs ()); t = build (MODIFY_EXPR, TREE_TYPE (arg_ptr), arg_ptr, saveregs); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); /* (AP)->__va_arg_num = __builtin_args_info (0) - 2; */ t = build (PLUS_EXPR, TREE_TYPE (arg_num), build_int_2 (words, 0), build_int_2 (-GPR_ARG_FIRST, 0)); t = build (MODIFY_EXPR, TREE_TYPE (arg_num), arg_num, t); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);}/* Expand __builtin_va_arg to do the va_arg macro. */rtxd30v_expand_builtin_va_arg(valist, type) tree valist; tree type;{ tree f_arg_ptr, f_arg_num; tree arg_ptr, arg_num, t, ptr; int num, size; rtx lab_false, ptr_rtx, r; f_arg_ptr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); f_arg_num = TREE_CHAIN (f_arg_ptr); valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist); arg_ptr = build (COMPONENT_REF, TREE_TYPE (f_arg_ptr), valist, f_arg_ptr); arg_num = build (COMPONENT_REF, TREE_TYPE (f_arg_num), valist, f_arg_num); size = int_size_in_bytes (type); lab_false = gen_label_rtx (); ptr_rtx = gen_reg_rtx (Pmode); /* if (sizeof (TYPE) > 4 && ((AP)->__va_arg_num & 1) != 0) (AP)->__va_arg_num++; */ if (size > UNITS_PER_WORD) { t = build (BIT_AND_EXPR, TREE_TYPE (arg_num), arg_num, build_int_2 (1, 0)); emit_cmp_and_jump_insns (expand_expr (t, NULL_RTX, QImode, EXPAND_NORMAL), GEN_INT (0), EQ, const1_rtx, QImode, 1, lab_false); t = build (POSTINCREMENT_EXPR, TREE_TYPE (arg_num), arg_num, build_int_2 (1, 0)); TREE_SIDE_EFFECTS (t) = 1; expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); emit_label (lab_false); } /* __ptr = (TYPE *)(((char *)(void *)((AP)->__va_arg_ptr + (AP)->__va_arg_num))); */ t = build (MULT_EXPR, TREE_TYPE (arg_num), arg_num, build_int_2 (4, 0)); t = build (PLUS_EXPR, ptr_type_node, arg_ptr, t); /* if (sizeof (TYPE) < 4) __ptr = (void *)__ptr + 4 - sizeof (TYPE); */ if (size < UNITS_PER_WORD) t = build (PLUS_EXPR, ptr_type_node, t, build_int_2 (UNITS_PER_WORD - size, 0)); TREE_SIDE_EFFECTS (t) = 1; ptr = build1 (NOP_EXPR, build_pointer_type (type), t); t = build (MODIFY_EXPR, type, ptr, t); r = expand_expr (t, ptr_rtx, Pmode, EXPAND_NORMAL); if (r != ptr_rtx) emit_move_insn (ptr_rtx, r); /* (AP)->__va_arg_num += (sizeof (TYPE) + 3) / 4; */ num = (size + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; t = build (POSTINCREMENT_EXPR, TREE_TYPE (arg_num), arg_num, build_int_2 (num, 0)); expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); return ptr_rtx;}/* Generate 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 function is responsible for knowing which registers should not be saved even if used. */static voidd30v_output_function_prologue (stream, size) FILE *stream ATTRIBUTE_UNUSED; HOST_WIDE_INT size ATTRIBUTE_UNUSED;{ /* For the d30v, move all of the prologue processing into separate insns. */}/* Called after register allocation to add any instructions needed for the prologue. Using a prologue insn is favored compared to putting all of the instructions in output_function_prologue (), since it allows the scheduler to intermix instructions with the saves of the caller saved registers. In some cases, it might be necessary to emit a barrier instruction as the last insn to prevent such scheduling. */voidd30v_expand_prologue (){ rtx sp = stack_pointer_rtx; d30v_stack_t *info = d30v_stack_info (); int i; rtx mem_di = NULL_RTX; rtx mem_si = NULL_RTX; int num_memrefs = (info->memrefs_2words + info->memrefs_1word + info->memrefs_varargs); if (TARGET_DEBUG_STACK) debug_stack_info (info); /* Grow the stack. */ if (info->total_size) emit_insn (gen_addsi3 (sp, sp, GEN_INT (- info->total_size))); /* If there is more than one save, use post-increment addressing which will result in smaller code, than would the normal references. If there is only one save, just do the store as normal. */ if (num_memrefs > 1) { rtx save_tmp = gen_rtx (REG, Pmode, GPR_STACK_TMP); rtx post_inc = gen_rtx (POST_INC, Pmode, save_tmp); mem_di = gen_rtx (MEM, DImode, post_inc); mem_si = gen_rtx (MEM, SImode, post_inc); emit_insn (gen_addsi3 (save_tmp, sp, GEN_INT (info->save_offset))); } else if (num_memrefs == 1) { rtx addr = plus_constant (sp, info->save_offset); mem_di = gen_rtx (MEM, DImode, addr); mem_si = gen_rtx (MEM, SImode, addr); } /* Save the accumulators. */ for (i = ACCUM_FIRST; i <= ACCUM_LAST; i++) if (info->save_p[i]) { rtx acc_tmp = gen_rtx (REG, DImode, GPR_ATMP_FIRST); emit_insn (gen_movdi (acc_tmp, gen_rtx (REG, DImode, i))); emit_insn (gen_movdi (mem_di, acc_tmp)); } /* Save the GPR registers that are adjacent to each other with st2w. */ for (i = GPR_FIRST; i <= GPR_LAST; i += 2) if (info->save_p[i] == 2) emit_insn (gen_movdi (mem_di, gen_rtx (REG, DImode, i))); /* Save the GPR registers that need to be saved with a single word store. */ for (i = GPR_FIRST; i <= GPR_LAST; i++) if (info->save_p[i] == 1) emit_insn (gen_movsi (mem_si, gen_rtx (REG, SImode, i))); /* Save the argument registers if this function accepts variable args. */ if (info->varargs_p) { /* Realign r22 if an odd # of GPRs were saved. */ if ((info->memrefs_1word & 1) != 0) { rtx save_tmp = XEXP (XEXP (mem_si, 0), 0); emit_insn (gen_addsi3 (save_tmp, save_tmp, GEN_INT (UNITS_PER_WORD))); } for (i = GPR_ARG_FIRST; i <= GPR_ARG_LAST; i += 2) emit_insn (gen_movdi (mem_di, gen_rtx (REG, DImode, i))); } /* Update the frame pointer. */ if (frame_pointer_needed) emit_move_insn (frame_pointer_rtx, sp); /* Hack for now, to prevent scheduler from being too cleaver */ emit_insn (gen_blockage ());}/* This function generates the assembly code for function exit. Args are as for output_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. */static voidd30v_output_function_epilogue (stream, size) FILE *stream ATTRIBUTE_UNUSED; HOST_WIDE_INT size ATTRIBUTE_UNUSED;{ /* For the d30v, move all processing to be as insns, but do any cleanup here, since it is done after handling all of the insns. */ d30v_stack_cache = (d30v_stack_t *)0; /* reset stack cache */}/* Called after register allocation to add any instructions needed for the epilogue. Using an epilogue insn is favored compared to putting all of the instructions in output_function_prologue(), since it allows the scheduler to intermix instructions with the saves of the caller saved registers. In some cases, it might be necessary to emit a barrier instruction as the last insn to prevent such scheduling. */voidd30v_expand_epilogue (){ rtx sp = stack_pointer_rtx; d30v_stack_t *info = d30v_stack_info (); int i; rtx mem_di = NULL_RTX; rtx mem_si = NULL_RTX; rtx post_inc; int extra_stack; /* Hack for now, to prevent scheduler from being too cleaver */ emit_insn (gen_blockage ()); /* Restore sp from fp. */ if (frame_pointer_needed) emit_move_insn (sp, frame_pointer_rtx); /* For the epilogue, use post-increment addressing all of the time. First adjust the sp, to eliminate all of the stack, except for the save area. */ if (info->save_offset) emit_insn (gen_addsi3 (sp, sp, GEN_INT (info->save_offset))); post_inc = gen_rtx (POST_INC, Pmode, sp); mem_di = gen_rtx (MEM, DImode, post_inc); mem_si = gen_rtx (MEM, SImode, post_inc); /* Restore the accumulators. */ for (i = ACCUM_FIRST; i <= ACCUM_LAST; i++) if (info->save_p[i]) { rtx acc_tmp = gen_rtx (REG, DImode, GPR_ATMP_FIRST); emit_insn (gen_movdi (acc_tmp, mem_di)); emit_insn (gen_movdi (gen_rtx (REG, DImode, i), acc_tmp)); } /* Restore the GPR registers that are adjacent to each other with ld2w. */ for (i = GPR_FIRST; i <= GPR_LAST; i += 2) if (info->save_p[i] == 2) emit_insn (gen_movdi (gen_rtx (REG, DImode, i), mem_di)); /* Save the GPR registers that need to be saved with a single word store. */ extra_stack = 0; for (i = GPR_FIRST; i <= GPR_LAST; i++) if (info->save_p[i] == 1) { if (cfun->machine->eh_epilogue_sp_ofs && i == GPR_LINK) extra_stack = 4; else { if (extra_stack) { emit_insn (gen_addsi3 (sp, sp, GEN_INT (extra_stack))); extra_stack = 0; } emit_insn (gen_movsi (gen_rtx (REG, SImode, i), mem_si)); } } /* Release any remaining stack that was allocated for saving the varargs registers or because an odd # of registers were stored. */ if ((info->memrefs_1word & 1) != 0) extra_stack += UNITS_PER_WORD; extra_stack += current_function_pretend_args_size + info->varargs_size; if (extra_stack) { if (cfun->machine->eh_epilogue_sp_ofs) emit_insn (gen_addsi3 (cfun->machine->eh_epilogue_sp_ofs, cfun->machine->eh_epilogue_sp_ofs, GEN_INT (extra_stack))); else emit_insn (gen_addsi3 (sp, sp, GEN_INT (extra_stack))); } if (cfun->machine->eh_epilogue_sp_ofs) emit_insn (gen_addsi3 (sp, sp, cfun->machine->eh_epilogue_sp_ofs)); /* Now emit the return instruction. */ emit_jump_insn (gen_rtx_RETURN (VOIDmode));}/* A C statement or compound statement to output to FILE some assembler code to call the profiling subroutine `mcount'. Before calling, the assembler code must load the address of a counter variable into a register where `mcount' expects to find the address. The name of this variable is `LP' followed by the number LABELNO, so you would generate the name using `LP%d' in a `fprintf'. The details of how the address should be passed to `mcount' are determined by your operating system environment, not by GNU CC. To figure t
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -