📄 calls.c
字号:
args_size.constant += args[i].size.constant; if (args[i].size.var) { ADD_PARM_SIZE (args_size, args[i].size.var); } /* Since the slot offset points to the bottom of the slot, we must record it after incrementing if the args grow down. */#ifdef ARGS_GROW_DOWNWARD args[i].slot_offset = args_size; args[i].slot_offset.constant = -args_size.constant; if (args_size.var) { SUB_PARM_SIZE (args[i].slot_offset, args_size.var); }#endif /* Increment ARGS_SO_FAR, which has info about which arg-registers have been used, etc. */ FUNCTION_ARG_ADVANCE (args_so_far, TYPE_MODE (type), type, argpos < n_named_args); }#ifdef FINAL_REG_PARM_STACK_SPACE reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant, args_size.var);#endif /* Compute the actual size of the argument block required. The variable and constant sizes must be combined, the size may have to be rounded, and there may be a minimum required size. */ original_args_size = args_size; if (args_size.var) { /* If this function requires a variable-sized argument list, don't try to make a cse'able block for this call. We may be able to do this eventually, but it is too complicated to keep track of what insns go in the cse'able block and which don't. */ is_const = 0; must_preallocate = 1; args_size.var = ARGS_SIZE_TREE (args_size); args_size.constant = 0;#ifdef STACK_BOUNDARY if (STACK_BOUNDARY != BITS_PER_UNIT) args_size.var = round_up (args_size.var, STACK_BYTES);#endif#ifdef REG_PARM_STACK_SPACE if (reg_parm_stack_space > 0) { args_size.var = size_binop (MAX_EXPR, args_size.var, size_int (REG_PARM_STACK_SPACE (fndecl)));#ifndef OUTGOING_REG_PARM_STACK_SPACE /* The area corresponding to register parameters is not to count in the size of the block we need. So make the adjustment. */ args_size.var = size_binop (MINUS_EXPR, args_size.var, size_int (reg_parm_stack_space));#endif }#endif } else {#ifdef STACK_BOUNDARY args_size.constant = (((args_size.constant + (STACK_BYTES - 1)) / STACK_BYTES) * STACK_BYTES);#endif#ifdef REG_PARM_STACK_SPACE args_size.constant = MAX (args_size.constant, reg_parm_stack_space);#ifdef MAYBE_REG_PARM_STACK_SPACE if (reg_parm_stack_space == 0) args_size.constant = 0;#endif#ifndef OUTGOING_REG_PARM_STACK_SPACE args_size.constant -= reg_parm_stack_space;#endif#endif } /* See if we have or want to preallocate stack space. If we would have to push a partially-in-regs parm before other stack parms, preallocate stack space instead. If the size of some parm is not a multiple of the required stack alignment, we must preallocate. If the total size of arguments that would otherwise create a copy in a temporary (such as a CALL) is more than half the total argument list size, preallocation is faster. Another reason to preallocate is if we have a machine (like the m88k) where stack alignment is required to be maintained between every pair of insns, not just when the call is made. However, we assume here that such machines either do not have push insns (and hence preallocation would occur anyway) or the problem is taken care of with PUSH_ROUNDING. */ if (! must_preallocate) { int partial_seen = 0; int copy_to_evaluate_size = 0; for (i = 0; i < num_actuals && ! must_preallocate; i++) { if (args[i].partial > 0 && ! args[i].pass_on_stack) partial_seen = 1; else if (partial_seen && args[i].reg == 0) must_preallocate = 1; if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) == BLKmode && (TREE_CODE (args[i].tree_value) == CALL_EXPR || TREE_CODE (args[i].tree_value) == TARGET_EXPR || TREE_CODE (args[i].tree_value) == COND_EXPR || TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value)))) copy_to_evaluate_size += int_size_in_bytes (TREE_TYPE (args[i].tree_value)); } if (copy_to_evaluate_size * 2 >= args_size.constant && args_size.constant > 0) must_preallocate = 1; } /* If the structure value address will reference the stack pointer, we must stabilize it. We don't need to do this if we know that we are not going to adjust the stack pointer in processing this call. */ if (structure_value_addr && (reg_mentioned_p (virtual_stack_dynamic_rtx, structure_value_addr) || reg_mentioned_p (virtual_outgoing_args_rtx, structure_value_addr)) && (args_size.var#ifndef ACCUMULATE_OUTGOING_ARGS || args_size.constant#endif )) structure_value_addr = copy_to_reg (structure_value_addr); /* If this function call is cse'able, precompute all the parameters. Note that if the parameter is constructed into a temporary, this will cause an additional copy because the parameter will be constructed into a temporary location and then copied into the outgoing arguments. If a parameter contains a call to alloca and this function uses the stack, precompute the parameter. */ /* If we preallocated the stack space, and some arguments must be passed on the stack, then we must precompute any parameter which contains a function call which will store arguments on the stack. Otherwise, evaluating the parameter may clobber previous parameters which have already been stored into the stack. */ for (i = 0; i < num_actuals; i++) if (is_const || ((args_size.var != 0 || args_size.constant != 0) && calls_function (args[i].tree_value, 1)) || (must_preallocate && (args_size.var != 0 || args_size.constant != 0) && calls_function (args[i].tree_value, 0))) { /* If this is an addressable type, we cannot pre-evaluate it. */ if (TREE_ADDRESSABLE (TREE_TYPE (args[i].tree_value))) abort (); push_temp_slots (); args[i].initial_value = args[i].value = expand_expr (args[i].tree_value, NULL_RTX, VOIDmode, 0); preserve_temp_slots (args[i].value); pop_temp_slots (); /* ANSI doesn't require a sequence point here, but PCC has one, so this will avoid some problems. */ emit_queue (); args[i].initial_value = args[i].value = protect_from_queue (args[i].initial_value, 0); if (TYPE_MODE (TREE_TYPE (args[i].tree_value)) != args[i].mode) args[i].value = convert_modes (args[i].mode, TYPE_MODE (TREE_TYPE (args[i].tree_value)), args[i].value, args[i].unsignedp); } /* Now we are about to start emitting insns that can be deleted if a libcall is deleted. */ if (is_const) start_sequence (); /* If we have no actual push instructions, or shouldn't use them, make space for all args right now. */ if (args_size.var != 0) { 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;#ifdef ACCUMULATE_OUTGOING_ARGS /* 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;#endif } argblock = push_block (ARGS_SIZE_RTX (args_size), 0, 0); } else { /* Note that we must go through the motions of allocating an argument block even if the size is zero because we may be storing args in the area reserved for register arguments, which may be part of the stack frame. */ int needed = args_size.constant; /* Store the maximum argument space used. It will be pushed by the prologue (if ACCUMULATE_OUTGOING_ARGS, or stack overflow checking). */ if (needed > current_function_outgoing_args_size) current_function_outgoing_args_size = needed; if (must_preallocate) {#ifdef ACCUMULATE_OUTGOING_ARGS /* 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 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, args[i].mode, addr); MEM_IN_STRUCT_P (args[i].stack) = AGGREGATE_TYPE_P (TREE_TYPE (args[i].tree_value)); if (GET_CODE (slot_offset) == CONST_INT) addr = plus_constant (arg_reg, INTVAL (slot_offset)); else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -