For the 486, align to 16-byte boundary for sake of cache. */
#undef ASM_OUTPUT_ALIGN_CODE
-#define ASM_OUTPUT_ALIGN_CODE(FILE) \
- fprintf ((FILE), "\t.align %d,0x90\n", \
- TARGET_486 ? 4 : 2); /* Use log of 16 or log of 4 as arg. */
+#define ASM_OUTPUT_ALIGN_CODE(FILE) \
+ fprintf ((FILE), "\t.align %d,0x90\n", 1 << i386_align_jumps)
/* Align start of loop at 4-byte boundary. */
#undef ASM_OUTPUT_LOOP_ALIGN
#define ASM_OUTPUT_LOOP_ALIGN(FILE) \
- fprintf ((FILE), "\t.align 2,0x90\n"); /* Use log of 4 as arg. */
+ fprintf ((FILE), "\t.align %d,0x90\n", 1 << i386_align_loops)
+
\f
/* A C statement or statements which output an assembler instruction
opcode to the stdio stream STREAM. The macro-operand PTR is a
#include "i386/gas.h"
-/* 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.
-
- This only happens if the function declaration has the STDCALL attribute and
- the number of arguments is not variable */
-
-#undef RETURN_POPS_ARGS
-#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) \
- ( \
- TREE_CODE (FUNTYPE) == IDENTIFIER_NODE \
- ? \
- 0 \
- : \
- ( \
- ((FUNDECL && (TREE_CODE_CLASS (TREE_CODE (FUNDECL)) == 'd') \
- ? \
- lookup_attribute ("stdcall", \
- DECL_MACHINE_ATTRIBUTES (FUNDECL)) != NULL_TREE \
- : 0 \
- ) \
- ) \
- && \
- ( \
- TYPE_ARG_TYPES (FUNTYPE) == 0 \
- || \
- TREE_VALUE (tree_last (TYPE_ARG_TYPES (FUNTYPE))) \
- == void_type_node \
- ) \
- ) \
- ? \
- (SIZE) \
- : \
- (aggregate_value_p (TREE_TYPE (FUNTYPE))) \
- ? \
- GET_MODE_SIZE (Pmode) \
- : \
- 0 \
- )
-
-/* Value is 1 if the declaration has either of the attributes: CDECL or
- STDCALL and 0 otherwise */
-
-#define VALID_MACHINE_DECL_ATTRIBUTE(DECL, ATTR, NAME, ARGS) \
- (((TREE_CODE(DECL) == FUNCTION_DECL) \
- || (TREE_CODE(DECL) == FIELD_DECL) \
- || (TREE_CODE(DECL) == TYPE_DECL)) \
- && (is_attribute_p ("stdcall", (NAME)) \
- || is_attribute_p ("cdecl", (NAME))) \
- && (ARGS) == NULL)
-
#ifdef CPP_PREDEFINES
#undef CPP_PREDEFINES
#endif
#include <stdio.h>
#include <setjmp.h>
+#include <ctype.h>
#include "config.h"
#include "rtl.h"
#include "regs.h"
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 */
+
\f
/* Sometimes certain combinations of command options do not make
sense on a particular target machine. You can define a macro
override_options ()
{
int ch, i, regno;
+ char *p;
+ int def_align;
#ifdef SUBTARGET_OVERRIDE_OPTIONS
SUBTARGET_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 = def_align;
+
+ /* 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;
}
\f
/* A C statement (sans semicolon) to choose the order in which to
}
}
+\f
+/* 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;
+}
+
+\f
+/* 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;
+
+ if (fundecl && TREE_CODE_CLASS (TREE_CODE (fundecl)) == 'd')
+ {
+ /* 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;
+}
+
+\f
+/* 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;
+}
+
\f
/* 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
* -- 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
case 'P':
break;
+ case 'J':
+ switch (GET_CODE (x))
+ {
+ case NE: fputs ("jne", file); return;
+ case EQ: fputs ("je", file); return;
+ case GE: fputs ("jge", file); return;
+ case GT: fputs ("jg", file); return;
+ case LE: fputs ("jle", file); return;
+ case LT: fputs ("jl", file); return;
+ case GEU: fputs ("jae", file); return;
+ case GTU: fputs ("ja", file); return;
+ case LEU: fputs ("jbe", file); return;
+ case LTU: fputs ("jb", file); return;
+ }
+ abort ();
+
default:
{
char str[50];
/* Masks for the -m switches */
#define MASK_80387 000000000001 /* Hardware floating point */
#define MASK_486 000000000002 /* 80486 specific */
-#define MASK_NOTUSED 000000000004 /* bit not currently used */
+#define MASK_NOTUSED1 000000000004 /* bit not currently used */
#define MASK_RTD 000000000010 /* Use ret that pops args */
-#define MASK_REGPARM 000000000020 /* Pass args in eax, edx */
+#define MASK_ALIGN_DOUBLE 000000000020 /* align doubles to 2 word boundary */
#define MASK_SVR3_SHLIB 000000000040 /* Uninit locals into bss */
#define MASK_IEEE_FP 000000000100 /* IEEE fp comparisons */
#define MASK_FLOAT_RETURNS 000000000200 /* Return float in st(0) */
#define MASK_DEBUG_ADDR 000001000000 /* Debug GO_IF_LEGITIMATE_ADDRESS */
#define MASK_NO_WIDE_MULTIPLY 000002000000 /* Disable 32x32->64 multiplies */
#define MASK_NO_MOVE 000004000000 /* Don't generate mem->mem */
+#define MASK_DEBUG_ARG 000010000000 /* Debug function_arg */
/* Use the floating point instructions */
#define TARGET_80387 (target_flags & MASK_80387)
for all functions that can take varying numbers of args. */
#define TARGET_RTD (target_flags & MASK_RTD)
-/* Compile passing first two args in regs 0 and 1.
- This exists only to test compiler features that will
- be needed for RISC chips. It is not usable
- and is not intended to be usable on this cpu. */
-#define TARGET_REGPARM (target_flags & MASK_RTD)
+/* Align doubles to a two word boundary. This breaks compatibility with
+ the published ABI's for structures containing doubles, but produces
+ faster code on the pentium. */
+#define TARGET_ALIGN_DOUBLE (target_flags & MASK_ALIGN_DOUBLE)
/* Put uninitialized locals into bss, not data.
Meaningful only on svr3. */
/* Debug GO_IF_LEGITIMATE_ADDRESS */
#define TARGET_DEBUG_ADDR (target_flags & MASK_DEBUG_ADDR)
+/* Debug FUNCTION_ARG macros */
+#define TARGET_DEBUG_ARG (target_flags & MASK_DEBUG_ARG)
+
/* Hack macros for tuning code generation */
#define TARGET_MOVE ((target_flags & MASK_NO_MOVE) == 0) /* Don't generate memory->memory */
{ "no-486", -MASK_486 }, \
{ "rtd", MASK_RTD }, \
{ "no-rtd", -MASK_RTD }, \
- { "regparm", MASK_REGPARM }, \
- { "no-regparm", -MASK_REGPARM }, \
+ { "align-double", MASK_ALIGN_DOUBLE }, \
+ { "no-align-double", -MASK_ALIGN_DOUBLE }, \
{ "svr3-shlib", MASK_SVR3_SHLIB }, \
{ "no-svr3-shlib", -MASK_SVR3_SHLIB }, \
{ "ieee-fp", MASK_IEEE_FP }, \
{ "no-debug-addr", -MASK_DEBUG_ADDR }, \
{ "move", -MASK_NO_MOVE }, \
{ "no-move", MASK_NO_MOVE }, \
+ { "debug-arg", MASK_DEBUG_ARG }, \
+ { "no-debug-arg", -MASK_DEBUG_ARG }, \
SUBTARGET_SWITCHES \
{ "", TARGET_DEFAULT | TARGET_CPU_DEFAULT}}
option if the fixed part matches. The actual option name is made
by appending `-m' to the specified name. */
#define TARGET_OPTIONS \
-{ { "reg-alloc=", &i386_reg_alloc_order }, \
- SUBTARGET_OPTIONS }
+{ { "reg-alloc=", &i386_reg_alloc_order }, \
+ { "regparm=", &i386_regparm_string }, \
+ { "align-loops=", &i386_align_loops_string }, \
+ { "align-jumps=", &i386_align_jumps_string }, \
+ { "align-functions=", &i386_align_funcs_string }, \
+ SUBTARGET_OPTIONS \
+}
/* Sometimes certain combinations of command options do not make
sense on a particular target machine. You can define a macro
/* Allocation boundary (in *bits*) for the code of a function.
For i486, we get better performance by aligning to a cache
line (i.e. 16 byte) boundary. */
-#define FUNCTION_BOUNDARY (TARGET_486 ? 128 : 32)
+#define FUNCTION_BOUNDARY (1 << (i386_align_funcs + 3))
/* Alignment of field after `int : 0' in a structure. */
and all fundamental data types supported by the hardware
might need to be aligned. No data type wants to be aligned
rounder than this. The i386 supports 64-bit floating point
- quantities, but these can be aligned on any 32-bit boundary. */
-#define BIGGEST_ALIGNMENT 32
+ quantities, but these can be aligned on any 32-bit boundary.
+ The published ABIs say that doubles should be aligned on word
+ boundaries, but the Pentium gets better performance with them
+ aligned on 64 bit boundaries. */
+#define BIGGEST_ALIGNMENT (TARGET_ALIGN_DOUBLE ? 64 : 32)
/* Set this non-zero if move instructions will actually fail to work
when given unaligned data. */
/* Required on the 386 since it doesn't have bitfield insns. */
#define PCC_BITFIELD_TYPE_MATTERS 1
+/* Maximum power of 2 that code can be aligned to. */
+#define MAX_CODE_ALIGN 6 /* 64 byte alignment */
+
/* Align loop starts for optimal branching. */
-#define ASM_OUTPUT_LOOP_ALIGN(FILE) \
- ASM_OUTPUT_ALIGN (FILE, 2)
+#define ASM_OUTPUT_LOOP_ALIGN(FILE) ASM_OUTPUT_ALIGN (FILE, i386_align_loops)
/* This is how to align an instruction for optimal branching.
On i486 we'll get better performance by aligning on a
cache line (i.e. 16 byte) boundary. */
-#define ASM_OUTPUT_ALIGN_CODE(FILE) \
- ASM_OUTPUT_ALIGN ((FILE), (TARGET_486 ? 4 : 2))
+#define ASM_OUTPUT_ALIGN_CODE(FILE) ASM_OUTPUT_ALIGN ((FILE), i386_align_jumps)
+
\f
/* Standard register usage. */
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 caller must always pop the args.
+
+ The attribute stdcall is equivalent to RTD on a per module basis. */
-#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) \
- (TREE_CODE (FUNTYPE) == IDENTIFIER_NODE ? 0 \
- : (TARGET_RTD \
- && (TYPE_ARG_TYPES (FUNTYPE) == 0 \
- || (TREE_VALUE (tree_last (TYPE_ARG_TYPES (FUNTYPE))) \
- == void_type_node))) ? (SIZE) \
- : (aggregate_value_p (TREE_TYPE (FUNTYPE))) ? GET_MODE_SIZE (Pmode) : 0)
+#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) \
+ (i386_return_pops_args (FUNDECL, FUNTYPE, SIZE))
/* Define how to find the value returned by a function.
VALTYPE is the data type of the value (as a tree).
#define APPLY_RESULT_SIZE (8+108)
-/* 1 if N is a possible register number for function argument passing.
- On the 80386, no registers are used in this way.
- *NOTE* -mregparm does not work.
- It exists only to test register calling conventions. */
-
-#define FUNCTION_ARG_REGNO_P(N) 0
+/* 1 if N is a possible register number for function argument passing. */
+#define FUNCTION_ARG_REGNO_P(N) ((N) >= 0 && (N) < REGPARM_MAX)
/* Define a data type for recording info about an argument list
during the scan of that argument list. This data type should
hold all necessary information about the function itself
and about the args processed so far, enough to enable macros
- such as FUNCTION_ARG to determine where the next arg should go.
+ such as FUNCTION_ARG to determine where the next arg should go. */
- On the 80386, this is a single integer, which is a number of bytes
- of arguments scanned so far. */
-
-#define CUMULATIVE_ARGS int
+typedef struct i386_args {
+ int words; /* # words passed so far */
+ int nregs; /* # registers available for passing */
+ int regno; /* next available register number */
+} CUMULATIVE_ARGS;
/* 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.
-
- On the 80386, the offset starts at 0. */
+ For a library call, FNTYPE is 0. */
#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME) \
- ((CUM) = 0)
+ (init_cumulative_args (&CUM, FNTYPE, LIBNAME))
/* 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.) */
#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED) \
- ((CUM) += ((MODE) != BLKmode \
- ? (GET_MODE_SIZE (MODE) + 3) & ~3 \
- : (int_size_in_bytes (TYPE) + 3) & ~3))
+ (function_arg_advance (&CUM, MODE, TYPE, NAMED))
/* Define where to put the arguments to a function.
Value is zero to push the argument on the stack,
NAMED is nonzero if this argument is a named parameter
(otherwise it is an extra parameter matching an ellipsis). */
-
-/* On the 80386 all args are pushed, except if -mregparm is specified
- then the first two words of arguments are passed in EAX, EDX.
- *NOTE* -mregparm does not work.
- It exists only to test register calling conventions. */
-
#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
-((TARGET_REGPARM && (CUM) < 8) ? gen_rtx (REG, (MODE), (CUM) / 4) : 0)
+ (function_arg (&CUM, MODE, TYPE, NAMED))
/* 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. */
-
#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
-((TARGET_REGPARM && (CUM) < 8 \
- && 8 < ((CUM) + ((MODE) == BLKmode \
- ? int_size_in_bytes (TYPE) \
- : GET_MODE_SIZE (MODE)))) \
- ? 2 - (CUM) / 4 : 0)
+ (function_arg_partial_nregs (&CUM, MODE, TYPE, NAMED))
/* This macro generates the assembly code for function entry.
FILE is a stdio stream to output the code to.
} \
while (0)
+\f
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+ with arguments ARGS is a valid machine specific attribute for DECL.
+ The attributes in ATTRIBUTES have previously been assigned to DECL. */
+
+#define VALID_MACHINE_DECL_ATTRIBUTE(DECL, ATTRTRIBUTES, NAME, ARGS) \
+ (i386_valid_decl_attribute_p (DECL, ATTRTRIBUTES, NAME, ARGS))
+
+/* If defined, a C expression whose value is nonzero if IDENTIFIER
+ with arguments ARGS is a valid machine specific attribute for TYPE.
+ The attributes in ATTRIBUTES have previously been assigned to TYPE. */
+
+#define VALID_MACHINE_TYPE_ATTRIBUTE(TYPE, ATTRTRIBUTES, NAME, ARGS) \
+ (i386_valid_type_attribute_p (TYPE, ATTRTRIBUTES, NAME, ARGS))
+
+/* If defined, a C expression whose value is zero if the attributes on
+ TYPE1 and TYPE2 are incompatible, one if they are compatible, and
+ two if they are nearly compatible (which causes a warning to be
+ generated). */
+
+#define COMP_TYPE_ATTRIBUTES(TYPE1, TYPE2) \
+ (i386_comp_type_attributes (TYPE1, TYPE2))
+
+/* If defined, a C statement that assigns default attributes to newly
+ defined TYPE. */
+
+/* #define SET_DEFAULT_TYPE_ATTRIBUTES (TYPE) */
+
+/* Max number of args passed in registers. If this is more than 3, we will
+ have problems with ebx (register #4), since it is a caller save register and
+ is also used as the pic register in ELF. So for now, don't allow more than
+ 3 registers to be passed in registers. */
+
+#define REGPARM_MAX 3
+
\f
/* Specify the machine mode that this machine uses
for the index in the tablejump instruction. */
/* Define this if addresses of constant functions
shouldn't be put through pseudo regs where they can be cse'd.
Desirable on the 386 because a CALL with a constant address is
- not much slower than one with a register address. */
+ not much slower than one with a register address. On a 486,
+ it is faster to call with a constant address than indirect. */
#define NO_FUNCTION_CSE
/* Provide the costs of a rtl expression. This is in the body of a
/* Functions in i386.c */
extern void override_options ();
extern void order_regs_for_local_alloc ();
+extern int i386_valid_decl_attribute_p ();
+extern int i386_valid_type_attribute_p ();
+extern int i386_return_pops_args ();
+extern int i386_comp_type_attributes ();
+extern void init_cumulative_args ();
+extern void function_arg_advance ();
+extern struct rtx_def *function_arg ();
+extern int function_arg_partial_nregs ();
extern void output_op_from_reg ();
extern void output_to_reg ();
extern char *singlemove_string ();
/* Variables in i386.c */
extern char *i386_reg_alloc_order; /* register allocation order */
+extern char *i386_regparm_string; /* # registers to use to pass args */
+extern char *i386_align_loops_string; /* power of two alignment for loops */
+extern char *i386_align_jumps_string; /* power of two alignment for non-loop jumps */
+extern char *i386_align_funcs_string; /* power of two alignment for functions */
+extern int i386_regparm; /* i386_regparm_string as a number */
+extern int i386_align_loops; /* power of two alignment for loops */
+extern int i386_align_jumps; /* power of two alignment for non-loop jumps */
+extern int i386_align_funcs; /* power of two alignment for functions */
extern char *hi_reg_name[]; /* names for 16 bit regs */
extern char *qi_reg_name[]; /* names for 8 bit regs (low) */
extern char *qi_high_reg_name[]; /* names for 8 bit regs (high) */
;; 'B' Print the opcode suffix for an 8-bit integer opcode.
;; 'S' Print the opcode suffix for a 32-bit float opcode.
;; 'Q' Print the opcode suffix for a 64-bit float opcode.
+;; 'T' Print the opcode suffix for an 80-bit extended real XFmode float opcode.
+;; 'J' Print the appropriate jump operand.
;; 'b' Print the QImode name of the register for the indicated operand.
;; %b0 would print %al if operands[0] is reg 0.
;; 'k' Likewise, print the SImode name of the register.
;; 'h' Print the QImode name for a "high" register, either ah, bh, ch or dh.
;; 'y' Print "st(0)" instead of "st" as a register.
-;; 'T' Print the opcode suffix for an 80-bit extended real XFmode float opcode.
;; UNSPEC usage:
;; 0 This is a `scas' operation. The mode of the UNSPEC is always SImode.
return AS1 (jmp,%*%0);
}")
+(define_expand "decrement_and_branch_until_zero"
+ [(parallel [(set (pc)
+ (if_then_else (ge (plus:SI (match_operand:SI 0 "general_operand" "")
+ (const_int -1))
+ (const_int 0))
+ (label_ref (match_operand 1 "" ""))
+ (pc)))
+ (set (match_dup 0)
+ (plus:SI (match_dup 0)
+ (const_int -1)))])]
+ ""
+ "")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(plus:SI (match_operand:SI 1 "general_operand" "+r,m")
+ (match_operand:SI 2 "general_operand" "rmi,ri"))
+ (const_int 0)])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))
+ (set (match_dup 1)
+ (plus:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ if (operands[2] == constm1_rtx)
+ output_asm_insn (AS1 (dec%L1,%1), operands);
+
+ else if (operands[1] == const1_rtx)
+ output_asm_insn (AS1 (inc%L1,%1), operands);
+
+ else
+ output_asm_insn (AS2 (add%L1,%2,%1), operands);
+
+ return AS1 (%J0,%l3);
+}")
+
+(define_insn ""
+ [(set (pc)
+ (if_then_else (match_operator 0 "comparison_operator"
+ [(minus:SI (match_operand:SI 1 "general_operand" "+r,m")
+ (match_operand:SI 2 "general_operand" "rmi,ri"))
+ (const_int 0)])
+ (label_ref (match_operand 3 "" ""))
+ (pc)))
+ (set (match_dup 1)
+ (minus:SI (match_dup 1)
+ (match_dup 2)))]
+ ""
+ "*
+{
+ CC_STATUS_INIT;
+ if (operands[2] == const1_rtx)
+ output_asm_insn (AS1 (dec%L1,%1), operands);
+
+ else if (operands[1] == constm1_rtx)
+ output_asm_insn (AS1 (inc%L1,%1), operands);
+
+ else
+ output_asm_insn (AS2 (sub%L1,%2,%1), operands);
+
+ return AS1 (%J0,%l3);
+}")
+
;; Implement switch statements when generating PIC code. Switches are
;; implemented by `tablejump' when not using -fpic.
#undef ASM_OUTPUT_ALIGN_CODE
#define ASM_OUTPUT_ALIGN_CODE(STREAM) \
fprintf (STREAM, "\t.align\t%d\n", \
- (TARGET_486 && TARGET_LARGE_ALIGN) ? 4 : 2)
+ (!TARGET_LARGE_ALIGN && i386_align_jumps > 2) ? 2 : i386_align_jumps)
/* A C expression to output text to align the location counter in the
way that is desirable at the beginning of a loop.
#undef ASM_OUTPUT_LOOP_ALIGN
#define ASM_OUTPUT_LOOP_ALIGN(STREAM) \
- fprintf (STREAM, "\t.align\t2\n")
+ fprintf (STREAM, "\t.align\t%d\n", i386_align_loops)
/* A C statement to output to the stdio stream STREAM an assembler
command to advance the location counter to a multiple of 2 to the
#undef ASM_OUTPUT_ALIGN_CODE
#define ASM_OUTPUT_ALIGN_CODE(FILE) \
fprintf ((FILE), "\t.align %d,0x90\n", \
- TARGET_486 ? 16 : 4); /* Use log of 16 or log of 4 as arg. */
+ 1 << i386_align_jumps)
/* Align start of loop at 4-byte boundary. */
#undef ASM_OUTPUT_LOOP_ALIGN
#define ASM_OUTPUT_LOOP_ALIGN(FILE) \
- fprintf ((FILE), "\t.align 4,0x90\n");
+ fprintf ((FILE), "\t.align %d,0x90\n", 1 << i386_align_loops);
/* Additional overrides needed for dbx-in-coff gas, mostly taken from pbb.h */
#include "i386/gas.h"
-/* 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.
-
- This only happens if the function declaration has the STDCALL attribute and
- the number of arguments is not variable */
-
-#undef RETURN_POPS_ARGS
-#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) \
- ( \
- TREE_CODE (FUNTYPE) == IDENTIFIER_NODE \
- ? \
- 0 \
- : \
- ( \
- ((FUNDECL && (TREE_CODE_CLASS (TREE_CODE (FUNDECL)) == 'd') \
- ? \
- lookup_attribute ("stdcall", \
- DECL_MACHINE_ATTRIBUTES (FUNDECL)) != NULL_TREE \
- : 0 \
- ) \
- ) \
- && \
- ( \
- TYPE_ARG_TYPES (FUNTYPE) == 0 \
- || \
- TREE_VALUE (tree_last (TYPE_ARG_TYPES (FUNTYPE))) \
- == void_type_node \
- ) \
- ) \
- ? \
- (SIZE) \
- : \
- (aggregate_value_p (TREE_TYPE (FUNTYPE))) \
- ? \
- GET_MODE_SIZE (Pmode) \
- : \
- 0 \
- )
-
#ifdef CPP_PREDEFINES
#undef CPP_PREDEFINES
#endif
} \
if (TREE_CODE (DECL) == FUNCTION_DECL) \
if (lookup_attribute ("stdcall", \
- DECL_MACHINE_ATTRIBUTES (DECL))) \
+ TYPE_ATTRIBUTES (TREE_TYPE (DECL)))) \
XEXP (DECL_RTL (DECL), 0) = \
gen_rtx (SYMBOL_REF, Pmode, gen_stdcall_suffix (DECL)); \
} \
while (0)
#endif
-/* Value is 1 if the declaration has either of the attributes: CDECL or
- STDCALL and 0 otherwise */
-
-#define VALID_MACHINE_DECL_ATTRIBUTE(DECL, ATTR, NAME, ARGS) \
- (((TREE_CODE(DECL) == FUNCTION_DECL) \
- || (TREE_CODE(DECL) == FIELD_DECL) \
- || (TREE_CODE(DECL) == TYPE_DECL)) \
- && (is_attribute_p ("stdcall", (NAME)) \
- || is_attribute_p ("cdecl", (NAME))) \
- && (ARGS) == NULL)
-
/* The global __fltused is necessary to cause the printf/scanf routines
for outputting/inputting floating point numbers to be loaded. Since this
is kind of hard to detect, we just do it all the time. */