--- gcc/config/i960/i960.c 2018/04/24 18:22:42 1.1.1.3 +++ gcc/config/i960/i960.c 2018/04/24 18:30:27 1.1.1.4 @@ -1,5 +1,5 @@ /* Subroutines used for code generation on intel 80960. - Copyright (C) 1992 Free Software Foundation, Inc. + Copyright (C) 1992, 1995 Free Software Foundation, Inc. Contributed by Steven McGeady, Intel Corp. Additional Work by Glenn Colon-Bonet, Jonathan Shapiro, Andy Wilson Converted to GCC 2.0 by Jim Wilson and Michael Tiemann, Cygnus Support. @@ -18,7 +18,8 @@ GNU General Public License for more deta You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ #include @@ -83,57 +84,86 @@ static int ret_label = 0; && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (TREE_TYPE (FNDECL)))) != void_type_node)) \ || current_function_varargs) -#if 0 /* Handle pragmas for compatibility with Intel's compilers. */ /* ??? This is incomplete, since it does not handle all pragmas that the - intel compilers understand. Also, it needs to be rewritten to accept - a stream instead of a string for GCC 2. */ + intel compilers understand. */ void -process_pragma(str) - char *str; +process_pragma (finput) + FILE *finput; { - int align; + int c; int i; - if ((i = sscanf (str, " align %d", &align)) == 1) - switch (align) - { - case 0: /* Return to last alignment. */ - align = i960_last_maxbitalignment / 8; + c = getc (finput); + while (c == ' ' || c == '\t') + c = getc (finput); + + if (c == 'a' + && getc (finput) == 'l' + && getc (finput) == 'i' + && getc (finput) == 'g' + && getc (finput) == 'n' + && ((c = getc (finput)) == ' ' || c == '\t' || c == '\n')) + { + char buf[20]; + char *s = buf; + int align; + + while (c == ' ' || c == '\t') + c = getc (finput); + if (c == '(') + c = getc (finput); + while (c >= '0' && c <= '9') + { + if (s < buf + sizeof buf - 1) + *s++ = c; + c = getc (finput); + } + *s = '\0'; - case 16: /* Byte alignments. */ - case 8: - case 4: - case 2: - case 1: - i960_last_maxbitalignment = i960_maxbitalignment; - i960_maxbitalignment = align * 8; - break; + align = atoi (buf); + switch (align) + { + case 0: + /* Return to last alignment. */ + align = i960_last_maxbitalignment / 8; + /* Fall through. */ + case 16: + case 8: + case 4: + case 2: + case 1: + i960_last_maxbitalignment = i960_maxbitalignment; + i960_maxbitalignment = align * 8; + break; - default: /* Unknown, silently ignore. */ - break; - } + default: + /* Silently ignore bad values. */ + break; + } - /* NOTE: ic960 R3.0 pragma align definition: + /* NOTE: ic960 R3.0 pragma align definition: - #pragma align [(size)] | (identifier=size[,...]) - #pragma noalign [(identifier)[,...]] + #pragma align [(size)] | (identifier=size[,...]) + #pragma noalign [(identifier)[,...]] - (all parens are optional) + (all parens are optional) - - size is [1,2,4,8,16] - - noalign means size==1 - - applies only to component elements of a struct (and union?) - - identifier applies to structure tag (only) - - missing identifier means next struct + - size is [1,2,4,8,16] + - noalign means size==1 + - applies only to component elements of a struct (and union?) + - identifier applies to structure tag (only) + - missing identifier means next struct - - alignment rules for bitfields need more investigation */ + - alignment rules for bitfields need more investigation */ + } /* Should be pragma 'far' or equivalent for callx/balx here. */ + + ungetc (c, finput); } -#endif /* Initialize variables before compiling any files. */ @@ -223,8 +253,7 @@ fp_literal_one (op, mode) rtx op; enum machine_mode mode; { - return (TARGET_NUMERICS && (mode == VOIDmode || mode == GET_MODE (op)) - && (op == CONST1_RTX (mode))); + return (TARGET_NUMERICS && mode == GET_MODE (op) && op == CONST1_RTX (mode)); } /* Return true if OP is a float constant of 0. */ @@ -234,8 +263,7 @@ fp_literal_zero (op, mode) rtx op; enum machine_mode mode; { - return (TARGET_NUMERICS && (mode == VOIDmode || mode == GET_MODE (op)) - && (op == CONST0_RTX (mode))); + return (TARGET_NUMERICS && mode == GET_MODE (op) && op == CONST0_RTX (mode)); } /* Return true if OP is a valid floating point literal. */ @@ -532,19 +560,174 @@ emit_move_sequence (operands, mode) rtx *operands; enum machine_mode mode; { - register rtx operand0 = operands[0]; - register rtx operand1 = operands[1]; - /* We can only store registers to memory. */ - if (GET_CODE (operand0) == MEM && GET_CODE (operand1) != REG) - operands[1] = force_reg (mode, operand1); + if (GET_CODE (operands[0]) == MEM && GET_CODE (operands[1]) != REG) + operands[1] = force_reg (mode, operands[1]); + + /* Storing multi-word values in unaligned hard registers to memory may + require a scratch since we have to store them a register at a time and + adding 4 to the memory address may not yield a valid insn. */ + /* ??? We don't always need the scratch, but that would complicate things. + Maybe later. */ + if (GET_MODE_SIZE (mode) > UNITS_PER_WORD + && GET_CODE (operands[0]) == MEM + && GET_CODE (operands[1]) == REG + && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER + && ! HARD_REGNO_MODE_OK (REGNO (operands[1]), mode)) + { + emit_insn (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (2, + gen_rtx (SET, VOIDmode, + operands[0], operands[1]), + gen_rtx (CLOBBER, VOIDmode, + gen_rtx (SCRATCH, Pmode))))); + return 1; + } return 0; } + +/* Output assembler to move a double word value. */ + +char * +i960_output_move_double (dst, src) + rtx dst, src; +{ + rtx operands[5]; + + if (GET_CODE (dst) == REG + && GET_CODE (src) == REG) + { + if ((REGNO (src) & 1) + || (REGNO (dst) & 1)) + { + /* We normally copy the low-numbered register first. However, if + the second source register is the same as the first destination + register, we must copy in the opposite order. */ + if (REGNO (src) + 1 == REGNO (dst)) + return "mov %D1,%D0\n\tmov %1,%0"; + else + return "mov %1,%0\n\tmov %D1,%D0"; + } + else + return "movl %1,%0"; + } + else if (GET_CODE (dst) == REG + && GET_CODE (src) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (src), 'I')) + { + if (REGNO (dst) & 1) + return "mov %1,%0\n\tmov 0,%D0"; + else + return "movl %1,%0"; + } + else if (GET_CODE (dst) == REG + && GET_CODE (src) == MEM) + { + if (REGNO (dst) & 1) + { + /* One can optimize a few cases here, but you have to be + careful of clobbering registers used in the address and + edge conditions. */ + operands[0] = dst; + operands[1] = src; + operands[2] = gen_rtx (REG, Pmode, REGNO (dst) + 1); + operands[3] = gen_rtx (MEM, word_mode, operands[2]); + operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD); + output_asm_insn ("lda %1,%2\n\tld %3,%0\n\tld %4,%D0", operands); + return ""; + } + else + return "ldl %1,%0"; + } + else if (GET_CODE (dst) == MEM + && GET_CODE (src) == REG) + { + if (REGNO (src) & 1) + { + /* This is handled by emit_move_sequence so we shouldn't get here. */ + abort (); + } + return "stl %1,%0"; + } + else + abort (); +} + +/* Output assembler to move a quad word value. */ + +char * +i960_output_move_quad (dst, src) + rtx dst, src; +{ + rtx operands[7]; + + if (GET_CODE (dst) == REG + && GET_CODE (src) == REG) + { + if ((REGNO (src) & 3) + || (REGNO (dst) & 3)) + { + /* We normally copy starting with the low numbered register. + However, if there is an overlap such that the first dest reg + is <= the last source reg but not < the first source reg, we + must copy in the opposite order. */ + if (REGNO (dst) <= REGNO (src) + 3 + && REGNO (dst) >= REGNO (src)) + return "mov %F1,%F0\n\tmov %E1,%E0\n\tmov %D1,%D0\n\tmov %1,%0"; + else + return "mov %1,%0\n\tmov %D1,%D0\n\tmov %E1,%E0\n\tmov %F1,%F0"; + } + else + return "movq %1,%0"; + } + else if (GET_CODE (dst) == REG + && GET_CODE (src) == CONST_INT + && CONST_OK_FOR_LETTER_P (INTVAL (src), 'I')) + { + if (REGNO (dst) & 3) + return "mov %1,%0\n\tmov 0,%D0\n\tmov 0,%E0\n\tmov 0,%F0"; + else + return "movq %1,%0"; + } + else if (GET_CODE (dst) == REG + && GET_CODE (src) == MEM) + { + if (REGNO (dst) & 3) + { + /* One can optimize a few cases here, but you have to be + careful of clobbering registers used in the address and + edge conditions. */ + operands[0] = dst; + operands[1] = src; + operands[2] = gen_rtx (REG, Pmode, REGNO (dst) + 3); + operands[3] = gen_rtx (MEM, word_mode, operands[2]); + operands[4] = adj_offsettable_operand (operands[3], UNITS_PER_WORD); + operands[5] = adj_offsettable_operand (operands[4], UNITS_PER_WORD); + operands[6] = adj_offsettable_operand (operands[5], UNITS_PER_WORD); + output_asm_insn ("lda %1,%2\n\tld %3,%0\n\tld %4,%D0\n\tld %5,%E0\n\tld %6,%F0", operands); + return ""; + } + else + return "ldq %1,%0"; + } + else if (GET_CODE (dst) == MEM + && GET_CODE (src) == REG) + { + if (REGNO (src) & 3) + { + /* This is handled by emit_move_sequence so we shouldn't get here. */ + abort (); + } + return "stq %1,%0"; + } + else + abort (); +} -/* Emit insns to load a constant. Uses several strategies to try to use - as few insns as possible. */ +/* Emit insns to load a constant to non-floating point registers. + Uses several strategies to try to use as few insns as possible. */ char * i960_output_ldconst (dst, src) @@ -554,7 +737,6 @@ i960_output_ldconst (dst, src) register unsigned rsrc2; enum machine_mode mode = GET_MODE (dst); rtx operands[4]; - union { long l[2]; double d; } x; operands[0] = operands[2] = dst; operands[1] = operands[3] = src; @@ -567,19 +749,37 @@ i960_output_ldconst (dst, src) output_asm_insn ("ldconst %1,%0", operands); return ""; } - else if (mode == DFmode) + else if (mode == XFmode) { - rtx first, second; + REAL_VALUE_TYPE d; + long value_long[3]; + int i; + + if (fp_literal_zero (src, XFmode)) + return "movt 0,%0"; - if (fp_literal_zero (src, VOIDmode)) + REAL_VALUE_FROM_CONST_DOUBLE (d, src); + REAL_VALUE_TO_TARGET_LONG_DOUBLE (d, value_long); + + output_asm_insn ("# ldconst %1,%0",operands); + + for (i = 0; i < 3; i++) { - if (FP_REG_P (dst)) - return "movrl %1,%0"; - else - return "movl 0,%0"; + operands[0] = gen_rtx (REG, SImode, REGNO (dst) + i); + operands[1] = GEN_INT (value_long[i]); + output_asm_insn (i960_output_ldconst (operands[0], operands[1]), + operands); } -#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT + return ""; + } + else if (mode == DFmode) + { + rtx first, second; + + if (fp_literal_zero (src, DFmode)) + return "movl 0,%0"; + split_double (src, &first, &second); output_asm_insn ("# ldconst %1,%0",operands); @@ -593,11 +793,21 @@ i960_output_ldconst (dst, src) output_asm_insn (i960_output_ldconst (operands[0], operands[1]), operands); return ""; -#else - if (fp_literal_one (src, VOIDmode)) - return "movrl 0f1.0,%0"; - fatal ("inline double constants not supported on this host"); -#endif + } + else if (mode == SFmode) + { + REAL_VALUE_TYPE d; + long value; + + REAL_VALUE_FROM_CONST_DOUBLE (d, src); + REAL_VALUE_TO_TARGET_SINGLE (d, value); + + output_asm_insn ("# ldconst %1,%0",operands); + operands[0] = gen_rtx (REG, SImode, REGNO (dst)); + operands[1] = gen_rtx (CONST_INT, VOIDmode, value); + output_asm_insn (i960_output_ldconst (operands[0], operands[1]), + operands); + return ""; } else if (mode == TImode) { @@ -615,18 +825,10 @@ i960_output_ldconst (dst, src) else if (mode == DImode) { rtx upperhalf, lowerhalf, xoperands[2]; - char *string; - if (GET_CODE (src) == CONST_DOUBLE) - { - upperhalf = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (src)); - lowerhalf = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (src)); - } - else if (GET_CODE (src) == CONST_INT) - { - lowerhalf = src; - upperhalf = INTVAL (src) < 0 ? constm1_rtx : const0_rtx; - } + if (GET_CODE (src) == CONST_DOUBLE || GET_CODE (src) == CONST_INT) + split_double (src, &lowerhalf, &upperhalf); + else abort (); @@ -643,29 +845,6 @@ i960_output_ldconst (dst, src) xoperands); /* The lower word is emitted as normally. */ } - else if (mode == SFmode) - { -#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - REAL_VALUE_TYPE d; - long value; - - REAL_VALUE_FROM_CONST_DOUBLE (d, src); - REAL_VALUE_TO_TARGET_SINGLE (d, value); - - output_asm_insn ("# ldconst %1,%0",operands); - operands[0] = gen_rtx (REG, SImode, REGNO (dst)); - operands[1] = gen_rtx (CONST_INT, VOIDmode, value); - output_asm_insn (i960_output_ldconst (operands[0], operands[1]), - operands); -#else - if (fp_literal_zero (src, VOIDmode)) - return "movr 0f0.0,%0"; - if (fp_literal_one (src, VOIDmode)) - return "movr 0f1.0,%0"; - fatal ("inline float constants not supported on this host"); -#endif - return ""; - } else { rsrc1 = INTVAL (src); @@ -828,7 +1007,7 @@ i960_function_name_declare (file, name, else leaf_proc_ok = 0; - /* Even if nobody uses extra parms, can't have leafroc or tail calls if + /* Even if nobody uses extra parms, can't have leafproc or tail calls if argblock, because argblock uses g14 implicitly. */ if (current_function_args_size != 0 || VARARGS_STDARG_FUNCTION (fndecl)) @@ -1287,10 +1466,10 @@ i960_function_epilogue (file, size) if (epilogue_string[0] != '\0') fprintf (file, "%s", epilogue_string); - /* Must clear g14 on return. */ + /* Must clear g14 on return if this function set it. + Only varargs/stdarg functions modify g14. */ - if (current_function_args_size != 0 - || VARARGS_STDARG_FUNCTION (current_function_decl)) + if (VARARGS_STDARG_FUNCTION (current_function_decl)) fprintf (file, "\tmov 0,g14\n"); fprintf (file, "\tret\n"); @@ -1338,8 +1517,13 @@ i960_output_call_insn (target, argsize_r output_asm_insn ("callx %0", operands); + /* If the caller sets g14 to the address of the argblock, then the caller + must clear it after the return. */ + if (current_function_args_size != 0 || varargs_stdarg_function) output_asm_insn ("mov r3,g14", operands); + else if (argsize > 48) + output_asm_insn ("mov 0,g14", operands); return ""; } @@ -1361,8 +1545,10 @@ i960_output_ret_insn (insn) return lbuf; } - if (current_function_args_size != 0 - || VARARGS_STDARG_FUNCTION (current_function_decl)) + /* Must clear g14 on return if this function set it. + Only varargs/stdarg functions modify g14. */ + + if (VARARGS_STDARG_FUNCTION (current_function_decl)) output_asm_insn ("mov 0,g14", 0); if (i960_leaf_ret_reg >= 0) @@ -1422,10 +1608,20 @@ i960_print_operand (file, x, code) switch (code) { case 'D': - /* Second reg of a double. */ + /* Second reg of a double or quad. */ fprintf (file, "%s", reg_names[REGNO (x)+1]); break; + case 'E': + /* Third reg of a quad. */ + fprintf (file, "%s", reg_names[REGNO (x)+2]); + break; + + case 'F': + /* Fourth reg of a quad. */ + fprintf (file, "%s", reg_names[REGNO (x)+3]); + break; + case 0: fprintf (file, "%s", reg_names[REGNO (x)]); break; @@ -1450,22 +1646,23 @@ i960_print_operand (file, x, code) } else if (rtxcode == CONST_DOUBLE) { - double d; + REAL_VALUE_TYPE d; + char dstr[30]; - if (x == CONST0_RTX (DFmode) || x == CONST0_RTX (SFmode)) + if (x == CONST0_RTX (GET_MODE (x))) { fprintf (file, "0f0.0"); return; } - else if (x == CONST1_RTX (DFmode) || x == CONST1_RTX (SFmode)) + else if (x == CONST1_RTX (GET_MODE (x))) { fprintf (file, "0f1.0"); return; } - /* This better be a comment. */ REAL_VALUE_FROM_CONST_DOUBLE (d, x); - fprintf (file, "%#g", d); + REAL_VALUE_TO_DECIMAL (d, "%#g", dstr); + fprintf (file, "0f%s", dstr); return; } @@ -1851,12 +2048,12 @@ i960_alignment (size, align) #define S_MODES \ (~C_MODES \ & ~ ((1 << (int) DImode) | (1 << (int) TImode) \ - | (1 << (int) DFmode) | (1 << (int) TFmode))) + | (1 << (int) DFmode) | (1 << (int) XFmode))) /* Modes for double-word (and smaller) quantities. */ #define D_MODES \ (~C_MODES \ - & ~ ((1 << (int) TImode) | (1 << (int) TFmode))) + & ~ ((1 << (int) TImode) | (1 << (int) XFmode))) /* Modes for quad-word quantities. */ #define T_MODES (~C_MODES) @@ -1868,7 +2065,7 @@ i960_alignment (size, align) #define DF_MODES (SF_MODES | (1 << (int) DFmode) | (1 << (int) SCmode)) /* Modes for quad-float quantities. */ -#define TF_MODES (DF_MODES | (1 << (int) TFmode) | (1 << (int) DCmode)) +#define XF_MODES (DF_MODES | (1 << (int) XFmode) | (1 << (int) DCmode)) unsigned int hard_regno_mode_ok[FIRST_PSEUDO_REGISTER] = { T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, @@ -1876,7 +2073,7 @@ unsigned int hard_regno_mode_ok[FIRST_PS T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, - TF_MODES, TF_MODES, TF_MODES, TF_MODES, C_MODES}; + XF_MODES, XF_MODES, XF_MODES, XF_MODES, C_MODES}; /* Return the minimum alignment of an expression rtx X in bytes. This takes @@ -2028,7 +2225,14 @@ i960_arg_size_and_align (mode, type, siz size = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; if (type == 0) - align = size; + { + /* ??? This is a hack to properly correct the alignment of XFmode + values without affecting anything else. */ + if (size == 3) + align = 4; + else + align = size; + } else if (TYPE_ALIGN (type) >= BITS_PER_WORD) align = TYPE_ALIGN (type) / BITS_PER_WORD; else @@ -2043,7 +2247,9 @@ i960_arg_size_and_align (mode, type, siz subsequent arguments are placed on the stack. Additionally, parameters with an alignment requirement stronger than - a word must be be aligned appropriately. */ + a word must be aligned appropriately. Note that this means that a + 64 bit object with a 32 bit alignment is not 64 bit aligned and may be + passed in an odd/even register pair. */ /* Update CUM to advance past an argument described by MODE and TYPE. */ @@ -2061,7 +2267,12 @@ i960_function_arg_advance (cum, mode, ty if (size > 4 || cum->ca_nstackparms != 0 || (size + ROUND_PARM (cum->ca_nregparms, align)) > NPARM_REGS || MUST_PASS_IN_STACK (mode, type)) - cum->ca_nstackparms = ROUND_PARM (cum->ca_nstackparms, align) + size; + { + /* Indicate that all the registers are in use, even if all are not, + so va_start will compute the right value. */ + cum->ca_nregparms = NPARM_REGS; + cum->ca_nstackparms = ROUND_PARM (cum->ca_nstackparms, align) + size; + } else cum->ca_nregparms = ROUND_PARM (cum->ca_nregparms, align) + size; } @@ -2100,28 +2311,49 @@ i960_function_arg (cum, mode, type, name /* Floating-point support. */ void -i960_output_double (file, value) +i960_output_long_double (file, value) FILE *file; - double value; + REAL_VALUE_TYPE value; { - if (REAL_VALUE_ISINF (value)) - { - fprintf (file, "\t.word 0\n"); - fprintf (file, "\t.word 0x7ff00000 # Infinity\n"); - } - else - fprintf (file, "\t.double 0d%.17e\n", (value)); + long value_long[3]; + char dstr[30]; + + REAL_VALUE_TO_TARGET_LONG_DOUBLE (value, value_long); + REAL_VALUE_TO_DECIMAL (value, "%.20g", dstr); + + fprintf (file, + "\t.word\t0x%08lx\t\t# %s\n\t.word\t0x%08lx\n\t.word\t0x%08lx\n", + value_long[0], dstr, value_long[1], value_long[2]); + fprintf (file, "\t.word\t0x0\n"); } void +i960_output_double (file, value) + FILE *file; + REAL_VALUE_TYPE value; +{ + long value_long[2]; + char dstr[30]; + + REAL_VALUE_TO_TARGET_DOUBLE (value, value_long); + REAL_VALUE_TO_DECIMAL (value, "%.20g", dstr); + + fprintf (file, "\t.word\t0x%08lx\t\t# %s\n\t.word\t0x%08lx\n", + value_long[0], dstr, value_long[1]); +} + +void i960_output_float (file, value) FILE *file; - double value; + REAL_VALUE_TYPE value; { - if (REAL_VALUE_ISINF (value)) - fprintf (file, "\t.word 0x7f800000 # Infinity\n"); - else - fprintf (file, "\t.float 0f%.12e\n", (value)); + long value_long; + char dstr[30]; + + REAL_VALUE_TO_TARGET_SINGLE (value, value_long); + REAL_VALUE_TO_DECIMAL (value, "%.12g", dstr); + + fprintf (file, "\t.word\t0x%08lx\t\t# %s (float)\n", value_long, dstr); } /* Return the number of bits that an object of size N bytes is aligned to. */ @@ -2139,45 +2371,29 @@ i960_object_bytes_bitalign (n) return n; } -/* Compute the size of an aggregate type TSIZE. */ - -tree -i960_round_size (tsize) - tree tsize; -{ - int size, byte_size, align; - - if (TREE_CODE (tsize) != INTEGER_CST) - return tsize; - - size = TREE_INT_CST_LOW (tsize); - byte_size = (size + BITS_PER_UNIT - 1) / BITS_PER_UNIT; - align = i960_object_bytes_bitalign (byte_size); - - /* Handle #pragma align. */ - if (align > i960_maxbitalignment) - align = i960_maxbitalignment; - - if (size % align) - size = ((size / align) + 1) * align; - - return size_int (size); -} - -/* Compute the alignment for an aggregate type TSIZE. */ +/* Compute the alignment for an aggregate type TSIZE. + Alignment is MAX (greatest member alignment, + MIN (pragma align, structure size alignment)). */ int i960_round_align (align, tsize) int align; tree tsize; { - int byte_size; + int new_align; if (TREE_CODE (tsize) != INTEGER_CST) return align; - byte_size = (TREE_INT_CST_LOW (tsize) + BITS_PER_UNIT - 1) / BITS_PER_UNIT; - align = i960_object_bytes_bitalign (byte_size); + new_align = i960_object_bytes_bitalign (TREE_INT_CST_LOW (tsize) + / BITS_PER_UNIT); + /* Handle #pragma align. */ + if (new_align > i960_maxbitalignment) + new_align = i960_maxbitalignment; + + if (align < new_align) + align = new_align; + return align; } @@ -2193,44 +2409,47 @@ i960_setup_incoming_varargs (cum, mode, int *pretend_size; int no_rtl; { - if (cum->ca_nregparms < NPARM_REGS) - { - int first_reg_offset = cum->ca_nregparms; + /* Note: for a varargs fn with only a va_alist argument, this is 0. */ + int first_reg = cum->ca_nregparms; - if (! (no_rtl)) - { - rtx label = gen_label_rtx (); - rtx regblock; - - /* If arg_pointer_rtx == 0, no arguments were passed on the stack - and we need to allocate a chunk to save the registers (if any - arguments were passed on the stack the caller would allocate the - 48 bytes as well). We must allocate all 48 bytes (12*4) because - arg_pointer_rtx is saved at the front, the anonymous args are - saved at the end. */ - emit_insn (gen_cmpsi (arg_pointer_rtx, const0_rtx)); - emit_jump_insn (gen_bne (label)); - emit_insn (gen_rtx (SET, VOIDmode, arg_pointer_rtx, - stack_pointer_rtx)); - emit_insn (gen_rtx (SET, VOIDmode, stack_pointer_rtx, - memory_address (SImode, - plus_constant (stack_pointer_rtx, - 48)))); - emit_label (label); - - /* Any anonymous args passed in regs? */ - if (first_reg_offset + 1 < NPARM_REGS) - { - rtx regblock; - regblock = gen_rtx (MEM, BLKmode, - plus_constant (arg_pointer_rtx, - (first_reg_offset + 1) * 4)); - move_block_from_reg (first_reg_offset + 1, regblock, - NPARM_REGS - first_reg_offset - 1, - ((NPARM_REGS - first_reg_offset - 1) - * UNITS_PER_WORD)); - } - } + /* Copy only unnamed register arguments to memory. If there are + any stack parms, there are no unnamed arguments in registers, and + an argument block was already allocated by the caller. + Remember that any arg bigger than 4 words is passed on the stack as + are all subsequent args. + + If there are no stack arguments but there are exactly NPARM_REGS + registers, either there were no extra arguments or the caller + allocated an argument block. */ + + if (cum->ca_nstackparms == 0 && first_reg < NPARM_REGS && !no_rtl) + { + rtx label = gen_label_rtx (); + rtx regblock; + + /* If arg_pointer_rtx == 0, no arguments were passed on the stack + and we need to allocate a chunk to save the registers (if any + arguments were passed on the stack the caller would allocate the + 48 bytes as well). We must allocate all 48 bytes (12*4) because + va_start assumes it. */ + emit_insn (gen_cmpsi (arg_pointer_rtx, const0_rtx)); + emit_jump_insn (gen_bne (label)); + emit_insn (gen_rtx (SET, VOIDmode, arg_pointer_rtx, + stack_pointer_rtx)); + emit_insn (gen_rtx (SET, VOIDmode, stack_pointer_rtx, + memory_address (SImode, + plus_constant (stack_pointer_rtx, + 48)))); + emit_label (label); + + /* ??? Note that we unnecessarily store one extra register for stdarg + fns. We could optimize this, but it's kept as for now. */ + regblock = gen_rtx (MEM, BLKmode, + plus_constant (arg_pointer_rtx, + first_reg * 4)); + move_block_from_reg (first_reg, regblock, + NPARM_REGS - first_reg, + (NPARM_REGS - first_reg) * UNITS_PER_WORD); } }