diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a198c85896e..a6572299b2f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,15 @@ +2002-11-26 John David Anglin + + * expr.c (gen_group_rtx, emit_group_move): New functions. + * expr.h (gen_group_rtx, emit_group_move): Prototype. + * function.c (expand_function_start): Use gen_group_rtx to create a + PARALLEL rtx to hold the return value when the real return rtx is a + PARALLEL. + (expand_function_end): Use emit_group_move to move the return value + from a PARALLEL to the real return registers. + * rtl.h (REG_FUNCTION_VALUE_P): Allow function values to be returned + in PARALLELs. + 2002-11-26 Jason Thorpe * config/t-libc-ok: Fix typo. diff --git a/gcc/expr.c b/gcc/expr.c index 8dd9a8678b5..8851343299a 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -2203,6 +2203,42 @@ move_block_from_reg (regno, x, nregs, size) } } +/* Generate a PARALLEL rtx for a new non-consecutive group of registers from + ORIG, where ORIG is a non-consecutive group of registers represented by + a PARALLEL. The clone is identical to the original except in that the + original set of registers is replaced by a new set of pseudo registers. + The new set has the same modes as the original set. */ + +rtx +gen_group_rtx (orig) + rtx orig; +{ + int i, length; + rtx *tmps; + + if (GET_CODE (orig) != PARALLEL) + abort (); + + length = XVECLEN (orig, 0); + tmps = (rtx *) alloca (sizeof (rtx) * length); + + /* Skip a NULL entry in first slot. */ + i = XEXP (XVECEXP (orig, 0, 0), 0) ? 0 : 1; + + if (i) + tmps[0] = 0; + + for (; i < length; i++) + { + enum machine_mode mode = GET_MODE (XEXP (XVECEXP (orig, 0, i), 0)); + rtx offset = XEXP (XVECEXP (orig, 0, i), 1); + + tmps[i] = gen_rtx_EXPR_LIST (VOIDmode, gen_reg_rtx (mode), offset); + } + + return gen_rtx_PARALLEL (GET_MODE (orig), gen_rtvec_v (length, tmps)); +} + /* Emit code to move a block SRC to a block DST, where DST is non-consecutive registers represented by a PARALLEL. SSIZE represents the total size of block SRC in bytes, or -1 if not known. */ @@ -2324,6 +2360,26 @@ emit_group_load (dst, orig_src, ssize) emit_move_insn (XEXP (XVECEXP (dst, 0, i), 0), tmps[i]); } +/* Emit code to move a block SRC to block DST, where SRC and DST are + non-consecutive groups of registers, each represented by a PARALLEL. */ + +void +emit_group_move (dst, src) + rtx dst, src; +{ + int i; + + if (GET_CODE (src) != PARALLEL + || GET_CODE (dst) != PARALLEL + || XVECLEN (src, 0) != XVECLEN (dst, 0)) + abort (); + + /* Skip first entry if NULL. */ + for (i = XEXP (XVECEXP (src, 0, 0), 0) ? 0 : 1; i < XVECLEN (src, 0); i++) + emit_move_insn (XEXP (XVECEXP (dst, 0, i), 0), + XEXP (XVECEXP (src, 0, i), 0)); +} + /* Emit code to move a block SRC to a block DST, where SRC is non-consecutive registers represented by a PARALLEL. SSIZE represents the total size of block DST, or -1 if not known. */ diff --git a/gcc/expr.h b/gcc/expr.h index e8b2c176064..6e8d19e994b 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -412,10 +412,17 @@ extern void move_block_to_reg PARAMS ((int, rtx, int, enum machine_mode)); The number of registers to be filled is NREGS. */ extern void move_block_from_reg PARAMS ((int, rtx, int, int)); +/* Generate a non-consecutive group of registers represented by a PARALLEL. */ +extern rtx gen_group_rtx PARAMS ((rtx)); + /* Load a BLKmode value into non-consecutive registers represented by a PARALLEL. */ extern void emit_group_load PARAMS ((rtx, rtx, int)); +/* Move a non-consecutive group of registers represented by a PARALLEL into + a non-consecutive group of registers represented by a PARALLEL. */ +extern void emit_group_move PARAMS ((rtx, rtx)); + /* Store a BLKmode value from non-consecutive registers represented by a PARALLEL. */ extern void emit_group_store PARAMS ((rtx, rtx, int)); diff --git a/gcc/function.c b/gcc/function.c index 0c066ed0358..6e6d6d7fbf9 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -6559,18 +6559,17 @@ expand_function_start (subr, parms_have_cleanups) subr, 1); /* Structures that are returned in registers are not aggregate_value_p, - so we may see a PARALLEL. Don't play pseudo games with this. */ - if (! REG_P (hard_reg)) - SET_DECL_RTL (DECL_RESULT (subr), hard_reg); + so we may see a PARALLEL or a REG. */ + if (REG_P (hard_reg)) + SET_DECL_RTL (DECL_RESULT (subr), gen_reg_rtx (GET_MODE (hard_reg))); + else if (GET_CODE (hard_reg) == PARALLEL) + SET_DECL_RTL (DECL_RESULT (subr), gen_group_rtx (hard_reg)); else - { - /* Create the pseudo. */ - SET_DECL_RTL (DECL_RESULT (subr), gen_reg_rtx (GET_MODE (hard_reg))); + abort (); - /* Needed because we may need to move this to memory - in case it's a named return value whose address is taken. */ - DECL_REGISTER (DECL_RESULT (subr)) = 1; - } + /* Set DECL_REGISTER flag so that expand_function_end will copy the + result to the real return register(s). */ + DECL_REGISTER (DECL_RESULT (subr)) = 1; } /* Initialize rtx for parameters and local variables. @@ -6998,8 +6997,16 @@ expand_function_end (filename, line, end_bindings) convert_move (real_decl_rtl, decl_rtl, unsignedp); } else if (GET_CODE (real_decl_rtl) == PARALLEL) - emit_group_load (real_decl_rtl, decl_rtl, - int_size_in_bytes (TREE_TYPE (decl_result))); + { + /* If expand_function_start has created a PARALLEL for decl_rtl, + move the result to the real return registers. Otherwise, do + a group load from decl_rtl for a named return. */ + if (GET_CODE (decl_rtl) == PARALLEL) + emit_group_move (real_decl_rtl, decl_rtl); + else + emit_group_load (real_decl_rtl, decl_rtl, + int_size_in_bytes (TREE_TYPE (decl_result))); + } else emit_move_insn (real_decl_rtl, decl_rtl); } diff --git a/gcc/rtl.h b/gcc/rtl.h index 1d71ec90ce4..5f727b08d9c 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -185,7 +185,7 @@ struct rtx_def GTY((chain_next ("RTX_NEXT (&%h)"), has used it as the function. */ unsigned int used : 1; /* Nonzero if this rtx came from procedure integration. - 1 in a REG means this reg refers to the return value + 1 in a REG or PARALLEL means this rtx refers to the return value of the current function. 1 in a SYMBOL_REF if the symbol is weak. */ unsigned integrated : 1; @@ -988,9 +988,10 @@ enum label_kind #define REGNO(RTX) XCUINT (RTX, 0, REG) #define ORIGINAL_REGNO(RTX) X0UINT (RTX, 1) -/* 1 if RTX is a reg that is the current function's return value. */ +/* 1 if RTX is a reg or parallel that is the current function's return + value. */ #define REG_FUNCTION_VALUE_P(RTX) \ - (RTL_FLAG_CHECK1("REG_FUNCTION_VALUE_P", (RTX), REG)->integrated) + (RTL_FLAG_CHECK2("REG_FUNCTION_VALUE_P", (RTX), REG, PARALLEL)->integrated) /* 1 if RTX is a reg that corresponds to a variable declared by the user. */ #define REG_USERVAR_P(RTX) \