📄 calls.c
字号:
end_sequence (); /* PARTIAL referred only to the first register, so clear it for the next time. */ partial = 0; } } /* Perform postincrements before actually calling the function. */ emit_queue (); /* All arguments and registers used for the call must be set up by now! */ funexp = prepare_call_address (funexp, fndecl, &use_insns); /* Generate the actual call instruction. */ emit_call_1 (funexp, funtype, args_size.constant, struct_value_size, FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1), valreg, old_inhibit_defer_pop, use_insns, is_const); /* If call is cse'able, make appropriate pair of reg-notes around it. Test valreg so we don't crash; may safely ignore `const' if return type is void. */ if (is_const && valreg != 0) { rtx note = 0; rtx temp = gen_reg_rtx (GET_MODE (valreg)); rtx insns; /* Construct an "equal form" for the value which mentions all the arguments in order as well as the function name. */#ifdef PUSH_ARGS_REVERSED for (i = 0; i < num_actuals; i++) note = gen_rtx (EXPR_LIST, VOIDmode, args[i].initial_value, note);#else for (i = num_actuals - 1; i >= 0; i--) note = gen_rtx (EXPR_LIST, VOIDmode, args[i].initial_value, note);#endif note = gen_rtx (EXPR_LIST, VOIDmode, funexp, note); insns = get_insns (); end_sequence (); emit_libcall_block (insns, temp, valreg, note); valreg = temp; } /* For calls to `setjmp', etc., inform flow.c it should complain if nonvolatile values are live. */ if (returns_twice) { emit_note (name, NOTE_INSN_SETJMP); current_function_calls_setjmp = 1; } if (is_longjmp) current_function_calls_longjmp = 1; /* Notice functions that cannot return. If optimizing, insns emitted below will be dead. If not optimizing, they will exist, which is useful if the user uses the `return' command in the debugger. */ if (is_volatile || is_longjmp) emit_barrier (); /* If value type not void, return an rtx for the value. */ /* If there are cleanups to be called, don't use a hard reg as target. */ if (cleanups_this_call != old_cleanups && target && REG_P (target) && REGNO (target) < FIRST_PSEUDO_REGISTER) target = 0; if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode || ignore) { target = const0_rtx; } else if (structure_value_addr) { if (target == 0 || GET_CODE (target) != MEM) { target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), memory_address (TYPE_MODE (TREE_TYPE (exp)), structure_value_addr)); MEM_IN_STRUCT_P (target) = (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE); } } else if (pcc_struct_value) { if (target == 0) { target = gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), copy_to_reg (valreg)); MEM_IN_STRUCT_P (target) = (TREE_CODE (TREE_TYPE (exp)) == ARRAY_TYPE || TREE_CODE (TREE_TYPE (exp)) == RECORD_TYPE || TREE_CODE (TREE_TYPE (exp)) == UNION_TYPE); } else if (TYPE_MODE (TREE_TYPE (exp)) != BLKmode) emit_move_insn (target, gen_rtx (MEM, TYPE_MODE (TREE_TYPE (exp)), copy_to_reg (valreg))); else emit_block_move (target, gen_rtx (MEM, BLKmode, copy_to_reg (valreg)), expr_size (exp), TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); } else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp)) && GET_MODE (target) == GET_MODE (valreg)) /* TARGET and VALREG cannot be equal at this point because the latter would not have REG_FUNCTION_VALUE_P true, while the former would if it were referring to the same register. If they refer to the same register, this move will be a no-op, except when function inlining is being done. */ emit_move_insn (target, valreg); else target = copy_to_reg (valreg);#ifdef PROMOTE_FUNCTION_RETURN /* If we promoted this return value, make the proper SUBREG. */ if (GET_MODE (target) != TYPE_MODE (TREE_TYPE (exp))) { enum machine_mode mode = GET_MODE (target); int unsignedp = TREE_UNSIGNED (TREE_TYPE (exp)); if (TREE_CODE (TREE_TYPE (exp)) == INTEGER_TYPE || TREE_CODE (TREE_TYPE (exp)) == ENUMERAL_TYPE || TREE_CODE (TREE_TYPE (exp)) == BOOLEAN_TYPE || TREE_CODE (TREE_TYPE (exp)) == CHAR_TYPE || TREE_CODE (TREE_TYPE (exp)) == REAL_TYPE || TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE || TREE_CODE (TREE_TYPE (exp)) == OFFSET_TYPE) { PROMOTE_MODE (mode, unsignedp, TREE_TYPE (exp)); } target = gen_rtx (SUBREG, TYPE_MODE (TREE_TYPE (exp)), target, 0); SUBREG_PROMOTED_VAR_P (target) = 1; SUBREG_PROMOTED_UNSIGNED_P (target) = unsignedp; }#endif /* Perform all cleanups needed for the arguments of this call (i.e. destructors in C++). */ expand_cleanups_to (old_cleanups); /* If size of args is variable or this was a constructor call for a stack argument, restore saved stack-pointer value. */ if (old_stack_level) { emit_stack_restore (SAVE_BLOCK, old_stack_level, NULL_RTX); pending_stack_adjust = old_pending_adj;#ifdef ACCUMULATE_OUTGOING_ARGS stack_arg_under_construction = old_stack_arg_under_construction; highest_outgoing_arg_in_use = initial_highest_arg_in_use; stack_usage_map = initial_stack_usage_map;#endif }#ifdef ACCUMULATE_OUTGOING_ARGS else {#ifdef REG_PARM_STACK_SPACE if (save_area) { enum machine_mode save_mode = GET_MODE (save_area); rtx 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) emit_move_insn (stack_area, save_area); else emit_block_move (stack_area, validize_mem (save_area), GEN_INT (high_to_save - low_to_save + 1), PARM_BOUNDARY / BITS_PER_UNIT); }#endif /* If we saved any argument areas, restore them. */ for (i = 0; i < num_actuals; i++) if (args[i].save_area) { enum machine_mode save_mode = GET_MODE (args[i].save_area); rtx stack_area = gen_rtx (MEM, save_mode, memory_address (save_mode, XEXP (args[i].stack_slot, 0))); if (save_mode != BLKmode) emit_move_insn (stack_area, args[i].save_area); else emit_block_move (stack_area, validize_mem (args[i].save_area), GEN_INT (args[i].size.constant), PARM_BOUNDARY / BITS_PER_UNIT); } highest_outgoing_arg_in_use = initial_highest_arg_in_use; stack_usage_map = initial_stack_usage_map; }#endif /* If this was alloca, record the new stack level for nonlocal gotos. Check for the handler slots since we might not have a save area for non-local gotos. */ if (may_be_alloca && nonlocal_goto_handler_slot != 0) emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX); pop_temp_slots (); return target;}#if 0/* Return an rtx which represents a suitable home on the stack given TYPE, the type of the argument looking for a home. This is called only for BLKmode arguments. SIZE is the size needed for this target. ARGS_ADDR is the address of the bottom of the argument block for this call. OFFSET describes this parameter's offset into ARGS_ADDR. It is meaningless if this machine uses push insns. */static rtxtarget_for_arg (type, size, args_addr, offset) tree type; rtx size; rtx args_addr; struct args_size offset;{ rtx target; rtx offset_rtx = ARGS_SIZE_RTX (offset); /* We do not call memory_address if possible, because we want to address as close to the stack as possible. For non-variable sized arguments, this will be stack-pointer relative addressing. */ if (GET_CODE (offset_rtx) == CONST_INT) target = plus_constant (args_addr, INTVAL (offset_rtx)); else { /* I have no idea how to guarantee that this will work in the presence of register parameters. */ target = gen_rtx (PLUS, Pmode, args_addr, offset_rtx); target = memory_address (QImode, target); } return gen_rtx (MEM, BLKmode, target);}#endif/* Store a single argument for a function call into the register or memory area where it must be passed. *ARG describes the argument value and where to pass it. ARGBLOCK is the address of the stack-block for all the arguments, or 0 on a machine where arguments are pushed individually. MAY_BE_ALLOCA nonzero says this could be a call to `alloca' so must be careful about how the stack is used. VARIABLE_SIZE nonzero says that this was a variable-sized outgoing argument stack. This is used if ACCUMULATE_OUTGOING_ARGS to indicate that we need not worry about saving and restoring the stack. FNDECL is the declaration of the function we are calling. */static voidstore_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl, reg_parm_stack_space) struct arg_data *arg; rtx argblock; int may_be_alloca; int variable_size; tree fndecl; int reg_parm_stack_space;{ register tree pval = arg->tree_value; rtx reg = 0; int partial = 0; int used = 0; int i, lower_bound, upper_bound; if (TREE_CODE (pval) == ERROR_MARK) return;#ifdef ACCUMULATE_OUTGOING_ARGS /* If this is being stored into a pre-allocated, fixed-size, stack area, save any previous data at that location. */ if (argblock && ! variable_size && arg->stack) {#ifdef ARGS_GROW_DOWNWARD /* stack_slot is negative, but we want to index stack_usage_map */ /* with positive values. */ if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS) upper_bound = -INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)) + 1; else abort (); lower_bound = upper_bound - arg->size.constant;#else if (GET_CODE (XEXP (arg->stack_slot, 0)) == PLUS) lower_bound = INTVAL (XEXP (XEXP (arg->stack_slot, 0), 1)); else lower_bound = 0; upper_bound = lower_bound + arg->size.constant;#endif for (i = lower_bound; i < upper_bound; i++) if (stack_usage_map[i]#ifdef REG_PARM_STACK_SPACE /* Don't store things in the fixed argument area at this point; it has already been saved. */ && i > reg_parm_stack_space#endif ) break; if (i != upper_bound) { /* We need to make a save area. See what mode we can make it. */ enum machine_mode save_mode = mode_for_size (arg->size.constant * BITS_PER_UNIT, MODE_INT, 1); rtx stack_area = gen_rtx (MEM, save_mode, memory_address (save_mode, XEXP (arg->stack_slot, 0))); if (save_mode == BLKmode) { arg->save_area = assign_stack_temp (BLKmode, arg->size.constant, 1); emit_block_move (validize_mem (arg->save_area), stack_area, GEN_INT (arg->size.constant), PARM_BOUNDARY / BITS_PER_UNIT); } else { arg->save_area = gen_reg_rtx (save_mode); emit_move_insn (arg->save_area, stack_area); } } }#endif /* If this isn't going to be placed on both the stack and in registers, set up the register and number of words. */ if (! arg->pass_on_stack) reg = arg->reg, partial = arg->partial; if (reg != 0 && partial == 0) /* Being passed entirely in a register. We shouldn't be called in this case. */ abort (); /* If this is being partially passed in a register, but multiple locations are specified, we assume that the one partially used is the one that is listed first. */ if (reg && GET_CODE (reg) == EXPR_LIST) reg = XEXP (reg, 0); /* If this is being passes partially in a register, we can't evaluate it directly into its stack slot. Otherwise, we can. */ if (arg->value == 0) {#ifdef ACCUMULATE_OUTGOING_ARGS /* stack_arg_under_construction is nonzero if a function argument is being evaluated directly into the outgoing argument list and expand_call must take special action to preserve the argument list if it is called recursively. For scalar function arguments stack_usage_map is sufficient to determine which stack slots must be saved and restored. Scalar arguments in general have pass_on_stack == 0. If this argument is initialized by a function which takes the address of the argument (a C++ constructor or a C function returning a BLKmode structure), then stack_usage_map is insufficient and expand_call must push the stack around the function call. Such arguments have pass_on_stack == 1. Note that it is always sa
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -