From 011c4be8179c2cfdcc8ec7c0cb2fc2d2e58490e2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 1 Jul 1998 05:10:51 +0000 Subject: * expr.c (emit_group_load, emit_group_store): Rewrite considering the size and alignment of the structure being manipulated. * expr.c, calls.c, function.c: Update all callers. * expr.h: Update prototypes. * cse.c (invalidate): Cope with parallels. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@20867 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 8 +++ gcc/calls.c | 13 +++- gcc/cse.c | 18 +++++ gcc/expr.c | 222 +++++++++++++++++++++++++++++++++++++++------------------ gcc/expr.h | 4 +- gcc/function.c | 11 ++- 6 files changed, 200 insertions(+), 76 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index f468786bc38..ff467bfd9ac 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,11 @@ +Wed Jul 1 05:04:41 1998 Richard Henderson + + * expr.c (emit_group_load, emit_group_store): Rewrite considering + the size and alignment of the structure being manipulated. + * expr.c, calls.c, function.c: Update all callers. + * expr.h: Update prototypes. + * cse.c (invalidate): Cope with parallels. + Wed Jul 1 04:22:23 1998 Richard Henderson * sparc.c (function_arg_record_value): Take a MODE arg with which to diff --git a/gcc/calls.c b/gcc/calls.c index 6271a611a8c..8b140190c7e 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1922,7 +1922,12 @@ expand_call (exp, target, ignore) locations. The Irix 6 ABI has examples of this. */ if (GET_CODE (reg) == PARALLEL) - emit_group_load (reg, args[i].value); + { + emit_group_load (reg, args[i].value, + int_size_in_bytes (TREE_TYPE (args[i].tree_value)), + (TYPE_ALIGN (TREE_TYPE (args[i].tree_value)) + / BITS_PER_UNIT)); + } /* If simple case, just do move. If normal partial, store_one_arg has already loaded the register for us. In all other cases, @@ -2089,15 +2094,17 @@ expand_call (exp, target, ignore) The Irix 6 ABI has examples of this. */ else if (GET_CODE (valreg) == PARALLEL) { + int bytes = int_size_in_bytes (TREE_TYPE (exp)); + if (target == 0) { - int bytes = int_size_in_bytes (TREE_TYPE (exp)); target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), bytes, 0); MEM_IN_STRUCT_P (target) = AGGREGATE_TYPE_P (TREE_TYPE (exp)); preserve_temp_slots (target); } - emit_group_store (target, valreg); + emit_group_store (target, valreg, bytes, + 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)) diff --git a/gcc/cse.c b/gcc/cse.c index bd2aa567d23..37055110575 100644 --- a/gcc/cse.c +++ b/gcc/cse.c @@ -1609,6 +1609,24 @@ invalidate (x, full_mode) return; } + /* If X is a parallel, invalidate all of its elements. */ + + if (GET_CODE (x) == PARALLEL) + { + for (i = XVECLEN (x, 0) - 1; i >= 0 ; --i) + invalidate (XVECEXP (x, 0, i), VOIDmode); + return; + } + + /* If X is an expr_list, this is part of a disjoint return value; + extract the location in question ignoring the offset. */ + + if (GET_CODE (x) == EXPR_LIST) + { + invalidate (XEXP (x, 0), VOIDmode); + return; + } + /* X is not a register; it must be a memory reference with a nonvarying address. Remove all hash table elements that refer to overlapping pieces of memory. */ diff --git a/gcc/expr.c b/gcc/expr.c index e01f08a69c8..a7abc5ab832 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -1822,103 +1822,187 @@ move_block_from_reg (regno, x, nregs, size) } } -/* Emit code to move a block Y to a block X, where X is non-consecutive - registers represented by a PARALLEL. */ +/* 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. ALIGN is the known alignment of + SRC in bits. */ +/* ??? If SSIZE % UNITS_PER_WORD != 0, we make the blatent assumption that + the balance will be in what would be the low-order memory addresses, i.e. + left justified for big endian, right justified for little endian. This + happens to be true for the targets currently using this support. If this + ever changes, a new target macro along the lines of FUNCTION_ARG_PADDING + would be needed. */ void -emit_group_load (x, y) - rtx x, y; +emit_group_load (dst, orig_src, ssize, align) + rtx dst, orig_src; + int align, ssize; { - rtx target_reg, source; - int i; + rtx *tmps, src; + int start, i; - if (GET_CODE (x) != PARALLEL) + if (GET_CODE (dst) != PARALLEL) abort (); /* Check for a NULL entry, used to indicate that the parameter goes both on the stack and in registers. */ - if (XEXP (XVECEXP (x, 0, 0), 0)) - i = 0; + if (XEXP (XVECEXP (dst, 0, 0), 0)) + start = 0; else - i = 1; - - for (; i < XVECLEN (x, 0); i++) - { - rtx element = XVECEXP (x, 0, i); - - target_reg = XEXP (element, 0); - - if (GET_CODE (y) == MEM) - source = change_address (y, GET_MODE (target_reg), - plus_constant (XEXP (y, 0), - INTVAL (XEXP (element, 1)))); - else if (XEXP (element, 1) == const0_rtx) - { - if (GET_MODE (target_reg) == GET_MODE (y)) - source = y; - /* Allow for the target_reg to be smaller than the input register - to allow for AIX with 4 DF arguments after a single SI arg. The - last DF argument will only load 1 word into the integer registers, - but load a DF value into the float registers. */ - else if ((GET_MODE_SIZE (GET_MODE (target_reg)) - <= GET_MODE_SIZE (GET_MODE (y))) - && GET_MODE (target_reg) == word_mode) - /* This might be a const_double, so we can't just use SUBREG. */ - source = operand_subword (y, 0, 0, VOIDmode); - else if (GET_MODE_SIZE (GET_MODE (target_reg)) - == GET_MODE_SIZE (GET_MODE (y))) - source = gen_lowpart (GET_MODE (target_reg), y); - else - abort (); + start = 1; + + tmps = (rtx *) alloca (sizeof(rtx) * XVECLEN (dst, 0)); + + /* If we won't be loading directly from memory, protect the real source + from strange tricks we might play. */ + src = orig_src; + if (GET_CODE (src) != MEM) + { + src = gen_reg_rtx (GET_MODE (orig_src)); + emit_move_insn (src, orig_src); + } + + /* Process the pieces. */ + for (i = start; i < XVECLEN (dst, 0); i++) + { + enum machine_mode mode = GET_MODE (XEXP (XVECEXP (dst, 0, i), 0)); + int bytepos = INTVAL (XEXP (XVECEXP (dst, 0, i), 1)); + int bytelen = GET_MODE_SIZE (mode); + int shift = 0; + + /* Handle trailing fragments that run over the size of the struct. */ + if (ssize >= 0 && bytepos + bytelen > ssize) + { + shift = (bytelen - (ssize - bytepos)) * BITS_PER_UNIT; + bytelen = ssize - bytepos; + if (bytelen <= 0) + abort(); + } + + /* Optimize the access just a bit. */ + if (GET_CODE (src) == MEM + && align*BITS_PER_UNIT >= GET_MODE_ALIGNMENT (mode) + && bytepos*BITS_PER_UNIT % GET_MODE_ALIGNMENT (mode) == 0 + && bytelen == GET_MODE_SIZE (mode)) + { + tmps[i] = gen_reg_rtx (mode); + emit_move_insn (tmps[i], + change_address (src, mode, + plus_constant (XEXP (src, 0), + bytepos))); } else - abort (); + { + tmps[i] = extract_bit_field (src, bytelen*BITS_PER_UNIT, + bytepos*BITS_PER_UNIT, 1, NULL_RTX, + mode, mode, align, ssize); + } - emit_move_insn (target_reg, source); + if (BYTES_BIG_ENDIAN && shift) + { + expand_binop (mode, ashl_optab, tmps[i], GEN_INT (shift), + tmps[i], 0, OPTAB_WIDEN); + } } + emit_queue(); + + /* Copy the extracted pieces into the proper (probable) hard regs. */ + for (i = start; i < XVECLEN (dst, 0); i++) + emit_move_insn (XEXP (XVECEXP (dst, 0, i), 0), tmps[i]); } -/* Emit code to move a block Y to a block X, where Y is non-consecutive - registers represented by a PARALLEL. */ +/* 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. ALIGN is the known alignment of DST. */ void -emit_group_store (x, y) - rtx x, y; +emit_group_store (orig_dst, src, ssize, align) + rtx orig_dst, src; + int ssize, align; { - rtx source_reg, target; - int i; + rtx *tmps, dst; + int start, i; - if (GET_CODE (y) != PARALLEL) + if (GET_CODE (src) != PARALLEL) abort (); /* Check for a NULL entry, used to indicate that the parameter goes both on the stack and in registers. */ - if (XEXP (XVECEXP (y, 0, 0), 0)) - i = 0; + if (XEXP (XVECEXP (src, 0, 0), 0)) + start = 0; else - i = 1; + start = 1; - for (; i < XVECLEN (y, 0); i++) + tmps = (rtx *) alloca (sizeof(rtx) * XVECLEN (src, 0)); + + /* Copy the (probable) hard regs into pseudos. */ + for (i = start; i < XVECLEN (src, 0); i++) { - rtx element = XVECEXP (y, 0, i); + rtx reg = XEXP (XVECEXP (src, 0, i), 0); + tmps[i] = gen_reg_rtx (GET_MODE (reg)); + emit_move_insn (tmps[i], reg); + } + emit_queue(); - source_reg = XEXP (element, 0); + /* If we won't be storing directly into memory, protect the real destination + from strange tricks we might play. */ + dst = orig_dst; + if (GET_CODE (dst) != MEM) + { + dst = gen_reg_rtx (GET_MODE (orig_dst)); + /* Make life a bit easier for combine. */ + emit_move_insn (dst, const0_rtx); + } + else if (! MEM_IN_STRUCT_P (dst)) + { + /* store_bit_field requires that memory operations have + mem_in_struct_p set; we might not. */ - if (GET_CODE (x) == MEM) - target = change_address (x, GET_MODE (source_reg), - plus_constant (XEXP (x, 0), - INTVAL (XEXP (element, 1)))); - else if (XEXP (element, 1) == const0_rtx) + dst = copy_rtx (orig_dst); + MEM_IN_STRUCT_P (dst) = 1; + } + + /* Process the pieces. */ + for (i = start; i < XVECLEN (src, 0); i++) + { + int bytepos = INTVAL (XEXP (XVECEXP (src, 0, i), 1)); + enum machine_mode mode = GET_MODE (tmps[i]); + int bytelen = GET_MODE_SIZE (mode); + + /* Handle trailing fragments that run over the size of the struct. */ + if (ssize >= 0 && bytepos + bytelen > ssize) { - target = x; - if (GET_MODE (target) != GET_MODE (source_reg)) - target = gen_lowpart (GET_MODE (source_reg), target); + if (BYTES_BIG_ENDIAN) + { + int shift = (bytelen - (ssize - bytepos)) * BITS_PER_UNIT; + expand_binop (mode, ashr_optab, tmps[i], GEN_INT (shift), + tmps[i], 0, OPTAB_WIDEN); + } + bytelen = ssize - bytepos; } - else - abort (); - emit_move_insn (target, source_reg); + /* Optimize the access just a bit. */ + if (GET_CODE (dst) == MEM + && align*BITS_PER_UNIT >= GET_MODE_ALIGNMENT (mode) + && bytepos*BITS_PER_UNIT % GET_MODE_ALIGNMENT (mode) == 0 + && bytelen == GET_MODE_SIZE (mode)) + { + emit_move_insn (change_address (dst, mode, + plus_constant (XEXP (dst, 0), + bytepos)), + tmps[i]); + } + else + { + store_bit_field (dst, bytelen*BITS_PER_UNIT, bytepos*BITS_PER_UNIT, + mode, tmps[i], align, ssize); + } } + emit_queue(); + + /* Copy from the pseudo into the (probable) hard reg. */ + if (GET_CODE (dst) == REG) + emit_move_insn (orig_dst, dst); } /* Add a USE expression for REG to the (possibly empty) list pointed @@ -2862,7 +2946,7 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra, /* Handle calls that pass values in multiple non-contiguous locations. The Irix 6 ABI has examples of this. */ if (GET_CODE (reg) == PARALLEL) - emit_group_load (reg, x); + emit_group_load (reg, x, -1, align); /* ??? size? */ else move_block_to_reg (REGNO (reg), x, partial, mode); } @@ -3071,7 +3155,8 @@ expand_assignment (to, from, want_value, suggest_reg) /* Handle calls that return values in multiple non-contiguous locations. The Irix 6 ABI has examples of this. */ if (GET_CODE (to_rtx) == PARALLEL) - emit_group_load (to_rtx, value); + emit_group_load (to_rtx, value, int_size_in_bytes (TREE_TYPE (from)), + TYPE_ALIGN (TREE_TYPE (from)) / BITS_PER_UNIT); else if (GET_MODE (to_rtx) == BLKmode) emit_block_move (to_rtx, value, expr_size (from), TYPE_ALIGN (TREE_TYPE (from)) / BITS_PER_UNIT); @@ -3476,7 +3561,8 @@ store_expr (exp, target, want_value) /* Handle calls that return values in multiple non-contiguous locations. The Irix 6 ABI has examples of this. */ else if (GET_CODE (target) == PARALLEL) - emit_group_load (target, temp); + emit_group_load (target, temp, int_size_in_bytes (TREE_TYPE (exp)), + TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); else if (GET_MODE (temp) == BLKmode) emit_block_move (target, temp, expr_size (exp), TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT); diff --git a/gcc/expr.h b/gcc/expr.h index f75828f212a..11f4673efa1 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -714,10 +714,10 @@ extern void move_block_from_reg PROTO((int, rtx, int, int)); /* Load a BLKmode value into non-consecutive registers represented by a PARALLEL. */ -extern void emit_group_load PROTO((rtx, rtx)); +extern void emit_group_load PROTO((rtx, rtx, int, int)); /* Store a BLKmode value from non-consecutive registers represented by a PARALLEL. */ -extern void emit_group_store PROTO((rtx, rtx)); +extern void emit_group_store PROTO((rtx, rtx, int, int)); /* Mark REG as holding a parameter for the next CALL_INSN. */ extern void use_reg PROTO((rtx *, rtx)); diff --git a/gcc/function.c b/gcc/function.c index 9150ebc2bac..67bbc698cfe 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -3945,8 +3945,10 @@ assign_parms (fndecl, second_time) /* Handle calls that pass values in multiple non-contiguous locations. The Irix 6 ABI has examples of this. */ if (GET_CODE (entry_parm) == PARALLEL) - emit_group_store (validize_mem (stack_parm), - entry_parm); + emit_group_store (validize_mem (stack_parm), entry_parm, + int_size_in_bytes (TREE_TYPE (parm)), + (TYPE_ALIGN (TREE_TYPE (parm)) + / BITS_PER_UNIT)); else move_block_from_reg (REGNO (entry_parm), validize_mem (stack_parm), nregs, @@ -4116,7 +4118,10 @@ assign_parms (fndecl, second_time) /* Handle calls that pass values in multiple non-contiguous locations. The Irix 6 ABI has examples of this. */ if (GET_CODE (entry_parm) == PARALLEL) - emit_group_store (validize_mem (stack_parm), entry_parm); + emit_group_store (validize_mem (stack_parm), entry_parm, + int_size_in_bytes (TREE_TYPE (parm)), + (TYPE_ALIGN (TREE_TYPE (parm)) + / BITS_PER_UNIT)); else move_block_from_reg (REGNO (entry_parm), validize_mem (stack_parm), -- cgit v1.2.3