--- gcc/config/i386/i386.c 2018/04/24 18:16:53 1.1.1.2 +++ 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 80386. - Copyright (C) 1988, 1992 Free Software Foundation, Inc. +/* Subroutines for insn-output.c for Intel X86. + 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" @@ -74,8 +76,506 @@ enum reg_class regclass_map[FIRST_PSEUDO /* Test and compare insns in i386.md store the information needed to generate branch and scc insns here. */ -struct rtx_def *i386_compare_op0, *i386_compare_op1; +struct rtx_def *i386_compare_op0 = NULL_RTX; +struct rtx_def *i386_compare_op1 = NULL_RTX; struct rtx_def *(*i386_compare_gen)(), *(*i386_compare_gen_eq)(); + +/* Register allocation order */ +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 + `OVERRIDE_OPTIONS' to take account of this. This macro, if + defined, is executed once just after all the command options have + been parsed. + + Don't use this macro to turn on various extra optimizations for + `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */ + +void +override_options () +{ + int ch, i, regno; + char *p; + int def_align; + +#ifdef SUBTARGET_OVERRIDE_OPTIONS + SUBTARGET_OVERRIDE_OPTIONS; +#endif + + /* Validate registers in register allocation order */ + if (i386_reg_alloc_order) + { + for (i = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++) + { + switch (ch) + { + case 'a': regno = 0; break; + case 'd': regno = 1; break; + case 'c': regno = 2; break; + case 'b': regno = 3; break; + case 'S': regno = 4; break; + case 'D': regno = 5; break; + case 'B': regno = 6; break; + + default: fatal ("Register '%c' is unknown", ch); + } + + if (regs_allocated[regno]) + fatal ("Register '%c' was already specified in the allocation order", ch); + + 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 + allocate hard registers for pseudo-registers local to a basic + block. + + Store the desired register order in the array `reg_alloc_order'. + Element 0 should be the register to allocate first; element 1, the + next register; and so on. + + The macro body should not assume anything about the contents of + `reg_alloc_order' before execution of the macro. + + On most machines, it is not necessary to define this macro. */ + +void +order_regs_for_local_alloc () +{ + int i, ch, order, regno; + + /* User specified the register allocation order */ + if (i386_reg_alloc_order) + { + for (i = order = 0; (ch = i386_reg_alloc_order[i]) != '\0'; i++) + { + switch (ch) + { + case 'a': regno = 0; break; + case 'd': regno = 1; break; + case 'c': regno = 2; break; + case 'b': regno = 3; break; + case 'S': regno = 4; break; + case 'D': regno = 5; break; + case 'B': regno = 6; break; + } + + reg_alloc_order[order++] = regno; + } + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + { + if (!regs_allocated[i]) + reg_alloc_order[order++] = i; + } + } + + /* If users did not specify a register allocation order, favor eax + normally except if DImode variables are used, in which case + favor edx before eax, which seems to cause less spill register + not found messages. */ + else + { + rtx insn; + + for (i = 0; i < FIRST_PSEUDO_REGISTER; i++) + reg_alloc_order[i] = i; + + if (optimize) + { + int use_dca = FALSE; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == INSN) + { + rtx set = NULL_RTX; + rtx pattern = PATTERN (insn); + + if (GET_CODE (pattern) == SET) + set = pattern; + + else if ((GET_CODE (pattern) == PARALLEL + || GET_CODE (pattern) == SEQUENCE) + && GET_CODE (XVECEXP (pattern, 0, 0)) == SET) + set = XVECEXP (pattern, 0, 0); + + if (set && GET_MODE (SET_SRC (set)) == DImode) + { + use_dca = TRUE; + break; + } + } + } + + if (use_dca) + { + reg_alloc_order[0] = 1; /* edx */ + reg_alloc_order[1] = 2; /* ecx */ + reg_alloc_order[2] = 0; /* eax */ + } + } + } +} + + +/* 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 @@ -235,6 +735,7 @@ find_addr_reg (addr) abort (); } + /* Output an insn to add the constant N to the register X. */ static void @@ -243,19 +744,25 @@ asm_add (n, x) rtx x; { rtx xops[2]; - xops[1] = x; - if (n < 0) + xops[0] = x; + + if (n == -1) + output_asm_insn (AS1 (dec%L0,%0), xops); + else if (n == 1) + output_asm_insn (AS1 (inc%L0,%0), xops); + else if (n < 0) { - xops[0] = GEN_INT (-n); - output_asm_insn (AS2 (sub%L0,%0,%1), xops); + xops[1] = GEN_INT (-n); + output_asm_insn (AS2 (sub%L0,%1,%0), xops); } else if (n > 0) { - xops[0] = GEN_INT (n); - output_asm_insn (AS2 (add%L0,%0,%1), xops); + xops[1] = GEN_INT (n); + output_asm_insn (AS2 (add%L0,%1,%0), xops); } } + /* Output assembler code to perform a doubleword move insn with operands OPERANDS. */ @@ -269,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; @@ -425,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]; } @@ -587,6 +1081,199 @@ compadr: return ""; } + + +#define MAX_TMPS 2 /* max temporary registers used */ + +/* Output the appropriate code to move push memory on the stack */ + +char * +output_move_pushmem (operands, insn, length, tmp_start, n_operands) + rtx operands[]; + rtx insn; + int length; + int tmp_start; + int n_operands; +{ + + struct { + char *load; + char *push; + rtx xops[2]; + } tmp_info[MAX_TMPS]; + + rtx src = operands[1]; + int max_tmps = 0; + int offset = 0; + int stack_p = reg_overlap_mentioned_p (stack_pointer_rtx, src); + int stack_offset = 0; + int i, num_tmps; + rtx xops[1]; + + if (!offsettable_memref_p (src)) + fatal_insn ("Source is not offsettable", insn); + + if ((length & 3) != 0) + fatal_insn ("Pushing non-word aligned size", insn); + + /* Figure out which temporary registers we have available */ + for (i = tmp_start; i < n_operands; i++) + { + if (GET_CODE (operands[i]) == REG) + { + if (reg_overlap_mentioned_p (operands[i], src)) + continue; + + tmp_info[ max_tmps++ ].xops[1] = operands[i]; + if (max_tmps == MAX_TMPS) + break; + } + } + + if (max_tmps == 0) + for (offset = length - 4; offset >= 0; offset -= 4) + { + xops[0] = adj_offsettable_operand (src, offset + stack_offset); + output_asm_insn (AS1(push%L0,%0), xops); + if (stack_p) + stack_offset += 4; + } + + else + for (offset = length - 4; offset >= 0; ) + { + for (num_tmps = 0; num_tmps < max_tmps && offset >= 0; num_tmps++) + { + tmp_info[num_tmps].load = AS2(mov%L0,%0,%1); + tmp_info[num_tmps].push = AS1(push%L0,%1); + tmp_info[num_tmps].xops[0] = adj_offsettable_operand (src, offset + stack_offset); + offset -= 4; + } + + for (i = 0; i < num_tmps; i++) + output_asm_insn (tmp_info[i].load, tmp_info[i].xops); + + for (i = 0; i < num_tmps; i++) + output_asm_insn (tmp_info[i].push, tmp_info[i].xops); + + if (stack_p) + stack_offset += 4*num_tmps; + } + + return ""; +} + + + +/* Output the appropriate code to move data between two memory locations */ + +char * +output_move_memory (operands, insn, length, tmp_start, n_operands) + rtx operands[]; + rtx insn; + int length; + int tmp_start; + int n_operands; +{ + struct { + char *load; + char *store; + rtx xops[3]; + } tmp_info[MAX_TMPS]; + + rtx dest = operands[0]; + rtx src = operands[1]; + rtx qi_tmp = NULL_RTX; + int max_tmps = 0; + int offset = 0; + int i, num_tmps; + rtx xops[3]; + + if (GET_CODE (dest) == MEM + && GET_CODE (XEXP (dest, 0)) == PRE_INC + && XEXP (XEXP (dest, 0), 0) == stack_pointer_rtx) + return output_move_pushmem (operands, insn, length, tmp_start, n_operands); + + if (!offsettable_memref_p (src)) + fatal_insn ("Source is not offsettable", insn); + + if (!offsettable_memref_p (dest)) + fatal_insn ("Destination is not offsettable", insn); + + /* Figure out which temporary registers we have available */ + for (i = tmp_start; i < n_operands; i++) + { + if (GET_CODE (operands[i]) == REG) + { + if ((length & 1) != 0 && !qi_tmp && QI_REG_P (operands[i])) + qi_tmp = operands[i]; + + if (reg_overlap_mentioned_p (operands[i], dest)) + fatal_insn ("Temporary register overlaps the destination", insn); + + if (reg_overlap_mentioned_p (operands[i], src)) + fatal_insn ("Temporary register overlaps the source", insn); + + tmp_info[ max_tmps++ ].xops[2] = operands[i]; + if (max_tmps == MAX_TMPS) + break; + } + } + + if (max_tmps == 0) + fatal_insn ("No scratch registers were found to do memory->memory moves", insn); + + if ((length & 1) != 0) + { + if (!qi_tmp) + fatal_insn ("No byte register found when moving odd # of bytes.", insn); + } + + while (length > 1) + { + for (num_tmps = 0; num_tmps < max_tmps; num_tmps++) + { + if (length >= 4) + { + tmp_info[num_tmps].load = AS2(mov%L0,%1,%2); + tmp_info[num_tmps].store = AS2(mov%L0,%2,%0); + tmp_info[num_tmps].xops[0] = adj_offsettable_operand (dest, offset); + tmp_info[num_tmps].xops[1] = adj_offsettable_operand (src, offset); + offset += 4; + length -= 4; + } + else if (length >= 2) + { + tmp_info[num_tmps].load = AS2(mov%W0,%1,%2); + tmp_info[num_tmps].store = AS2(mov%W0,%2,%0); + tmp_info[num_tmps].xops[0] = adj_offsettable_operand (dest, offset); + tmp_info[num_tmps].xops[1] = adj_offsettable_operand (src, offset); + offset += 2; + length -= 2; + } + else + break; + } + + for (i = 0; i < num_tmps; i++) + output_asm_insn (tmp_info[i].load, tmp_info[i].xops); + + for (i = 0; i < num_tmps; i++) + output_asm_insn (tmp_info[i].store, tmp_info[i].xops); + } + + if (length == 1) + { + xops[0] = adj_offsettable_operand (dest, offset); + xops[1] = adj_offsettable_operand (src, offset); + xops[2] = qi_tmp; + output_asm_insn (AS2(mov%B0,%1,%2), xops); + output_asm_insn (AS2(mov%B0,%2,%0), xops); + } + + return ""; +} + int standard_80387_constant_p (x) @@ -710,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 */ @@ -741,111 +1447,6 @@ symbolic_reference_mentioned_p (op) return 0; } -/* Return a legitimate reference for ORIG (an address) using the - register REG. If REG is 0, a new pseudo is generated. - - There are three types of references that must be handled: - - 1. Global data references must load the address from the GOT, via - the PIC reg. An insn is emitted to do this load, and the reg is - returned. - - 2. Static data references must compute the address as an offset - from the GOT, whose base is in the PIC reg. An insn is emitted to - compute the address into a reg, and the reg is returned. Static - data objects have SYMBOL_REF_FLAG set to differentiate them from - global data objects. - - 3. Constant pool addresses must be handled special. They are - considered legitimate addresses, but only if not used with regs. - When printed, the output routines know to print the reference with the - PIC reg, even though the PIC reg doesn't appear in the RTL. - - GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC - reg also appears in the address (except for constant pool references, - noted above). - - "switch" statements also require special handling when generating - PIC code. See comments by the `casesi' insn in i386.md for details. */ - -rtx -legitimize_pic_address (orig, reg) - rtx orig; - rtx reg; -{ - rtx addr = orig; - rtx new = orig; - - if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) - { - if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr)) - reg = new = orig; - else - { - if (reg == 0) - reg = gen_reg_rtx (Pmode); - - if (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr)) - new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig); - else - new = gen_rtx (MEM, Pmode, - gen_rtx (PLUS, Pmode, - pic_offset_table_rtx, orig)); - - emit_move_insn (reg, new); - } - current_function_uses_pic_offset_table = 1; - return reg; - } - else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS) - { - rtx base; - - if (GET_CODE (addr) == CONST) - { - addr = XEXP (addr, 0); - if (GET_CODE (addr) != PLUS) - abort (); - } - - if (XEXP (addr, 0) == pic_offset_table_rtx) - return orig; - - if (reg == 0) - reg = gen_reg_rtx (Pmode); - - base = legitimize_pic_address (XEXP (addr, 0), reg); - addr = legitimize_pic_address (XEXP (addr, 1), - base == reg ? NULL_RTX : reg); - - if (GET_CODE (addr) == CONST_INT) - return plus_constant (base, INTVAL (addr)); - - if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1))) - { - base = gen_rtx (PLUS, Pmode, base, XEXP (addr, 0)); - addr = XEXP (addr, 1); - } - return gen_rtx (PLUS, Pmode, base, addr); - } - return new; -} - -/* Emit insns to move operands[1] into operands[0]. */ - -void -emit_pic_move (operands, mode) - rtx *operands; - enum machine_mode mode; -{ - rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode); - - if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1])) - operands[1] = (rtx) force_reg (SImode, operands[1]); - else - operands[1] = legitimize_pic_address (operands[1], temp); -} - /* This function generates the assembly code for function entry. FILE is an stdio stream to output the code to. SIZE is an int: how many units of temporary storage to allocate. */ @@ -939,6 +1540,7 @@ simple_386_epilogue () return nregs == 0 || ! frame_pointer_needed; } + /* This function generates the assembly code for function exit. FILE is an stdio stream to output the code to. SIZE is an int: how many units of temporary storage to deallocate. */ @@ -1012,7 +1614,7 @@ function_epilogue (file, size) { /* On i486, mov & pop is faster than "leave". */ - if (TARGET_486) + if (!TARGET_386) { xops[0] = frame_pointer_rtx; output_asm_insn (AS2 (mov%L2,%0,%2), xops); @@ -1051,6 +1653,527 @@ function_epilogue (file, size) else output_asm_insn ("ret", xops); } + + +/* GO_IF_LEGITIMATE_ADDRESS recognizes an RTL expression + that is a valid memory address for an instruction. + The MODE argument is the machine mode for the MEM expression + that wants to use this address. + + On x86, legitimate addresses are: + base movl (base),reg + displacement movl disp,reg + base + displacement movl disp(base),reg + index + base movl (base,index),reg + (index + base) + displacement movl disp(base,index),reg + index*scale movl (,index,scale),reg + index*scale + disp movl disp(,index,scale),reg + index*scale + base movl (base,index,scale),reg + (index*scale + base) + disp movl disp(base,index,scale),reg + + In each case, scale can be 1, 2, 4, 8. */ + +/* This is exactly the same as print_operand_addr, except that + it recognizes addresses instead of printing them. + + It only recognizes address in canonical form. LEGITIMIZE_ADDRESS should + convert common non-canonical forms to canonical form so that they will + be recognized. */ + +#define ADDR_INVALID(msg,insn) \ +do { \ + if (TARGET_DEBUG_ADDR) \ + { \ + fprintf (stderr, msg); \ + debug_rtx (insn); \ + } \ +} while (0) + +int +legitimate_address_p (mode, addr, strict) + enum machine_mode mode; + register rtx addr; + int strict; +{ + rtx base = NULL_RTX; + rtx indx = NULL_RTX; + rtx scale = NULL_RTX; + rtx disp = NULL_RTX; + + if (TARGET_DEBUG_ADDR) + { + fprintf (stderr, + "\n==========\nGO_IF_LEGITIMATE_ADDRESS, mode = %s, strict = %d\n", + GET_MODE_NAME (mode), strict); + + debug_rtx (addr); + } + + if (GET_CODE (addr) == REG || GET_CODE (addr) == SUBREG) + base = addr; /* base reg */ + + else if (GET_CODE (addr) == PLUS) + { + rtx op0 = XEXP (addr, 0); + rtx op1 = XEXP (addr, 1); + enum rtx_code code0 = GET_CODE (op0); + enum rtx_code code1 = GET_CODE (op1); + + if (code0 == REG || code0 == SUBREG) + { + if (code1 == REG || code1 == SUBREG) + { + indx = op0; /* index + base */ + base = op1; + } + + else + { + base = op0; /* base + displacement */ + disp = op1; + } + } + + else if (code0 == MULT) + { + indx = XEXP (op0, 0); + scale = XEXP (op0, 1); + + if (code1 == REG || code1 == SUBREG) + base = op1; /* index*scale + base */ + + else + disp = op1; /* index*scale + disp */ + } + + else if (code0 == PLUS && GET_CODE (XEXP (op0, 0)) == MULT) + { + indx = XEXP (XEXP (op0, 0), 0); /* index*scale + base + disp */ + scale = XEXP (XEXP (op0, 0), 1); + base = XEXP (op0, 1); + disp = op1; + } + + else if (code0 == PLUS) + { + indx = XEXP (op0, 0); /* index + base + disp */ + base = XEXP (op0, 1); + disp = op1; + } + + else + { + ADDR_INVALID ("PLUS subcode is not valid.\n", op0); + return FALSE; + } + } + + else if (GET_CODE (addr) == MULT) + { + indx = XEXP (addr, 0); /* index*scale */ + scale = XEXP (addr, 1); + } + + else + disp = addr; /* displacement */ + + /* Allow arg pointer and stack pointer as index if there is not scaling */ + if (base && indx && !scale + && (indx == arg_pointer_rtx || indx == stack_pointer_rtx)) + { + rtx tmp = base; + base = indx; + indx = tmp; + } + + /* Validate base register */ + /* Don't allow SUBREG's here, it can lead to spill failures when the base + is one word out of a two word structure, which is represented internally + as a DImode int. */ + if (base) + { + if (GET_CODE (base) != REG) + { + ADDR_INVALID ("Base is not a register.\n", base); + return FALSE; + } + + if ((strict && !REG_OK_FOR_BASE_STRICT_P (base)) + || (!strict && !REG_OK_FOR_BASE_NONSTRICT_P (base))) + { + ADDR_INVALID ("Base is not valid.\n", base); + return FALSE; + } + } + + /* Validate index register */ + /* Don't allow SUBREG's here, it can lead to spill failures when the index + is one word out of a two word structure, which is represented internally + as a DImode int. */ + if (indx) + { + if (GET_CODE (indx) != REG) + { + ADDR_INVALID ("Index is not a register.\n", indx); + return FALSE; + } + + if ((strict && !REG_OK_FOR_INDEX_STRICT_P (indx)) + || (!strict && !REG_OK_FOR_INDEX_NONSTRICT_P (indx))) + { + ADDR_INVALID ("Index is not valid.\n", indx); + return FALSE; + } + } + else if (scale) + abort (); /* scale w/o index invalid */ + + /* Validate scale factor */ + if (scale) + { + HOST_WIDE_INT value; + + if (GET_CODE (scale) != CONST_INT) + { + ADDR_INVALID ("Scale is not valid.\n", scale); + return FALSE; + } + + value = INTVAL (scale); + if (value != 1 && value != 2 && value != 4 && value != 8) + { + ADDR_INVALID ("Scale is not a good multiplier.\n", scale); + return FALSE; + } + } + + /* Validate displacement */ + if (disp) + { + if (!CONSTANT_ADDRESS_P (disp)) + { + ADDR_INVALID ("Displacement is not valid.\n", disp); + return FALSE; + } + + if (GET_CODE (disp) == CONST_DOUBLE) + { + ADDR_INVALID ("Displacement is a const_double.\n", disp); + return FALSE; + } + + if (flag_pic && SYMBOLIC_CONST (disp) && base != pic_offset_table_rtx + && (indx != pic_offset_table_rtx || scale != NULL_RTX)) + { + ADDR_INVALID ("Displacement is an invalid pic reference.\n", disp); + return FALSE; + } + + if (HALF_PIC_P () && HALF_PIC_ADDRESS_P (disp) + && (base != NULL_RTX || indx != NULL_RTX)) + { + ADDR_INVALID ("Displacement is an invalid half-pic reference.\n", disp); + return FALSE; + } + } + + if (TARGET_DEBUG_ADDR) + fprintf (stderr, "Address is valid.\n"); + + /* Everything looks valid, return true */ + return TRUE; +} + + +/* Return a legitimate reference for ORIG (an address) using the + register REG. If REG is 0, a new pseudo is generated. + + There are three types of references that must be handled: + + 1. Global data references must load the address from the GOT, via + the PIC reg. An insn is emitted to do this load, and the reg is + returned. + + 2. Static data references must compute the address as an offset + from the GOT, whose base is in the PIC reg. An insn is emitted to + compute the address into a reg, and the reg is returned. Static + data objects have SYMBOL_REF_FLAG set to differentiate them from + global data objects. + + 3. Constant pool addresses must be handled special. They are + considered legitimate addresses, but only if not used with regs. + When printed, the output routines know to print the reference with the + PIC reg, even though the PIC reg doesn't appear in the RTL. + + GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC + reg also appears in the address (except for constant pool references, + noted above). + + "switch" statements also require special handling when generating + PIC code. See comments by the `casesi' insn in i386.md for details. */ + +rtx +legitimize_pic_address (orig, reg) + rtx orig; + rtx reg; +{ + rtx addr = orig; + rtx new = orig; + + if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) + { + if (GET_CODE (addr) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (addr)) + reg = new = orig; + else + { + if (reg == 0) + reg = gen_reg_rtx (Pmode); + + if ((GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_FLAG (addr)) + || GET_CODE (addr) == LABEL_REF) + new = gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig); + else + new = gen_rtx (MEM, Pmode, + gen_rtx (PLUS, Pmode, + pic_offset_table_rtx, orig)); + + emit_move_insn (reg, new); + } + current_function_uses_pic_offset_table = 1; + return reg; + } + else if (GET_CODE (addr) == CONST || GET_CODE (addr) == PLUS) + { + rtx base; + + if (GET_CODE (addr) == CONST) + { + addr = XEXP (addr, 0); + if (GET_CODE (addr) != PLUS) + abort (); + } + + if (XEXP (addr, 0) == pic_offset_table_rtx) + return orig; + + if (reg == 0) + reg = gen_reg_rtx (Pmode); + + base = legitimize_pic_address (XEXP (addr, 0), reg); + addr = legitimize_pic_address (XEXP (addr, 1), + base == reg ? NULL_RTX : reg); + + if (GET_CODE (addr) == CONST_INT) + return plus_constant (base, INTVAL (addr)); + + if (GET_CODE (addr) == PLUS && CONSTANT_P (XEXP (addr, 1))) + { + base = gen_rtx (PLUS, Pmode, base, XEXP (addr, 0)); + addr = XEXP (addr, 1); + } + return gen_rtx (PLUS, Pmode, base, addr); + } + return new; +} + + +/* Emit insns to move operands[1] into operands[0]. */ + +void +emit_pic_move (operands, mode) + rtx *operands; + enum machine_mode mode; +{ + rtx temp = reload_in_progress ? operands[0] : gen_reg_rtx (Pmode); + + if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1])) + operands[1] = (rtx) force_reg (SImode, operands[1]); + else + operands[1] = legitimize_pic_address (operands[1], temp); +} + + +/* Try machine-dependent ways of modifying an illegitimate address + to be legitimate. If we find one, return the new, valid address. + This macro is used in only one place: `memory_address' in explow.c. + + OLDX is the address as it was before break_out_memory_refs was called. + In some cases it is useful to look at this to decide what needs to be done. + + MODE and WIN are passed so that this macro can use + GO_IF_LEGITIMATE_ADDRESS. + + It is always safe for this macro to do nothing. It exists to recognize + opportunities to optimize the output. + + For the 80386, we handle X+REG by loading X into a register R and + using R+REG. R will go in a general reg and indexing will be used. + However, if REG is a broken-out memory address or multiplication, + nothing needs to be done because REG can certainly go in a general reg. + + When -fpic is used, special handling is needed for symbolic references. + See comments by legitimize_pic_address in i386.c for details. */ + +rtx +legitimize_address (x, oldx, mode) + register rtx x; + register rtx oldx; + enum machine_mode mode; +{ + int changed = 0; + unsigned log; + + if (TARGET_DEBUG_ADDR) + { + fprintf (stderr, "\n==========\nLEGITIMIZE_ADDRESS, mode = %s\n", GET_MODE_NAME (mode)); + debug_rtx (x); + } + + if (flag_pic && SYMBOLIC_CONST (x)) + return legitimize_pic_address (x, 0); + + /* Canonicalize shifts by 0, 1, 2, 3 into multiply */ + if (GET_CODE (x) == ASHIFT + && GET_CODE (XEXP (x, 1)) == CONST_INT + && (log = (unsigned)exact_log2 (INTVAL (XEXP (x, 1)))) < 4) + { + changed = 1; + x = gen_rtx (MULT, Pmode, + force_reg (Pmode, XEXP (x, 0)), + GEN_INT (1 << log)); + } + + if (GET_CODE (x) == PLUS) + { + /* Canonicalize shifts by 0, 1, 2, 3 into multiply */ + if (GET_CODE (XEXP (x, 0)) == ASHIFT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && (log = (unsigned)exact_log2 (INTVAL (XEXP (XEXP (x, 0), 1)))) < 4) + { + changed = 1; + XEXP (x, 0) = gen_rtx (MULT, Pmode, + force_reg (Pmode, XEXP (XEXP (x, 0), 0)), + GEN_INT (1 << log)); + } + + if (GET_CODE (XEXP (x, 1)) == ASHIFT + && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT + && (log = (unsigned)exact_log2 (INTVAL (XEXP (XEXP (x, 1), 1)))) < 4) + { + changed = 1; + XEXP (x, 1) = gen_rtx (MULT, Pmode, + force_reg (Pmode, XEXP (XEXP (x, 1), 0)), + GEN_INT (1 << log)); + } + + /* Put multiply first if it isn't already */ + if (GET_CODE (XEXP (x, 1)) == MULT) + { + rtx tmp = XEXP (x, 0); + XEXP (x, 0) = XEXP (x, 1); + XEXP (x, 1) = tmp; + changed = 1; + } + + /* Canonicalize (plus (mult (reg) (const)) (plus (reg) (const))) + into (plus (plus (mult (reg) (const)) (reg)) (const)). This can be + created by virtual register instantiation, register elimination, and + similar optimizations. */ + if (GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (x, 1)) == PLUS) + { + changed = 1; + x = gen_rtx (PLUS, Pmode, + gen_rtx (PLUS, Pmode, XEXP (x, 0), XEXP (XEXP (x, 1), 0)), + XEXP (XEXP (x, 1), 1)); + } + + /* Canonicalize (plus (plus (mult (reg) (const)) (plus (reg) (const))) const) + into (plus (plus (mult (reg) (const)) (reg)) (const)). */ + else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == PLUS + && CONSTANT_P (XEXP (x, 1))) + { + rtx constant, other; + + if (GET_CODE (XEXP (x, 1)) == CONST_INT) + { + constant = XEXP (x, 1); + other = XEXP (XEXP (XEXP (x, 0), 1), 1); + } + else if (GET_CODE (XEXP (XEXP (XEXP (x, 0), 1), 1)) == CONST_INT) + { + constant = XEXP (XEXP (XEXP (x, 0), 1), 1); + other = XEXP (x, 1); + } + else + constant = 0; + + if (constant) + { + changed = 1; + x = gen_rtx (PLUS, Pmode, + gen_rtx (PLUS, Pmode, XEXP (XEXP (x, 0), 0), + XEXP (XEXP (XEXP (x, 0), 1), 0)), + plus_constant (other, INTVAL (constant))); + } + } + + if (changed && legitimate_address_p (mode, x, FALSE)) + return x; + + if (GET_CODE (XEXP (x, 0)) == MULT) + { + changed = 1; + XEXP (x, 0) = force_operand (XEXP (x, 0), 0); + } + + if (GET_CODE (XEXP (x, 1)) == MULT) + { + changed = 1; + XEXP (x, 1) = force_operand (XEXP (x, 1), 0); + } + + if (changed + && GET_CODE (XEXP (x, 1)) == REG + && GET_CODE (XEXP (x, 0)) == REG) + return x; + + if (flag_pic && SYMBOLIC_CONST (XEXP (x, 1))) + { + changed = 1; + x = legitimize_pic_address (x, 0); + } + + if (changed && legitimate_address_p (mode, x, FALSE)) + return x; + + if (GET_CODE (XEXP (x, 0)) == REG) + { + register rtx temp = gen_reg_rtx (Pmode); + register rtx val = force_operand (XEXP (x, 1), temp); + if (val != temp) + emit_move_insn (temp, val); + + XEXP (x, 1) = temp; + return x; + } + + else if (GET_CODE (XEXP (x, 1)) == REG) + { + register rtx temp = gen_reg_rtx (Pmode); + register rtx val = force_operand (XEXP (x, 0), temp); + if (val != temp) + emit_move_insn (temp, val); + + XEXP (x, 0) = temp; + return x; + } + } + + return x; +} + /* Print an integer constant expression in assembler syntax. Addition and subtraction are the only arithmetic that may appear in these @@ -1089,7 +2212,9 @@ output_pic_addr_const (file, x, code) fprintf (file, "@GOTOFF(%%ebx)"); else if (code == 'P') fprintf (file, "@PLT"); - else if (GET_CODE (x) == LABEL_REF || ! SYMBOL_REF_FLAG (x)) + else if (GET_CODE (x) == LABEL_REF) + fprintf (file, "@GOTOFF"); + else if (! SYMBOL_REF_FLAG (x)) fprintf (file, "@GOT"); else fprintf (file, "@GOTOFF"); @@ -1164,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 @@ -1260,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]; @@ -1411,15 +2557,14 @@ print_operand_address (file, addr) if (addr != 0) { - if (GET_CODE (addr) == LABEL_REF) + if (flag_pic) + output_pic_addr_const (file, addr, 0); + + else if (GET_CODE (addr) == LABEL_REF) output_asm_label (addr); + else - { - if (flag_pic) - output_pic_addr_const (file, addr, 0); - else - output_addr_const (file, addr); - } + output_addr_const (file, addr); } if (ireg != 0 && GET_CODE (ireg) == MULT) @@ -1480,7 +2625,7 @@ print_operand_address (file, addr) /* Set the cc_status for the results of an insn whose pattern is EXP. On the 80386, we assume that only test and compare insns, as well - as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, ASHIFT, LSHIFT, + as SI, HI, & DI mode ADD, SUB, NEG, AND, IOR, XOR, ASHIFT, ASHIFTRT, and LSHIFTRT instructions set the condition codes usefully. Also, we assume that jumps, moves and sCOND don't affect the condition codes. All else clobbers the condition codes, by assumption. @@ -1548,7 +2693,7 @@ notice_update_cc (exp) switch (GET_CODE (SET_SRC (exp))) { case ASHIFTRT: case LSHIFTRT: - case ASHIFT: case LSHIFT: + case ASHIFT: /* Shifts on the 386 don't set the condition codes if the shift count is zero. */ if (GET_CODE (XEXP (SET_SRC (exp), 1)) != CONST_INT) @@ -1653,32 +2798,7 @@ binary_387_op (op, mode) } } -/* Return 1 if this is a valid conversion operation on a 387. - OP is the expression matched, and MODE is its mode. */ - -int -convert_387_op (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (mode != VOIDmode && mode != GET_MODE (op)) - return 0; - - switch (GET_CODE (op)) - { - case FLOAT: - return GET_MODE (XEXP (op, 0)) == SImode; - - case FLOAT_EXTEND: - return ((mode == DFmode && GET_MODE (XEXP (op, 0)) == SFmode) - || (mode == XFmode && GET_MODE (XEXP (op, 0)) == DFmode) - || (mode == XFmode && GET_MODE (XEXP (op, 0)) == SFmode)); - - default: - return 0; - } -} - + /* Return 1 if this is a valid shift or rotate operation on a 386. OP is the expression matched, and MODE is its mode. */ @@ -1800,9 +2920,9 @@ output_387_binary_op (insn, operands) return strcat (buf, AS2 (p,%2,%0)); if (STACK_TOP_P (operands[0])) - return strcat (buf, AS2 (,%y2,%0)); + return strcat (buf, AS2C (%y2,%0)); else - return strcat (buf, AS2 (,%2,%0)); + return strcat (buf, AS2C (%2,%0)); case MINUS: case DIV: @@ -1835,12 +2955,12 @@ output_387_binary_op (insn, operands) if (STACK_TOP_P (operands[0])) { if (STACK_TOP_P (operands[1])) - return strcat (buf, AS2 (,%y2,%0)); + return strcat (buf, AS2C (%y2,%0)); else return strcat (buf, AS2 (r,%y1,%0)); } else if (STACK_TOP_P (operands[1])) - return strcat (buf, AS2 (,%1,%0)); + return strcat (buf, AS2C (%1,%0)); else return strcat (buf, AS2 (r,%2,%0)); @@ -2070,7 +3190,7 @@ save_386_machine_status (p) struct function *p; { p->machine = (struct machine_function *) xmalloc (sizeof i386_stack_locals); - bcopy (i386_stack_locals, p->machine->i386_stack_locals, + bcopy ((char *) i386_stack_locals, (char *) p->machine->i386_stack_locals, sizeof i386_stack_locals); } @@ -2078,7 +3198,7 @@ void restore_386_machine_status (p) struct function *p; { - bcopy (p->machine->i386_stack_locals, i386_stack_locals, + bcopy ((char *) p->machine->i386_stack_locals, (char *) i386_stack_locals, sizeof i386_stack_locals); free (p->machine); }