📄 calls.c
字号:
#endif actparms = tree_cons (error_mark_node, make_tree (build_pointer_type (TREE_TYPE (funtype)), temp), actparms); structure_value_addr_parm = 1; } /* Count the arguments and set NUM_ACTUALS. */ for (p = actparms, i = 0; p; p = TREE_CHAIN (p)) i++; num_actuals = i; /* Compute number of named args. Normally, don't include the last named arg if anonymous args follow. (If no anonymous args follow, the result of list_length is actually one too large.) If SETUP_INCOMING_VARARGS is defined, this machine will be able to place unnamed args that were passed in registers into the stack. So treat all args as named. This allows the insns emitting for a specific argument list to be independent of the function declaration. If SETUP_INCOMING_VARARGS is not defined, we do not have any reliable way to pass unnamed args in registers, so we must force them into memory. */#ifndef SETUP_INCOMING_VARARGS if (TYPE_ARG_TYPES (funtype) != 0) n_named_args = list_length (TYPE_ARG_TYPES (funtype)) - 1 /* Count the struct value address, if it is passed as a parm. */ + structure_value_addr_parm; else#endif /* If we know nothing, treat all args as named. */ n_named_args = num_actuals; /* Make a vector to hold all the information about each arg. */ args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data)); bzero (args, num_actuals * sizeof (struct arg_data)); args_size.constant = 0; args_size.var = 0; /* In this loop, we consider args in the order they are written. We fill up ARGS from the front of from the back if necessary so that in any case the first arg to be pushed ends up at the front. */#ifdef PUSH_ARGS_REVERSED i = num_actuals - 1, inc = -1; /* In this case, must reverse order of args so that we compute and push the last arg first. */#else i = 0, inc = 1;#endif /* I counts args in order (to be) pushed; ARGPOS counts in order written. */ for (p = actparms, argpos = 0; p; p = TREE_CHAIN (p), i += inc, argpos++) { tree type = TREE_TYPE (TREE_VALUE (p)); enum machine_mode mode; args[i].tree_value = TREE_VALUE (p); /* Replace erroneous argument with constant zero. */ if (type == error_mark_node || TYPE_SIZE (type) == 0) args[i].tree_value = integer_zero_node, type = integer_type_node; /* Decide where to pass this arg. args[i].reg is nonzero if all or part is passed in registers. args[i].partial is nonzero if part but not all is passed in registers, and the exact value says how many words are passed in registers. args[i].pass_on_stack is nonzero if the argument must at least be computed on the stack. It may then be loaded back into registers if args[i].reg is nonzero. These decisions are driven by the FUNCTION_... macros and must agree with those made by function.c. */#ifdef FUNCTION_ARG_PASS_BY_REFERENCE /* See if this argument should be passed by invisible reference. */ if (FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type), type, argpos < n_named_args)) { /* We make a copy of the object and pass the address to the function being called. */ int size = int_size_in_bytes (type); rtx copy; if (size < 0) { /* This is a variable-sized object. Make space on the stack for it. */ rtx size_rtx = expand_expr (size_in_bytes (type), NULL_RTX, VOIDmode, 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; } copy = gen_rtx (MEM, BLKmode, allocate_dynamic_stack_space (size_rtx, NULL_RTX, TYPE_ALIGN (type))); } else copy = assign_stack_temp (TYPE_MODE (type), size, 1); store_expr (args[i].tree_value, copy, 0); args[i].tree_value = build1 (ADDR_EXPR, build_pointer_type (type), make_tree (type, copy)); type = build_pointer_type (type); }#endif mode = TYPE_MODE (type);#ifdef PROMOTE_FUNCTION_ARGS /* Compute the mode in which the arg is actually to be extended to. */ if (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == ENUMERAL_TYPE || TREE_CODE (type) == BOOLEAN_TYPE || TREE_CODE (type) == CHAR_TYPE || TREE_CODE (type) == REAL_TYPE || TREE_CODE (type) == POINTER_TYPE || TREE_CODE (type) == OFFSET_TYPE) { int unsignedp = TREE_UNSIGNED (type); PROMOTE_MODE (mode, unsignedp, type); args[i].unsignedp = unsignedp; }#endif args[i].reg = FUNCTION_ARG (args_so_far, mode, type, argpos < n_named_args);#ifdef FUNCTION_ARG_PARTIAL_NREGS if (args[i].reg) args[i].partial = FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode, type, argpos < n_named_args);#endif args[i].pass_on_stack = MUST_PASS_IN_STACK (mode, type); /* If FUNCTION_ARG returned an (expr_list (nil) FOO), it means that we are to pass this arg in the register(s) designated by FOO, but also to pass it in the stack. */ if (args[i].reg && GET_CODE (args[i].reg) == EXPR_LIST && XEXP (args[i].reg, 0) == 0) args[i].pass_on_stack = 1, args[i].reg = XEXP (args[i].reg, 1); /* If this is an addressable type, we must preallocate the stack since we must evaluate the object into its final location. If this is to be passed in both registers and the stack, it is simpler to preallocate. */ if (TREE_ADDRESSABLE (type) || (args[i].pass_on_stack && args[i].reg != 0)) must_preallocate = 1; /* If this is an addressable type, we cannot pre-evaluate it. Thus, we cannot consider this function call constant. */ if (TREE_ADDRESSABLE (type)) is_const = 0; /* Compute the stack-size of this argument. */ if (args[i].reg == 0 || args[i].partial != 0#ifdef REG_PARM_STACK_SPACE || reg_parm_stack_space > 0#endif || args[i].pass_on_stack) locate_and_pad_parm (TYPE_MODE (type), type,#ifdef STACK_PARMS_IN_REG_PARM_AREA 1,#else args[i].reg != 0,#endif fndecl, &args_size, &args[i].offset, &args[i].size);#ifndef ARGS_GROW_DOWNWARD args[i].slot_offset = args_size;#endif#ifndef REG_PARM_STACK_SPACE /* If a part of the arg was put into registers, don't include that part in the amount pushed. */ if (! args[i].pass_on_stack) args[i].size.constant -= ((args[i].partial * UNITS_PER_WORD) / (PARM_BOUNDARY / BITS_PER_UNIT) * (PARM_BOUNDARY / BITS_PER_UNIT));#endif /* Update ARGS_SIZE, the total stack space for args so far. */ 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);#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. */ for (i = 0; i < num_actuals; i++) if (is_const || ((args_size.var != 0 || args_size.constant != 0) && calls_alloca (args[i].tree_value))) { args[i].initial_value = 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 (); } /* 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 if (must_preallocate) { /* 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -