--- gcc/config/i386/i386.c 2018/04/24 18:23:04 1.1.1.3 +++ gcc/config/i386/i386.c 2018/04/24 18:30:49 1.1.1.4 @@ -1,5 +1,5 @@ /* Subroutines for insn-output.c for Intel X86. - Copyright (C) 1988, 1992, 1994 Free Software Foundation, Inc. + Copyright (C) 1988, 1992, 1994, 1995 Free Software Foundation, Inc. This file is part of GNU CC. @@ -15,10 +15,12 @@ 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 #include +#include #include "config.h" #include "rtl.h" #include "regs.h" @@ -79,9 +81,22 @@ struct rtx_def *i386_compare_op1 = NULL_ struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)(); /* Register allocation order */ -char *i386_reg_alloc_order = (char *)0; +char *i386_reg_alloc_order; static char regs_allocated[FIRST_PSEUDO_REGISTER]; +/* # of registers to use to pass arguments. */ +char *i386_regparm_string; /* # registers to use to pass args */ +int i386_regparm; /* i386_regparm_string as a number */ + +/* Alignment to use for loops and jumps */ +char *i386_align_loops_string; /* power of two alignment for loops */ +char *i386_align_jumps_string; /* power of two alignment for non-loop jumps */ +char *i386_align_funcs_string; /* power of two alignment for functions */ + +int i386_align_loops; /* power of two alignment for loops */ +int i386_align_jumps; /* power of two alignment for non-loop jumps */ +int i386_align_funcs; /* power of two alignment for functions */ + /* Sometimes certain combinations of command options do not make sense on a particular target machine. You can define a macro @@ -96,6 +111,8 @@ void override_options () { int ch, i, regno; + char *p; + int def_align; #ifdef SUBTARGET_OVERRIDE_OPTIONS SUBTARGET_OVERRIDE_OPTIONS; @@ -125,6 +142,49 @@ override_options () regs_allocated[regno] = 1; } } + + /* Validate -mregparm= value */ + if (i386_regparm_string) + { + i386_regparm = atoi (i386_regparm_string); + if (i386_regparm < 0 || i386_regparm > REGPARM_MAX) + fatal ("-mregparm=%d is not between 0 and %d", i386_regparm, REGPARM_MAX); + } + + def_align = (TARGET_386) ? 2 : 4; + + /* Validate -malign-loops= value, or provide default */ + if (i386_align_loops_string) + { + i386_align_loops = atoi (i386_align_loops_string); + if (i386_align_loops < 0 || i386_align_loops > MAX_CODE_ALIGN) + fatal ("-malign-loops=%d is not between 0 and %d", + i386_align_loops, MAX_CODE_ALIGN); + } + else + i386_align_loops = 2; + + /* Validate -malign-jumps= value, or provide default */ + if (i386_align_jumps_string) + { + i386_align_jumps = atoi (i386_align_jumps_string); + if (i386_align_jumps < 0 || i386_align_jumps > MAX_CODE_ALIGN) + fatal ("-malign-jumps=%d is not between 0 and %d", + i386_align_jumps, MAX_CODE_ALIGN); + } + else + i386_align_jumps = def_align; + + /* Validate -malign-functions= value, or provide default */ + if (i386_align_funcs_string) + { + i386_align_funcs = atoi (i386_align_funcs_string); + if (i386_align_funcs < 0 || i386_align_funcs > MAX_CODE_ALIGN) + fatal ("-malign-functions=%d is not between 0 and %d", + i386_align_funcs, MAX_CODE_ALIGN); + } + else + i386_align_funcs = def_align; } /* A C statement (sans semicolon) to choose the order in which to @@ -220,6 +280,303 @@ order_regs_for_local_alloc () } +/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine specific + attribute for DECL. The attributes in ATTRIBUTES have previously been + assigned to DECL. */ + +int +i386_valid_decl_attribute_p (decl, attributes, identifier, args) + tree decl; + tree attributes; + tree identifier; + tree args; +{ + return 0; +} + +/* Return nonzero if IDENTIFIER with arguments ARGS is a valid machine specific + attribute for TYPE. The attributes in ATTRIBUTES have previously been + assigned to TYPE. */ + +int +i386_valid_type_attribute_p (type, attributes, identifier, args) + tree type; + tree attributes; + tree identifier; + tree args; +{ + if (TREE_CODE (type) != FUNCTION_TYPE + && TREE_CODE (type) != FIELD_DECL + && TREE_CODE (type) != TYPE_DECL) + return 0; + + /* Stdcall attribute says callee is responsible for popping arguments + if they are not variable. */ + if (is_attribute_p ("stdcall", identifier)) + return (args == NULL_TREE); + + /* Cdecl attribute says the callee is a normal C declaration */ + if (is_attribute_p ("cdecl", identifier)) + return (args == NULL_TREE); + + /* Regparm attribute specifies how many integer arguments are to be + passed in registers */ + if (is_attribute_p ("regparm", identifier)) + { + tree cst; + + if (!args || TREE_CODE (args) != TREE_LIST + || TREE_CHAIN (args) != NULL_TREE + || TREE_VALUE (args) == NULL_TREE) + return 0; + + cst = TREE_VALUE (args); + if (TREE_CODE (cst) != INTEGER_CST) + return 0; + + if (TREE_INT_CST_HIGH (cst) != 0 + || TREE_INT_CST_LOW (cst) < 0 + || TREE_INT_CST_LOW (cst) > REGPARM_MAX) + return 0; + + return 1; + } + + return 0; +} + +/* Return 0 if the attributes for two types are incompatible, 1 if they + are compatible, and 2 if they are nearly compatible (which causes a + warning to be generated). */ + +int +i386_comp_type_attributes (type1, type2) + tree type1; + tree type2; +{ + return 1; +} + + +/* Value is the number of bytes of arguments automatically + popped when returning from a subroutine call. + FUNDECL is the declaration node of the function (as a tree), + FUNTYPE is the data type of the function (as a tree), + or for a library call it is an identifier node for the subroutine name. + SIZE is the number of bytes of arguments passed on the stack. + + On the 80386, the RTD insn may be used to pop them if the number + of args is fixed, but if the number is variable then the caller + must pop them all. RTD can't be used for library calls now + because the library is compiled with the Unix compiler. + Use of RTD is a selectable option, since it is incompatible with + standard Unix calling sequences. If the option is not selected, + the caller must always pop the args. + + The attribute stdcall is equivalent to RTD on a per module basis. */ + +int +i386_return_pops_args (fundecl, funtype, size) + tree fundecl; + tree funtype; + int size; +{ + int rtd = TARGET_RTD; + + if (TREE_CODE (funtype) == IDENTIFIER_NODE) + return 0; + + /* Cdecl functions override -mrtd, and never pop the stack */ + if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (funtype))) + return 0; + + /* Stdcall functions will pop the stack if not variable args */ + if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype))) + rtd = 1; + + if (rtd) + { + if (TYPE_ARG_TYPES (funtype) == NULL_TREE + || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (funtype))) == void_type_node)) + return size; + + if (aggregate_value_p (TREE_TYPE (funtype))) + return GET_MODE_SIZE (Pmode); + } + + return 0; +} + + +/* Argument support functions. */ + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. */ + +void +init_cumulative_args (cum, fntype, libname) + CUMULATIVE_ARGS *cum; /* argument info to initialize */ + tree fntype; /* tree ptr for function decl */ + rtx libname; /* SYMBOL_REF of library name or 0 */ +{ + static CUMULATIVE_ARGS zero_cum; + tree param, next_param; + + if (TARGET_DEBUG_ARG) + { + fprintf (stderr, "\ninit_cumulative_args ("); + if (fntype) + { + tree ret_type = TREE_TYPE (fntype); + fprintf (stderr, "fntype code = %s, ret code = %s", + tree_code_name[ (int)TREE_CODE (fntype) ], + tree_code_name[ (int)TREE_CODE (ret_type) ]); + } + else + fprintf (stderr, "no fntype"); + + if (libname) + fprintf (stderr, ", libname = %s", XSTR (libname, 0)); + } + + *cum = zero_cum; + + /* Set up the number of registers to use for passing arguments. */ + cum->nregs = i386_regparm; + if (fntype) + { + tree attr = lookup_attribute ("regparm", TYPE_ATTRIBUTES (fntype)); + if (attr) + cum->nregs = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (attr))); + } + + /* Determine if this function has variable arguments. This is + indicated by the last argument being 'void_type_mode' if there + are no variable arguments. If there are variable arguments, then + we won't pass anything in registers */ + + if (cum->nregs) + { + for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0; + param != (tree)0; + param = next_param) + { + next_param = TREE_CHAIN (param); + if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node) + cum->nregs = 0; + } + } + + if (TARGET_DEBUG_ARG) + fprintf (stderr, ", nregs=%d )\n", cum->nregs); + + return; +} + +/* Update the data in CUM to advance over an argument + of mode MODE and data type TYPE. + (TYPE is null for libcalls where that information may not be available.) */ + +void +function_arg_advance (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type; /* type of the argument or 0 if lib support */ + int named; /* whether or not the argument was named */ +{ + int bytes = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode); + int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + + if (TARGET_DEBUG_ARG) + fprintf (stderr, + "function_adv( size=%d, words=%2d, nregs=%d, mode=%4s, named=%d )\n\n", + words, cum->words, cum->nregs, GET_MODE_NAME (mode), named); + + cum->words += words; + cum->nregs -= words; + cum->regno += words; + + if (cum->nregs <= 0) + { + cum->nregs = 0; + cum->regno = 0; + } + + return; +} + +/* Define where to put the arguments to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). */ + +struct rtx_def * +function_arg (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type; /* type of the argument or 0 if lib support */ + int named; /* != 0 for normal args, == 0 for ... args */ +{ + rtx ret = NULL_RTX; + int bytes = (mode == BLKmode) ? int_size_in_bytes (type) : GET_MODE_SIZE (mode); + int words = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; + + switch (mode) + { + default: /* for now, pass fp/complex values on the stack */ + break; + + case BLKmode: + case DImode: + case SImode: + case HImode: + case QImode: + if (words <= cum->nregs) + ret = gen_rtx (REG, mode, cum->regno); + break; + } + + if (TARGET_DEBUG_ARG) + { + fprintf (stderr, + "function_arg( size=%d, words=%2d, nregs=%d, mode=%4s, named=%d", + words, cum->words, cum->nregs, GET_MODE_NAME (mode), named); + + if (ret) + fprintf (stderr, ", reg=%%e%s", reg_names[ REGNO(ret) ]); + else + fprintf (stderr, ", stack"); + + fprintf (stderr, " )\n"); + } + + return ret; +} + +/* For an arg passed partly in registers and partly in memory, + this is the number of registers used. + For args passed entirely in registers or entirely in memory, zero. */ + +int +function_arg_partial_nregs (cum, mode, type, named) + CUMULATIVE_ARGS *cum; /* current arg information */ + enum machine_mode mode; /* current arg mode */ + tree type; /* type of the argument or 0 if lib support */ + int named; /* != 0 for normal args, == 0 for ... args */ +{ + return 0; +} + + /* Output an insn whose source is a 386 integer register. SRC is the rtx for the register, and TEMPLATE is the op-code template. SRC may be either SImode or DImode. @@ -419,7 +776,7 @@ output_move_double (operands) rtx xops[2]; rtx addreg0 = 0, addreg1 = 0; int dest_overlapped_low = 0; - int size = GET_MODE_SIZE (GET_MODE (operands[1])); + int size = GET_MODE_SIZE (GET_MODE (operands[0])); middlehalf[0] = 0; middlehalf[1] = 0; @@ -575,20 +932,7 @@ output_move_double (operands) else if (optype1 == OFFSOP) latehalf[1] = adj_offsettable_operand (operands[1], 4); else if (optype1 == CNSTOP) - { - if (GET_CODE (operands[1]) == CONST_DOUBLE) - split_double (operands[1], &operands[1], &latehalf[1]); - else if (CONSTANT_P (operands[1])) - { - /* ??? jrv: Can this really happen? A DImode constant - that isn't a CONST_DOUBLE? */ - if (GET_CODE (operands[1]) == CONST_INT - && INTVAL (operands[1]) < 0) - latehalf[1] = constm1_rtx; - else - latehalf[1] = const0_rtx; - } - } + split_double (operands[1], &operands[1], &latehalf[1]); else latehalf[1] = operands[1]; } @@ -1053,6 +1397,25 @@ expander_call_insn_operand (op, mode) return 1; return 0; } + +/* Return 1 if OP is a comparison operator that can use the condition code + generated by an arithmetic operation. */ + +int +arithmetic_comparison_operator (op, mode) + register rtx op; + enum machine_mode mode; +{ + enum rtx_code code; + + if (mode != VOIDmode && mode != GET_MODE (op)) + return 0; + code = GET_CODE (op); + if (GET_RTX_CLASS (code) != '<') + return 0; + + return (code != GT && code != LE); +} /* Returns 1 if OP contains a symbol reference */ @@ -1463,7 +1826,7 @@ legitimate_address_p (mode, addr, strict } } else if (scale) - abort (); /* scale w/o index illegal */ + abort (); /* scale w/o index invalid */ /* Validate scale factor */ if (scale) @@ -1926,6 +2289,7 @@ output_pic_addr_const (file, x, code) * -- print a star (in certain assembler syntax) w -- print the operand as if it's a "word" (HImode) even if it isn't. c -- don't print special prefixes before constant operands. + J -- print the appropriate jump operand. */ void @@ -2022,6 +2386,26 @@ print_operand (file, x, code) case 'P': break; + case 'J': + switch (GET_CODE (x)) + { + /* These conditions are appropriate for testing the result + of an arithmetic operation, not for a compare operation. + Cases GE, LT assume CC_NO_OVERFLOW true. All cases assume + CC_Z_IN_NOT_C false and not floating point. */ + case NE: fputs ("jne", file); return; + case EQ: fputs ("je", file); return; + case GE: fputs ("jns", file); return; + case LT: fputs ("js", file); return; + case GEU: fputs ("jmp", file); return; + case GTU: fputs ("jne", file); return; + case LEU: fputs ("je", file); return; + case LTU: fputs ("#branch never", file); return; + + /* no matching branches for GT nor LE */ + } + abort (); + default: { char str[50];