diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 12afa4c4978..359dea29197 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,14 @@ +Wed Jan 6 16:08:54 1999 Jeffrey A Law (law@cygnus.com) + + * calls.c (special_function_p): New function broken out of + expand_call. + (precompute_register_parameters): Likewise. + (store_one_arg): Likewise. + (store_unaligned_argumetns_into_pseudos): Likewise. + (save_fixed_argument_area): Likewise. + (restore_fixed_argument_area): Likewise. + (expand_call): Corresponding changes. + Wed Jan 6 10:43:29 1999 Andreas Schwab * config/m68k/m68k.c (const_uint32_operand): Remove CONSTANT_P_RTX diff --git a/gcc/calls.c b/gcc/calls.c index 79cae9f6998..2d2b4f40a11 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -124,13 +124,24 @@ static int highest_outgoing_arg_in_use; int stack_arg_under_construction; #endif -static int calls_function PROTO((tree, int)); -static int calls_function_1 PROTO((tree, int)); -static void emit_call_1 PROTO((rtx, tree, tree, HOST_WIDE_INT, - HOST_WIDE_INT, rtx, rtx, - int, rtx, int)); +static int calls_function PROTO ((tree, int)); +static int calls_function_1 PROTO ((tree, int)); +static void emit_call_1 PROTO ((rtx, tree, tree, HOST_WIDE_INT, + HOST_WIDE_INT, rtx, rtx, + int, rtx, int)); +static void special_function_p PROTO ((char *, tree, int *, int *, + int *, int *)); +static void precompute_register_parameters PROTO ((int, struct arg_data *, + int *)); static void store_one_arg PROTO ((struct arg_data *, rtx, int, int, int)); +static void store_unaligned_arguments_into_pseudos PROTO ((struct arg_data *, + int)); + +#if defined(ACCUMULATE_OUTGOING_ARGS) && defined(REG_PARM_STACK_SPACE) +static rtx save_fixed_argument_area PROTO ((int, rtx, int *, int *)); +static void restore_fixed_argument_area PROTO ((rtx, rtx, int, int)); +#endif /* If WHICH is 1, return 1 if EXP contains a call to the built-in function `alloca'. @@ -475,6 +486,338 @@ emit_call_1 (funexp, fndecl, funtype, stack_size, struct_value_size, #endif } +/* Determine if the function identified by NAME and FNDECL is one with + special properties we wish to know about. + + For example, if the function might return more than one time (setjmp), then + set RETURNS_TWICE to a nonzero value. + + Similarly set IS_LONGJMP for if the function is in the longjmp family. + + Set IS_MALLOC for any of the standard memory allocation functions which + allocate from the heap. + + Set MAY_BE_ALLOCA for any memory allocation function that might allocate + space from the stack such as alloca. */ + +static void +special_function_p (name, fndecl, returns_twice, is_longjmp, + is_malloc, may_be_alloca) + char *name; + tree fndecl; + int *returns_twice; + int *is_longjmp; + int *is_malloc; + int *may_be_alloca; +{ + *returns_twice = 0; + *is_longjmp = 0; + *is_malloc = 0; + *may_be_alloca = 0; + + /* We assume that alloca will always be called by name. It + makes no sense to pass it as a pointer-to-function to + anything that does not understand its behavior. */ + *may_be_alloca + = (name && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6 + && name[0] == 'a' + && ! strcmp (name, "alloca")) + || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16 + && name[0] == '_' + && ! strcmp (name, "__builtin_alloca")))); + + if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17 + /* Exclude functions not at the file scope, or not `extern', + since they are not the magic functions we would otherwise + think they are. */ + && DECL_CONTEXT (fndecl) == NULL_TREE && TREE_PUBLIC (fndecl)) + { + char *tname = name; + + /* Disregard prefix _, __ or __x. */ + if (name[0] == '_') + { + if (name[1] == '_' && name[2] == 'x') + tname += 3; + else if (name[1] == '_') + tname += 2; + else + tname += 1; + } + + if (tname[0] == 's') + { + *returns_twice + = ((tname[1] == 'e' + && (! strcmp (tname, "setjmp") + || ! strcmp (tname, "setjmp_syscall"))) + || (tname[1] == 'i' + && ! strcmp (tname, "sigsetjmp")) + || (tname[1] == 'a' + && ! strcmp (tname, "savectx"))); + if (tname[1] == 'i' + && ! strcmp (tname, "siglongjmp")) + *is_longjmp = 1; + } + else if ((tname[0] == 'q' && tname[1] == 's' + && ! strcmp (tname, "qsetjmp")) + || (tname[0] == 'v' && tname[1] == 'f' + && ! strcmp (tname, "vfork"))) + *returns_twice = 1; + + else if (tname[0] == 'l' && tname[1] == 'o' + && ! strcmp (tname, "longjmp")) + *is_longjmp = 1; + /* XXX should have "malloc" attribute on functions instead + of recognizing them by name. */ + else if (! strcmp (tname, "malloc") + || ! strcmp (tname, "calloc") + || ! strcmp (tname, "realloc") + /* Note use of NAME rather than TNAME here. These functions + are only reserved when preceded with __. */ + || ! strcmp (name, "__vn") /* mangled __builtin_vec_new */ + || ! strcmp (name, "__nw") /* mangled __builtin_new */ + || ! strcmp (name, "__builtin_new") + || ! strcmp (name, "__builtin_vec_new")) + *is_malloc = 1; + } +} + +/* Precompute all register parameters as described by ARGS, storing values + into fields within the ARGS array. + + NUM_ACTUALS indicates the total number elements in the ARGS array. + + Set REG_PARM_SEEN if we encounter a register parameter. */ + +static void +precompute_register_parameters (num_actuals, args, reg_parm_seen) + int num_actuals; + struct arg_data *args; + int *reg_parm_seen; +{ + int i; + + *reg_parm_seen = 0; + + for (i = 0; i < num_actuals; i++) + if (args[i].reg != 0 && ! args[i].pass_on_stack) + { + *reg_parm_seen = 1; + + if (args[i].value == 0) + { + push_temp_slots (); + 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 (); + } + + /* If we are to promote the function arg to a wider mode, + do it now. */ + + if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value))) + args[i].value + = convert_modes (args[i].mode, + TYPE_MODE (TREE_TYPE (args[i].tree_value)), + args[i].value, args[i].unsignedp); + + /* If the value is expensive, and we are inside an appropriately + short loop, put the value into a pseudo and then put the pseudo + into the hard reg. + + For small register classes, also do this if this call uses + register parameters. This is to avoid reload conflicts while + loading the parameters registers. */ + + if ((! (GET_CODE (args[i].value) == REG + || (GET_CODE (args[i].value) == SUBREG + && GET_CODE (SUBREG_REG (args[i].value)) == REG))) + && args[i].mode != BLKmode + && rtx_cost (args[i].value, SET) > 2 + && ((SMALL_REGISTER_CLASSES && *reg_parm_seen) + || preserve_subexpressions_p ())) + args[i].value = copy_to_mode_reg (args[i].mode, args[i].value); + } +} + +#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. */ +static rtx +save_fixed_argument_area (reg_parm_stack_space, argblock, + low_to_save, high_to_save) + int reg_parm_stack_space; + rtx argblock; + int *low_to_save; + int *high_to_save; +{ + int i; + rtx save_area = NULL_RTX; + + /* 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; + +#ifdef ARGS_GROW_DOWNWARD + stack_area = gen_rtx_MEM (save_mode, + memory_address (save_mode, + plus_constant (argblock, + - *high_to_save))); +#else + stack_area = gen_rtx_MEM (save_mode, + memory_address (save_mode, + plus_constant (argblock, + *low_to_save))); +#endif + if (save_mode == BLKmode) + { + save_area = assign_stack_temp (BLKmode, num_to_save, 0); + MEM_IN_STRUCT_P (save_area) = 0; + 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); + } + } + return save_area; +} + +static void +restore_fixed_argument_area (save_area, argblock, high_to_save, low_to_save) + rtx save_area; + rtx argblock; + int high_to_save; + int low_to_save; +{ + enum machine_mode save_mode = GET_MODE (save_area); +#ifdef ARGS_GROW_DOWNWARD + rtx stack_area + = gen_rtx_MEM (save_mode, + memory_address (save_mode, + plus_constant (argblock, + - high_to_save))); +#else + rtx stack_area + = gen_rtx_MEM (save_mode, + memory_address (save_mode, + 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 any elements in ARGS refer to parameters that are to be passed in + registers, but not in memory, and whose alignment does not permit a + direct copy into registers. Copy the values into a group of pseudos + which we will later copy into the appropriate hard registers. */ +static void +store_unaligned_arguments_into_pseudos (args, num_actuals) + struct arg_data *args; + int num_actuals; +{ + int i, j; + + for (i = 0; i < num_actuals; i++) + if (args[i].reg != 0 && ! args[i].pass_on_stack + && args[i].mode == BLKmode + && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value)) + < (unsigned int) MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD))) + { + int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value)); + int big_endian_correction = 0; + + args[i].n_aligned_regs + = args[i].partial ? args[i].partial + : (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; + + args[i].aligned_regs = (rtx *) alloca (sizeof (rtx) + * args[i].n_aligned_regs); + + /* Structures smaller than a word are aligned to the least + significant byte (to the right). On a BYTES_BIG_ENDIAN machine, + this means we must skip the empty high order bytes when + calculating the bit offset. */ + if (BYTES_BIG_ENDIAN && bytes < UNITS_PER_WORD) + big_endian_correction = (BITS_PER_WORD - (bytes * BITS_PER_UNIT)); + + for (j = 0; j < args[i].n_aligned_regs; j++) + { + rtx reg = gen_reg_rtx (word_mode); + rtx word = operand_subword_force (args[i].value, j, BLKmode); + int bitsize = MIN (bytes * BITS_PER_UNIT, BITS_PER_WORD); + int bitalign = TYPE_ALIGN (TREE_TYPE (args[i].tree_value)); + + args[i].aligned_regs[j] = reg; + + /* There is no need to restrict this code to loading items + in TYPE_ALIGN sized hunks. The bitfield instructions can + load up entire word sized registers efficiently. + + ??? This may not be needed anymore. + We use to emit a clobber here but that doesn't let later + passes optimize the instructions we emit. By storing 0 into + the register later passes know the first AND to zero out the + bitfield being set in the register is unnecessary. The store + of 0 will be deleted as will at least the first AND. */ + + emit_move_insn (reg, const0_rtx); + + bytes -= bitsize / BITS_PER_UNIT; + store_bit_field (reg, bitsize, big_endian_correction, word_mode, + extract_bit_field (word, bitsize, 0, 1, + NULL_RTX, word_mode, + word_mode, + bitalign / BITS_PER_UNIT, + BITS_PER_WORD), + bitalign / BITS_PER_UNIT, BITS_PER_WORD); + } + } +} + /* Generate all the code for a function call and return an rtx for its value. Store the value in TARGET (specified as an rtx) if convenient. @@ -827,89 +1170,10 @@ expand_call (exp, target, ignore) if (fndecl && DECL_NAME (fndecl)) name = IDENTIFIER_POINTER (DECL_NAME (fndecl)); -#if 0 - /* Unless it's a call to a specific function that isn't alloca, - if it has one argument, we must assume it might be alloca. */ - - may_be_alloca - = (!(fndecl != 0 && strcmp (name, "alloca")) - && actparms != 0 - && TREE_CHAIN (actparms) == 0); -#else - /* We assume that alloca will always be called by name. It - makes no sense to pass it as a pointer-to-function to - anything that does not understand its behavior. */ - may_be_alloca - = (name && ((IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 6 - && name[0] == 'a' - && ! strcmp (name, "alloca")) - || (IDENTIFIER_LENGTH (DECL_NAME (fndecl)) == 16 - && name[0] == '_' - && ! strcmp (name, "__builtin_alloca")))); -#endif - /* See if this is a call to a function that can return more than once - or a call to longjmp. */ - - returns_twice = 0; - is_longjmp = 0; - is_malloc = 0; - - if (name != 0 && IDENTIFIER_LENGTH (DECL_NAME (fndecl)) <= 17 - /* Exclude functions not at the file scope, or not `extern', - since they are not the magic functions we would otherwise - think they are. */ - && DECL_CONTEXT (fndecl) == NULL_TREE && TREE_PUBLIC (fndecl)) - { - char *tname = name; - - /* Disregard prefix _, __ or __x. */ - if (name[0] == '_') - { - if (name[1] == '_' && name[2] == 'x') - tname += 3; - else if (name[1] == '_') - tname += 2; - else - tname += 1; - } - - if (tname[0] == 's') - { - returns_twice - = ((tname[1] == 'e' - && (! strcmp (tname, "setjmp") - || ! strcmp (tname, "setjmp_syscall"))) - || (tname[1] == 'i' - && ! strcmp (tname, "sigsetjmp")) - || (tname[1] == 'a' - && ! strcmp (tname, "savectx"))); - if (tname[1] == 'i' - && ! strcmp (tname, "siglongjmp")) - is_longjmp = 1; - } - else if ((tname[0] == 'q' && tname[1] == 's' - && ! strcmp (tname, "qsetjmp")) - || (tname[0] == 'v' && tname[1] == 'f' - && ! strcmp (tname, "vfork"))) - returns_twice = 1; - - else if (tname[0] == 'l' && tname[1] == 'o' - && ! strcmp (tname, "longjmp")) - is_longjmp = 1; - /* XXX should have "malloc" attribute on functions instead - of recognizing them by name. */ - else if (! strcmp (tname, "malloc") - || ! strcmp (tname, "calloc") - || ! strcmp (tname, "realloc") - /* Note use of NAME rather than TNAME here. These functions - are only reserved when preceded with __. */ - || ! strcmp (name, "__vn") /* mangled __builtin_vec_new */ - || ! strcmp (name, "__nw") /* mangled __builtin_new */ - || ! strcmp (name, "__builtin_new") - || ! strcmp (name, "__builtin_vec_new")) - is_malloc = 1; - } + or a call to longjmp or malloc. */ + special_function_p (name, fndecl, &returns_twice, &is_longjmp, + &is_malloc, &may_be_alloca); if (may_be_alloca) current_function_calls_alloca = 1; @@ -1650,115 +1914,16 @@ expand_call (exp, target, ignore) /* 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) - { - reg_parm_seen = 1; - - if (args[i].value == 0) - { - push_temp_slots (); - 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 (); - } - - /* If we are to promote the function arg to a wider mode, - do it now. */ - - if (args[i].mode != TYPE_MODE (TREE_TYPE (args[i].tree_value))) - args[i].value - = convert_modes (args[i].mode, - TYPE_MODE (TREE_TYPE (args[i].tree_value)), - args[i].value, args[i].unsignedp); - - /* If the value is expensive, and we are inside an appropriately - short loop, put the value into a pseudo and then put the pseudo - into the hard reg. - - For small register classes, also do this if this call uses - register parameters. This is to avoid reload conflicts while - loading the parameters registers. */ - - if ((! (GET_CODE (args[i].value) == REG - || (GET_CODE (args[i].value) == SUBREG - && GET_CODE (SUBREG_REG (args[i].value)) == REG))) - && args[i].mode != BLKmode - && rtx_cost (args[i].value, SET) > 2 - && ((SMALL_REGISTER_CLASSES && reg_parm_seen) - || preserve_subexpressions_p ())) - args[i].value = copy_to_mode_reg (args[i].mode, args[i].value); - } + precompute_register_parameters (num_actuals, args, ®_parm_seen); #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++) + /* Save the fixed argument area if it's part of the caller's frame and + is clobbered by argument setup for this call. */ + save_area = save_fixed_argument_area (reg_parm_stack_space, argblock, + &low_to_save, &high_to_save); #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; - -#ifdef ARGS_GROW_DOWNWARD - stack_area = gen_rtx_MEM (save_mode, - memory_address (save_mode, - plus_constant (argblock, - - high_to_save))); -#else - stack_area = gen_rtx_MEM (save_mode, - memory_address (save_mode, - plus_constant (argblock, - low_to_save))); -#endif - if (save_mode == BLKmode) - { - save_area = assign_stack_temp (BLKmode, num_to_save, 0); - MEM_IN_STRUCT_P (save_area) = 0; - 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, @@ -1775,63 +1940,8 @@ expand_call (exp, target, ignore) and whose alignment does not permit a direct copy into registers, make a group of pseudos that correspond to each register that we will later fill. */ - if (STRICT_ALIGNMENT) - for (i = 0; i < num_actuals; i++) - if (args[i].reg != 0 && ! args[i].pass_on_stack - && args[i].mode == BLKmode - && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value)) - < (unsigned int) MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD))) - { - int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value)); - int big_endian_correction = 0; - - args[i].n_aligned_regs - = args[i].partial ? args[i].partial - : (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD; - - args[i].aligned_regs = (rtx *) alloca (sizeof (rtx) - * args[i].n_aligned_regs); - - /* Structures smaller than a word are aligned to the least - significant byte (to the right). On a BYTES_BIG_ENDIAN machine, - this means we must skip the empty high order bytes when - calculating the bit offset. */ - if (BYTES_BIG_ENDIAN && bytes < UNITS_PER_WORD) - big_endian_correction = (BITS_PER_WORD - (bytes * BITS_PER_UNIT)); - - for (j = 0; j < args[i].n_aligned_regs; j++) - { - rtx reg = gen_reg_rtx (word_mode); - rtx word = operand_subword_force (args[i].value, j, BLKmode); - int bitsize = MIN (bytes * BITS_PER_UNIT, BITS_PER_WORD); - int bitalign = TYPE_ALIGN (TREE_TYPE (args[i].tree_value)); - - args[i].aligned_regs[j] = reg; - - /* There is no need to restrict this code to loading items - in TYPE_ALIGN sized hunks. The bitfield instructions can - load up entire word sized registers efficiently. - - ??? This may not be needed anymore. - We use to emit a clobber here but that doesn't let later - passes optimize the instructions we emit. By storing 0 into - the register later passes know the first AND to zero out the - bitfield being set in the register is unnecessary. The store - of 0 will be deleted as will at least the first AND. */ - - emit_move_insn (reg, const0_rtx); - - bytes -= bitsize / BITS_PER_UNIT; - store_bit_field (reg, bitsize, big_endian_correction, word_mode, - extract_bit_field (word, bitsize, 0, 1, - NULL_RTX, word_mode, - word_mode, - bitalign / BITS_PER_UNIT, - BITS_PER_WORD), - bitalign / BITS_PER_UNIT, BITS_PER_WORD); - } - } + store_unaligned_arguments_into_pseudos (args, num_actuals); /* Now store any partially-in-registers parm. This is the last place a block-move can happen. */ @@ -2152,31 +2262,10 @@ expand_call (exp, target, ignore) { #ifdef REG_PARM_STACK_SPACE if (save_area) - { - enum machine_mode save_mode = GET_MODE (save_area); -#ifdef ARGS_GROW_DOWNWARD - rtx stack_area - = gen_rtx_MEM (save_mode, - memory_address (save_mode, - plus_constant (argblock, - - high_to_save))); -#else - rtx stack_area - = gen_rtx_MEM (save_mode, - memory_address (save_mode, - plus_constant (argblock, - low_to_save))); + restore_fixed_argument_area (save_area, argblock, + high_to_save, 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)