📄 calls.c
字号:
#ifdef ACCUMULATE_OUTGOING_ARGS /* Store the maximum argument space used. It will be pushed by the prologue. Since the stack pointer will never be pushed, it is possible for the evaluation of a parm to clobber something we have already written to the stack. Since most function calls on RISC machines do not use the stack, this is uncommon, but must work correctly. Therefore, we save any area of the stack that was already written and that we are using. Here we set up to do this by making a new stack usage map from the old one. The actual save will be done by store_one_arg. Another approach might be to try to reorder the argument evaluations to avoid this conflicting stack usage. */ if (needed > current_function_outgoing_args_size) current_function_outgoing_args_size = needed;#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) /* Since we will be writing into the entire argument area, the map must be allocated for its entire size, not just the part that is the responsibility of the caller. */ needed += reg_parm_stack_space;#endif#ifdef ARGS_GROW_DOWNWARD highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, needed + 1);#else highest_outgoing_arg_in_use = MAX (initial_highest_arg_in_use, needed);#endif stack_usage_map = (char *) alloca (highest_outgoing_arg_in_use); if (initial_highest_arg_in_use) bcopy (initial_stack_usage_map, stack_usage_map, initial_highest_arg_in_use); if (initial_highest_arg_in_use != highest_outgoing_arg_in_use) bzero (&stack_usage_map[initial_highest_arg_in_use], highest_outgoing_arg_in_use - initial_highest_arg_in_use); needed = 0; /* The address of the outgoing argument list must not be copied to a register here, because argblock would be left pointing to the wrong place after the call to allocate_dynamic_stack_space below. */ argblock = virtual_outgoing_args_rtx;#else /* not ACCUMULATE_OUTGOING_ARGS */ if (inhibit_defer_pop == 0) { /* Try to reuse some or all of the pending_stack_adjust to get this space. Maybe we can avoid any pushing. */ if (needed > pending_stack_adjust) { needed -= pending_stack_adjust; pending_stack_adjust = 0; } else { pending_stack_adjust -= needed; needed = 0; } } /* Special case this because overhead of `push_block' in this case is non-trivial. */ if (needed == 0) argblock = virtual_outgoing_args_rtx; else argblock = push_block (GEN_INT (needed), 0, 0); /* We only really need to call `copy_to_reg' in the case where push insns are going to be used to pass ARGBLOCK to a function call in ARGS. In that case, the stack pointer changes value from the allocation point to the call point, and hence the value of VIRTUAL_OUTGOING_ARGS_RTX changes as well. But might as well always do it. */ argblock = copy_to_reg (argblock);#endif /* not ACCUMULATE_OUTGOING_ARGS */ }#ifdef ACCUMULATE_OUTGOING_ARGS /* The save/restore code in store_one_arg handles all cases except one: a constructor call (including a C function returning a BLKmode struct) to initialize an argument. */ if (stack_arg_under_construction) {#if defined(REG_PARM_STACK_SPACE) && ! defined(OUTGOING_REG_PARM_STACK_SPACE) rtx push_size = GEN_INT (reg_parm_stack_space + args_size.constant);#else rtx push_size = GEN_INT (args_size.constant);#endif if (old_stack_level == 0) { emit_stack_save (SAVE_BLOCK, &old_stack_level, NULL_RTX); old_pending_adj = pending_stack_adjust; pending_stack_adjust = 0; /* stack_arg_under_construction says whether a stack arg is being constructed at the old stack level. Pushing the stack gets a clean outgoing argument block. */ old_stack_arg_under_construction = stack_arg_under_construction; stack_arg_under_construction = 0; /* Make a new map for the new argument list. */ stack_usage_map = (char *)alloca (highest_outgoing_arg_in_use); bzero (stack_usage_map, highest_outgoing_arg_in_use); highest_outgoing_arg_in_use = 0; } allocate_dynamic_stack_space (push_size, NULL_RTX, BITS_PER_UNIT); } /* If argument evaluation might modify the stack pointer, copy the address of the argument list to a register. */ for (i = 0; i < num_actuals; i++) if (args[i].pass_on_stack) { argblock = copy_addr_to_reg (argblock); break; }#endif /* If we preallocated stack space, compute the address of each argument. We need not ensure it is a valid memory address here; it will be validized when it is used. */ if (argblock) { rtx arg_reg = argblock; int arg_offset = 0; if (GET_CODE (argblock) == PLUS) arg_reg = XEXP (argblock, 0), arg_offset = INTVAL (XEXP (argblock, 1)); for (i = 0; i < num_actuals; i++) { rtx offset = ARGS_SIZE_RTX (args[i].offset); rtx slot_offset = ARGS_SIZE_RTX (args[i].slot_offset); rtx addr; /* Skip this parm if it will not be passed on the stack. */ if (! args[i].pass_on_stack && args[i].reg != 0) continue; if (GET_CODE (offset) == CONST_INT) addr = plus_constant (arg_reg, INTVAL (offset)); else addr = gen_rtx (PLUS, Pmode, arg_reg, offset); addr = plus_constant (addr, arg_offset); args[i].stack = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (args[i].tree_value)), addr); if (GET_CODE (slot_offset) == CONST_INT) addr = plus_constant (arg_reg, INTVAL (slot_offset)); else addr = gen_rtx (PLUS, Pmode, arg_reg, slot_offset); addr = plus_constant (addr, arg_offset); args[i].stack_slot = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (args[i].tree_value)), addr); } } #ifdef PUSH_ARGS_REVERSED#ifdef STACK_BOUNDARY /* If we push args individually in reverse order, perform stack alignment before the first push (the last arg). */ if (argblock == 0) anti_adjust_stack (GEN_INT (args_size.constant - original_args_size.constant));#endif#endif /* Don't try to defer pops if preallocating, not even from the first arg, since ARGBLOCK probably refers to the SP. */ if (argblock) NO_DEFER_POP; /* Get the function to call, in the form of RTL. */ if (fndecl) /* Get a SYMBOL_REF rtx for the function address. */ funexp = XEXP (DECL_RTL (fndecl), 0); else /* Generate an rtx (probably a pseudo-register) for the address. */ { funexp = expand_expr (TREE_OPERAND (exp, 0), NULL_RTX, VOIDmode, 0); free_temp_slots (); /* FUNEXP can't be BLKmode */ emit_queue (); } /* Figure out the register where the value, if any, will come back. */ valreg = 0; if (TYPE_MODE (TREE_TYPE (exp)) != VOIDmode && ! structure_value_addr) { if (pcc_struct_value) valreg = hard_function_value (build_pointer_type (TREE_TYPE (exp)), fndecl); else valreg = hard_function_value (TREE_TYPE (exp), fndecl); } /* Precompute all register parameters. It isn't safe to compute anything once we have started filling any specific hard regs. */ reg_parm_seen = 0; for (i = 0; i < num_actuals; i++) if (args[i].reg != 0 && ! args[i].pass_on_stack) { enum machine_mode mode; reg_parm_seen = 1; if (args[i].value == 0) { args[i].value = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0); preserve_temp_slots (args[i].value); free_temp_slots (); /* ANSI doesn't require a sequence point here, but PCC has one, so this will avoid some problems. */ emit_queue (); } /* If we are to promote the function arg to a wider mode, do it now. */ mode = (GET_CODE (args[i].reg) == EXPR_LIST ? GET_MODE (XEXP (args[i].reg, 0)) : GET_MODE (args[i].reg)); if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != mode) args[i].value = convert_to_mode (mode, args[i].value, args[i].unsignedp); }#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE) /* The argument list is the property of the called routine and it may clobber it. If the fixed area has been used for previous parameters, we must save and restore it. Here we compute the boundary of the that needs to be saved, if any. */#ifdef ARGS_GROW_DOWNWARD for (i = 0; i < reg_parm_stack_space + 1; i++)#else for (i = 0; i < reg_parm_stack_space; i++)#endif { if (i >= highest_outgoing_arg_in_use || stack_usage_map[i] == 0) continue; if (low_to_save == -1) low_to_save = i; high_to_save = i; } if (low_to_save >= 0) { int num_to_save = high_to_save - low_to_save + 1; enum machine_mode save_mode = mode_for_size (num_to_save * BITS_PER_UNIT, MODE_INT, 1); rtx stack_area; /* If we don't have the required alignment, must do this in BLKmode. */ if ((low_to_save & (MIN (GET_MODE_SIZE (save_mode), BIGGEST_ALIGNMENT / UNITS_PER_WORD) - 1))) save_mode = BLKmode; stack_area = gen_rtx (MEM, save_mode, memory_address (save_mode, #ifdef ARGS_GROW_DOWNWARD plus_constant (argblock, - high_to_save)#else plus_constant (argblock, low_to_save)#endif )); if (save_mode == BLKmode) { save_area = assign_stack_temp (BLKmode, num_to_save, 1); emit_block_move (validize_mem (save_area), stack_area, GEN_INT (num_to_save), PARM_BOUNDARY / BITS_PER_UNIT); } else { save_area = gen_reg_rtx (save_mode); emit_move_insn (save_area, stack_area); } }#endif /* Now store (and compute if necessary) all non-register parms. These come before register parms, since they can require block-moves, which could clobber the registers used for register parms. Parms which have partial registers are not stored here, but we do preallocate space here if they want that. */ for (i = 0; i < num_actuals; i++) if (args[i].reg == 0 || args[i].pass_on_stack) store_one_arg (&args[i], argblock, may_be_alloca, args_size.var != 0, fndecl, reg_parm_stack_space); /* Now store any partially-in-registers parm. This is the last place a block-move can happen. */ if (reg_parm_seen) for (i = 0; i < num_actuals; i++) if (args[i].partial != 0 && ! args[i].pass_on_stack) store_one_arg (&args[i], argblock, may_be_alloca, args_size.var != 0, fndecl, reg_parm_stack_space);#ifndef PUSH_ARGS_REVERSED#ifdef STACK_BOUNDARY /* If we pushed args in forward order, perform stack alignment after pushing the last arg. */ if (argblock == 0) anti_adjust_stack (GEN_INT (args_size.constant - original_args_size.constant));#endif#endif /* If register arguments require space on the stack and stack space was not preallocated, allocate stack space here for arguments passed in registers. */#if ! defined(ALLOCATE_OUTGOING_ARGS) && defined(OUTGOING_REG_PARM_STACK_SPACE) if (must_preallocate == 0 && reg_parm_stack_space > 0) anti_adjust_stack (GEN_INT (reg_parm_stack_space));#endif /* Pass the function the address in which to return a structure value. */ if (structure_value_addr && ! structure_value_addr_parm) { emit_move_insn (struct_value_rtx, force_reg (Pmode, force_operand (structure_value_addr, NULL_RTX))); if (GET_CODE (struct_value_rtx) == REG) { push_to_sequence (use_insns); emit_insn (gen_rtx (USE, VOIDmode, struct_value_rtx)); use_insns = get_insns (); end_sequence (); } } /* Now do the register loads required for any wholly-register parms or any parms which are passed both on the stack and in a register. Their expressions were already evaluated. Mark all register-parms as living through the call, putting these USE insns in a list headed by USE_INSNS. */ for (i = 0; i < num_actuals; i++) { rtx list = args[i].reg; int partial = args[i].partial; while (list) { rtx reg; int nregs; /* Process each register that needs to get this arg. */ if (GET_CODE (list) == EXPR_LIST) reg = XEXP (list, 0), list = XEXP (list, 1); else reg = list, list = 0; /* Set to non-zero if must move a word at a time, even if just one word (e.g, partial == 1 && mode == DFmode). Set to zero if we just use a normal move insn. */ nregs = (partial ? partial : (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode ? ((int_size_in_bytes (TREE_TYPE (args[i].tree_value)) + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD) : 0)); /* If simple case, just do move. If normal partial, store_one_arg has already loaded the register for us. In all other cases, load the register(s) from memory. */ if (nregs == 0) emit_move_insn (reg, args[i].value); else if (args[i].partial == 0 || args[i].pass_on_stack) move_block_to_reg (REGNO (reg), validize_mem (args[i].value), nregs, TYPE_MODE (TREE_TYPE (args[i].tree_value))); push_to_sequence (use_insns); if (nregs == 0) emit_insn (gen_rtx (USE, VOIDmode, reg)); else use_regs (REGNO (reg), nregs); use_insns = get_insns ();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -