diff options
author | law <law@138bc75d-0d04-0410-961f-82ee72b054a4> | 1999-02-24 19:06:17 +0000 |
---|---|---|
committer | law <law@138bc75d-0d04-0410-961f-82ee72b054a4> | 1999-02-24 19:06:17 +0000 |
commit | cb543c542961f9a7958583d899bdc9cd5ac38800 (patch) | |
tree | f118bf5a5afcf7a63b8a156e3f96310bc8d42f53 /gcc/calls.c | |
parent | c4a0f13a37c8adb96de4dac7065b640c2b41e108 (diff) | |
download | gcc-cb543c542961f9a7958583d899bdc9cd5ac38800.tar.gz |
* calls.c (initialize_argument_information): New function extracted
from expand_call.
(expand_call): Use initialize_argument_information. Remove variables
which are no longer used due to cleanups.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@25416 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/calls.c')
-rw-r--r-- | gcc/calls.c | 530 |
1 files changed, 295 insertions, 235 deletions
diff --git a/gcc/calls.c b/gcc/calls.c index d0ca24ad81f..b7c4aa1dc1e 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -145,6 +145,13 @@ static void precompute_arguments PROTO ((int, int, int, struct args_size *)); static int compute_argument_block_size PROTO ((int, struct args_size *)); +static void initialize_argument_information PROTO ((int, + struct arg_data *, + struct args_size *, + int, tree, tree, + CUMULATIVE_ARGS, + int, rtx *, int *, + int *, int *)); #if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE) static rtx save_fixed_argument_area PROTO ((int, rtx, int *, int *)); @@ -830,6 +837,287 @@ store_unaligned_arguments_into_pseudos (args, num_actuals) } } +/* Fill in ARGS_SIZE and ARGS array based on the parameters found in + ACTPARMS. + + NUM_ACTUALS is the total number of parameters. + + N_NAMED_ARGS is the total number of named arguments. + + FNDECL is the tree code for the target of this call (if known) + + ARGS_SO_FAR holds state needed by the target to know where to place + the next argument. + + REG_PARM_STACK_SPACE is the number of bytes of stack space reserved + for arguments which are passed in registers. + + OLD_STACK_LEVEL is a pointer to an rtx which olds the old stack level + and may be modified by this routine. + + OLD_PENDING_ADJ, MUST_PREALLOCATE and IS_CONST are pointers to integer + flags which may may be modified by this routine. */ + +static void +initialize_argument_information (num_actuals, args, args_size, n_named_args, + actparms, fndecl, args_so_far, + reg_parm_stack_space, old_stack_level, + old_pending_adj, must_preallocate, is_const) + int num_actuals; + struct arg_data *args; + struct args_size *args_size; + int n_named_args; + tree actparms; + tree fndecl; + CUMULATIVE_ARGS args_so_far; + int reg_parm_stack_space; + rtx *old_stack_level; + int *old_pending_adj; + int *must_preallocate; + int *is_const; +{ + /* 1 if scanning parms front to back, -1 if scanning back to front. */ + int inc; + + /* Count arg position in order args appear. */ + int argpos; + + int i; + tree p; + + 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 or 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)); + int unsignedp; + 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; + + /* If TYPE is a transparent union, pass things the way we would + pass the first field of the union. We have already verified that + the modes are the same. */ + if (TYPE_TRANSPARENT_UNION (type)) + type = TREE_TYPE (TYPE_FIELDS (type)); + + /* 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. */ + + /* See if this argument should be passed by invisible reference. */ + if ((TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST + && contains_placeholder_p (TYPE_SIZE (type))) + || TREE_ADDRESSABLE (type) +#ifdef FUNCTION_ARG_PASS_BY_REFERENCE + || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type), + type, argpos < n_named_args) +#endif + ) + { + /* If we're compiling a thunk, pass through invisible + references instead of making a copy. */ + if (current_function_is_thunk +#ifdef FUNCTION_ARG_CALLEE_COPIES + || (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type), + type, argpos < n_named_args) + /* If it's in a register, we must make a copy of it too. */ + /* ??? Is this a sufficient test? Is there a better one? */ + && !(TREE_CODE (args[i].tree_value) == VAR_DECL + && REG_P (DECL_RTL (args[i].tree_value))) + && ! TREE_ADDRESSABLE (type)) +#endif + ) + { + /* C++ uses a TARGET_EXPR to indicate that we want to make a + new object from the argument. If we are passing by + invisible reference, the callee will do that for us, so we + can strip off the TARGET_EXPR. This is not always safe, + but it is safe in the only case where this is a useful + optimization; namely, when the argument is a plain object. + In that case, the frontend is just asking the backend to + make a bitwise copy of the argument. */ + + if (TREE_CODE (args[i].tree_value) == TARGET_EXPR + && (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND + (args[i].tree_value, 1))) + == 'd') + && ! REG_P (DECL_RTL (TREE_OPERAND (args[i].tree_value, 1)))) + args[i].tree_value = TREE_OPERAND (args[i].tree_value, 1); + + args[i].tree_value = build1 (ADDR_EXPR, + build_pointer_type (type), + args[i].tree_value); + type = build_pointer_type (type); + } + else + { + /* We make a copy of the object and pass the address to the + function being called. */ + rtx copy; + + if (TYPE_SIZE (type) == 0 + || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST + || (flag_stack_check && ! STACK_CHECK_BUILTIN + && (TREE_INT_CST_HIGH (TYPE_SIZE (type)) != 0 + || (TREE_INT_CST_LOW (TYPE_SIZE (type)) + > STACK_CHECK_MAX_VAR_SIZE * BITS_PER_UNIT)))) + { + /* This is a variable-sized object. Make space on the stack + for it. */ + rtx size_rtx = expr_size (TREE_VALUE (p)); + + 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 + { + int size = int_size_in_bytes (type); + copy = assign_stack_temp (TYPE_MODE (type), size, 0); + } + + MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type)); + + store_expr (args[i].tree_value, copy, 0); + *is_const = 0; + + args[i].tree_value = build1 (ADDR_EXPR, + build_pointer_type (type), + make_tree (type, copy)); + type = build_pointer_type (type); + } + } + + mode = TYPE_MODE (type); + unsignedp = TREE_UNSIGNED (type); + +#ifdef PROMOTE_FUNCTION_ARGS + mode = promote_mode (type, mode, &unsignedp, 1); +#endif + + args[i].unsignedp = unsignedp; + args[i].mode = mode; + 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 a (parallel [(expr_list (nil) ...) ...]), + it means that we are to pass this arg in the register(s) designated + by the PARALLEL, but also to pass it in the stack. */ + if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL + && XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0) + args[i].pass_on_stack = 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 + || reg_parm_stack_space > 0 + || args[i].pass_on_stack) + locate_and_pad_parm (mode, 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 + + /* If a part of the arg was put into registers, + don't include that part in the amount pushed. */ + if (reg_parm_stack_space == 0 && ! 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)); + + /* 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); + } +} + /* Update ARGS_SIZE to contain the total size for the argument block. Return the original constant component of the argument block's size. @@ -1071,8 +1359,6 @@ expand_call (exp, target, ignore) /* Number of named args. Args after this are anonymous ones and they must all go on the stack. */ int n_named_args; - /* Count arg position in order args appear. */ - int argpos; /* Vector of information about each argument. Arguments are numbered in the order they will be pushed, @@ -1104,8 +1390,6 @@ expand_call (exp, target, ignore) /* Size of the stack reserved for parameter registers. */ int reg_parm_stack_space = 0; - /* 1 if scanning parms front to back, -1 if scanning back to front. */ - int inc; /* Address of space preallocated for stack parms (on machines that lack push insns), or 0 if space not preallocated. */ rtx argblock = 0; @@ -1477,237 +1761,13 @@ expand_call (exp, target, ignore) args = (struct arg_data *) alloca (num_actuals * sizeof (struct arg_data)); bzero ((char *) 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 or 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)); - int unsignedp; - 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; - - /* If TYPE is a transparent union, pass things the way we would - pass the first field of the union. We have already verified that - the modes are the same. */ - if (TYPE_TRANSPARENT_UNION (type)) - type = TREE_TYPE (TYPE_FIELDS (type)); - - /* 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. */ - - /* See if this argument should be passed by invisible reference. */ - if ((TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST - && contains_placeholder_p (TYPE_SIZE (type))) - || TREE_ADDRESSABLE (type) -#ifdef FUNCTION_ARG_PASS_BY_REFERENCE - || FUNCTION_ARG_PASS_BY_REFERENCE (args_so_far, TYPE_MODE (type), - type, argpos < n_named_args) -#endif - ) - { - /* If we're compiling a thunk, pass through invisible - references instead of making a copy. */ - if (current_function_is_thunk -#ifdef FUNCTION_ARG_CALLEE_COPIES - || (FUNCTION_ARG_CALLEE_COPIES (args_so_far, TYPE_MODE (type), - type, argpos < n_named_args) - /* If it's in a register, we must make a copy of it too. */ - /* ??? Is this a sufficient test? Is there a better one? */ - && !(TREE_CODE (args[i].tree_value) == VAR_DECL - && REG_P (DECL_RTL (args[i].tree_value))) - && ! TREE_ADDRESSABLE (type)) -#endif - ) - { - /* C++ uses a TARGET_EXPR to indicate that we want to make a - new object from the argument. If we are passing by - invisible reference, the callee will do that for us, so we - can strip off the TARGET_EXPR. This is not always safe, - but it is safe in the only case where this is a useful - optimization; namely, when the argument is a plain object. - In that case, the frontend is just asking the backend to - make a bitwise copy of the argument. */ - - if (TREE_CODE (args[i].tree_value) == TARGET_EXPR - && (TREE_CODE_CLASS (TREE_CODE (TREE_OPERAND - (args[i].tree_value, 1))) - == 'd') - && ! REG_P (DECL_RTL (TREE_OPERAND (args[i].tree_value, 1)))) - args[i].tree_value = TREE_OPERAND (args[i].tree_value, 1); - - args[i].tree_value = build1 (ADDR_EXPR, - build_pointer_type (type), - args[i].tree_value); - type = build_pointer_type (type); - } - else - { - /* We make a copy of the object and pass the address to the - function being called. */ - rtx copy; - - if (TYPE_SIZE (type) == 0 - || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST - || (flag_stack_check && ! STACK_CHECK_BUILTIN - && (TREE_INT_CST_HIGH (TYPE_SIZE (type)) != 0 - || (TREE_INT_CST_LOW (TYPE_SIZE (type)) - > STACK_CHECK_MAX_VAR_SIZE * BITS_PER_UNIT)))) - { - /* This is a variable-sized object. Make space on the stack - for it. */ - rtx size_rtx = expr_size (TREE_VALUE (p)); - - 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 - { - int size = int_size_in_bytes (type); - copy = assign_stack_temp (TYPE_MODE (type), size, 0); - } - - MEM_SET_IN_STRUCT_P (copy, AGGREGATE_TYPE_P (type)); - - store_expr (args[i].tree_value, copy, 0); - is_const = 0; - - args[i].tree_value = build1 (ADDR_EXPR, - build_pointer_type (type), - make_tree (type, copy)); - type = build_pointer_type (type); - } - } - - mode = TYPE_MODE (type); - unsignedp = TREE_UNSIGNED (type); - -#ifdef PROMOTE_FUNCTION_ARGS - mode = promote_mode (type, mode, &unsignedp, 1); -#endif - - args[i].unsignedp = unsignedp; - args[i].mode = mode; - 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 a (parallel [(expr_list (nil) ...) ...]), - it means that we are to pass this arg in the register(s) designated - by the PARALLEL, but also to pass it in the stack. */ - if (args[i].reg && GET_CODE (args[i].reg) == PARALLEL - && XEXP (XVECEXP (args[i].reg, 0, 0), 0) == 0) - args[i].pass_on_stack = 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 - || reg_parm_stack_space > 0 - || args[i].pass_on_stack) - locate_and_pad_parm (mode, 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 - - /* If a part of the arg was put into registers, - don't include that part in the amount pushed. */ - if (reg_parm_stack_space == 0 && ! 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)); - - /* 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); - } + /* Build up entries inthe ARGS array, compute the size of the arguments + into ARGS_SIZE, etc. */ + initialize_argument_information (num_actuals, args, &args_size, n_named_args, + actparms, fndecl, args_so_far, + reg_parm_stack_space, &old_stack_level, + &old_pending_adj, &must_preallocate, + &is_const); #ifdef FINAL_REG_PARM_STACK_SPACE reg_parm_stack_space = FINAL_REG_PARM_STACK_SPACE (args_size.constant, |